style fix: decoder/tensor_region dos2unix & trailing whitespaces
authorMyungJoo Ham <myungjoo.ham@samsung.com>
Wed, 6 Sep 2023 08:18:41 +0000 (17:18 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Wed, 11 Oct 2023 07:35:54 +0000 (16:35 +0900)
It was formatted in "DOS" type. Apply dos2unix.
Then, remove trailing whitespaces.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
ext/nnstreamer/tensor_decoder/tensordec-tensor_region.c

index 0d3b9b6..12b5fc7 100644 (file)
-/* SPDX-License-Identifier: LGPL-2.1-only */\r
-/**\r
- * GStreamer / NNStreamer tensor_decoder subplugin, "tensor_region"\r
- * Copyright (C) 2023 Harsh Jain <hjain24in@gmail.com>\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] data The Tensor_region internal data.\r
- * @param[in] results The final results to be transfered.\r
- */\r
-static void\r
-gst_tensor_top_detectedObjects_cropInfo (GstMapInfo *out_info, const 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
-  guint *out_data = (guint *)out_info->data;\r
-  crop_region region;\r
-  guint maxx = MIN (results->len, data->num);\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 / sizeof (guint);\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
-  boxes_size = results->len;\r
-  if (boxes_size == 0U)\r
-    return;\r
-    \r
-  g_array_sort (results, compare_detection);\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
-/**\r
- * @brief Private function to initialize the meta info\r
- */\r
-static void \r
-init_meta(GstTensorMetaInfo * meta, const tensor_region * trData){\r
-  gst_tensor_meta_info_init (meta);\r
-  meta->type = _NNS_UINT32;\r
-  meta->dimension[0] = BOX_SIZE;\r
-  meta->dimension[1] = trData->num;\r
-  meta->media_type = _NNS_TENSOR;\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
-  GstTensorMetaInfo meta;\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 * sizeof(uint32_t); /**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
-  gst_tensor_top_detectedObjects_cropInfo (&out_info, trData, results);\r
-\r
-  g_array_free (results, TRUE);\r
-\r
-  gst_memory_unmap (out_mem, &out_info);\r
-\r
-  /** converting to Flexible tensor since  \r
-   * info pad of tensor_crop has capability for flexible tensor stream \r
-  */\r
-  init_meta(&meta, trData);\r
-  out_mem = gst_tensor_meta_info_append_header(&meta, out_mem);\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, guint max_detection, 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
-  /** @todo unused dimension value should be 0 */\r
-  for (i = 3; i < NNS_TENSOR_RANK_LIMIT; i++)\r
-    g_return_val_if_fail (dim1[i] == 0 || 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] == 0 || 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("other/tensors,format=flexible");\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
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/**
+ * GStreamer / NNStreamer tensor_decoder subplugin, "tensor_region"
+ * Copyright (C) 2023 Harsh Jain <hjain24in@gmail.com>
+ */
+/**
+ * @file        tensordec-tensor_region.c
+ * @date        15th June, 2023
+ * @brief       NNStreamer tensor-decoder subplugin, "tensor region",
+ *              which converts tensors to cropping info for tensor _crop element
+ *              This code is NYI/WIP and not compilable.
+ *
+ * @see         https://github.com/nnstreamer/nnstreamer
+ * @author      Harsh Jain <hjain24in@gmail.com>
+ * @bug         No known bugs except for NYI items
+ *
+ * option1: number of cropping regions required (default is 1)
+ * option2: Location of label file
+ *          This is independent from option1
+ * option3:
+ *          for mobilenet-ssd mode:
+ *            The option3 definition scheme is, in order, the following:
+ *                - box priors location file (mandatory)
+ *                - Detection threshold (optional, default set to 0.5)
+ *                - Y box scale (optional, default set to 10.0)
+ *                - X box scale (optional, default set to 10.0)
+ *                - h box scale (optional, default set to 5.0)
+ *                - w box scale (optional, default set to 5.0)
+ *                - IOU box valid threshold (optional, default set to 0.5)
+ *            The default parameters value could be set in the following ways:
+ *            option3=box-priors.txt:0.5:10.0:10.0:5.0:5.0:0.5
+ *            option3=box-priors.txt
+ *            option3=box-priors.txt::::::
+ *
+ *            It's possible to set only few values, using the default values for
+ *            those not specified through the command line.
+ *            You could specify respectively the detection and IOU thresholds to 0.65
+ *            and 0.6 with the option3 parameter as follow:
+ *            option3=box-priors.txt:0.65:::::0.6
+ * option4: Video input Dimension (WIDTH:HEIGHT) (default 300:300)
+ *          This is independent from option1
+ *
+ * @todo Remove duplicate codes*
+ * @todo give support for other models*
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <glib.h>
+#include <gst/gst.h>
+#include <math.h> /** expf */
+#include <nnstreamer_log.h>
+#include <nnstreamer_plugin_api.h>
+#include <nnstreamer_plugin_api_decoder.h>
+#include <nnstreamer_util.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "tensordecutil.h"
+
+#define _tensor_region_size_default_ 1
+void init_tr (void) __attribute__ ((constructor));
+void fini_tr (void) __attribute__ ((destructor));
+
+#define BOX_SIZE (4)
+#define MOBILENET_SSD_DETECTION_MAX (2034) /**add ssd_mobilenet v3 */
+#define MOBILENET_SSD_MAX_TENSORS (2U)
+#define INPUT_VIDEO_WIDTH_DEFAULT (300)
+#define INPUT_VIDEO_HEIGHT_DEFAULT (300)
+/**
+ * @brief There can be different schemes for input tensor.
+ */
+typedef enum
+{
+  MOBILENET_SSD_BOUNDING_BOX = 0,
+  BOUNDING_BOX_UNKNOWN,
+} tensor_region_modes;
+
+/** @brief Internal data structure for identifying cropping region */
+typedef struct {
+  guint x;
+  guint y;
+  guint w;
+  guint h;
+} crop_region;
+
+/**
+ * @brief Data structure for SSD tensor_region info for mobilenet ssd model.
+ */
+typedef struct {
+  /**From option3, box prior data */
+  char *box_prior_path; /**< Box Prior file path */
+  gfloat box_priors[BOX_SIZE][MOBILENET_SSD_DETECTION_MAX + 1]; /** loaded box prior */
+#define MOBILENET_SSD_PARAMS_THRESHOLD_IDX 0
+#define MOBILENET_SSD_PARAMS_Y_SCALE_IDX 1
+#define MOBILENET_SSD_PARAMS_X_SCALE_IDX 2
+#define MOBILENET_SSD_PARAMS_H_SCALE_IDX 3
+#define MOBILENET_SSD_PARAMS_W_SCALE_IDX 4
+#define MOBILENET_SSD_PARAMS_IOU_THRESHOLD_IDX 5
+#define MOBILENET_SSD_PARAMS_MAX 6
+
+  gfloat params[MOBILENET_SSD_PARAMS_MAX]; /** Post Processing parameters */
+  gfloat sigmoid_threshold; /** Inverse value of valid detection threshold in sigmoid domain */
+} properties_MOBILENET_SSD;
+
+/** @brief Internal data structure for tensor region */
+typedef struct {
+  tensor_region_modes mode;
+  union {
+    properties_MOBILENET_SSD mobilenet_ssd; /**< Properties for mobilenet_ssd  */
+  };
+  imglabel_t labeldata;
+  char *label_path;
+
+  /**From option4 */
+  guint i_width; /**< Input Video Width */
+  guint i_height; /**< Input Video Height */
+
+  /**From option1 */
+  guint num;  /**number of cropped regions required */
+
+  guint max_detections;
+  GArray *regions;
+  gboolean flag_use_label;
+
+} tensor_region;
+
+
+/** @brief Mathematic inverse of sigmoid function, aka logit */
+static float
+logit (float x)
+{
+  if (x <= 0.0f)
+    return -INFINITY;
+
+  if (x >= 1.0f)
+    return INFINITY;
+
+  return log (x / (1.0 - x));
+}
+
+/**
+ * @brief Load box-prior data from a file
+ * @param[in/out] trData The internal data.
+ * @return TRUE if loaded and configured. FALSE if failed to do so.
+ */
+static int
+_mobilenet_ssd_loadBoxPrior (tensor_region *trData)
+{
+  properties_MOBILENET_SSD *mobilenet_ssd = &trData->mobilenet_ssd;
+  gboolean failed = FALSE;
+  GError *err = NULL;
+  gchar **priors;
+  gchar *line = NULL;
+  gchar *contents = NULL;
+  guint row;
+  gint prev_reg = -1;
+
+  /**Read file contents */
+  if (!g_file_get_contents (mobilenet_ssd->box_prior_path, &contents, NULL, &err)) {
+    GST_ERROR ("Decoder/Tensor-Region/SSD's box prior file %s cannot be read: %s",
+        mobilenet_ssd->box_prior_path, err->message);
+    g_clear_error (&err);
+    return FALSE;
+  }
+
+  priors = g_strsplit (contents, "\n", -1);
+  /**If given prior file is inappropriate, report back to tensor-decoder */
+  if (g_strv_length (priors) < BOX_SIZE) {
+    ml_loge ("The given prior file, %s, should have at least %d lines.\n",
+        mobilenet_ssd->box_prior_path, BOX_SIZE);
+    failed = TRUE;
+    goto error;
+  }
+
+  for (row = 0; row < BOX_SIZE; row++) {
+    gint column = 0, registered = 0;
+
+    line = priors[row];
+    if (line) {
+      gchar **list = g_strsplit_set (line, " \t,", -1);
+      gchar *word;
+
+      while ((word = list[column]) != NULL) {
+        column++;
+
+        if (word && *word) {
+          if (registered > MOBILENET_SSD_DETECTION_MAX) {
+            GST_WARNING ("Decoder/Tensor-region/SSD's box prior data file has too many priors. %d >= %d",
+                registered, MOBILENET_SSD_DETECTION_MAX);
+            break;
+          }
+          mobilenet_ssd->box_priors[row][registered]
+              = (gfloat) g_ascii_strtod (word, NULL);
+          registered++;
+        }
+      }
+
+      g_strfreev (list);
+    }
+
+    if (prev_reg != -1 && prev_reg != registered) {
+      GST_ERROR ("Decoder/Tensor-Region/SSD's box prior data file is not consistent.");
+      failed = TRUE;
+      break;
+    }
+    prev_reg = registered;
+  }
+
+error:
+  g_strfreev (priors);
+  g_free (contents);
+  return !failed;
+}
+
+
+/** @brief Initialize tensor_region per mode */
+static int
+_init_modes (tensor_region * trData){
+  if(trData->mode == MOBILENET_SSD_BOUNDING_BOX){
+    properties_MOBILENET_SSD *data = &trData->mobilenet_ssd;
+#define DETECTION_THRESHOLD (.5f)
+#define THRESHOLD_IOU (.5f)
+#define Y_SCALE (10.0f)
+#define X_SCALE (10.0f)
+#define H_SCALE (5.0f)
+#define W_SCALE (5.0f)
+
+    data->params[MOBILENET_SSD_PARAMS_THRESHOLD_IDX] = DETECTION_THRESHOLD;
+    data->params[MOBILENET_SSD_PARAMS_Y_SCALE_IDX] = Y_SCALE;
+    data->params[MOBILENET_SSD_PARAMS_X_SCALE_IDX] = X_SCALE;
+    data->params[MOBILENET_SSD_PARAMS_H_SCALE_IDX] = H_SCALE;
+    data->params[MOBILENET_SSD_PARAMS_W_SCALE_IDX] = W_SCALE;
+    data->params[MOBILENET_SSD_PARAMS_IOU_THRESHOLD_IDX] = THRESHOLD_IOU;
+    data->sigmoid_threshold = logit (DETECTION_THRESHOLD);
+    return TRUE;
+  }
+  return TRUE;
+}
+
+
+/** @brief tensordec-plugin's GstTensorDecoderDef callback */
+static int
+tr_init (void **pdata)
+{
+  tensor_region *trData;
+  trData = *pdata = g_new0 (tensor_region, 1);
+  if (*pdata == NULL) {
+    GST_ERROR ("Failed to allocate memory for decoder subplugin.");
+    return FALSE;
+  }
+  trData->mode = MOBILENET_SSD_BOUNDING_BOX;
+  trData->num = 1;
+  trData->max_detections = 0;
+  trData->regions = NULL;
+  trData->flag_use_label = FALSE;
+  trData->i_width = INPUT_VIDEO_WIDTH_DEFAULT;
+  trData->i_height = INPUT_VIDEO_HEIGHT_DEFAULT;
+  return _init_modes(trData);
+}
+
+/** @brief tensordec-plugin's GstTensorDecoderDef callback */
+static void
+tr_exit (void **pdata)
+{
+  tensor_region *trData = *pdata;
+  g_array_free (trData->regions, TRUE);
+  _free_labels (&trData->labeldata);
+
+  if (trData->label_path)
+    g_free (trData->label_path);
+  g_free (*pdata);
+  *pdata = NULL;
+}
+
+/** @brief configure per-mode option (option1) */
+static int
+_setOption_mode (tensor_region *trData, const char *param)
+{
+  if(trData->mode == MOBILENET_SSD_BOUNDING_BOX){
+    properties_MOBILENET_SSD *mobilenet_ssd = &trData->mobilenet_ssd;
+    gchar **options;
+    int noptions, idx;
+    int ret = 1;
+
+    options = g_strsplit (param, ":", -1);
+    noptions = g_strv_length (options);
+
+    if (mobilenet_ssd->box_prior_path)
+      g_free (mobilenet_ssd->box_prior_path);
+
+    mobilenet_ssd->box_prior_path = g_strdup (options[0]);
+
+    if (NULL != mobilenet_ssd->box_prior_path) {
+      ret = _mobilenet_ssd_loadBoxPrior (trData);
+      if (ret == 0)
+        goto exit_mobilenet_ssd;
+    }
+
+    for (idx = 1; idx < noptions; idx++) {
+      if (strlen (options[idx]) == 0)
+        continue;
+      mobilenet_ssd->params[idx - 1] = strtod (options[idx], NULL);
+    }
+
+    mobilenet_ssd->sigmoid_threshold
+        = logit (mobilenet_ssd->params[MOBILENET_SSD_PARAMS_THRESHOLD_IDX]);
+  exit_mobilenet_ssd:
+    g_strfreev (options);
+    return ret;
+  }
+  return TRUE;
+}
+
+/** @brief tensordec-plugin's GstTensorDecoderDef callback */
+static int
+tr_setOption (void **pdata, int opNum, const char *param)
+{
+  tensor_region *trData = *pdata;
+  if (opNum == 0) {
+    /**option1 number of crop regions required */
+    trData->num = atoi (param);
+    return TRUE;
+  } else if (opNum == 1) {
+    /**option2 label path for mobilenet_ssd model */
+    if (NULL != trData->label_path)
+      g_free (trData->label_path);
+    trData->label_path = g_strdup (param);
+
+    if (NULL != trData->label_path)
+      loadImageLabels (trData->label_path, &trData->labeldata);
+
+    if (trData->labeldata.total_labels > 0)
+      return TRUE;
+    else
+      return FALSE;
+  } else if (opNum == 2){
+    /**option3 setting box prior path for mobilenet ssd model */
+    return _setOption_mode (trData, param);
+  }
+  else if (opNum == 3) {
+    /**option4 = input model size (width:height) */
+    tensor_dim dim;
+    int rank = gst_tensor_parse_dimension (param, dim);
+
+    trData->i_width = 0;
+    trData->i_height = 0;
+    if (param == NULL || *param == '\0')
+      return TRUE;
+
+    if (rank < 2) {
+      GST_ERROR ("mode-option-4 of tensor region is input video dimension (WIDTH:HEIGHT). The given parameter, \"%s\", is not acceptable.",
+          param);
+      return TRUE; /**Ignore this param */
+    }
+    if (rank > 2) {
+      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.",
+          param);
+    }
+    trData->i_width = dim[0];
+    trData->i_height = dim[1];
+    return TRUE;
+  }
+
+  GST_INFO ("Property mode-option-%d is ignored", opNum + 1);
+  return TRUE;
+}
+
+typedef struct {
+  int valid;
+  int class_id;
+  gfloat score;
+  int x;
+  int y;
+  int height;
+  int width;
+} detected_object;
+
+/**
+ * @brief transfer crop region info with the given results to the output buffer
+ * @param[out] out_info The output buffer
+ * @param[in] data The Tensor_region internal data.
+ * @param[in] results The final results to be transfered.
+ */
+static void
+gst_tensor_top_detectedObjects_cropInfo (GstMapInfo *out_info, const tensor_region *data, GArray *results)
+{
+
+  guint i;
+  guint size = sizeof (crop_region); /**Assuming crop_region is a structure with four integer fields */
+  guint *out_data = (guint *)out_info->data;
+  crop_region region;
+  guint maxx = MIN (results->len, data->num);
+  for (i = 0; i < maxx; i++) {
+    detected_object *temp = &g_array_index (results, detected_object, i);
+    region.x = temp->x;
+    region.y = temp->y;
+    region.w = temp->width;
+    region.h = temp->height;
+    memcpy (out_data, &region, size);
+    out_data += size / sizeof (guint);
+  }
+}
+
+#define _expit(x) (1.f / (1.f + expf (-((float) x))))
+
+
+/**
+ * @brief C++-Template-like box location calculation for box-priors
+ * @bug This is not macro-argument safe. Use paranthesis!
+ * @param[in] bb The configuration, "tensor region"
+ * @param[in] index The index (3rd dimension of BOX_SIZE:1:MOBILENET_SSD_DETECTION_MAX:1)
+ * @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)
+ * @param[in] boxprior The box prior data from the box file of SSD.
+ * @param[in] boxinputptr Cursor pointer of input + byte-per-index * index (box)
+ * @param[in] detinputptr Cursor pointer of input + byte-per-index * index (detection)
+ * @param[in] result The object returned. (pointer to object)
+ */
+#define _get_object_i_mobilenet_ssd(                                              \
+    bb, index, total_labels, boxprior, boxinputptr, detinputptr, result)          \
+  do {                                                                            \
+    unsigned int c;                                                               \
+    properties_MOBILENET_SSD *data = &bb->mobilenet_ssd;                          \
+    float sigmoid_threshold = data->sigmoid_threshold;                            \
+    float y_scale = data->params[MOBILENET_SSD_PARAMS_Y_SCALE_IDX];               \
+    float x_scale = data->params[MOBILENET_SSD_PARAMS_X_SCALE_IDX];               \
+    float h_scale = data->params[MOBILENET_SSD_PARAMS_H_SCALE_IDX];               \
+    float w_scale = data->params[MOBILENET_SSD_PARAMS_W_SCALE_IDX];               \
+    result->valid = FALSE;                                                        \
+    for (c = 1; c < total_labels; c++) {                                          \
+      if (detinputptr[c] >= sigmoid_threshold) {                                  \
+        gfloat score = _expit (detinputptr[c]);                                   \
+        float ycenter                                                             \
+            = boxinputptr[0] / y_scale * boxprior[2][index] + boxprior[0][index]; \
+        float xcenter                                                             \
+            = boxinputptr[1] / x_scale * boxprior[3][index] + boxprior[1][index]; \
+        float h = (float) expf (boxinputptr[2] / h_scale) * boxprior[2][index];   \
+        float w = (float) expf (boxinputptr[3] / w_scale) * boxprior[3][index];   \
+        float ymin = ycenter - h / 2.f;                                           \
+        float xmin = xcenter - w / 2.f;                                           \
+        int x = xmin * bb->i_width;                                               \
+        int y = ymin * bb->i_height;                                              \
+        int width = w * bb->i_width;                                              \
+        int height = h * bb->i_height;                                            \
+        result->class_id = c;                                                     \
+        result->x = MAX (0, x);                                                   \
+        result->y = MAX (0, y);                                                   \
+        result->width = width;                                                    \
+        result->height = height;                                                  \
+        result->score = score;                                                    \
+        result->valid = TRUE;                                                     \
+        break;                                                                    \
+      }                                                                           \
+    }                                                                             \
+  } while (0);
+
+/**
+ * @brief C++-Template-like box location calculation for box-priors for Mobilenet SSD Model
+ * @param[in] bb The configuration, "tensor region"
+ * @param[in] type The tensor type of inputptr
+ * @param[in] typename nnstreamer enum corresponding to the type
+ * @param[in] boxprior The box prior data from the box file of MOBILENET_SSD.
+ * @param[in] boxinput Input Tensor Data (Boxes)
+ * @param[in] detinput Input Tensor Data (Detection). Null if not available. (numtensor ==1)
+ * @param[in] config Tensor configs of the input tensors
+ * @param[out] results The object returned. (GArray with detectedObject)
+ */
+#define _get_objects_mobilenet_ssd(                                                         \
+    bb, _type, typename, boxprior, boxinput, detinput, config, results)                     \
+  case typename:                                                                            \
+    {                                                                                       \
+      int d;                                                                                \
+      _type *boxinput_ = (_type *) boxinput;                                                \
+      size_t boxbpi = config->info.info[0].dimension[0];                                    \
+      _type *detinput_ = (_type *) detinput;                                                \
+      size_t detbpi = config->info.info[1].dimension[0];                                    \
+      int num = (MOBILENET_SSD_DETECTION_MAX > bb->max_detections) ?                        \
+                    bb->max_detections :                                                    \
+                    MOBILENET_SSD_DETECTION_MAX;                                            \
+      detected_object object = {                                                            \
+        .valid = FALSE, .class_id = 0, .x = 0, .y = 0, .width = 0, .height = 0, .score = .0 \
+      };                                                                                    \
+      for (d = 0; d < num; d++) {                                                           \
+        _get_object_i_mobilenet_ssd (bb, d, detbpi, boxprior,                               \
+            (boxinput_ + (d * boxbpi)), (detinput_ + (d * detbpi)), (&object));             \
+        if (object.valid == TRUE) {                                                         \
+          g_array_append_val (results, object);                                             \
+        }                                                                                   \
+      }                                                                                     \
+    }                                                                                       \
+    break
+
+/** @brief Macro to simplify calling _get_objects_mobilenet_ssd */
+#define _get_objects_mobilenet_ssd_(type, typename)                                       \
+  _get_objects_mobilenet_ssd (trData, type, typename, (trData->mobilenet_ssd.box_priors), \
+      (boxes->data), (detections->data), config, results)
+
+/**
+ * @brief Compare Function for g_array_sort with detectedObject.
+ */
+static gint
+compare_detection (gconstpointer _a, gconstpointer _b)
+{
+  const detected_object *a = _a;
+  const detected_object *b = _b;
+
+  /**Larger comes first */
+  return (a->score > b->score) ? -1 : ((a->score == b->score) ? 0 : 1);
+}
+
+/**
+ * @brief Calculate the intersected surface
+ */
+static gfloat
+iou (detected_object *a, detected_object *b)
+{
+  int x1 = MAX (a->x, b->x);
+  int y1 = MAX (a->y, b->y);
+  int x2 = MIN (a->x + a->width, b->x + b->width);
+  int y2 = MIN (a->y + a->height, b->y + b->height);
+  int w = MAX (0, (x2 - x1 + 1));
+  int h = MAX (0, (y2 - y1 + 1));
+  float inter = w * h;
+  float areaA = a->width * a->height;
+  float areaB = b->width * b->height;
+  float o = inter / (areaA + areaB - inter);
+  return (o >= 0) ? o : 0;
+}
+
+/**
+ * @brief Apply NMS to the given results (objects[])
+ * @param[in/out] results The results to be filtered with nms
+ */
+static void
+nms (GArray *results, gfloat threshold)
+{
+  guint boxes_size;
+  guint i, j;
+
+  boxes_size = results->len;
+  if (boxes_size == 0U)
+    return;
+
+  g_array_sort (results, compare_detection);
+
+  for (i = 0; i < boxes_size; i++) {
+    detected_object *a = &g_array_index (results, detected_object, i);
+    if (a->valid == TRUE) {
+      for (j = i + 1; j < boxes_size; j++) {
+        detected_object *b = &g_array_index (results, detected_object, j);
+        if (b->valid == TRUE) {
+          if (iou (a, b) > threshold) {
+            b->valid = FALSE;
+          }
+        }
+      }
+    }
+  }
+
+  i = 0;
+  do {
+    detected_object *a = &g_array_index (results, detected_object, i);
+    if (a->valid == FALSE)
+      g_array_remove_index (results, i);
+    else
+      i++;
+  } while (i < results->len);
+}
+
+/**
+ * @brief Private function to initialize the meta info
+ */
+static void
+init_meta(GstTensorMetaInfo * meta, const tensor_region * trData){
+  gst_tensor_meta_info_init (meta);
+  meta->type = _NNS_UINT32;
+  meta->dimension[0] = BOX_SIZE;
+  meta->dimension[1] = trData->num;
+  meta->media_type = _NNS_TENSOR;
+}
+
+/** @brief tensordec-plugin's GstTensorDecoderDef callback */
+static GstFlowReturn
+tr_decode (void **pdata, const GstTensorsConfig *config,
+    const GstTensorMemory *input, GstBuffer *outbuf)
+{
+  tensor_region *trData = *pdata;
+  GstTensorMetaInfo meta;
+  GstMapInfo out_info;
+  GstMemory *out_mem;
+  GArray *results = NULL;
+  const guint num_tensors = config->info.num_tensors;
+  gboolean need_output_alloc = gst_buffer_get_size (outbuf) == 0;
+  const size_t size = (size_t) 4 * trData->num * sizeof(uint32_t); /**4 field per block */
+
+  g_assert (outbuf);
+  /**Ensure we have outbuf properly allocated */
+  if (need_output_alloc) {
+    out_mem = gst_allocator_alloc (NULL, size, NULL);
+  } else {
+    if (gst_buffer_get_size (outbuf) < size) {
+      gst_buffer_set_size (outbuf, size);
+    }
+    out_mem = gst_buffer_get_all_memory (outbuf);
+  }
+  if (!gst_memory_map (out_mem, &out_info, GST_MAP_WRITE)) {
+    ml_loge ("Cannot map output memory / tensordec-tensor_region.\n");
+    goto error_free;
+  }
+
+  /** reset the buffer with 0 */
+  memset (out_info.data, 0, size);
+  if(trData->mode == MOBILENET_SSD_BOUNDING_BOX){
+    const GstTensorMemory *boxes, *detections = NULL;
+    properties_MOBILENET_SSD *data = &trData->mobilenet_ssd;
+
+    g_assert (num_tensors >= MOBILENET_SSD_MAX_TENSORS);
+    results = g_array_sized_new (FALSE, TRUE, sizeof (detected_object), 100);
+
+    boxes = &input[0];
+    if (num_tensors >= MOBILENET_SSD_MAX_TENSORS) /**lgtm[cpp/constant-comparison] */
+      detections = &input[1];
+
+    switch (config->info.info[0].type) {
+      _get_objects_mobilenet_ssd_ (uint8_t, _NNS_UINT8);
+      _get_objects_mobilenet_ssd_ (int8_t, _NNS_INT8);
+      _get_objects_mobilenet_ssd_ (uint16_t, _NNS_UINT16);
+      _get_objects_mobilenet_ssd_ (int16_t, _NNS_INT16);
+      _get_objects_mobilenet_ssd_ (uint32_t, _NNS_UINT32);
+      _get_objects_mobilenet_ssd_ (int32_t, _NNS_INT32);
+      _get_objects_mobilenet_ssd_ (uint64_t, _NNS_UINT64);
+      _get_objects_mobilenet_ssd_ (int64_t, _NNS_INT64);
+      _get_objects_mobilenet_ssd_ (float, _NNS_FLOAT32);
+      _get_objects_mobilenet_ssd_ (double, _NNS_FLOAT64);
+      default:
+        g_assert (0);
+    }
+
+    nms (results, data->params[MOBILENET_SSD_PARAMS_IOU_THRESHOLD_IDX]);
+  } else {
+    GST_ERROR ("Failed to get output buffer, unknown mode %d.", trData->mode);
+    goto error_unmap;
+  }
+  gst_tensor_top_detectedObjects_cropInfo (&out_info, trData, results);
+
+  g_array_free (results, TRUE);
+
+  gst_memory_unmap (out_mem, &out_info);
+
+  /** converting to Flexible tensor since
+   * info pad of tensor_crop has capability for flexible tensor stream
+  */
+  init_meta(&meta, trData);
+  out_mem = gst_tensor_meta_info_append_header(&meta, out_mem);
+
+  if (need_output_alloc)
+    gst_buffer_append_memory (outbuf, out_mem);
+  else
+    gst_memory_unref (out_mem);
+
+  return GST_FLOW_OK;
+error_unmap:
+  gst_memory_unmap (out_mem, &out_info);
+error_free:
+  gst_memory_unref (out_mem);
+
+  return GST_FLOW_ERROR;
+}
+
+/**
+ * @brief set the max_detection
+ */
+static int
+_set_max_detection (tensor_region *data, guint max_detection, unsigned int limit)
+{
+  /**Check consistency with max_detection */
+  if (data->max_detections == 0)
+    data->max_detections = max_detection;
+  else
+    g_return_val_if_fail (max_detection == data->max_detections, FALSE);
+
+  if (data->max_detections > limit) {
+    GST_ERROR ("Incoming tensor has too large detection-max : %u", max_detection);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+
+/**
+ * @brief tensordec-plugin's GstTensorDecoderDef callback
+ *
+ * [Mobilenet SSD Model]
+ * The first tensor is boxes. BOX_SIZE : 1 : #MaxDetection, ANY-TYPE
+ * The second tensor is labels. #MaxLabel : #MaxDetection, ANY-TYPE
+ * Both tensors are MANDATORY!
+ * If there are third or more tensors, such tensors will be ignored.
+ */
+
+static GstCaps *
+tr_getOutCaps (void **pdata, const GstTensorsConfig *config)
+{
+  tensor_region *data = *pdata;
+  GstCaps *caps;
+  char *str;
+  guint max_detection, max_label;
+  const uint32_t *dim1, *dim2;
+  int i;
+
+  /**Check if the first tensor is compatible */
+  dim1 = config->info.info[0].dimension;
+  g_return_val_if_fail (dim1[0] == BOX_SIZE, NULL);
+  g_return_val_if_fail (dim1[1] == 1, NULL);
+  max_detection = dim1[2];
+  g_return_val_if_fail (max_detection > 0, NULL);
+  /** @todo unused dimension value should be 0 */
+  for (i = 3; i < NNS_TENSOR_RANK_LIMIT; i++)
+    g_return_val_if_fail (dim1[i] == 0 || dim1[i] == 1, NULL);
+
+  /**Check if the second tensor is compatible */
+  dim2 = config->info.info[1].dimension;
+  max_label = dim2[0];
+  g_return_val_if_fail (max_label <= data->labeldata.total_labels, NULL);
+  if (max_label < data->labeldata.total_labels)
+    GST_WARNING ("The given tensor (2nd) has max_label (first dimension: %u) smaller than the number of labels in labels file (%s: %u).",
+        max_label, data->label_path, data->labeldata.total_labels);
+  g_return_val_if_fail (max_detection == dim2[1], NULL);
+  for (i = 2; i < NNS_TENSOR_RANK_LIMIT; i++)
+    g_return_val_if_fail (dim2[i] == 0 || dim2[i] == 1, NULL);
+
+  /**Check consistency with max_detection */
+  if (!_set_max_detection (data, max_detection, MOBILENET_SSD_DETECTION_MAX)) {
+    return NULL;
+  }
+  str = g_strdup_printf("other/tensors,format=flexible");
+  caps = gst_caps_from_string (str);
+  setFramerateFromConfig (caps, config);
+  g_free (str);
+  (void) *pdata;
+  return caps;
+}
+
+
+static gchar decoder_subplugin_tensor_region[] = "tensor_region";
+
+/** @brief Tensor Region tensordec-plugin GstTensorDecoderDef instance */
+static GstTensorDecoderDef tensorRegion = { .modename = decoder_subplugin_tensor_region,
+  .init = tr_init,
+  .exit = tr_exit,
+  .setOption = tr_setOption,
+  .getOutCaps = tr_getOutCaps,
+  /** .getTransformSize = tr_getTransformsize, */
+  .decode = tr_decode };
+
+/** @brief Initialize this object for tensordec-plugin */
+void
+init_tr (void)
+{
+  nnstreamer_decoder_probe (&tensorRegion);
+}
+
+/** @brief Destruct this object for tensordec-plugin */
+void
+fini_tr (void)
+{
+  nnstreamer_decoder_exit (tensorRegion.modename);
+}