Add detection dataloader for yolo example.
- Set target directory, then this dataloader loads dataset
from "images" and "annotations" folders.
- Currently, It only supports "bmp" image format.
- For variable label data, it adds zero padding to label.
Signed-off-by: Seungbaek Hong <sb92.hong@samsung.com>
$(NNTRAINER_ROOT)/api \
$(NNTRAINER_ROOT)/api/ccapi/include \
${ML_API_COMMON_INCLUDES}
+
LOCAL_MODULE := nntrainer
LOCAL_SRC_FILES := $(NNTRAINER_ROOT)/libs/$(TARGET_ARCH_ABI)/libnntrainer.so
include $(CLEAR_VARS)
-CIFARDIR = ../../utils/datagen/cifar
-
LOCAL_ARM_NEON := true
LOCAL_CFLAGS += -std=c++17 -Ofast -mcpu=cortex-a53 -Ilz4-nougat/lib
LOCAL_LDFLAGS += -Llz4-nougat/lib/obj/local/$(TARGET_ARCH_ABI)/
LOCAL_LDFLAGS += -fexceptions
LOCAL_MODULE_TAGS := optional
LOCAL_ARM_MODE := arm
-LOCAL_MODULE := nntrainer_resnet
+LOCAL_MODULE := nntrainer_yolo
LOCAL_LDLIBS := -llog -landroid -fopenmp
-LOCAL_SRC_FILES := main.cpp $(CIFARDIR)/cifar_dataloader.cpp
-
+LOCAL_SRC_FILES := main.cpp det_dataloader.cpp
LOCAL_SHARED_LIBRARIES := nntrainer ccapi-nntrainer
-LOCAL_C_INCLUDES += $(NNTRAINER_INCLUDES) $(CIFARDIR)
+LOCAL_C_INCLUDES += $(NNTRAINER_INCLUDES) $(NNTRAINER_ROOT)/Applications/YOLO/jni
include $(BUILD_EXECUTABLE)
--- /dev/null
+// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copyright (C) 2023 Seungbaek Hong <sb92.hong@samsung.com>
+ *
+ * @file det_dataloader.h
+ * @date 22 March 2023
+ * @brief dataloader for object detection dataset
+ * @see https://github.com/nnstreamer/nntrainer
+ * @author Seungbaek Hong <sb92.hong@samsung.com>
+ * @author Jijoong Moon <jijoong.moon@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include "det_dataloader.h"
+
+#include <cstring>
+#include <filesystem>
+#include <fstream>
+#include <nntrainer_error.h>
+#include <random>
+
+namespace nntrainer::util {
+
+// It supports bmp image file only now.
+DirDataLoader::DirDataLoader(const char *directory_, unsigned int max_num_label,
+ unsigned int c, unsigned int w, unsigned int h,
+ bool is_train_) :
+ max_num_label(max_num_label),
+ channel(c),
+ height(h),
+ width(w),
+ is_train(is_train_) {
+ dir_path.assign(directory_);
+
+ // set data list
+ std::filesystem::directory_iterator itr(dir_path + "images");
+ while (itr != std::filesystem::end(itr)) {
+ // get image file name
+ std::string img_file = itr->path().string();
+
+ // check if it is bmp image file
+ if (img_file.find(".bmp") == std::string::npos) {
+ itr++;
+ continue;
+ }
+
+ // set label file name
+ std::string label_file = img_file;
+ label_file.replace(label_file.find(".bmp"), 4, ".txt");
+ label_file.replace(label_file.find("/images"), 7, "/annotations");
+
+ // check if there is paired label file
+ if (!std::filesystem::exists(label_file)) {
+ itr++;
+ continue;
+ }
+
+ // set data list
+ data_list.push_back(make_pair(img_file, label_file));
+ itr++;
+ }
+
+ // set index and shuffle data
+ idxes = std::vector<unsigned int>(data_list.size());
+ std::iota(idxes.begin(), idxes.end(), 0);
+ if (is_train)
+ std::shuffle(idxes.begin(), idxes.end(), rng);
+
+ data_size = data_list.size();
+ count = 0;
+}
+
+void read_image(const std::string path, float *input, uint &width,
+ uint &height) {
+ FILE *f = fopen(path.c_str(), "rb");
+
+ if (f == nullptr)
+ throw std::invalid_argument("Cannot open file: " + path);
+
+ unsigned char info[54];
+ size_t s = fread(info, sizeof(unsigned char), 54, f);
+
+ unsigned int w = *(int *)&info[18];
+ unsigned int h = *(int *)&info[22];
+
+ if (w != width or h != height) {
+ fclose(f);
+ throw std::invalid_argument("the dimension of image file does not match" +
+ std::to_string(s));
+ }
+
+ int row_padded = (width * 3 + 3) & (~3);
+ unsigned char *data = new unsigned char[row_padded];
+
+ for (uint i = 0; i < height; i++) {
+ s = fread(data, sizeof(unsigned char), row_padded, f);
+ for (uint j = 0; j < width; j++) {
+ input[height * (height - i - 1) + j] = (float)data[j * 3 + 2] / 255;
+ input[(height * width) + height * (height - i - 1) + j] =
+ (float)data[j * 3 + 1] / 255;
+ input[(height * width) * 2 + height * (height - i - 1) + j] =
+ (float)data[j * 3] / 255;
+ }
+ }
+
+ delete[] data;
+ fclose(f);
+}
+
+void DirDataLoader::next(float **input, float **label, bool *last) {
+ auto fill_one_sample = [this](float *input_, float *label_, int index) {
+ // set input data
+ std::string img_file = data_list[index].first;
+ read_image(img_file, input_, width, height);
+
+ // set label data
+ std::string label_file = data_list[index].second;
+ std::memset(label_, 0.0, 5 * sizeof(float) * max_num_label);
+
+ std::ifstream file(label_file);
+ std::string cur_line;
+
+ int line_idx = 0;
+ while (getline(file, cur_line)) {
+ std::stringstream ss(cur_line);
+ std::string cur_value;
+
+ int row_idx = 0;
+ while (getline(ss, cur_value, ' ')) {
+ if (row_idx == 0) {
+ label_[line_idx * 5 + 4] = std::stof(cur_value);
+ } else {
+ label_[line_idx * 5 + row_idx - 1] = std::stof(cur_value) / 416;
+ }
+ row_idx++;
+ }
+
+ line_idx++;
+ }
+
+ file.close();
+ };
+
+ fill_one_sample(*input, *label, idxes[count]);
+
+ count++;
+
+ if (count < data_size) {
+ *last = false;
+ } else {
+ *last = true;
+ count = 0;
+ std::shuffle(idxes.begin(), idxes.end(), rng);
+ }
+}
+
+} // namespace nntrainer::util
--- /dev/null
+// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copyright (C) 2023 Seungbaek Hong <sb92.hong@samsung.com>
+ *
+ * @file det_dataloader.h
+ * @date 22 March 2023
+ * @brief dataloader for object detection dataset
+ * @see https://github.com/nnstreamer/nntrainer
+ * @author Seungbaek Hong <sb92.hong@samsung.com>
+ * @author Jijoong Moon <jijoong.moon@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <random>
+#include <string>
+#include <tensor_dim.h>
+#include <vector>
+
+namespace nntrainer::util {
+
+using TensorDim = ml::train::TensorDim;
+
+/**
+ * @brief user data object
+ */
+class DirDataLoader {
+public:
+ /**
+ * @brief Construct a new Dir Data Loader object
+ */
+ DirDataLoader(const char *directory_, unsigned int max_num_label,
+ unsigned int c, unsigned int w, unsigned int h, bool is_train_);
+ /**
+ * @brief Destroy the Dir Data Loader object
+ */
+ ~DirDataLoader(){};
+
+ /**
+ * @copydoc void DataLoader::next(float **input, float**label, bool *last)
+ */
+ void next(float **input, float **label, bool *last);
+
+ /**
+ * @brief getter for current file name
+ * @return current file name
+ */
+ std::string getCurFileName() { return cur_file_name; };
+
+ /**
+ * @brief setter for current file name
+ */
+ void setCurFileName(std::string s) { cur_file_name = s; };
+
+private:
+ std::string dir_path;
+ unsigned int data_size;
+ unsigned int max_num_label;
+ unsigned int channel;
+ unsigned int height;
+ unsigned int width;
+ bool is_train;
+
+ std::vector<std::pair<std::string, std::string>> data_list;
+ std::vector<unsigned int> idxes;
+ unsigned int count;
+ std::string cur_file_name;
+
+ // random number generator
+ std::mt19937 rng;
+};
+
+} // namespace nntrainer::util
#include <model.h>
#include <optimizer.h>
-#include <cifar_dataloader.h>
+#include <det_dataloader.h>
using LayerHandle = std::shared_ptr<ml::train::Layer>;
using ModelHandle = std::unique_ptr<ml::train::Model>;
-using UserDataType = std::unique_ptr<nntrainer::util::DataLoader>;
+using UserDataType = std::unique_ptr<nntrainer::util::DirDataLoader>;
int trainData_cb(float **input, float **label, bool *last, void *user_data) {
- auto data = reinterpret_cast<nntrainer::util::DataLoader *>(user_data);
+ auto data = reinterpret_cast<nntrainer::util::DirDataLoader *>(user_data);
data->next(input, label, last);
return 0;
}
int validData_cb(float **input, float **label, bool *last, void *user_data) {
- auto data = reinterpret_cast<nntrainer::util::DataLoader *>(user_data);
+ auto data = reinterpret_cast<nntrainer::util::DirDataLoader *>(user_data);
data->next(input, label, last);
return 0;
}
-std::array<UserDataType, 2>
-createFakeDataGenerator(unsigned int batch_size,
- unsigned int simulated_data_size,
- unsigned int data_split) {
- UserDataType train_data(new nntrainer::util::RandomDataLoader(
- {{batch_size, 3, 416, 416}}, {{batch_size, (5 + 5) * 5, 13, 13}},
- simulated_data_size / data_split));
- UserDataType valid_data(new nntrainer::util::RandomDataLoader(
- {{batch_size, 3, 416, 416}}, {{batch_size, (5 + 5) * 5, 13, 13}},
- simulated_data_size / data_split));
+std::array<UserDataType, 2> createDetDataGenerator(const char *train_dir,
+ const char *valid_dir,
+ int max_num_label, int c,
+ int h, int w) {
+ UserDataType train_data(new nntrainer::util::DirDataLoader(
+ train_dir, max_num_label, c, h, w, true));
+ UserDataType valid_data(new nntrainer::util::DirDataLoader(
+ valid_dir, max_num_label, c, h, w, false));
return {std::move(train_data), std::move(valid_data)};
}
// create train and validation data
std::array<UserDataType, 2> user_datas;
try {
- user_datas = createFakeDataGenerator(batch_size, data_size, data_split);
+ const char *train_dir = "./train_dir/";
+ const char *valid_dir = "./valid_dir/";
+ const int max_num_label = 5;
+ const int channel = 3;
+ const int width = 416;
+ const int height = 416;
+ user_datas = createDetDataGenerator(train_dir, valid_dir, max_num_label,
+ channel, width, height);
} catch (const std::exception &e) {
std::cerr << "uncaught error while creating data generator! details: "
<< e.what() << std::endl;
yolo_sources = [
'main.cpp',
- cifar_path / 'cifar_dataloader.cpp'
+ 'det_dataloader.cpp'
]
yolo_dependencies = [app_utils_dep,
e = executable('nntrainer_yolov2',
yolo_sources,
- include_directories: [include_directories('.'), cifar_include_dir],
+ include_directories: [include_directories('.')],
dependencies: yolo_dependencies,
install: get_option('install-app'),
install_dir: application_install_dir