Tensor_region subplugin, currently supports mobilenet_ssd
authorHarshj20 <hjain24in@gmail.com>
Thu, 15 Jun 2023 03:42:37 +0000 (09:12 +0530)
committergichan-jang <56856496+gichan-jang@users.noreply.github.com>
Tue, 20 Jun 2023 04:14:31 +0000 (13:14 +0900)
    - updated nnstreamer.mk for tensor_region.c
    - A subplugin for tensor_decoder for providing cropping info to tensor-crop element
    - Code cleaned, global scalability for supporting other models
    - @brief tags added for init_modes, finalize, getOutCaps and nms functions
    - pre-build for ubuntu passed
    - newline added
    - multi line comments properly configured

Signed-off-by: HarshJ20 <hjain24in@gmail.com>
debian/nnstreamer-core.install
ext/nnstreamer/tensor_decoder/meson.build
ext/nnstreamer/tensor_decoder/tensordec-tensor_region.c [new file with mode: 0644]
jni/nnstreamer.mk
packaging/nnstreamer.spec

index 34a851f..68c3d5a 100644 (file)
@@ -1,4 +1,5 @@
 /usr/lib/nnstreamer/decoders/libnnstreamer_decoder_bounding_boxes.so
+/usr/lib/nnstreamer/decoders/libnnstreamer_decoder_tensor_region.so
 /usr/lib/nnstreamer/decoders/libnnstreamer_decoder_pose_estimation.so
 /usr/lib/nnstreamer/decoders/libnnstreamer_decoder_image_segment.so
 /usr/lib/nnstreamer/decoders/libnnstreamer_decoder_image_labeling.so
index 8295b90..303eae9 100644 (file)
@@ -56,6 +56,26 @@ static_library('nnstreamer_decoder_bounding_boxes',
   install_dir: nnstreamer_libdir
 )
 
+# tensor_region
+decoder_sub_tensor_region_sources = [
+  'tensordec-tensor_region.c',
+  'tensordecutil.c'
+]
+
+shared_library('nnstreamer_decoder_tensor_region',
+  decoder_sub_tensor_region_sources,
+  dependencies: [nnstreamer_dep, glib_dep, gst_dep],
+  install: true,
+  install_dir: decoder_subplugin_install_dir
+)
+
+static_library('nnstreamer_decoder_tensor_region',
+  decoder_sub_tensor_region_sources,
+  dependencies: [nnstreamer_dep, glib_dep, gst_dep],
+  install: true,
+  install_dir: nnstreamer_libdir
+)
+
 # pose estimation
 decoder_sub_pose_estimation_sources = [
   'tensordec-pose.c',
diff --git a/ext/nnstreamer/tensor_decoder/tensordec-tensor_region.c b/ext/nnstreamer/tensor_decoder/tensordec-tensor_region.c
new file mode 100644 (file)
index 0000000..f37bcd0
--- /dev/null
@@ -0,0 +1,758 @@
+/**\r
+ * GStreamer / NNStreamer tensor_decoder subplugin, "bounding boxes"\r
+ * Copyright (C) 2018 Samsung Electronics Co. Ltd.\r
+ * Copyright (C) 2018 MyungJoo Ham <myungjoo.ham@samsung.com>\r
+ * Copyright 2021 NXP\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Library General Public\r
+ * License as published by the Free Software Foundation;\r
+ * version 2.1 of the License.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Library General Public License for more details.\r
+ *\r
+ */\r
+/**\r
+ * @file        tensordec-tensor_region.c\r
+ * @date        15th June, 2023\r
+ * @brief       NNStreamer tensor-decoder subplugin, "tensor region",\r
+ *              which converts tensors to cropping info for tensor _crop element\r
+ *              This code is NYI/WIP and not compilable.\r
+ *\r
+ * @see         https://github.com/nnstreamer/nnstreamer\r
+ * @author      Harsh Jain <hjain24in@gmail.com>\r
+ * @bug         No known bugs except for NYI items\r
+ *\r
+ * option1: number of cropping regions required (default is 1)\r
+ * option2: Location of label file\r
+ *          This is independent from option1\r
+ * option3: \r
+ *          for mobilenet-ssd mode:\r
+ *            The option3 definition scheme is, in order, the following:\r
+ *                - box priors location file (mandatory)\r
+ *                - Detection threshold (optional, default set to 0.5)\r
+ *                - Y box scale (optional, default set to 10.0)\r
+ *                - X box scale (optional, default set to 10.0)\r
+ *                - h box scale (optional, default set to 5.0)\r
+ *                - w box scale (optional, default set to 5.0)\r
+ *                - IOU box valid threshold (optional, default set to 0.5)\r
+ *            The default parameters value could be set in the following ways:\r
+ *            option3=box-priors.txt:0.5:10.0:10.0:5.0:5.0:0.5\r
+ *            option3=box-priors.txt\r
+ *            option3=box-priors.txt::::::\r
+ *\r
+ *            It's possible to set only few values, using the default values for\r
+ *            those not specified through the command line.\r
+ *            You could specify respectively the detection and IOU thresholds to 0.65\r
+ *            and 0.6 with the option3 parameter as follow:\r
+ *            option3=box-priors.txt:0.65:::::0.6\r
+ * option4: Video input Dimension (WIDTH:HEIGHT) (default 300:300)\r
+ *          This is independent from option1\r
+ *\r
+ * @todo Remove duplicate codes*\r
+ * @todo give support for other models*\r
+ */\r
+#ifndef _GNU_SOURCE\r
+#define _GNU_SOURCE\r
+#endif\r
+#include <glib.h>\r
+#include <gst/gst.h>\r
+#include <math.h> /** expf */\r
+#include <nnstreamer_log.h>\r
+#include <nnstreamer_plugin_api.h>\r
+#include <nnstreamer_plugin_api_decoder.h>\r
+#include <nnstreamer_util.h>\r
+#include <stdint.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include "tensordecutil.h"\r
+\r
+#define _tensor_region_size_default_ 1\r
+void init_tr (void) __attribute__ ((constructor));\r
+void fini_tr (void) __attribute__ ((destructor));\r
+\r
+#define BOX_SIZE (4)\r
+#define MOBILENET_SSD_DETECTION_MAX (2034) /**add ssd_mobilenet v3 */\r
+#define MOBILENET_SSD_MAX_TENSORS (2U)\r
+#define INPUT_VIDEO_WIDTH_DEFAULT (300)\r
+#define INPUT_VIDEO_HEIGHT_DEFAULT (300)\r
+/**\r
+ * @brief There can be different schemes for input tensor.\r
+ */\r
+typedef enum\r
+{\r
+  MOBILENET_SSD_BOUNDING_BOX = 0,\r
+  BOUNDING_BOX_UNKNOWN,\r
+} tensor_region_modes;\r
+\r
+/** @brief Internal data structure for identifying cropping region */\r
+typedef struct {\r
+  guint x;\r
+  guint y;\r
+  guint w;\r
+  guint h;\r
+} crop_region;\r
+\r
+/**\r
+ * @brief Data structure for SSD tensor_region info for mobilenet ssd model.\r
+ */\r
+typedef struct {\r
+  /**From option3, box prior data */\r
+  char *box_prior_path; /**< Box Prior file path */\r
+  gfloat box_priors[BOX_SIZE][MOBILENET_SSD_DETECTION_MAX + 1]; /** loaded box prior */\r
+#define MOBILENET_SSD_PARAMS_THRESHOLD_IDX 0\r
+#define MOBILENET_SSD_PARAMS_Y_SCALE_IDX 1\r
+#define MOBILENET_SSD_PARAMS_X_SCALE_IDX 2\r
+#define MOBILENET_SSD_PARAMS_H_SCALE_IDX 3\r
+#define MOBILENET_SSD_PARAMS_W_SCALE_IDX 4\r
+#define MOBILENET_SSD_PARAMS_IOU_THRESHOLD_IDX 5\r
+#define MOBILENET_SSD_PARAMS_MAX 6\r
+\r
+  gfloat params[MOBILENET_SSD_PARAMS_MAX]; /** Post Processing parameters */\r
+  gfloat sigmoid_threshold; /** Inverse value of valid detection threshold in sigmoid domain */\r
+} properties_MOBILENET_SSD;\r
+\r
+/** @brief Internal data structure for tensor region */\r
+typedef struct {\r
+  tensor_region_modes mode;\r
+  union {\r
+    properties_MOBILENET_SSD mobilenet_ssd; /**< Properties for mobilenet_ssd  */\r
+  };\r
+  imglabel_t labeldata;\r
+  char *label_path;\r
+\r
+  /**From option4 */\r
+  guint i_width; /**< Input Video Width */\r
+  guint i_height; /**< Input Video Height */\r
+\r
+  /**From option1 */\r
+  guint num;  /**number of cropped regions required */\r
+\r
+  guint max_detections;\r
+  GArray *regions;\r
+  gboolean flag_use_label;\r
+\r
+} tensor_region;\r
+\r
+\r
+/** @brief Mathematic inverse of sigmoid function, aka logit */\r
+static float\r
+logit (float x)\r
+{\r
+  if (x <= 0.0f)\r
+    return -INFINITY;\r
+\r
+  if (x >= 1.0f)\r
+    return INFINITY;\r
+\r
+  return log (x / (1.0 - x));\r
+}\r
+\r
+/**\r
+ * @brief Load box-prior data from a file\r
+ * @param[in/out] trData The internal data.\r
+ * @return TRUE if loaded and configured. FALSE if failed to do so.\r
+ */\r
+static int\r
+_mobilenet_ssd_loadBoxPrior (tensor_region *trData)\r
+{\r
+  properties_MOBILENET_SSD *mobilenet_ssd = &trData->mobilenet_ssd;\r
+  gboolean failed = FALSE;\r
+  GError *err = NULL;\r
+  gchar **priors;\r
+  gchar *line = NULL;\r
+  gchar *contents = NULL;\r
+  guint row;\r
+  gint prev_reg = -1;\r
+\r
+  /**Read file contents */\r
+  if (!g_file_get_contents (mobilenet_ssd->box_prior_path, &contents, NULL, &err)) {\r
+    GST_ERROR ("Decoder/Tensor-Region/SSD's box prior file %s cannot be read: %s",\r
+        mobilenet_ssd->box_prior_path, err->message);\r
+    g_clear_error (&err);\r
+    return FALSE;\r
+  }\r
+\r
+  priors = g_strsplit (contents, "\n", -1);\r
+  /**If given prior file is inappropriate, report back to tensor-decoder */\r
+  if (g_strv_length (priors) < BOX_SIZE) {\r
+    ml_loge ("The given prior file, %s, should have at least %d lines.\n",\r
+        mobilenet_ssd->box_prior_path, BOX_SIZE);\r
+    failed = TRUE;\r
+    goto error;\r
+  }\r
+\r
+  for (row = 0; row < BOX_SIZE; row++) {\r
+    gint column = 0, registered = 0;\r
+\r
+    line = priors[row];\r
+    if (line) {\r
+      gchar **list = g_strsplit_set (line, " \t,", -1);\r
+      gchar *word;\r
+\r
+      while ((word = list[column]) != NULL) {\r
+        column++;\r
+\r
+        if (word && *word) {\r
+          if (registered > MOBILENET_SSD_DETECTION_MAX) {\r
+            GST_WARNING ("Decoder/Tensor-region/SSD's box prior data file has too many priors. %d >= %d",\r
+                registered, MOBILENET_SSD_DETECTION_MAX);\r
+            break;\r
+          }\r
+          mobilenet_ssd->box_priors[row][registered]\r
+              = (gfloat) g_ascii_strtod (word, NULL);\r
+          registered++;\r
+        }\r
+      }\r
+\r
+      g_strfreev (list);\r
+    }\r
+\r
+    if (prev_reg != -1 && prev_reg != registered) {\r
+      GST_ERROR ("Decoder/Tensor-Region/SSD's box prior data file is not consistent.");\r
+      failed = TRUE;\r
+      break;\r
+    }\r
+    prev_reg = registered;\r
+  }\r
+\r
+error:\r
+  g_strfreev (priors);\r
+  g_free (contents);\r
+  return !failed;\r
+}\r
+\r
+\r
+/** @brief Initialize tensor_region per mode */\r
+static int\r
+_init_modes (tensor_region * trData){\r
+  if(trData->mode == MOBILENET_SSD_BOUNDING_BOX){\r
+    properties_MOBILENET_SSD *data = &trData->mobilenet_ssd;\r
+#define DETECTION_THRESHOLD (.5f)\r
+#define THRESHOLD_IOU (.5f)\r
+#define Y_SCALE (10.0f)\r
+#define X_SCALE (10.0f)\r
+#define H_SCALE (5.0f)\r
+#define W_SCALE (5.0f)\r
+\r
+    data->params[MOBILENET_SSD_PARAMS_THRESHOLD_IDX] = DETECTION_THRESHOLD;\r
+    data->params[MOBILENET_SSD_PARAMS_Y_SCALE_IDX] = Y_SCALE;\r
+    data->params[MOBILENET_SSD_PARAMS_X_SCALE_IDX] = X_SCALE;\r
+    data->params[MOBILENET_SSD_PARAMS_H_SCALE_IDX] = H_SCALE;\r
+    data->params[MOBILENET_SSD_PARAMS_W_SCALE_IDX] = W_SCALE;\r
+    data->params[MOBILENET_SSD_PARAMS_IOU_THRESHOLD_IDX] = THRESHOLD_IOU;\r
+    data->sigmoid_threshold = logit (DETECTION_THRESHOLD);\r
+    return TRUE;\r
+  }\r
+  return TRUE;\r
+}\r
+\r
+\r
+/** @brief tensordec-plugin's GstTensorDecoderDef callback */\r
+static int\r
+tr_init (void **pdata)\r
+{\r
+  tensor_region *trData;\r
+  trData = *pdata = g_new0 (tensor_region, 1);\r
+  if (*pdata == NULL) {\r
+    GST_ERROR ("Failed to allocate memory for decoder subplugin.");\r
+    return FALSE;\r
+  }\r
+  trData->mode = MOBILENET_SSD_BOUNDING_BOX;\r
+  trData->num = 1;\r
+  trData->max_detections = 0;\r
+  trData->regions = NULL;\r
+  trData->flag_use_label = FALSE;\r
+  trData->i_width = INPUT_VIDEO_WIDTH_DEFAULT;\r
+  trData->i_height = INPUT_VIDEO_HEIGHT_DEFAULT;\r
+  return _init_modes(trData);\r
+}\r
+\r
+/** @brief tensordec-plugin's GstTensorDecoderDef callback */\r
+static void\r
+tr_exit (void **pdata)\r
+{\r
+  tensor_region *trData = *pdata;\r
+  g_array_free (trData->regions, TRUE);\r
+  _free_labels (&trData->labeldata);\r
+\r
+  if (trData->label_path)\r
+    g_free (trData->label_path);\r
+  g_free (*pdata);\r
+  *pdata = NULL;\r
+}\r
+\r
+/** @brief configure per-mode option (option1) */\r
+static int\r
+_setOption_mode (tensor_region *trData, const char *param)\r
+{\r
+  if(trData->mode == MOBILENET_SSD_BOUNDING_BOX){\r
+    properties_MOBILENET_SSD *mobilenet_ssd = &trData->mobilenet_ssd;\r
+    gchar **options;\r
+    int noptions, idx;\r
+    int ret = 1;\r
+\r
+    options = g_strsplit (param, ":", -1);\r
+    noptions = g_strv_length (options);\r
+\r
+    if (mobilenet_ssd->box_prior_path)\r
+      g_free (mobilenet_ssd->box_prior_path);\r
+\r
+    mobilenet_ssd->box_prior_path = g_strdup (options[0]);\r
+\r
+    if (NULL != mobilenet_ssd->box_prior_path) {\r
+      ret = _mobilenet_ssd_loadBoxPrior (trData);\r
+      if (ret == 0)\r
+        goto exit_mobilenet_ssd;\r
+    }\r
+\r
+    for (idx = 1; idx < noptions; idx++) {\r
+      if (strlen (options[idx]) == 0)\r
+        continue;\r
+      mobilenet_ssd->params[idx - 1] = strtod (options[idx], NULL);\r
+    }\r
+\r
+    mobilenet_ssd->sigmoid_threshold\r
+        = logit (mobilenet_ssd->params[MOBILENET_SSD_PARAMS_THRESHOLD_IDX]);\r
+  exit_mobilenet_ssd:\r
+    g_strfreev (options);\r
+    return ret;\r
+  }\r
+  return TRUE;\r
+}\r
+\r
+/** @brief tensordec-plugin's GstTensorDecoderDef callback */\r
+static int\r
+tr_setOption (void **pdata, int opNum, const char *param)\r
+{\r
+  tensor_region *trData = *pdata;\r
+  if (opNum == 0) {\r
+    /**option1 number of crop regions required */\r
+    trData->num = atoi (param);\r
+    return TRUE;\r
+  } else if (opNum == 1) {\r
+    /**option2 label path for mobilenet_ssd model */\r
+    if (NULL != trData->label_path)\r
+      g_free (trData->label_path);\r
+    trData->label_path = g_strdup (param);\r
+\r
+    if (NULL != trData->label_path)\r
+      loadImageLabels (trData->label_path, &trData->labeldata);\r
+\r
+    if (trData->labeldata.total_labels > 0)\r
+      return TRUE;\r
+    else\r
+      return FALSE;\r
+  } else if (opNum == 2){\r
+    /**option3 setting box prior path for mobilenet ssd model */\r
+    return _setOption_mode (trData, param);\r
+  }\r
+  else if (opNum == 3) {\r
+    /**option4 = input model size (width:height) */\r
+    tensor_dim dim;\r
+    int rank = gst_tensor_parse_dimension (param, dim);\r
+\r
+    trData->i_width = 0;\r
+    trData->i_height = 0;\r
+    if (param == NULL || *param == '\0')\r
+      return TRUE;\r
+\r
+    if (rank < 2) {\r
+      GST_ERROR ("mode-option-4 of tensor region is input video dimension (WIDTH:HEIGHT). The given parameter, \"%s\", is not acceptable.",\r
+          param);\r
+      return TRUE; /**Ignore this param */\r
+    }\r
+    if (rank > 2) {\r
+      GST_WARNING ("mode-option-4 of tensor region is input video dimension (WIDTH:HEIGHT). The third and later elements of the given parameter, \"%s\", are ignored.",\r
+          param);\r
+    }\r
+    trData->i_width = dim[0];\r
+    trData->i_height = dim[1];\r
+    return TRUE;\r
+  }\r
+\r
+  GST_INFO ("Property mode-option-%d is ignored", opNum + 1);\r
+  return TRUE;\r
+}\r
+\r
+typedef struct {\r
+  int valid;\r
+  int class_id;\r
+  gfloat score;\r
+  int x;\r
+  int y;\r
+  int height;\r
+  int width;\r
+} detected_object;\r
+\r
+/**\r
+ * @brief transfer crop region info with the given results to the output buffer\r
+ * @param[out] out_info The output buffer \r
+ * @param[in] trData The Tensor_region internal data.\r
+ * @param[in] results The final results to be transfered.\r
+ */\r
+static void\r
+finalize (GstMapInfo *out_info, tensor_region *data, GArray *results)\r
+{\r
+\r
+  guint i;\r
+  guint size = sizeof (crop_region); /**Assuming crop_region is a structure with four integer fields */\r
+  guint8 *out_data = out_info->data;\r
+  crop_region region;\r
+  guint maxx = MIN (results->len, data->num);\r
+\r
+  for (i = 0; i < maxx; i++) {\r
+    detected_object *temp = &g_array_index (results, detected_object, i);\r
+    region.x = temp->x;\r
+    region.y = temp->y;\r
+    region.w = temp->width;\r
+    region.h = temp->height;\r
+    memcpy (out_data, &region, size);\r
+    out_data += size;\r
+  }\r
+}\r
+\r
+#define _expit(x) (1.f / (1.f + expf (-((float) x))))\r
+\r
+\r
+/**\r
+ * @brief C++-Template-like box location calculation for box-priors\r
+ * @bug This is not macro-argument safe. Use paranthesis!\r
+ * @param[in] bb The configuration, "tensor region"\r
+ * @param[in] index The index (3rd dimension of BOX_SIZE:1:MOBILENET_SSD_DETECTION_MAX:1)\r
+ * @param[in] total_labels The count of total labels. We can get this from input tensor info. (1st dimension of LABEL_SIZE:MOBILENET_SSD_DETECTION_MAX:1:1)\r
+ * @param[in] boxprior The box prior data from the box file of SSD.\r
+ * @param[in] boxinputptr Cursor pointer of input + byte-per-index * index (box)\r
+ * @param[in] detinputptr Cursor pointer of input + byte-per-index * index (detection)\r
+ * @param[in] result The object returned. (pointer to object)\r
+ */\r
+#define _get_object_i_mobilenet_ssd(                                              \\r
+    bb, index, total_labels, boxprior, boxinputptr, detinputptr, result)          \\r
+  do {                                                                            \\r
+    unsigned int c;                                                               \\r
+    properties_MOBILENET_SSD *data = &bb->mobilenet_ssd;                          \\r
+    float sigmoid_threshold = data->sigmoid_threshold;                            \\r
+    float y_scale = data->params[MOBILENET_SSD_PARAMS_Y_SCALE_IDX];               \\r
+    float x_scale = data->params[MOBILENET_SSD_PARAMS_X_SCALE_IDX];               \\r
+    float h_scale = data->params[MOBILENET_SSD_PARAMS_H_SCALE_IDX];               \\r
+    float w_scale = data->params[MOBILENET_SSD_PARAMS_W_SCALE_IDX];               \\r
+    result->valid = FALSE;                                                        \\r
+    for (c = 1; c < total_labels; c++) {                                          \\r
+      if (detinputptr[c] >= sigmoid_threshold) {                                  \\r
+        gfloat score = _expit (detinputptr[c]);                                   \\r
+        float ycenter                                                             \\r
+            = boxinputptr[0] / y_scale * boxprior[2][index] + boxprior[0][index]; \\r
+        float xcenter                                                             \\r
+            = boxinputptr[1] / x_scale * boxprior[3][index] + boxprior[1][index]; \\r
+        float h = (float) expf (boxinputptr[2] / h_scale) * boxprior[2][index];   \\r
+        float w = (float) expf (boxinputptr[3] / w_scale) * boxprior[3][index];   \\r
+        float ymin = ycenter - h / 2.f;                                           \\r
+        float xmin = xcenter - w / 2.f;                                           \\r
+        int x = xmin * bb->i_width;                                               \\r
+        int y = ymin * bb->i_height;                                              \\r
+        int width = w * bb->i_width;                                              \\r
+        int height = h * bb->i_height;                                            \\r
+        result->class_id = c;                                                     \\r
+        result->x = MAX (0, x);                                                   \\r
+        result->y = MAX (0, y);                                                   \\r
+        result->width = width;                                                    \\r
+        result->height = height;                                                  \\r
+        result->score = score;                                                    \\r
+        result->valid = TRUE;                                                     \\r
+        break;                                                                    \\r
+      }                                                                           \\r
+    }                                                                             \\r
+  } while (0);\r
+\r
+/**\r
+ * @brief C++-Template-like box location calculation for box-priors for Mobilenet SSD Model\r
+ * @param[in] bb The configuration, "tensor region"\r
+ * @param[in] type The tensor type of inputptr\r
+ * @param[in] typename nnstreamer enum corresponding to the type\r
+ * @param[in] boxprior The box prior data from the box file of MOBILENET_SSD.\r
+ * @param[in] boxinput Input Tensor Data (Boxes)\r
+ * @param[in] detinput Input Tensor Data (Detection). Null if not available. (numtensor ==1)\r
+ * @param[in] config Tensor configs of the input tensors\r
+ * @param[out] results The object returned. (GArray with detectedObject)\r
+ */\r
+#define _get_objects_mobilenet_ssd(                                                         \\r
+    bb, _type, typename, boxprior, boxinput, detinput, config, results)                     \\r
+  case typename:                                                                            \\r
+    {                                                                                       \\r
+      int d;                                                                                \\r
+      _type *boxinput_ = (_type *) boxinput;                                                \\r
+      size_t boxbpi = config->info.info[0].dimension[0];                                    \\r
+      _type *detinput_ = (_type *) detinput;                                                \\r
+      size_t detbpi = config->info.info[1].dimension[0];                                    \\r
+      int num = (MOBILENET_SSD_DETECTION_MAX > bb->max_detections) ?                        \\r
+                    bb->max_detections :                                                    \\r
+                    MOBILENET_SSD_DETECTION_MAX;                                            \\r
+      detected_object object = {                                                            \\r
+        .valid = FALSE, .class_id = 0, .x = 0, .y = 0, .width = 0, .height = 0, .score = .0 \\r
+      };                                                                                    \\r
+      for (d = 0; d < num; d++) {                                                           \\r
+        _get_object_i_mobilenet_ssd (bb, d, detbpi, boxprior,                               \\r
+            (boxinput_ + (d * boxbpi)), (detinput_ + (d * detbpi)), (&object));             \\r
+        if (object.valid == TRUE) {                                                         \\r
+          g_array_append_val (results, object);                                             \\r
+        }                                                                                   \\r
+      }                                                                                     \\r
+    }                                                                                       \\r
+    break\r
+\r
+/** @brief Macro to simplify calling _get_objects_mobilenet_ssd */\r
+#define _get_objects_mobilenet_ssd_(type, typename)                                       \\r
+  _get_objects_mobilenet_ssd (trData, type, typename, (trData->mobilenet_ssd.box_priors), \\r
+      (boxes->data), (detections->data), config, results)\r
+\r
+/**\r
+ * @brief Compare Function for g_array_sort with detectedObject.\r
+ */\r
+static gint\r
+compare_detection (gconstpointer _a, gconstpointer _b)\r
+{\r
+  const detected_object *a = _a;\r
+  const detected_object *b = _b;\r
+\r
+  /**Larger comes first */\r
+  return (a->score > b->score) ? -1 : ((a->score == b->score) ? 0 : 1);\r
+}\r
+\r
+/**\r
+ * @brief Calculate the intersected surface\r
+ */\r
+static gfloat\r
+iou (detected_object *a, detected_object *b)\r
+{\r
+  int x1 = MAX (a->x, b->x);\r
+  int y1 = MAX (a->y, b->y);\r
+  int x2 = MIN (a->x + a->width, b->x + b->width);\r
+  int y2 = MIN (a->y + a->height, b->y + b->height);\r
+  int w = MAX (0, (x2 - x1 + 1));\r
+  int h = MAX (0, (y2 - y1 + 1));\r
+  float inter = w * h;\r
+  float areaA = a->width * a->height;\r
+  float areaB = b->width * b->height;\r
+  float o = inter / (areaA + areaB - inter);\r
+  return (o >= 0) ? o : 0;\r
+}\r
+\r
+/**\r
+ * @brief Apply NMS to the given results (objects[])\r
+ * @param[in/out] results The results to be filtered with nms\r
+ */\r
+static void\r
+nms (GArray *results, gfloat threshold)\r
+{\r
+  guint boxes_size;\r
+  guint i, j;\r
\r
+  g_array_sort (results, compare_detection);\r
+  boxes_size = results->len;\r
\r
+  for (i = 0; i < boxes_size; i++) {\r
+    detected_object *a = &g_array_index (results, detected_object, i);\r
+    if (a->valid == TRUE) {\r
+      for (j = i + 1; j < boxes_size; j++) {\r
+        detected_object *b = &g_array_index (results, detected_object, j);\r
+        if (b->valid == TRUE) {\r
+          if (iou (a, b) > threshold) {\r
+            b->valid = FALSE;\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+\r
+  i = 0;\r
+  do {\r
+    detected_object *a = &g_array_index (results, detected_object, i);\r
+    if (a->valid == FALSE)\r
+      g_array_remove_index (results, i);\r
+    else\r
+      i++;\r
+  } while (i < results->len);\r
+}\r
+\r
+/** @brief tensordec-plugin's GstTensorDecoderDef callback */\r
+static GstFlowReturn\r
+tr_decode (void **pdata, const GstTensorsConfig *config,\r
+    const GstTensorMemory *input, GstBuffer *outbuf)\r
+{\r
+  tensor_region *trData = *pdata;\r
+  GstMapInfo out_info;\r
+  GstMemory *out_mem;\r
+  GArray *results = NULL;\r
+  const guint num_tensors = config->info.num_tensors;\r
+  gboolean need_output_alloc = gst_buffer_get_size (outbuf) == 0;\r
+  const size_t size = (size_t) 4 * trData->num; /**4 field per block */\r
+\r
+  g_assert (outbuf);\r
+  /**Ensure we have outbuf properly allocated */\r
+  if (need_output_alloc) {\r
+    out_mem = gst_allocator_alloc (NULL, size, NULL);\r
+  } else {\r
+    if (gst_buffer_get_size (outbuf) < size) {\r
+      gst_buffer_set_size (outbuf, size);\r
+    }\r
+    out_mem = gst_buffer_get_all_memory (outbuf);\r
+  }\r
+  if (!gst_memory_map (out_mem, &out_info, GST_MAP_WRITE)) {\r
+    ml_loge ("Cannot map output memory / tensordec-tensor_region.\n");\r
+    goto error_free;\r
+  }\r
+\r
+  /** reset the buffer with 0 */\r
+  memset (out_info.data, 0, size);\r
+  if(trData->mode == MOBILENET_SSD_BOUNDING_BOX){\r
+    const GstTensorMemory *boxes, *detections = NULL;\r
+    properties_MOBILENET_SSD *data = &trData->mobilenet_ssd;\r
+\r
+    g_assert (num_tensors >= MOBILENET_SSD_MAX_TENSORS);\r
+    results = g_array_sized_new (FALSE, TRUE, sizeof (detected_object), 100);\r
+\r
+    boxes = &input[0];\r
+    if (num_tensors >= MOBILENET_SSD_MAX_TENSORS) /**lgtm[cpp/constant-comparison] */\r
+      detections = &input[1];\r
+    \r
+    switch (config->info.info[0].type) {\r
+      _get_objects_mobilenet_ssd_ (uint8_t, _NNS_UINT8);\r
+      _get_objects_mobilenet_ssd_ (int8_t, _NNS_INT8);\r
+      _get_objects_mobilenet_ssd_ (uint16_t, _NNS_UINT16);\r
+      _get_objects_mobilenet_ssd_ (int16_t, _NNS_INT16);\r
+      _get_objects_mobilenet_ssd_ (uint32_t, _NNS_UINT32);\r
+      _get_objects_mobilenet_ssd_ (int32_t, _NNS_INT32);\r
+      _get_objects_mobilenet_ssd_ (uint64_t, _NNS_UINT64);\r
+      _get_objects_mobilenet_ssd_ (int64_t, _NNS_INT64);\r
+      _get_objects_mobilenet_ssd_ (float, _NNS_FLOAT32);\r
+      _get_objects_mobilenet_ssd_ (double, _NNS_FLOAT64);\r
+      default:\r
+        g_assert (0);\r
+    }\r
+\r
+    nms (results, data->params[MOBILENET_SSD_PARAMS_IOU_THRESHOLD_IDX]);\r
+  } else {\r
+    GST_ERROR ("Failed to get output buffer, unknown mode %d.", trData->mode);\r
+    goto error_unmap;\r
+  }\r
+  finalize (&out_info, trData, results);\r
+\r
+  g_array_free (results, TRUE);\r
+\r
+  gst_memory_unmap (out_mem, &out_info);\r
+\r
+  if (need_output_alloc)\r
+    gst_buffer_append_memory (outbuf, out_mem);\r
+  else\r
+    gst_memory_unref (out_mem);\r
+\r
+  return GST_FLOW_OK;\r
+error_unmap:\r
+  gst_memory_unmap (out_mem, &out_info);\r
+error_free:\r
+  gst_memory_unref (out_mem);\r
+\r
+  return GST_FLOW_ERROR;\r
+}\r
+\r
+/**\r
+ * @brief set the max_detection\r
+ */\r
+static int\r
+_set_max_detection (tensor_region *data, const guint max_detection, const unsigned int limit)\r
+{\r
+  /**Check consistency with max_detection */\r
+  if (data->max_detections == 0)\r
+    data->max_detections = max_detection;\r
+  else\r
+    g_return_val_if_fail (max_detection == data->max_detections, FALSE);\r
+\r
+  if (data->max_detections > limit) {\r
+    GST_ERROR ("Incoming tensor has too large detection-max : %u", max_detection);\r
+    return FALSE;\r
+  }\r
+  return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ * @brief tensordec-plugin's GstTensorDecoderDef callback\r
+ *\r
+ * [Mobilenet SSD Model]\r
+ * The first tensor is boxes. BOX_SIZE : 1 : #MaxDetection, ANY-TYPE\r
+ * The second tensor is labels. #MaxLabel : #MaxDetection, ANY-TYPE\r
+ * Both tensors are MANDATORY!\r
+ * If there are third or more tensors, such tensors will be ignored.\r
+ */\r
+\r
+static GstCaps *\r
+tr_getOutCaps (void **pdata, const GstTensorsConfig *config)\r
+{\r
+  tensor_region *data = *pdata;\r
+  GstCaps *caps;\r
+  char *str;\r
+  guint max_detection, max_label;\r
+  const uint32_t *dim1, *dim2;\r
+  int i;\r
+\r
+  /**Check if the first tensor is compatible */\r
+  dim1 = config->info.info[0].dimension;\r
+  g_return_val_if_fail (dim1[0] == BOX_SIZE, NULL);\r
+  g_return_val_if_fail (dim1[1] == 1, NULL);\r
+  max_detection = dim1[2];\r
+  g_return_val_if_fail (max_detection > 0, NULL);\r
+  for (i = 3; i < NNS_TENSOR_RANK_LIMIT; i++)\r
+    g_return_val_if_fail (dim1[i] == 1, NULL);\r
+\r
+  /**Check if the second tensor is compatible */\r
+  dim2 = config->info.info[1].dimension;\r
+  max_label = dim2[0];\r
+  g_return_val_if_fail (max_label <= data->labeldata.total_labels, NULL);\r
+  if (max_label < data->labeldata.total_labels)\r
+    GST_WARNING ("The given tensor (2nd) has max_label (first dimension: %u) smaller than the number of labels in labels file (%s: %u).",\r
+        max_label, data->label_path, data->labeldata.total_labels);\r
+  g_return_val_if_fail (max_detection == dim2[1], NULL);\r
+  for (i = 2; i < NNS_TENSOR_RANK_LIMIT; i++)\r
+    g_return_val_if_fail (dim2[i] == 1, NULL);\r
+\r
+  /**Check consistency with max_detection */\r
+  if (!_set_max_detection (data, max_detection, MOBILENET_SSD_DETECTION_MAX)) {\r
+    return NULL;\r
+  }\r
+  str = g_strdup_printf ("testing");\r
+  caps = gst_caps_from_string (str);\r
+  setFramerateFromConfig (caps, config);\r
+  g_free (str);\r
+  (void) *pdata;\r
+  return caps;\r
+}\r
+\r
+\r
+static gchar decoder_subplugin_tensor_region[] = "tensor_region";\r
+\r
+/** @brief Tensor Region tensordec-plugin GstTensorDecoderDef instance */\r
+static GstTensorDecoderDef tensorRegion = { .modename = decoder_subplugin_tensor_region,\r
+  .init = tr_init,\r
+  .exit = tr_exit,\r
+  .setOption = tr_setOption,\r
+  .getOutCaps = tr_getOutCaps,\r
+  /** .getTransformSize = tr_getTransformsize, */\r
+  .decode = tr_decode };\r
+\r
+/** @brief Initialize this object for tensordec-plugin */\r
+void\r
+init_tr (void)\r
+{\r
+  nnstreamer_decoder_probe (&tensorRegion);\r
+}\r
+\r
+/** @brief Destruct this object for tensordec-plugin */\r
+void\r
+fini_tr (void)\r
+{\r
+  nnstreamer_decoder_exit (tensorRegion.modename);\r
+}\r
index adcb060..620e7e5 100644 (file)
@@ -143,6 +143,11 @@ NNSTREAMER_DECODER_BB_SRCS := \
     $(NNSTREAMER_EXT_HOME)/tensor_decoder/tensordecutil.c \
     $(NNSTREAMER_EXT_HOME)/tensor_decoder/tensordec-font.c
 
+#decoder tensorRegion 
+NNSTREAMER_DECODER_TR_SRCS := \
+    $(NNSTREAMER_EXT_HOME)/tensor_decoder/tensordec-tensor_region.c \
+    $(NNSTREAMER_EXT_HOME)/tensor_decoder/tensordecutil.c \
+
 # decoder directvideo
 NNSTREAMER_DECODER_DV_SRCS := \
     $(NNSTREAMER_EXT_HOME)/tensor_decoder/tensordec-directvideo.c \
@@ -215,4 +220,4 @@ endif
 GST_BUILDING_BLOCK_LIST := $(GST_LIBS_COMMON) $(GST_LIBS_GST)
 
 # gstreamer building block for decoder and filter
-NNSTREAMER_BUILDING_BLOCK_LIST := $(GST_BUILDING_BLOCK_LIST) nnstreamer nnstreamer_decoder_bounding_boxes nnstreamer_decoder_pose_estimation nnstreamer_filter_tensorflow-lite nnstreamer_decoder_flatbuf
+NNSTREAMER_BUILDING_BLOCK_LIST := $(GST_BUILDING_BLOCK_LIST) nnstreamer nnstreamer_decoder_bounding_boxes nnstreamer_decoder_tensor_region nnstreamer_decoder_pose_estimation nnstreamer_filter_tensorflow-lite nnstreamer_decoder_flatbuf
index 014ce04..35377d3 100644 (file)
@@ -995,6 +995,7 @@ cp -r result %{buildroot}%{_datadir}/nnstreamer/unittest/
 %defattr(-,root,root,-)
 %license LICENSE
 %{_prefix}/lib/nnstreamer/decoders/libnnstreamer_decoder_bounding_boxes.so
+%{_prefix}/lib/nnstreamer/decoders/libnnstreamer_decoder_tensor_region.so
 %{_prefix}/lib/nnstreamer/decoders/libnnstreamer_decoder_pose_estimation.so
 %{_prefix}/lib/nnstreamer/decoders/libnnstreamer_decoder_image_segment.so
 %{_prefix}/lib/nnstreamer/decoders/libnnstreamer_decoder_image_labeling.so