[Application] dataloader for yolo
authorSeungbaek Hong <sb92.hong@samsung.com>
Tue, 21 Mar 2023 11:50:14 +0000 (20:50 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Wed, 12 Apr 2023 22:24:23 +0000 (07:24 +0900)
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>
Applications/YOLO/jni/Android.mk
Applications/YOLO/jni/det_dataloader.cpp [new file with mode: 0644]
Applications/YOLO/jni/det_dataloader.h [new file with mode: 0644]
Applications/YOLO/jni/main.cpp
Applications/YOLO/jni/meson.build

index 1c807ec..8e057ba 100644 (file)
@@ -24,6 +24,7 @@ NNTRAINER_INCLUDES := $(NNTRAINER_ROOT)/nntrainer \
        $(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
@@ -39,8 +40,6 @@ include $(PREBUILT_SHARED_LIBRARY)
 
 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)/
@@ -49,13 +48,12 @@ LOCAL_CFLAGS += -pthread -fexceptions -fopenmp
 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)
diff --git a/Applications/YOLO/jni/det_dataloader.cpp b/Applications/YOLO/jni/det_dataloader.cpp
new file mode 100644 (file)
index 0000000..b48d0da
--- /dev/null
@@ -0,0 +1,157 @@
+// 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
diff --git a/Applications/YOLO/jni/det_dataloader.h b/Applications/YOLO/jni/det_dataloader.h
new file mode 100644 (file)
index 0000000..468148d
--- /dev/null
@@ -0,0 +1,72 @@
+// 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
index 0f325b3..4ef8735 100644 (file)
 #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)};
 }
@@ -217,7 +215,14 @@ int main(int argc, char *argv[]) {
   // 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;
index dd09610..f7a45db 100644 (file)
@@ -1,6 +1,6 @@
 yolo_sources = [
   'main.cpp',
-  cifar_path / 'cifar_dataloader.cpp'
+  'det_dataloader.cpp'
 ]
 
 yolo_dependencies = [app_utils_dep,
@@ -10,7 +10,7 @@ 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