LCOV - code coverage report
Current view: top level - nnstreamer-2.4.2/ext/nnstreamer/tensor_decoder - tensordec-tensor_region.c (source / functions) Coverage Total Hit
Test: nnstreamer 2.4.2-0 nnstreamer/nnstreamer#eca68b8d050408568af95d831a8eef62aaee7784 Lines: 76.0 % 292 222
Test Date: 2025-03-13 05:38:21 Functions: 100.0 % 18 18

            Line data    Source code
       1              : /* SPDX-License-Identifier: LGPL-2.1-only */
       2              : /**
       3              :  * GStreamer / NNStreamer tensor_decoder subplugin, "tensor_region"
       4              :  * Copyright (C) 2023 Harsh Jain <hjain24in@gmail.com>
       5              :  */
       6              : /**
       7              :  * @file        tensordec-tensor_region.c
       8              :  * @date        15th June, 2023
       9              :  * @brief       NNStreamer tensor-decoder subplugin, "tensor region",
      10              :  *              which converts tensors to cropping info for tensor _crop element
      11              :  *              This code is NYI/WIP and not compilable.
      12              :  *
      13              :  * @see         https://github.com/nnstreamer/nnstreamer
      14              :  * @author      Harsh Jain <hjain24in@gmail.com>
      15              :  * @bug         No known bugs except for NYI items
      16              :  *
      17              :  * option1: number of cropping regions required (default is 1)
      18              :  * option2: Location of label file
      19              :  *          This is independent from option1
      20              :  * option3:
      21              :  *          for mobilenet-ssd mode:
      22              :  *            The option3 definition scheme is, in order, the following:
      23              :  *                - box priors location file (mandatory)
      24              :  *                - Detection threshold (optional, default set to 0.5)
      25              :  *                - Y box scale (optional, default set to 10.0)
      26              :  *                - X box scale (optional, default set to 10.0)
      27              :  *                - h box scale (optional, default set to 5.0)
      28              :  *                - w box scale (optional, default set to 5.0)
      29              :  *                - IOU box valid threshold (optional, default set to 0.5)
      30              :  *            The default parameters value could be set in the following ways:
      31              :  *            option3=box-priors.txt:0.5:10.0:10.0:5.0:5.0:0.5
      32              :  *            option3=box-priors.txt
      33              :  *            option3=box-priors.txt::::::
      34              :  *
      35              :  *            It's possible to set only few values, using the default values for
      36              :  *            those not specified through the command line.
      37              :  *            You could specify respectively the detection and IOU thresholds to 0.65
      38              :  *            and 0.6 with the option3 parameter as follow:
      39              :  *            option3=box-priors.txt:0.65:::::0.6
      40              :  * option4: Video input Dimension (WIDTH:HEIGHT) (default 300:300)
      41              :  *          This is independent from option1
      42              :  *
      43              :  * @todo Remove duplicate codes*
      44              :  * @todo give support for other models*
      45              :  */
      46              : 
      47              : #include <glib.h>
      48              : #include <gst/gst.h>
      49              : #include <math.h> /** expf */
      50              : #include <nnstreamer_log.h>
      51              : #include <nnstreamer_plugin_api.h>
      52              : #include <nnstreamer_plugin_api_decoder.h>
      53              : #include <nnstreamer_util.h>
      54              : #include <stdint.h>
      55              : #include <stdio.h>
      56              : #include <stdlib.h>
      57              : #include <string.h>
      58              : #include "tensordecutil.h"
      59              : 
      60              : #define _tensor_region_size_default_ 1
      61              : void init_tr (void) __attribute__ ((constructor));
      62              : void fini_tr (void) __attribute__ ((destructor));
      63              : 
      64              : #define BOX_SIZE (4)
      65              : #define MOBILENET_SSD_DETECTION_MAX (2034) /**add ssd_mobilenet v3 */
      66              : #define MOBILENET_SSD_MAX_TENSORS (2U)
      67              : #define INPUT_VIDEO_WIDTH_DEFAULT (300)
      68              : #define INPUT_VIDEO_HEIGHT_DEFAULT (300)
      69              : /**
      70              :  * @brief There can be different schemes for input tensor.
      71              :  */
      72              : typedef enum
      73              : {
      74              :   MOBILENET_SSD_BOUNDING_BOX = 0,
      75              :   BOUNDING_BOX_UNKNOWN,
      76              : } tensor_region_modes;
      77              : 
      78              : /** @brief Internal data structure for identifying cropping region */
      79              : typedef struct {
      80              :   guint x;
      81              :   guint y;
      82              :   guint w;
      83              :   guint h;
      84              : } crop_region;
      85              : 
      86              : /**
      87              :  * @brief Data structure for SSD tensor_region info for mobilenet ssd model.
      88              :  */
      89              : typedef struct {
      90              :   /**From option3, box prior data */
      91              :   char *box_prior_path; /**< Box Prior file path */
      92              :   gfloat box_priors[BOX_SIZE][MOBILENET_SSD_DETECTION_MAX + 1]; /** loaded box prior */
      93              : #define MOBILENET_SSD_PARAMS_THRESHOLD_IDX 0
      94              : #define MOBILENET_SSD_PARAMS_Y_SCALE_IDX 1
      95              : #define MOBILENET_SSD_PARAMS_X_SCALE_IDX 2
      96              : #define MOBILENET_SSD_PARAMS_H_SCALE_IDX 3
      97              : #define MOBILENET_SSD_PARAMS_W_SCALE_IDX 4
      98              : #define MOBILENET_SSD_PARAMS_IOU_THRESHOLD_IDX 5
      99              : #define MOBILENET_SSD_PARAMS_MAX 6
     100              : 
     101              :   gfloat params[MOBILENET_SSD_PARAMS_MAX]; /** Post Processing parameters */
     102              :   gfloat sigmoid_threshold; /** Inverse value of valid detection threshold in sigmoid domain */
     103              : } properties_MOBILENET_SSD;
     104              : 
     105              : /** @brief Internal data structure for tensor region */
     106              : typedef struct {
     107              :   tensor_region_modes mode; /**< When a mode is being changed, _cleanup_mode_properties () should be called before changing it */
     108              :   union {
     109              :     properties_MOBILENET_SSD mobilenet_ssd; /**< Properties for mobilenet_ssd  */
     110              :   };
     111              :   imglabel_t labeldata;
     112              :   char *label_path;
     113              : 
     114              :   /**From option4 */
     115              :   guint i_width; /**< Input Video Width */
     116              :   guint i_height; /**< Input Video Height */
     117              : 
     118              :   /**From option1 */
     119              :   guint num;  /**number of cropped regions required */
     120              : 
     121              :   guint max_detections;
     122              :   GArray *regions;
     123              :   gboolean flag_use_label;
     124              : 
     125              : } tensor_region;
     126              : 
     127              : /** @brief Internal function for mode change preparation */
     128              : static void
     129            2 : _cleanup_mode_properties (tensor_region *tr)
     130              : {
     131            2 :   switch (tr->mode) {
     132            2 :   case MOBILENET_SSD_BOUNDING_BOX: {
     133            2 :     properties_MOBILENET_SSD *mobilenet_ssd = &tr->mobilenet_ssd;
     134            2 :     g_free (mobilenet_ssd->box_prior_path);
     135            2 :     break;
     136              :   }
     137            0 :   default:
     138            0 :     break;
     139              :   }
     140            2 : }
     141              : 
     142              : /** @brief Mathematic inverse of sigmoid function, aka logit */
     143              : static float
     144            4 : logit (float x)
     145              : {
     146            4 :   if (x <= 0.0f)
     147            0 :     return -INFINITY;
     148              : 
     149            4 :   if (x >= 1.0f)
     150            0 :     return INFINITY;
     151              : 
     152            4 :   return log (x / (1.0 - x));
     153              : }
     154              : 
     155              : /**
     156              :  * @brief Load box-prior data from a file
     157              :  * @param[in/out] trData The internal data.
     158              :  * @return TRUE if loaded and configured. FALSE if failed to do so.
     159              :  */
     160              : static int
     161            2 : _mobilenet_ssd_loadBoxPrior (tensor_region *trData)
     162              : {
     163            2 :   properties_MOBILENET_SSD *mobilenet_ssd = &trData->mobilenet_ssd;
     164            2 :   gboolean failed = FALSE;
     165            2 :   GError *err = NULL;
     166              :   gchar **priors;
     167            2 :   gchar *line = NULL;
     168            2 :   gchar *contents = NULL;
     169              :   guint row;
     170            2 :   gint prev_reg = -1;
     171              : 
     172              :   /**Read file contents */
     173            2 :   if (!g_file_get_contents (mobilenet_ssd->box_prior_path, &contents, NULL, &err)) {
     174            0 :     GST_ERROR ("Decoder/Tensor-Region/SSD's box prior file %s cannot be read: %s",
     175              :         mobilenet_ssd->box_prior_path, err->message);
     176            0 :     g_clear_error (&err);
     177            2 :     return FALSE;
     178              :   }
     179              : 
     180            2 :   priors = g_strsplit (contents, "\n", -1);
     181              :   /**If given prior file is inappropriate, report back to tensor-decoder */
     182            2 :   if (g_strv_length (priors) < BOX_SIZE) {
     183            0 :     ml_loge ("The given prior file, %s, should have at least %d lines.\n",
     184              :         mobilenet_ssd->box_prior_path, BOX_SIZE);
     185            0 :     failed = TRUE;
     186            0 :     goto error;
     187              :   }
     188              : 
     189           10 :   for (row = 0; row < BOX_SIZE; row++) {
     190            8 :     gint column = 0, registered = 0;
     191              : 
     192            8 :     line = priors[row];
     193            8 :     if (line) {
     194            8 :       gchar **list = g_strsplit_set (line, " \t,", -1);
     195              :       gchar *word;
     196              : 
     197        15416 :       while ((word = list[column]) != NULL) {
     198        15408 :         column++;
     199              : 
     200        15408 :         if (word && *word) {
     201        15336 :           if (registered > MOBILENET_SSD_DETECTION_MAX) {
     202            0 :             GST_WARNING ("Decoder/Tensor-region/SSD's box prior data file has too many priors. %d >= %d",
     203              :                 registered, MOBILENET_SSD_DETECTION_MAX);
     204            0 :             break;
     205              :           }
     206              :           mobilenet_ssd->box_priors[row][registered]
     207        15336 :               = (gfloat) g_ascii_strtod (word, NULL);
     208        15336 :           registered++;
     209              :         }
     210              :       }
     211              : 
     212            8 :       g_strfreev (list);
     213              :     }
     214              : 
     215            8 :     if (prev_reg != -1 && prev_reg != registered) {
     216            0 :       GST_ERROR ("Decoder/Tensor-Region/SSD's box prior data file is not consistent.");
     217            0 :       failed = TRUE;
     218            0 :       break;
     219              :     }
     220            8 :     prev_reg = registered;
     221              :   }
     222              : 
     223            2 : error:
     224            2 :   g_strfreev (priors);
     225            2 :   g_free (contents);
     226            2 :   return !failed;
     227              : }
     228              : 
     229              : 
     230              : /** @brief Initialize tensor_region per mode */
     231              : static int
     232            2 : _init_modes (tensor_region * trData){
     233            2 :   if(trData->mode == MOBILENET_SSD_BOUNDING_BOX){
     234            2 :     properties_MOBILENET_SSD *data = &trData->mobilenet_ssd;
     235              : #define DETECTION_THRESHOLD (.5f)
     236              : #define THRESHOLD_IOU (.5f)
     237              : #define Y_SCALE (10.0f)
     238              : #define X_SCALE (10.0f)
     239              : #define H_SCALE (5.0f)
     240              : #define W_SCALE (5.0f)
     241              : 
     242            2 :     data->params[MOBILENET_SSD_PARAMS_THRESHOLD_IDX] = DETECTION_THRESHOLD;
     243            2 :     data->params[MOBILENET_SSD_PARAMS_Y_SCALE_IDX] = Y_SCALE;
     244            2 :     data->params[MOBILENET_SSD_PARAMS_X_SCALE_IDX] = X_SCALE;
     245            2 :     data->params[MOBILENET_SSD_PARAMS_H_SCALE_IDX] = H_SCALE;
     246            2 :     data->params[MOBILENET_SSD_PARAMS_W_SCALE_IDX] = W_SCALE;
     247            2 :     data->params[MOBILENET_SSD_PARAMS_IOU_THRESHOLD_IDX] = THRESHOLD_IOU;
     248            2 :     data->sigmoid_threshold = logit (DETECTION_THRESHOLD);
     249            2 :     return TRUE;
     250              :   }
     251            0 :   return TRUE;
     252              : }
     253              : 
     254              : 
     255              : /** @brief tensordec-plugin's GstTensorDecoderDef callback */
     256              : static int
     257            2 : tr_init (void **pdata)
     258              : {
     259              :   tensor_region *trData;
     260            2 :   trData = *pdata = g_new0 (tensor_region, 1);
     261            2 :   if (*pdata == NULL) {
     262            0 :     GST_ERROR ("Failed to allocate memory for decoder subplugin.");
     263            0 :     return FALSE;
     264              :   }
     265            2 :   trData->mode = MOBILENET_SSD_BOUNDING_BOX;
     266            2 :   trData->num = 1;
     267            2 :   trData->max_detections = 0;
     268            2 :   trData->regions = NULL;
     269            2 :   trData->flag_use_label = FALSE;
     270            2 :   trData->i_width = INPUT_VIDEO_WIDTH_DEFAULT;
     271            2 :   trData->i_height = INPUT_VIDEO_HEIGHT_DEFAULT;
     272            2 :   return _init_modes(trData);
     273              : }
     274              : 
     275              : /** @brief tensordec-plugin's GstTensorDecoderDef callback */
     276              : static void
     277            2 : tr_exit (void **pdata)
     278              : {
     279            2 :   tensor_region *trData = *pdata;
     280            2 :   g_array_free (trData->regions, TRUE);
     281            2 :   _free_labels (&trData->labeldata);
     282            2 :   _cleanup_mode_properties (trData);
     283              : 
     284            2 :   if (trData->label_path)
     285            2 :     g_free (trData->label_path);
     286            2 :   g_free (*pdata);
     287            2 :   *pdata = NULL;
     288            2 : }
     289              : 
     290              : /** @brief configure per-mode option (option1) */
     291              : static int
     292            2 : _setOption_mode (tensor_region *trData, const char *param)
     293              : {
     294            2 :   if (trData->mode == MOBILENET_SSD_BOUNDING_BOX) {
     295            2 :     properties_MOBILENET_SSD *mobilenet_ssd = &trData->mobilenet_ssd;
     296              :     gchar **options;
     297              :     int noptions, idx;
     298            2 :     int ret = 1;
     299              : 
     300            2 :     options = g_strsplit (param, ":", -1);
     301            2 :     noptions = g_strv_length (options);
     302              : 
     303            2 :     if (mobilenet_ssd->box_prior_path)
     304            0 :       g_free (mobilenet_ssd->box_prior_path);
     305              : 
     306            2 :     mobilenet_ssd->box_prior_path = g_strdup (options[0]);
     307              : 
     308            2 :     if (NULL != mobilenet_ssd->box_prior_path) {
     309            2 :       ret = _mobilenet_ssd_loadBoxPrior (trData);
     310            2 :       if (ret == 0) {
     311            0 :         g_strfreev (options);
     312            0 :         return ret;
     313              :       }
     314              :     }
     315              : 
     316            2 :     for (idx = 1; idx < noptions; idx++) {
     317            0 :       if (strlen (options[idx]) == 0)
     318            0 :         continue;
     319            0 :       mobilenet_ssd->params[idx - 1] = strtod (options[idx], NULL);
     320              :     }
     321              : 
     322              :     mobilenet_ssd->sigmoid_threshold
     323            2 :         = logit (mobilenet_ssd->params[MOBILENET_SSD_PARAMS_THRESHOLD_IDX]);
     324            2 :     g_strfreev (options);
     325            2 :     return ret;
     326              :   }
     327            0 :   return TRUE;
     328              : }
     329              : 
     330              : /** @brief tensordec-plugin's GstTensorDecoderDef callback */
     331              : static int
     332            6 : tr_setOption (void **pdata, int opNum, const char *param)
     333              : {
     334            6 :   tensor_region *trData = *pdata;
     335            6 :   if (opNum == 0) {
     336              :     /**option1 number of crop regions required */
     337            2 :     trData->num = atoi (param);
     338            2 :     return TRUE;
     339            4 :   } else if (opNum == 1) {
     340              :     /**option2 label path for mobilenet_ssd model */
     341            2 :     if (NULL != trData->label_path)
     342            0 :       g_free (trData->label_path);
     343            2 :     trData->label_path = g_strdup (param);
     344              : 
     345            2 :     if (NULL != trData->label_path)
     346            2 :       loadImageLabels (trData->label_path, &trData->labeldata);
     347              : 
     348            2 :     if (trData->labeldata.total_labels > 0)
     349            2 :       return TRUE;
     350              :     else
     351            0 :       return FALSE;
     352            2 :   } else if (opNum == 2){
     353              :     /**option3 setting box prior path for mobilenet ssd model */
     354            2 :     return _setOption_mode (trData, param);
     355              :   }
     356            0 :   else if (opNum == 3) {
     357              :     /**option4 = input model size (width:height) */
     358              :     tensor_dim dim;
     359            0 :     int rank = gst_tensor_parse_dimension (param, dim);
     360              : 
     361            0 :     trData->i_width = 0;
     362            0 :     trData->i_height = 0;
     363            0 :     if (param == NULL || *param == '\0')
     364            0 :       return TRUE;
     365              : 
     366            0 :     if (rank < 2) {
     367            0 :       GST_ERROR ("mode-option-4 of tensor region is input video dimension (WIDTH:HEIGHT). The given parameter, \"%s\", is not acceptable.",
     368              :           param);
     369            0 :       return TRUE; /**Ignore this param */
     370              :     }
     371            0 :     if (rank > 2) {
     372            0 :       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.",
     373              :           param);
     374              :     }
     375            0 :     trData->i_width = dim[0];
     376            0 :     trData->i_height = dim[1];
     377            0 :     return TRUE;
     378              :   }
     379              : 
     380            0 :   GST_INFO ("Property mode-option-%d is ignored", opNum + 1);
     381            0 :   return TRUE;
     382              : }
     383              : 
     384              : typedef struct {
     385              :   int valid;
     386              :   int class_id;
     387              :   gfloat score;
     388              :   int x;
     389              :   int y;
     390              :   int height;
     391              :   int width;
     392              : } detected_object;
     393              : 
     394              : /**
     395              :  * @brief transfer crop region info with the given results to the output buffer
     396              :  * @param[out] out_info The output buffer
     397              :  * @param[in] data The Tensor_region internal data.
     398              :  * @param[in] results The final results to be transferred.
     399              :  */
     400              : static void
     401            3 : gst_tensor_top_detectedObjects_cropInfo (GstMapInfo *out_info, const tensor_region *data, GArray *results)
     402              : {
     403              : 
     404              :   guint i;
     405            3 :   gsize size = sizeof (crop_region); /**Assuming crop_region is a structure with four integer fields */
     406            3 :   guint *out_data = (guint *) out_info->data;
     407              :   crop_region region;
     408            3 :   guint maxx = MIN (results->len, data->num);
     409            6 :   for (i = 0; i < maxx; i++) {
     410            3 :     detected_object *temp = &g_array_index (results, detected_object, i);
     411            3 :     region.x = temp->x;
     412            3 :     region.y = temp->y;
     413            3 :     region.w = temp->width;
     414            3 :     region.h = temp->height;
     415            3 :     memcpy (out_data, &region, size);
     416            3 :     out_data += size / sizeof (guint);
     417              :   }
     418            3 : }
     419              : 
     420              : #define _expit(x) (1.f / (1.f + expf (-((float) x))))
     421              : 
     422              : 
     423              : /**
     424              :  * @brief C++-Template-like box location calculation for box-priors
     425              :  * @bug This is not macro-argument safe. Use parenthesis!
     426              :  * @param[in] bb The configuration, "tensor region"
     427              :  * @param[in] index The index (3rd dimension of BOX_SIZE:1:MOBILENET_SSD_DETECTION_MAX:1)
     428              :  * @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)
     429              :  * @param[in] boxprior The box prior data from the box file of SSD.
     430              :  * @param[in] boxinputptr Cursor pointer of input + byte-per-index * index (box)
     431              :  * @param[in] detinputptr Cursor pointer of input + byte-per-index * index (detection)
     432              :  * @param[in] result The object returned. (pointer to object)
     433              :  */
     434              : #define _get_object_i_mobilenet_ssd(                                              \
     435              :     bb, index, total_labels, boxprior, boxinputptr, detinputptr, result)          \
     436              :   do {                                                                            \
     437              :     unsigned int c;                                                               \
     438              :     properties_MOBILENET_SSD *data = &bb->mobilenet_ssd;                          \
     439              :     float sigmoid_threshold = data->sigmoid_threshold;                            \
     440              :     float y_scale = data->params[MOBILENET_SSD_PARAMS_Y_SCALE_IDX];               \
     441              :     float x_scale = data->params[MOBILENET_SSD_PARAMS_X_SCALE_IDX];               \
     442              :     float h_scale = data->params[MOBILENET_SSD_PARAMS_H_SCALE_IDX];               \
     443              :     float w_scale = data->params[MOBILENET_SSD_PARAMS_W_SCALE_IDX];               \
     444              :     result->valid = FALSE;                                                        \
     445              :     for (c = 1; c < total_labels; c++) {                                          \
     446              :       if (detinputptr[c] >= sigmoid_threshold) {                                  \
     447              :         gfloat score = _expit (detinputptr[c]);                                   \
     448              :         float ycenter                                                             \
     449              :             = boxinputptr[0] / y_scale * boxprior[2][index] + boxprior[0][index]; \
     450              :         float xcenter                                                             \
     451              :             = boxinputptr[1] / x_scale * boxprior[3][index] + boxprior[1][index]; \
     452              :         float h = (float) expf (boxinputptr[2] / h_scale) * boxprior[2][index];   \
     453              :         float w = (float) expf (boxinputptr[3] / w_scale) * boxprior[3][index];   \
     454              :         float ymin = ycenter - h / 2.f;                                           \
     455              :         float xmin = xcenter - w / 2.f;                                           \
     456              :         int x = xmin * bb->i_width;                                               \
     457              :         int y = ymin * bb->i_height;                                              \
     458              :         int width = w * bb->i_width;                                              \
     459              :         int height = h * bb->i_height;                                            \
     460              :         result->class_id = c;                                                     \
     461              :         result->x = MAX (0, x);                                                   \
     462              :         result->y = MAX (0, y);                                                   \
     463              :         result->width = width;                                                    \
     464              :         result->height = height;                                                  \
     465              :         result->score = score;                                                    \
     466              :         result->valid = TRUE;                                                     \
     467              :         break;                                                                    \
     468              :       }                                                                           \
     469              :     }                                                                             \
     470              :   } while (0);
     471              : 
     472              : /**
     473              :  * @brief C++-Template-like box location calculation for box-priors for Mobilenet SSD Model
     474              :  * @param[in] bb The configuration, "tensor region"
     475              :  * @param[in] type The tensor type of inputptr
     476              :  * @param[in] typename nnstreamer enum corresponding to the type
     477              :  * @param[in] boxprior The box prior data from the box file of MOBILENET_SSD.
     478              :  * @param[in] boxinput Input Tensor Data (Boxes)
     479              :  * @param[in] detinput Input Tensor Data (Detection). Null if not available. (numtensor ==1)
     480              :  * @param[in] config Tensor configs of the input tensors
     481              :  * @param[out] results The object returned. (GArray with detectedObject)
     482              :  */
     483              : #define _get_objects_mobilenet_ssd(                                                         \
     484              :     bb, _type, typename, boxprior, boxinput, detinput, config, results)                     \
     485              :   case typename:                                                                            \
     486              :     {                                                                                       \
     487              :       int d;                                                                                \
     488              :       _type *boxinput_ = (_type *) boxinput;                                                \
     489              :       size_t boxbpi = config->info.info[0].dimension[0];                                    \
     490              :       _type *detinput_ = (_type *) detinput;                                                \
     491              :       size_t detbpi = config->info.info[1].dimension[0];                                    \
     492              :       int num = (MOBILENET_SSD_DETECTION_MAX > bb->max_detections) ?                        \
     493              :                     bb->max_detections :                                                    \
     494              :                     MOBILENET_SSD_DETECTION_MAX;                                            \
     495              :       detected_object object = {                                                            \
     496              :         .valid = FALSE, .class_id = 0, .x = 0, .y = 0, .width = 0, .height = 0, .score = .0 \
     497              :       };                                                                                    \
     498              :       for (d = 0; d < num; d++) {                                                           \
     499              :         _get_object_i_mobilenet_ssd (bb, d, detbpi, boxprior,                               \
     500              :             (boxinput_ + (d * boxbpi)), (detinput_ + (d * detbpi)), (&object));             \
     501              :         if (object.valid == TRUE) {                                                         \
     502              :           g_array_append_val (results, object);                                             \
     503              :         }                                                                                   \
     504              :       }                                                                                     \
     505              :     }                                                                                       \
     506              :     break
     507              : 
     508              : /** @brief Macro to simplify calling _get_objects_mobilenet_ssd */
     509              : #define _get_objects_mobilenet_ssd_(type, typename)                                       \
     510              :   _get_objects_mobilenet_ssd (trData, type, typename, (trData->mobilenet_ssd.box_priors), \
     511              :       (boxes->data), (detections->data), config, results)
     512              : 
     513              : /**
     514              :  * @brief Compare Function for g_array_sort with detectedObject.
     515              :  */
     516              : static gint
     517           57 : compare_detection (gconstpointer _a, gconstpointer _b)
     518              : {
     519           57 :   const detected_object *a = _a;
     520           57 :   const detected_object *b = _b;
     521              : 
     522              :   /**Larger comes first */
     523           57 :   return (a->score > b->score) ? -1 : ((a->score == b->score) ? 0 : 1);
     524              : }
     525              : 
     526              : /**
     527              :  * @brief Calculate the intersected surface
     528              :  */
     529              : static gfloat
     530           24 : iou (detected_object *a, detected_object *b)
     531              : {
     532           24 :   int x1 = MAX (a->x, b->x);
     533           24 :   int y1 = MAX (a->y, b->y);
     534           24 :   int x2 = MIN (a->x + a->width, b->x + b->width);
     535           24 :   int y2 = MIN (a->y + a->height, b->y + b->height);
     536           24 :   int w = MAX (0, (x2 - x1 + 1));
     537           24 :   int h = MAX (0, (y2 - y1 + 1));
     538           24 :   float inter = w * h;
     539           24 :   float areaA = a->width * a->height;
     540           24 :   float areaB = b->width * b->height;
     541           24 :   float o = inter / (areaA + areaB - inter);
     542           24 :   return (o >= 0) ? o : 0;
     543              : }
     544              : 
     545              : /**
     546              :  * @brief Apply NMS to the given results (objects[])
     547              :  * @param[in/out] results The results to be filtered with nms
     548              :  */
     549              : static void
     550            3 : nms (GArray *results, gfloat threshold)
     551              : {
     552              :   guint boxes_size;
     553              :   guint i, j;
     554              : 
     555            3 :   boxes_size = results->len;
     556            3 :   if (boxes_size == 0U)
     557            0 :     return;
     558              : 
     559            3 :   g_array_sort (results, compare_detection);
     560              : 
     561           30 :   for (i = 0; i < boxes_size; i++) {
     562           27 :     detected_object *a = &g_array_index (results, detected_object, i);
     563           27 :     if (a->valid == TRUE) {
     564           27 :       for (j = i + 1; j < boxes_size; j++) {
     565           24 :         detected_object *b = &g_array_index (results, detected_object, j);
     566           24 :         if (b->valid == TRUE) {
     567           24 :           if (iou (a, b) > threshold) {
     568           24 :             b->valid = FALSE;
     569              :           }
     570              :         }
     571              :       }
     572              :     }
     573              :   }
     574              : 
     575            3 :   i = 0;
     576              :   do {
     577           27 :     detected_object *a = &g_array_index (results, detected_object, i);
     578           27 :     if (a->valid == FALSE)
     579           24 :       g_array_remove_index (results, i);
     580              :     else
     581            3 :       i++;
     582           27 :   } while (i < results->len);
     583              : }
     584              : 
     585              : /**
     586              :  * @brief Private function to initialize the meta info
     587              :  */
     588              : static void
     589            3 : init_meta(GstTensorMetaInfo * meta, const tensor_region * trData){
     590            3 :   gst_tensor_meta_info_init (meta);
     591            3 :   meta->type = _NNS_UINT32;
     592            3 :   meta->dimension[0] = BOX_SIZE;
     593            3 :   meta->dimension[1] = trData->num;
     594            3 :   meta->media_type = _NNS_TENSOR;
     595            3 : }
     596              : 
     597              : /** @brief tensordec-plugin's GstTensorDecoderDef callback */
     598              : static GstFlowReturn
     599            3 : tr_decode (void **pdata, const GstTensorsConfig *config,
     600              :     const GstTensorMemory *input, GstBuffer *outbuf)
     601              : {
     602            3 :   tensor_region *trData = *pdata;
     603              :   GstTensorMetaInfo meta;
     604              :   GstMapInfo out_info;
     605              :   GstMemory *out_mem, *tmp_mem;
     606            3 :   GArray *results = NULL;
     607            3 :   const guint num_tensors = config->info.num_tensors;
     608            3 :   gboolean need_output_alloc = gst_buffer_get_size (outbuf) == 0;
     609            3 :   const size_t size = (size_t) 4 * trData->num * sizeof(uint32_t); /**4 field per block */
     610              : 
     611            3 :   g_assert (outbuf);
     612              :   /** Ensure we have outbuf properly allocated */
     613            3 :   if (need_output_alloc) {
     614            3 :     out_mem = gst_allocator_alloc (NULL, size, NULL);
     615              :   } else {
     616            0 :     if (gst_buffer_get_size (outbuf) < size) {
     617            0 :       gst_buffer_set_size (outbuf, size);
     618              :     }
     619            0 :     out_mem = gst_buffer_get_all_memory (outbuf);
     620              :   }
     621            3 :   if (!gst_memory_map (out_mem, &out_info, GST_MAP_WRITE)) {
     622            0 :     ml_loge ("Cannot map output memory / tensordec-tensor_region.\n");
     623            0 :     goto error_free;
     624              :   }
     625              : 
     626              :   /** reset the buffer with 0 */
     627            3 :   memset (out_info.data, 0, size);
     628            3 :   if(trData->mode == MOBILENET_SSD_BOUNDING_BOX){
     629            3 :     const GstTensorMemory *boxes, *detections = NULL;
     630            3 :     properties_MOBILENET_SSD *data = &trData->mobilenet_ssd;
     631              : 
     632            3 :     g_assert (num_tensors >= MOBILENET_SSD_MAX_TENSORS);
     633            3 :     results = g_array_sized_new (FALSE, TRUE, sizeof (detected_object), 100);
     634              : 
     635            3 :     boxes = &input[0];
     636            3 :     if (num_tensors >= MOBILENET_SSD_MAX_TENSORS) /**lgtm[cpp/constant-comparison] */
     637            3 :       detections = &input[1];
     638              : 
     639            3 :     switch (config->info.info[0].type) {
     640            0 :       _get_objects_mobilenet_ssd_ (uint8_t, _NNS_UINT8);
     641            0 :       _get_objects_mobilenet_ssd_ (int8_t, _NNS_INT8);
     642            0 :       _get_objects_mobilenet_ssd_ (uint16_t, _NNS_UINT16);
     643            0 :       _get_objects_mobilenet_ssd_ (int16_t, _NNS_INT16);
     644            0 :       _get_objects_mobilenet_ssd_ (uint32_t, _NNS_UINT32);
     645            0 :       _get_objects_mobilenet_ssd_ (int32_t, _NNS_INT32);
     646            0 :       _get_objects_mobilenet_ssd_ (uint64_t, _NNS_UINT64);
     647            0 :       _get_objects_mobilenet_ssd_ (int64_t, _NNS_INT64);
     648       522372 :       _get_objects_mobilenet_ssd_ (float, _NNS_FLOAT32);
     649            0 :       _get_objects_mobilenet_ssd_ (double, _NNS_FLOAT64);
     650            0 :       default:
     651            0 :         g_assert (0);
     652              :     }
     653              : 
     654            3 :     nms (results, data->params[MOBILENET_SSD_PARAMS_IOU_THRESHOLD_IDX]);
     655              :   } else {
     656            0 :     GST_ERROR ("Failed to get output buffer, unknown mode %d.", trData->mode);
     657            0 :     goto error_unmap;
     658              :   }
     659            3 :   gst_tensor_top_detectedObjects_cropInfo (&out_info, trData, results);
     660              : 
     661            3 :   g_array_free (results, TRUE);
     662              : 
     663            3 :   gst_memory_unmap (out_mem, &out_info);
     664              : 
     665              :   /** converting to Flexible tensor since
     666              :    * info pad of tensor_crop has capability for flexible tensor stream
     667              :    */
     668            3 :   init_meta (&meta, trData);
     669            3 :   tmp_mem = out_mem;
     670            3 :   out_mem = gst_tensor_meta_info_append_header (&meta, tmp_mem);
     671            3 :   gst_memory_unref (tmp_mem);
     672              : 
     673            3 :   if (need_output_alloc) {
     674            3 :     gst_buffer_append_memory (outbuf, out_mem);
     675              :   } else {
     676            0 :     gst_buffer_replace_all_memory (outbuf, out_mem);
     677              :   }
     678              : 
     679            3 :   return GST_FLOW_OK;
     680            0 : error_unmap:
     681            0 :   gst_memory_unmap (out_mem, &out_info);
     682            0 : error_free:
     683            0 :   gst_memory_unref (out_mem);
     684              : 
     685            0 :   return GST_FLOW_ERROR;
     686              : }
     687              : 
     688              : /**
     689              :  * @brief set the max_detection
     690              :  */
     691              : static int
     692           10 : _set_max_detection (tensor_region *data, guint max_detection, unsigned int limit)
     693              : {
     694              :   /**Check consistency with max_detection */
     695           10 :   if (data->max_detections == 0)
     696            2 :     data->max_detections = max_detection;
     697              :   else
     698            8 :     g_return_val_if_fail (max_detection == data->max_detections, FALSE);
     699              : 
     700           10 :   if (data->max_detections > limit) {
     701            0 :     GST_ERROR ("Incoming tensor has too large detection-max : %u", max_detection);
     702            0 :     return FALSE;
     703              :   }
     704           10 :   return TRUE;
     705              : }
     706              : 
     707              : 
     708              : /**
     709              :  * @brief tensordec-plugin's GstTensorDecoderDef callback
     710              :  *
     711              :  * [Mobilenet SSD Model]
     712              :  * The first tensor is boxes. BOX_SIZE : 1 : #MaxDetection, ANY-TYPE
     713              :  * The second tensor is labels. #MaxLabel : #MaxDetection, ANY-TYPE
     714              :  * Both tensors are MANDATORY!
     715              :  * If there are third or more tensors, such tensors will be ignored.
     716              :  */
     717              : 
     718              : static GstCaps *
     719           13 : tr_getOutCaps (void **pdata, const GstTensorsConfig *config)
     720              : {
     721           13 :   tensor_region *data = *pdata;
     722              :   GstCaps *caps;
     723              :   char *str;
     724              :   guint max_detection, max_label;
     725              :   const uint32_t *dim1, *dim2;
     726              :   int i;
     727              : 
     728              :   /**Check if the first tensor is compatible */
     729           13 :   dim1 = config->info.info[0].dimension;
     730           13 :   g_return_val_if_fail (dim1[0] == BOX_SIZE, NULL);
     731           10 :   g_return_val_if_fail (dim1[1] == 1, NULL);
     732           10 :   max_detection = dim1[2];
     733           10 :   g_return_val_if_fail (max_detection > 0, NULL);
     734              :   /** @todo unused dimension value should be 0 */
     735          140 :   for (i = 3; i < NNS_TENSOR_RANK_LIMIT; i++)
     736          130 :     g_return_val_if_fail (dim1[i] == 0 || dim1[i] == 1, NULL);
     737              : 
     738              :   /**Check if the second tensor is compatible */
     739           10 :   dim2 = config->info.info[1].dimension;
     740           10 :   max_label = dim2[0];
     741           10 :   g_return_val_if_fail (max_label <= data->labeldata.total_labels, NULL);
     742           10 :   if (max_label < data->labeldata.total_labels)
     743            5 :     GST_WARNING ("The given tensor (2nd) has max_label (first dimension: %u) smaller than the number of labels in labels file (%s: %u).",
     744              :         max_label, data->label_path, data->labeldata.total_labels);
     745           10 :   g_return_val_if_fail (max_detection == dim2[1], NULL);
     746          150 :   for (i = 2; i < NNS_TENSOR_RANK_LIMIT; i++)
     747          140 :     g_return_val_if_fail (dim2[i] == 0 || dim2[i] == 1, NULL);
     748              : 
     749              :   /**Check consistency with max_detection */
     750           10 :   if (!_set_max_detection (data, max_detection, MOBILENET_SSD_DETECTION_MAX)) {
     751            0 :     return NULL;
     752              :   }
     753           10 :   str = g_strdup_printf("other/tensors,format=flexible");
     754           10 :   caps = gst_caps_from_string (str);
     755           10 :   setFramerateFromConfig (caps, config);
     756           10 :   g_free (str);
     757              :   (void) *pdata;
     758           10 :   return caps;
     759              : }
     760              : 
     761              : 
     762              : static gchar decoder_subplugin_tensor_region[] = "tensor_region";
     763              : 
     764              : /** @brief Tensor Region tensordec-plugin GstTensorDecoderDef instance */
     765              : static GstTensorDecoderDef tensorRegion = { .modename = decoder_subplugin_tensor_region,
     766              :   .init = tr_init,
     767              :   .exit = tr_exit,
     768              :   .setOption = tr_setOption,
     769              :   .getOutCaps = tr_getOutCaps,
     770              :   /** .getTransformSize = tr_getTransformsize, */
     771              :   .decode = tr_decode };
     772              : 
     773              : /** @brief Initialize this object for tensordec-plugin */
     774              : void
     775           33 : init_tr (void)
     776              : {
     777           33 :   nnstreamer_decoder_probe (&tensorRegion);
     778           33 : }
     779              : 
     780              : /** @brief Destruct this object for tensordec-plugin */
     781              : void
     782           33 : fini_tr (void)
     783              : {
     784           33 :   nnstreamer_decoder_exit (tensorRegion.modename);
     785           33 : }
        

Generated by: LCOV version 2.0-1