[Example/OpenCV] Add OpenCV custom filter example
authorSangjung Woo <sangjung.woo@samsung.com>
Mon, 22 Oct 2018 16:14:31 +0000 (01:14 +0900)
committersewon <sewon.oh@samsung.com>
Wed, 24 Oct 2018 07:06:45 +0000 (16:06 +0900)
This patch newly adds the OpenCV custom filter example, which scales an
input tensor of [N][input_h][input_w][M] to an ouput tensor of
[N][output_h][output_w][M]. The shape of an output tensor could set by
custom property as "custom=[new-x]x[new-y]", where new-x and new-y are
unsigned integers.

This example is developed based on
nnstreamer_customfilter_example_scaler_allocator.c file.

Signed-off-by: Sangjung Woo <sangjung.woo@samsung.com>
debian/control
nnstreamer_example/CMakeLists.txt
nnstreamer_example/custom_example_opencv/CMakeLists.txt [new file with mode: 0644]
nnstreamer_example/custom_example_opencv/nnstreamer_customfilter_opencv_scaler.cc [new file with mode: 0644]
packaging/nnstreamer.spec
tests/nnstreamer_filter_custom/runTest.sh

index 985c1f5..698d71b 100644 (file)
@@ -6,7 +6,7 @@ Build-Depends: gcc, cmake, libgstreamer1.0-dev, libgstreamer-plugins-base1.0-dev
  libgtest-dev,
  debhelper (>=9),
  gstreamer1.0-tools, gstreamer1.0-plugins-base, gstreamer1.0-plugins-good,
- libpng-dev, tensorflow-lite-dev, tensorflow-dev, libcairo2-dev
+ libpng-dev, tensorflow-lite-dev, tensorflow-dev, libcairo2-dev, libopencv-dev
 Standards-Version: 3.9.6
 Homepage: https://github.com/nnsuite/nnstreamer
 
index 1358d6f..affe3d5 100644 (file)
@@ -12,6 +12,7 @@ pkg_check_modules(apppkgs REQUIRED ${PKGAPP_MODULES})
 ADD_SUBDIRECTORY(custom_example_passthrough)
 ADD_SUBDIRECTORY(custom_example_scaler)
 ADD_SUBDIRECTORY(custom_example_average)
+ADD_SUBDIRECTORY(custom_example_opencv)
 ADD_SUBDIRECTORY(example_cam)
 ADD_SUBDIRECTORY(example_sink)
 ADD_SUBDIRECTORY(example_filter)
diff --git a/nnstreamer_example/custom_example_opencv/CMakeLists.txt b/nnstreamer_example/custom_example_opencv/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1e82eef
--- /dev/null
@@ -0,0 +1,12 @@
+pkg_check_modules(opencv_pkg REQUIRED opencv)
+
+ADD_LIBRARY(nnstreamer_customfilter_opencv_scaler SHARED nnstreamer_customfilter_opencv_scaler.cc)
+TARGET_LINK_LIBRARIES(nnstreamer_customfilter_opencv_scaler ${pkgs_LIBRARIES} ${opencv_pkg_LIBRARIES})
+TARGET_INCLUDE_DIRECTORIES(nnstreamer_customfilter_opencv_scaler PUBLIC ${pkgs_INCLUDE_DIRS} ${opencv_pkg_INCLUDE_DIRS})
+TARGET_COMPILE_OPTIONS(nnstreamer_customfilter_opencv_scaler PUBLIC ${pkgs_CFLAGS_OTHER})
+
+INSTALL(TARGETS nnstreamer_customfilter_opencv_scaler
+       RUNTIME DESTINATION ${EXEC_PREFIX}
+       LIBRARY DESTINATION ${LIB_INSTALL_DIR}
+       ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
+)
diff --git a/nnstreamer_example/custom_example_opencv/nnstreamer_customfilter_opencv_scaler.cc b/nnstreamer_example/custom_example_opencv/nnstreamer_customfilter_opencv_scaler.cc
new file mode 100644 (file)
index 0000000..90a06b3
--- /dev/null
@@ -0,0 +1,170 @@
+/**
+ * NNStreamer OpenCV Custom Filter Example: Scaler
+ * 
+ * Copyright (C) 2018 Sangjung Woo <sangjung.woo@samsung.com>
+ *
+ * LICENSE: LGPL-2.1
+ *
+ * @file  nnstreamer_customfilter_opencv_scaler.cc
+ * @date  22 Oct 2018
+ * @brief  OpenCV Custom NNStreamer Filter Example: Scaler
+ * @author  Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug  No known bugs
+ * @see  nnstreamer_customfilter_example_scaler_allocator.c
+ * 
+ * This example scales an input tensor of [N][input_h][input_w][M] 
+ * to an ouput tensor of [N][output_h][output_w][M].
+ *
+ * The custom property is to be given as, "custom=[new-x]x[new-y]", where new-x and new-y are unsigned integers.
+ * E.g., custom=640x480
+ *
+ */
+
+#include "opencv2/opencv.hpp"
+#include "opencv2/highgui/highgui.hpp"
+
+#include <cassert>
+#include <glib.h>
+#include <tensor_filter_custom.h>
+#include <tensor_common.h>
+
+/**
+ * @brief Private data structure
+ */
+typedef struct _pt_data
+{
+  uint32_t in_height;
+  uint32_t in_width;
+  uint32_t out_height;
+  uint32_t out_width;
+} pt_data;
+
+/**
+ * @brief init callback of tensor_filter custom
+ */
+static void *
+pt_init (const GstTensorFilterProperties * prop)
+{
+  pt_data *data = g_new(pt_data, 1);
+  data->out_width = 0;
+  data->out_height = 0;
+
+  /* In case that custom property is given */
+  if (prop->custom_properties && strlen (prop->custom_properties) > 0) {
+    const char s[7] = "xX:_/ ";
+    gchar **strv = g_strsplit_set (prop->custom_properties, s, 3);
+    if (strv[0] != NULL) {
+      data->out_width = (uint32_t) g_ascii_strtoll (strv[0], NULL, 10);
+    } else {
+      data->out_width = 0;
+    }
+    if (strv[1] != NULL) {
+      data->out_height = (uint32_t) g_ascii_strtoll (strv[1], NULL, 10);
+    } else {
+      data->out_height = 0;
+    }
+    g_strfreev (strv);
+  }
+
+  return data;
+}
+
+/**
+ * @brief exit callback of tensor_filter custom
+ */
+static void
+pt_exit (void *private_data, const GstTensorFilterProperties * prop)
+{
+  pt_data *pdata = static_cast<pt_data *> (private_data);
+  assert (pdata);
+  g_free (pdata);
+}
+
+/**
+ * @brief setInputDimension callback of tensor_filter custom
+ */
+static int
+set_inputDim (void *private_data, const GstTensorFilterProperties * prop,
+    const GstTensorsInfo * in_info, GstTensorsInfo * out_info)
+{
+  pt_data *pdata = static_cast<pt_data *> (private_data);
+
+  assert (pdata);
+  assert (in_info);
+  assert (out_info);
+
+  out_info->num_tensors = 1;
+
+  for (int i = 0; i < NNS_TENSOR_RANK_LIMIT; i++)
+    out_info->info[0].dimension[i] = in_info->info[0].dimension[i];
+
+  /* Save width and height of an input tensor */
+  pdata->in_width = in_info->info[0].dimension[1];
+  pdata->in_height = in_info->info[0].dimension[2];
+
+  /* Update output dimension [1] and [2] with new-x, new-y */
+  if (pdata->out_width > 0)
+    out_info->info[0].dimension[1] = pdata->out_width;
+  if (pdata->out_height > 0)
+    out_info->info[0].dimension[2] = pdata->out_height;
+
+  out_info->info[0].type = in_info->info[0].type;
+  return 0;
+}
+
+/**
+ * @brief invoke-alloc callback of tensor_filter custom
+ */
+static int
+pt_allocate_invoke (void *private_data,
+    const GstTensorFilterProperties * prop, const GstTensorMemory * input,
+    GstTensorMemory * output)
+{
+  pt_data *pdata = static_cast<pt_data *> (private_data);
+  size_t in_size, out_size;
+  void* buffer;
+  cv::Mat img_src, img_dst;
+
+  assert (pdata);
+  assert (input);
+  assert (output);
+
+  in_size = gst_tensor_info_get_size (&prop->input_meta.info[0]);
+  out_size = gst_tensor_info_get_size (&prop->output_meta.info[0]);
+  buffer = g_malloc (in_size);
+  output[0].data = g_malloc (out_size);
+
+  /* Get Mat object from input tensor */
+  memcpy (buffer, input[0].data, in_size);
+  img_src = cv::Mat (pdata->in_height, pdata->in_width, CV_8UC3, buffer);
+  cv::cvtColor (img_src, img_src, CV_BGR2RGB);
+
+  /* Scale from the shape of input tensor to that of output tensor
+   * which is given as custom property */
+  cv::resize (img_src, img_dst, cv::Size(pdata->out_width, pdata->out_height), 
+            0, 0, CV_INTER_NN);
+
+  /* Convert Mat object to output tensor */
+  cv::cvtColor (img_dst, img_dst, CV_RGB2BGR);
+  memcpy (output[0].data, img_dst.data, out_size);
+
+  g_free(buffer);
+
+  return 0;
+}
+
+/**
+ * @brief tensor_filter custom subplugin definition
+ */
+static NNStreamer_custom_class NNStreamer_custom_body = {
+  .initfunc = pt_init,
+  .exitfunc = pt_exit,
+  .getInputDim = NULL,
+  .getOutputDim = NULL,
+  .setInputDim = set_inputDim,
+  .invoke = NULL,
+  .allocate_invoke = pt_allocate_invoke,
+};
+
+/* The dyn-loaded object */
+NNStreamer_custom_class *NNStreamer_custom = &NNStreamer_custom_body;
index a123ba2..381afb1 100644 (file)
@@ -38,6 +38,8 @@ BuildRequires: tensorflow-lite-devel
 # for cairo (nnstreamer_example_object_detection)
 BuildRequires: coregl-devel
 BuildRequires: cairo-devel
+# custom_example_opencv filter requires opencv-devel
+BuildRequires: opencv-devel
 
 %if 0%{?testcoverage}
 BuildRequires: lcov
index 4a42bf2..e1d2647 100755 (executable)
@@ -77,4 +77,18 @@ gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/
 python checkScaledTensor.py testcase11.direct.log 640 480 testcase11.scaled.log 320 240 3
 casereport 11 $? "Golden test comparison"
 
+# OpenCV Test
+# Test scaler using OpenCV (12, 13, 14)
+PATH_TO_MODEL="../../build/nnstreamer_example/custom_example_opencv/libnnstreamer_customfilter_opencv_scaler.so"
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=RGB,width=640,height=480,framerate=0/1 ! videoconvert ! video/x-raw, format=RGB ! tensor_converter ! tee name=t ! queue ! tensor_filter framework=\"custom\" model=\"${PATH_TO_MODEL}\" custom=\"640x480\" ! filesink location=\"testcase12.passthrough.log\" sync=true t. ! queue ! filesink location=\"testcase12.direct.log\" sync=true" 12
+compareAll testcase12.direct.log testcase12.passthrough.log 12
+
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=RGB,width=640,height=480,framerate=0/1 ! videoconvert ! video/x-raw, format=RGB ! tensor_converter ! tee name=t ! queue ! tensor_filter framework=\"custom\" model=\"${PATH_TO_MODEL}\" custom=\"320x240\" ! filesink location=\"testcase13.scaled.log\" sync=true t. ! queue ! filesink location=\"testcase13.direct.log\" sync=true" 13
+python checkScaledTensor.py testcase13.direct.log 640 480 testcase13.scaled.log 320 240 3
+casereport 13 $? "Golden test comparison"
+
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=RGB,width=640,height=480,framerate=0/1 ! videoconvert ! video/x-raw, format=RGB ! tensor_converter ! tee name=t ! queue ! tensor_filter framework=\"custom\" model=\"${PATH_TO_MODEL}\" custom=\"1920x1080\" ! filesink location=\"testcase14.scaled.log\" sync=true t. ! queue ! filesink location=\"testcase14.direct.log\" sync=true" 14
+python checkScaledTensor.py testcase14.direct.log 640 480 testcase14.scaled.log 1920 1080 3
+casereport 14 $? "Golden test comparison"
+
 report