[Filter/Main] First working prototype of filter::main
authorMyungJoo Ham <myungjoo.ham@samsung.com>
Fri, 8 Jun 2018 06:02:01 +0000 (15:02 +0900)
committer문지중/동작제어Lab(SR)/Principal Engineer/삼성전자 <jijoong.moon@samsung.com>
Thu, 14 Jun 2018 04:20:50 +0000 (13:20 +0900)
The full execution path of tensor_filter::main is tested with testcase-01
although it does not process anything in the model.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
common/tensor_common.c
include/tensor_common.h
tensor_filter/tensor_filter.c
tensor_filter/tensor_filter.h
tensor_filter/tensor_filter_custom.c
tensor_filter/tensor_filter_custom.h
tensor_filter/tensor_filter_tensorflow_lite.c
tensor_filter/test/nothing [new file with mode: 0644]
tensor_filter/test/runTest.sh [new file with mode: 0755]
tensor_filter/test/testcase01.sh [new file with mode: 0755]

index 81c7406..2874d75 100644 (file)
@@ -161,3 +161,19 @@ int get_tensor_dimension(const gchar* param, uint32_t dim[NNS_TENSOR_RANK_LIMIT]
   g_strfreev(strv);
   return retval;
 }
+
+/**
+ * @brief Count the number of elemnts of a tensor
+ * @return The number of elements. 0 if error.
+ * @param dim The tensor dimension
+ */
+size_t get_tensor_element_count(uint32_t dim[NNS_TENSOR_RANK_LIMIT]) {
+  size_t count = 1;
+  int i;
+
+  for (i = 0; i < NNS_TENSOR_RANK_LIMIT; i++) {
+    count *= dim[i];
+  }
+
+  return count;
+}
index b063861..c2bb3ed 100644 (file)
@@ -124,6 +124,13 @@ extern int find_key_strv(const gchar **strv, const gchar *key);
  */
 extern int get_tensor_dimension(const gchar* param, uint32_t dim[NNS_TENSOR_RANK_LIMIT]);
 
+/**
+ * @brief Count the number of elemnts of a tensor
+ * @return The number of elements. 0 if error.
+ * @param dim The tensor dimension
+ */
+extern size_t get_tensor_element_count(uint32_t dim[NNS_TENSOR_RANK_LIMIT]);
+
 G_END_DECLS
 
 #endif /* __GST_TENSOR_COMMON_H__ */
index a810976..ee90f16 100644 (file)
@@ -246,6 +246,7 @@ gst_tensor_filter_init (GstTensor_Filter * filter)
   filter->silent = FALSE;
   filter->debug = FALSE;
   filter->nnfw = _T_F_UNDEFINED;
+  filter->fw = NULL;
   filter->inputConfigured = FALSE;
   filter->outputConfigured = FALSE;
   filter->modelFilename = NULL;
@@ -255,12 +256,14 @@ gst_tensor_filter_init (GstTensor_Filter * filter)
   filter->inputDimension[2] = 1;
   filter->inputDimension[3] = 1; // out
   filter->inputType = _NNS_END; // not initialized
+  filter->inputCapNegotiated = FALSE;
 
   filter->outputDimension[0] = 1; // innermost
   filter->outputDimension[1] = 1;
   filter->outputDimension[2] = 1;
   filter->outputDimension[3] = 1; // out
   filter->outputType = _NNS_END; // not initialized
+  filter->outputCapNegotiated = FALSE;
 
   filter->privateData = NULL; // mark not initialized.
 }
@@ -410,7 +413,7 @@ gst_tensor_filter_set_property (GObject * object, guint prop_id,
     case PROP_MODEL:
       g_assert(filter->modelFilename == NULL && value);
       /* Once configures, it cannot be changed in runtime */
-      filter->modelFilename = g_value_get_string(value);
+      filter->modelFilename = g_value_dup_string(value);
       if (filter->debug == TRUE)
         g_printerr("Model = %s\n", filter->modelFilename);
       g_assert(g_file_test(filter->modelFilename, G_FILE_TEST_IS_REGULAR) == TRUE);
@@ -566,22 +569,77 @@ static GstFlowReturn gst_tensor_filter_transform(GstBaseTransform *trans,
                                                   GstBuffer *inbuf,
                                                   GstBuffer *outbuf)
 {
-#if 0
-  GstFlowReturn res;
   GstTensor_Filter *filter = GST_TENSOR_FILTER_CAST(trans);
-
-  if (G_UNLIKELY(!filter->negotiated))
+  int ret;
+  uint32_t inputDimChk[NNS_TENSOR_RANK_LIMIT];
+  uint32_t outputDimChk[NNS_TENSOR_RANK_LIMIT];
+  tensor_type inputType, outputType;
+  size_t outBufSize;
+  uint8_t *inptr, *outptr;
+  GstMapInfo inInfo, outInfo;
+
+  if (G_UNLIKELY(filter->inputCapNegotiated == FALSE || filter->outputCapNegotiated == FALSE))
     goto unknown_format;
-  if (G_UNLIKELY(!filter->tensorConfigured))
-    goto unknown_tensor;
-#endif
-  g_assert(1 == 0);
+  if (G_UNLIKELY(!filter->fw))
+    goto unknown_framework;
+  if (G_UNLIKELY(!filter->modelFilename))
+    goto unknown_model;
+  if (G_UNLIKELY(!filter->fw->invoke_NN))
+    goto unknown_invoke;
+
+  /* 0. Check all properties and inbuf size. */
+  if (filter->debug)
+    g_printerr("Invoking %s with %s model\n", filter->fw->name, filter->modelFilename);
+
+  if (filter->fw->getInputDimension) {
+    ret = filter->fw->getInputDimension(filter, inputDimChk, &inputType);
+    /* @TODO check inputDimChk / inputType with filter internal info */
+  } else {
+    /* @TODO printout debug msg */
+  }
 
-  /* @TODO 0. Check all properties and inbuf size. */
-  /* @TODO 1. Allocate outbuf */
+  if (filter->fw->getOutputDimension) {
+    ret = filter->fw->getOutputDimension(filter, outputDimChk, &outputType);
+    /* @TODO check outputDimChk / outputType with filter internal info */
+  } else {
+    /* @TODO printout debug msg */
+  }
+
+  /* 1. Allocate outbuf */
   g_assert(outbuf);
-  /* @TODO 2. Call the filter-subplugin callback, "invoke" */
-  /* @TODO 3. Return result! */
+  outBufSize = tensor_element_size[filter->inputType] *
+      get_tensor_element_count(filter->inputDimension);
+  if (gst_buffer_get_size(outbuf) < outBufSize) {
+    gst_buffer_set_size(outbuf, outBufSize);
+  }
+  g_assert(gst_buffer_get_size(outbuf) >= outBufSize);
+
+  /* 2. Call the filter-subplugin callback, "invoke" */
+  gst_buffer_map(inbuf, &inInfo, GST_MAP_READ);
+  gst_buffer_map(outbuf, &outInfo, GST_MAP_WRITE);
+  inptr = inInfo.data;
+  outptr = outInfo.data;
+
+  ret = filter->fw->invoke_NN(filter, inptr, outptr);
+
+  gst_buffer_unmap(inbuf, &inInfo);
+  gst_buffer_unmap(outbuf, &outInfo);
+
+  /* 3. Return result! */
+  if (ret)
+    return GST_FLOW_ERROR;
+  return GST_FLOW_OK;
+unknown_format:
+  GST_ELEMENT_ERROR(filter, CORE, NOT_IMPLEMENTED, (NULL), ("unknown format"));
+  return GST_FLOW_NOT_NEGOTIATED;
+unknown_framework:
+  GST_ELEMENT_ERROR(filter, CORE, NOT_IMPLEMENTED, (NULL), ("framework not configured"));
+  return GST_FLOW_ERROR;
+unknown_model:
+  GST_ELEMENT_ERROR(filter, CORE, NOT_IMPLEMENTED, (NULL), ("model filepath not configured"));
+  return GST_FLOW_ERROR;
+unknown_invoke:
+  GST_ELEMENT_ERROR(filter, CORE, NOT_IMPLEMENTED, (NULL), ("invoke function is not defined"));
   return GST_FLOW_ERROR;
 }
 
@@ -615,12 +673,14 @@ static GstCaps* gst_tensor_filter_transform_caps(GstBaseTransform *trans,
 
   if (direction == GST_PAD_SINK) {
     /* caps: sink pad. get src pad info */
+    obj->outputCapNegotiated = TRUE;
 
     /* @TODO 1. Check caps w/ getInputDimension && saved input dimension */
     /* @TODO 2. Check returning-caps w/ getOutputDimension && saved output dimension */
     return gst_tensor_filter_fix_caps(obj, FALSE, filter, FALSE);
   } else {
     /* caps: src pad. get sink pad info */
+    obj->inputCapNegotiated = TRUE;
 
     /* @TODO 1. Check caps w/ getOutputDimension && saved output dimension */
     /* @TODO 2. Check returning-caps w/ getInputDimension && saved input dimension */
@@ -640,6 +700,8 @@ static GstCaps* gst_tensor_filter_fixate_caps(GstBaseTransform *trans,
 {
   GstCaps *supposed = gst_tensor_filter_transform_caps(trans, direction, caps, NULL);
   GstCaps *result = gst_caps_intersect(othercaps, supposed);
+  GstTensor_Filter *obj = GST_TENSOR_FILTER_CAST(trans);
+
   g_assert(!gst_caps_is_empty(result));
   gst_caps_unref(othercaps);
 
@@ -650,6 +712,9 @@ static GstCaps* gst_tensor_filter_fixate_caps(GstBaseTransform *trans,
     if (gst_caps_is_subset(caps, result)) {
       gst_caps_replace(&result, caps);
     }
+    obj->inputCapNegotiated = TRUE;
+  } else {
+    obj->outputCapNegotiated = TRUE;
   }
   return result;
 }
index 81e6dc2..812c3d7 100644 (file)
@@ -132,8 +132,10 @@ struct _GstTensor_Filter
 
   uint32_t inputDimension[NNS_TENSOR_RANK_LIMIT]; /**< The input tensor dimension */
   tensor_type inputType; /**< The type for each element in the input tensor */
+  gboolean inputCapNegotiated;
   uint32_t outputDimension[NNS_TENSOR_RANK_LIMIT]; /**< The output tensor dimension */
   tensor_type outputType; /**< The type for each element in the output tensor */
+  gboolean outputCapNegotiated;
 
   void *privateData; /**< NNFW plugin's private data is stored here */
 };
@@ -159,7 +161,7 @@ struct _GstTensor_Filter_Framework
 {
   gchar *name; /**< Name of the neural network framework, searchable by FRAMEWORK property */
   gboolean allow_in_place; /**< TRUE if InPlace transfer of input-to-output is allowed. Not supported in main, yet */
-  int (*invoke_NN)(GstTensor_Filter *filter, void *inputptr, void *outputptr); /**< Mandatory callback. Invoke the given network model. */
+  int (*invoke_NN)(GstTensor_Filter *filter, uint8_t *inputptr, uint8_t *outputptr); /**< Mandatory callback. Invoke the given network model. */
   int (*getInputDimension)(GstTensor_Filter *filter, uint32_t *inputDimension, tensor_type *type); /**< Optional. Set NULL if not supported. Get dimension of input tensor */
   int (*getOutputDimension)(GstTensor_Filter *filter, uint32_t *outputDimension, tensor_type *type); /**< Optional. Set NULL if not supported. Get dimension of output tensor */
   void (*close)(GstTensor_Filter *filter); /**< Optional. Close this instance! */
index 8f6ba91..1a950a7 100644 (file)
@@ -113,7 +113,7 @@ static int custom_loadlib(GstTensor_Filter *filter) {
  * @param[in] inptr The input tensor
  * @param[out] outptr The output tensor
  */
-static int custom_invoke(GstTensor_Filter *filter, void *inptr, void *outptr) {
+static int custom_invoke(GstTensor_Filter *filter, uint8_t *inptr, uint8_t *outptr) {
   int retval = custom_loadlib(filter);
   internal_data *ptr;
 
index ee85c8d..791416e 100644 (file)
@@ -99,7 +99,7 @@ typedef int (*NNS_custom_get_output_dimension)(void *private_data,
  * @param[in] inputPtr pointer to output tensor, size = dim1 x dim2 x dim3 x dim4 x typesize, allocated by caller
  */
 typedef int (*NNS_custom_invoke)(void *private_data,
-    void *inputPtr, void *outputPtr);
+    uint8_t *inputPtr, uint8_t *outputPtr);
 
 /**
  * @brief Custom Filter Class
index 406632c..54814c3 100644 (file)
@@ -57,8 +57,8 @@
 /**
  * @brief The mandatory callback for GstTensor_Filter_Framework
  */
-static int tflite_invoke(GstTensor_Filter *filter, void *inptr, void *outptr) {
-  return -1; // NYI
+static int tflite_invoke(GstTensor_Filter *filter, uint8_t *inptr, uint8_t *outptr) {
+  return 0; // NYI
 }
 
 /**
@@ -66,7 +66,7 @@ static int tflite_invoke(GstTensor_Filter *filter, void *inptr, void *outptr) {
  */
 static int tflite_getInputDim(GstTensor_Filter *filter, uint32_t *inputDimension, tensor_type *type) {
   // @TODO fill in *inputDimension (uint32_t[MAX_RANK]), *type
-  return -1; // NYI
+  return 0; // NYI
 }
 
 /**
@@ -74,7 +74,7 @@ static int tflite_getInputDim(GstTensor_Filter *filter, uint32_t *inputDimension
  */
 static int tflite_getOutputDim(GstTensor_Filter *filter, uint32_t *outputDimension, tensor_type *type) {
   // @TODO fill in *outputDimension (uint32_t[MAX_RANK]), *type
-  return -1; // NYI
+  return 0; // NYI
 }
 
 GstTensor_Filter_Framework NNS_support_tensorflow_lite = {
diff --git a/tensor_filter/test/nothing b/tensor_filter/test/nothing
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tensor_filter/test/runTest.sh b/tensor_filter/test/runTest.sh
new file mode 100755 (executable)
index 0000000..d5f862a
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+failed=0
+sopath=""
+
+./testcase01.sh $sopath || failed=1
+
+echo ""
+echo ""
+echo "=================================================="
+echo "            Test for tensor_converter"
+echo "=================================================="
+if [ "$failed" -eq "0" ]
+then
+  echo SUCCESS
+  echo ""
+  exit 0
+else
+  echo FAILED
+  echo ""
+  exit -1
+fi
diff --git a/tensor_filter/test/testcase01.sh b/tensor_filter/test/testcase01.sh
new file mode 100755 (executable)
index 0000000..3cddb4f
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+##
+# Copyright (C) 2018 Samsung Electronics
+# License: Apache-2.0
+#
+# @file testcase01.sh
+# @brief Test tensor_converter for testcase 01
+# @author MyungJoo Ham <myungjoo.ham@samsung.com>
+# @dependency cmp
+
+if [ $# -eq 0 ]
+then
+  PATH_TO_PLUGIN="$PWD/../../build/tensor_converter:$PWD/../../build/tensor_filter"
+else
+  PATH_TO_PLUGIN="$1"
+fi
+
+failed=0
+
+# Generate one frame only (num-buffers=1)
+
+gst-launch-1.0 -v --gst-debug=GST_CAPS:4 --gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=RGB,width=280,height=40,framerate=0/1 ! videoconvert ! video/x-raw, format=BGRx ! tensor_converter silent=FALSE ! tensor_filter debug=TRUE framework='tensorflow-lite' model='./nothing' input='4:280:40' inputtype='uint8' output='1' outputtype='uint8' ! filesink location="test.bgrx.log" sync=true || failed=1
+
+if [ "$failed" -eq "0" ]
+then
+  echo Testcase 01: SUCCESS
+  exit 0
+else
+  echo Testcase 01: FAILED
+  exit -1
+fi