--- /dev/null
+##\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
--- /dev/null
+/**\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