[TEST] Test case for tensor_decoder::tensor_region
authorHarshJ20 <hjain24in@gmail.com>
Tue, 20 Jun 2023 06:12:04 +0000 (11:42 +0530)
committergichan-jang <56856496+gichan-jang@users.noreply.github.com>
Thu, 6 Jul 2023 00:42:49 +0000 (09:42 +0900)
    - ssat based runTest.sh [incomplete]
    - getCropInfo.py for viewing the data produces of orange.unittest_plugins
    - gtest based test case in unittest_plugins with name TensorDecoder.TensorRegion [removed]
    - temporary file removed
    - Separate test unit for tensor_region
    - included missing Doxygen tags

Signed-off-by: HarshJ20 <hjain24in@gmail.com>
tests/meson.build
tests/nnstreamer_decoder_tensorRegion/getCropInfo.py [new file with mode: 0644]
tests/nnstreamer_decoder_tensorRegion/runTest.sh [new file with mode: 0644]
tests/nnstreamer_decoder_tensorRegion/unittest_tensorRegion.cc [new file with mode: 0644]
tests/nnstreamer_plugins/unittest_plugins.cc

index 21df1fc..3a47c4b 100644 (file)
@@ -84,6 +84,28 @@ if gtest_dep.found()
 
     test('unittest_sink', unittest_sink, timeout: 120, env: testenv)
 
+    # RUN unittest_tensorRegion
+    if tflite2_support_is_available
+      message('ssd_mobilenet_v2_coco.tflite model will be downloaded')
+      download_url = 'https://github.com/nnsuite/testcases/raw/master/DeepLearningModels/tensorflow-lite/ssd_mobilenet_v2_coco'
+      filename = 'ssd_mobilenet_v2_coco.tflite'
+      unittest_model = join_paths(unittest_base_dir, 'tests', 'test_models', 'models')
+      r = run_command('wget', '-nc', '-P', unittest_model, download_url + filename)
+      if r.returncode() == 0
+        output = r.stdout().strip()
+      else
+        errortxt = r.stderr().strip()
+      endif
+
+      unittest_tensorRegion = executable('unittest_tensorRegion',
+        join_paths('nnstreamer_decoder_tensorRegion', 'unittest_tensorRegion.cc'),
+        dependencies: [nnstreamer_unittest_deps],
+        install: get_option('install-test'),
+        install_dir: unittest_install_dir
+      )
+      test('unittest_tensorRegion', unittest_tensorRegion, env: testenv)
+    endif
+
     # Run unittest_plugins
     unittest_plugins = executable('unittest_plugins',
       join_paths('nnstreamer_plugins', 'unittest_plugins.cc'),
diff --git a/tests/nnstreamer_decoder_tensorRegion/getCropInfo.py b/tests/nnstreamer_decoder_tensorRegion/getCropInfo.py
new file mode 100644 (file)
index 0000000..1feff2f
--- /dev/null
@@ -0,0 +1,76 @@
+##\r
+# SPDX-License-Identifier: LGPL-2.1-only\r
+#\r
+# Copyright (C) 2023 Harsh Jain <hjain24in@gmail.com>\r
+#\r
+# @file getCropInfo.py\r
+# @brief generate human readable output from out buffers\r
+# @author Harsh Jain <hjain24in@gmail.com>\r
+\r
+import sys\r
+import gi\r
+import struct\r
+\r
+gi.require_version('Gst', '1.0')\r
+from gi.repository import Gst, GLib\r
+\r
+# Initialize GStreamer\r
+Gst.init(None)\r
+\r
+# Check if the image path argument is provided\r
+if len(sys.argv) < 2:\r
+    print("Please provide the path to the image file as an argument.")\r
+    sys.exit(1)\r
+\r
+image_path = sys.argv[1]\r
+\r
+# Create the GStreamer pipeline\r
+pipeline_str = """\r
+    filesrc location={} ! decodebin ! videoconvert ! videoscale ! video/x-raw,width=640,height=480,format=RGB ! videoscale ! video/x-raw,width=300,height=300,format=RGB ! tensor_converter ! tensor_transform mode=arithmetic option=typecast:float32,add:-127.5,div:127.5 ! tensor_filter framework=tensorflow2-lite model=ssd_mobilenet_v2_coco.tflite ! tensor_decoder mode=tensor_region option1=1 option2=../nnstreamer_decoder_boundingbox/coco_labels_list.txt option3=../nnstreamer_decoder_boundingbox/box_priors.txt ! appsink name=output\r
+""".format(image_path)\r
+\r
+pipeline = Gst.parse_launch(pipeline_str)\r
+\r
+# Define callback function to process the tensor data\r
+\r
+def process_tensor_data(appsink):\r
+    sample = appsink.emit("pull-sample")\r
+    buffer = sample.get_buffer()\r
+\r
+    # Extract tensor data\r
+    header_buffer = buffer.extract_dup(0, 128)\r
+    tensor_data_buffer = buffer.extract_dup(128, 16)  # Extract the remaining data as the tensor\r
+\r
+    # Process tensor data\r
+    tensor_size = len(tensor_data_buffer) // 4  # Assuming each value is a uint32 (4 bytes)\r
+    tensor_data = struct.unpack(f"{tensor_size}I", tensor_data_buffer)\r
+\r
+    # Print the tensor data\r
+    print("Tensor data:", tensor_data)\r
+\r
+    # Stop the main loop\r
+    loop.quit()\r
+\r
+    return Gst.FlowReturn.OK\r
+\r
+\r
+\r
+\r
+# Set up the appsink element to capture tensor data\r
+output = pipeline.get_by_name("output")\r
+output.set_property("emit-signals", True)\r
+output.set_property("max-buffers", 1)\r
+output.connect("new-sample", process_tensor_data)\r
+\r
+# Start the pipeline\r
+pipeline.set_state(Gst.State.PLAYING)\r
+\r
+# Run the main loop\r
+loop = GLib.MainLoop()\r
+try:\r
+    loop.run()\r
+except KeyboardInterrupt:\r
+    pass\r
+\r
+# Stop the pipeline and clean up\r
+pipeline.set_state(Gst.State.NULL)\r
diff --git a/tests/nnstreamer_decoder_tensorRegion/runTest.sh b/tests/nnstreamer_decoder_tensorRegion/runTest.sh
new file mode 100644 (file)
index 0000000..193a254
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+##
+## SPDX-License-Identifier: LGPL-2.1-only
+##
+## @file runTest.sh
+## @author Harsh Jain <hjain24in@gmail.com>
+## @date 18 JUNE, 2023
+## @brief SSAT Test Cases for NNStreamer
+##
+if [[ "$SSATAPILOADED" != "1" ]]; then
+    SILENT=0
+    INDEPENDENT=1
+    search="ssat-api.sh"
+    source $search
+    printf "${Blue}Independent Mode${NC}"
+fi
+
+# This is compatible with SSAT (https://github.com/myungjoo/SSAT)
+testInit $1
+
+PATH_TO_PLUGIN="../../build"
+PATH_TO_IMAGE="../test_models/data/orange.png"
+PATH_TO_LABELS="../nnstreamer_decoder_boundingbox/coco_labels_list.txt"
+PATH_TO_BOX_PRIORS="../nnstreamer_decoder_boundingbox/box_priors.txt"
+PATH_TO_MODEL="../test_models/models/ssd_mobilenet_v2_coco.tflite"
+
+
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} filesrc location=${PATH_TO_IMAGE} ! pngdec ! videoconvert ! videoscale !     video/x-raw,width=300,height=300,format=RGB,framerate=0/1 ! tee name=t t. ! queue ! tensor_converter ! crop.raw     t. ! queue ! tensor_converter ! tensor_transform mode=arithmetic option=typecast:float32,add:-127.5,div:127.5 !     tensor_filter framework=tensorflow2-lite model=${PATH_TO_MODEL} !     tensor_decoder mode=tensor_region option1=1 option2=${PATH_TO_LABELS} option3=${PATH_TO_BOX_PRIORS} ! crop.info     tensor_crop name=crop ! other/tensors,format=flexible ! tensor_converter ! tensor_decoder mode=direct_video ! videoconvert ! videoscale !     video/x-raw,width=300,height=300,format=RGB ! pngenc  ! filesink location=tensor_region_output_orange.png
+" 0 0 0 $PERFORMANCE
+
+
+callCompareTest tensor_region_orange.png tensor_region_output_orange.png 0 "mobilenet-ssd Decode 1" 0
+rm tensor_region_output_*
+report
diff --git a/tests/nnstreamer_decoder_tensorRegion/unittest_tensorRegion.cc b/tests/nnstreamer_decoder_tensorRegion/unittest_tensorRegion.cc
new file mode 100644 (file)
index 0000000..18b7bdc
--- /dev/null
@@ -0,0 +1,218 @@
+/**\r
+ * @file       unittest_tensorRegion.cc\r
+ * @date       20 June 2023\r
+ * @brief      Unit test for tensor_decoder::tensor_region. (testcases to check data conversion or buffer transfer)\r
+ * @see                https://github.com/nnstreamer/nnstreamer\r
+ * @author     Harsh Jain <hjain24in@gmail.com>\r
+ * @bug                No known bugs.\r
+ */\r
+#include <gtest/gtest.h>\r
+#include <glib/gstdio.h>\r
+#include <gst/check/gstcheck.h>\r
+#include <gst/gst.h>\r
+#include <nnstreamer_plugin_api_decoder.h>\r
+#include <nnstreamer_subplugin.h>\r
+#include <string.h>\r
+#include <tensor_common.h>\r
+#include <tensor_meta.h>\r
+#include <unistd.h>\r
+\r
+#include "../gst/nnstreamer/elements/gsttensor_sparseutil.h"\r
+#include "../gst/nnstreamer/elements/gsttensor_transform.h"\r
+#include "../unittest_util.h"\r
+\r
+#if defined(ENABLE_TENSORFLOW_LITE) || defined(ENABLE_TENSORFLOW2_LITE)\r
+#define TEST_REQUIRE_TFLITE(Case, Name) TEST (Case, Name)\r
+#else\r
+#define TEST_REQUIRE_TFLITE(Case, Name) TEST (Case, DISABLED_##Name)\r
+#endif\r
+\r
+/**\r
+ * @brief Call back function for tensor_region to parse outbuf\r
+ * \r
+ * @param sink The sink element\r
+ * @param user_data User data passed to the callback function\r
+ */\r
+static void new_data_cb(GstElement* sink, const gpointer user_data)\r
+{\r
+  GstSample* sample = nullptr;\r
+\r
+  g_signal_emit_by_name(sink, "pull-sample", &sample);\r
+\r
+  /** Expected values of cropping info for orange.png */\r
+  guint32 expected_values[] = {58, 61, 219, 213};\r
+\r
+  if (sample != nullptr) {\r
+    GstBuffer* outbuf = gst_sample_get_buffer(sample);\r
+    GstMemory* mem = gst_buffer_peek_memory(outbuf, 0);\r
+\r
+    if (mem != nullptr) {\r
+      GstMapInfo out_info;\r
+\r
+      if (gst_memory_map(mem, &out_info, GST_MAP_READ)) {\r
+        GstTensorMetaInfo map;\r
+        int* data_ptr = nullptr;\r
+\r
+        gst_tensor_meta_info_parse_header(&map, out_info.data);\r
+\r
+        gsize hsize = gst_tensor_meta_info_get_header_size(&map);\r
+        gsize dsize = gst_tensor_meta_info_get_data_size(&map);\r
+        ASSERT_EQ(map.type, _NNS_UINT32);\r
+\r
+        gsize esize = sizeof(guint32);\r
+\r
+        ASSERT_EQ(hsize + dsize, out_info.size);\r
+        ASSERT_EQ((dsize % (esize * 4)), 0);\r
+\r
+        data_ptr = reinterpret_cast<int*>(out_info.data + hsize);\r
+\r
+        for (int i = 0; i < 4; i++) {\r
+          EXPECT_EQ(data_ptr[i], expected_values[i]);\r
+        }\r
+\r
+        gst_memory_unmap(mem, &out_info);\r
+      }\r
+    }\r
+\r
+    gst_sample_unref(sample);\r
+  }\r
+}\r
+\r
+/**\r
+ * @brief Structure to hold information related to TensorRegion.\r
+ */\r
+struct TensorRegion {\r
+  GstElement* pipeline;   /**< The pipeline element */\r
+  GstElement* app_sink;   /**< The app sink element */\r
+  GMainLoop* main_loop;   /**< The main loop */\r
+};\r
+\r
+\r
+/**\r
+ * @brief Callback function to handle pipeline messages.\r
+ *\r
+ * @param bus The GStreamer bus.\r
+ * @param message The GStreamer message.\r
+ * @param data Pointer to the TensorRegion structure.\r
+ * @return gboolean Returns TRUE to continue receiving messages.\r
+ */\r
+static gboolean on_pipeline_message(GstBus* bus, GstMessage* message, TensorRegion* data)\r
+{\r
+  switch (GST_MESSAGE_TYPE(message)) {\r
+    case GST_MESSAGE_EOS:\r
+      g_main_loop_quit(data->main_loop);\r
+      break;\r
+    case GST_MESSAGE_ERROR: {\r
+      g_print("Received error\n");\r
+\r
+      GError* err = NULL;\r
+      gchar* dbg_info = NULL;\r
+\r
+      gst_message_parse_error(message, &err, &dbg_info);\r
+      g_printerr("ERROR from element %s: %s\n", GST_OBJECT_NAME(message->src), err->message);\r
+      g_printerr("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");\r
+      g_error_free(err);\r
+      g_free(dbg_info);\r
+    }\r
+\r
+    g_main_loop_quit(data->main_loop);\r
+    break;\r
+    case GST_MESSAGE_STATE_CHANGED:\r
+      break;\r
+    default:\r
+      break;\r
+  }\r
+\r
+  /** Return FALSE to stop receiving messages after the callback function\r
+  * has handled the current message. */\r
+  return G_SOURCE_CONTINUE;\r
+}\r
+\r
+\r
+/**\r
+ * @brief Test for tensor_decoder::tensor_region\r
+ */\r
+TEST_REQUIRE_TFLITE (TensorDecoder, TensorRegion)\r
+{\r
+  GstBus* bus;\r
+  const gchar* root_path = g_getenv("NNSTREAMER_SOURCE_ROOT_PATH");\r
+  if (root_path == nullptr)\r
+    root_path = "..";\r
+\r
+  const gchar* image_path = g_build_filename(root_path, "tests", "test_models", "data",\r
+                                             "orange.png", nullptr);\r
+  const gchar* model = g_build_filename(root_path, "tests", "test_models", "models", "ssd_mobilenet_v2_coco.tflite", nullptr);\r
+  const gchar* labels_path = g_build_filename(root_path, "tests", "test_models", "labels", "labels.txt", nullptr);\r
+  const gchar* box_priors_path = g_build_filename(root_path, "tests", "nnstreamer_decoder_boundingbox", "box_priors.txt", nullptr);\r
+\r
+  ASSERT_TRUE(g_file_test(image_path, G_FILE_TEST_EXISTS));\r
+  ASSERT_TRUE(g_file_test(model, G_FILE_TEST_EXISTS));\r
+  ASSERT_TRUE(g_file_test(labels_path, G_FILE_TEST_EXISTS));\r
+  ASSERT_TRUE(g_file_test(box_priors_path, G_FILE_TEST_EXISTS));\r
+\r
+  /** Create the GStreamer pipeline */\r
+  gchar* pipeline_str = g_strdup_printf(\r
+      "filesrc location=%s ! decodebin ! videoconvert ! videoscale ! "\r
+      "video/x-raw,width=640,height=480,format=RGB ! videoscale ! "\r
+      "video/x-raw,width=300,height=300,format=RGB ! tensor_converter ! "\r
+      "tensor_transform mode=arithmetic option=typecast:float32,add:-127.5,div:127.5 ! "\r
+      "tensor_filter framework=tensorflow2-lite model=%s ! "\r
+      "tensor_decoder mode=tensor_region option1=1 option2=%s option3=%s ! "\r
+      "appsink name=sinkx",\r
+      image_path, model, labels_path, box_priors_path);\r
+\r
+  GstElement* pipeline = gst_parse_launch(pipeline_str, nullptr);\r
+  g_free(pipeline_str);\r
+\r
+  GstElement* app_sink = gst_bin_get_by_name(GST_BIN(pipeline), "sinkx");\r
+\r
+  /** Create the TensorRegion structure and assign pipeline and app_sink */\r
+  TensorRegion data;\r
+  data.pipeline = pipeline;\r
+  data.app_sink = app_sink;\r
+  bus = gst_element_get_bus(data.pipeline);\r
+  gst_bus_add_watch(bus, (GstBusFunc)on_pipeline_message, &data);\r
+  gst_object_unref(bus);\r
+\r
+  /** Enable signal emission from the app_sink */\r
+  g_object_set(app_sink, "emit-signals", TRUE, NULL);\r
+\r
+  /** Connect the new-sample callback to the app_sink */\r
+  g_signal_connect(app_sink, "new-sample", G_CALLBACK(new_data_cb), nullptr);\r
+\r
+  /** Start playing the pipeline */\r
+  gst_element_set_state(pipeline, GST_STATE_PLAYING);\r
+\r
+  /** Create a GLib Main Loop and set it to run */\r
+  data.main_loop = g_main_loop_new(NULL, FALSE);\r
+  g_main_loop_run(data.main_loop);\r
+\r
+  /** Free resources */\r
+  gst_element_set_state(pipeline, GST_STATE_NULL);\r
+  gst_object_unref(pipeline);\r
+}\r
+\r
+\r
+/**\r
+ * @brief Main function for unit test.\r
+ */\r
+int\r
+main (int argc, char **argv)\r
+{\r
+  int ret = -1;\r
+  try {\r
+    testing::InitGoogleTest (&argc, argv);\r
+  } catch (...) {\r
+    g_warning ("catch 'testing::internal::<unnamed>::ClassUniqueToAlwaysTrue'");\r
+  }\r
+\r
+  gst_init (&argc, &argv);\r
+\r
+  try {\r
+    ret = RUN_ALL_TESTS ();\r
+  } catch (...) {\r
+    g_warning ("catch `testing::internal::GoogleTestFailureException`");\r
+  }\r
+\r
+  return ret;\r
+}\r
index 25a7e3b..4b3adc3 100644 (file)
@@ -7097,6 +7097,7 @@ TEST (testConverterSubplugins, flexbufInvalidParam1_n)
 }
 #endif /** ENABLE_FLATBUF && ENABLE_PROTOBUF */
 
+
 /**
  * @brief Data structure for tensor-crop test.
  */