[single] unittest for flexible dimension with tflite
authorParichay Kapoor <pk.kapoor@samsung.com>
Mon, 21 Oct 2019 04:18:14 +0000 (13:18 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Fri, 25 Oct 2019 06:32:02 +0000 (15:32 +0900)
Added unittest for flexible dimension with tflite
Added some bug fixes which were missed from previous commits due to commit over-write

Signed-off-by: Parichay Kapoor <pk.kapoor@samsung.com>
api/capi/include/tensor_filter_single.h
api/capi/src/nnstreamer-capi-single-new.c
api/capi/src/tensor_filter_single.c
ext/nnstreamer/tensor_filter/tensor_filter_tensorflow_lite_core.cc
tests/test_models/models/add.tflite [new file with mode: 0644]
tests/tizen_capi/unittest_tizen_capi.cpp

index 9502a6b..c5c1071 100644 (file)
@@ -77,7 +77,7 @@ struct _GTensorFilterSingleClass
   gboolean (*output_configured) (GTensorFilterSingle * self);
   /** Set the info about the input tensor */
   gboolean (*set_input_info) (GTensorFilterSingle * self,
-      const GstTensorsInfo * in_info);
+      const GstTensorsInfo * in_info, GstTensorsInfo * out_info);
 };
 
 /**
index e1b281f..7dcceca 100644 (file)
@@ -752,24 +752,40 @@ int ml_single_set_input_info (ml_single_h single, const ml_tensors_info_h info)
   ml_single *single_h;
   GTensorFilterSingleClass *klass;
   int status = ML_ERROR_NONE;
+  ml_tensors_info_s *in_info;
+  GstTensorsInfo gst_in_info, gst_out_info;
+  bool valid = false;
 
   check_feature_state ();
 
   if (!single || !info)
     return ML_ERROR_INVALID_PARAMETER;
 
+  if (!ml_tensors_info_is_valid (info, valid))
+    return ML_ERROR_INVALID_PARAMETER;
+
   ML_SINGLE_GET_VALID_HANDLE_LOCKED (single_h, single, 0);
 
+  in_info = (ml_tensors_info_s *) info;
   switch (single_h->nnfw) {
     case ML_NNFW_TYPE_TENSORFLOW_LITE:
+      ml_tensors_info_copy_from_ml (&gst_in_info, in_info);
+
       klass = g_type_class_peek (G_TYPE_TENSOR_FILTER_SINGLE);
-      if (klass == NULL || klass->set_input_info (single_h->filter, info) == FALSE)
+      if (klass == NULL || klass->set_input_info (
+            single_h->filter, &gst_in_info, &gst_out_info) == FALSE) {
         status = ML_ERROR_INVALID_PARAMETER;
+        goto exit;
+      }
+
+      ml_tensors_info_copy_from_gst (&single_h->in_info, &gst_in_info);
+      ml_tensors_info_copy_from_gst (&single_h->out_info, &gst_out_info);
       break;
     default:
       status = ML_ERROR_NOT_SUPPORTED;
   }
 
+exit:
   ML_SINGLE_HANDLE_UNLOCK (single_h);
   return status;
 }
index 2c52c64..a14f417 100644 (file)
@@ -81,7 +81,7 @@ static gboolean g_tensor_filter_single_invoke (GTensorFilterSingle * self,
 static gboolean g_tensor_filter_input_configured (GTensorFilterSingle * self);
 static gboolean g_tensor_filter_output_configured (GTensorFilterSingle * self);
 static gboolean g_tensor_filter_set_input_info (GTensorFilterSingle * self,
-    const GstTensorsInfo * in_info);
+    const GstTensorsInfo * in_info, GstTensorsInfo * out_info);
 
 /* Private functions */
 static gboolean g_tensor_filter_single_start (GTensorFilterSingle * self);
@@ -392,10 +392,9 @@ error:
  */
 static gboolean
 g_tensor_filter_set_input_info (GTensorFilterSingle * self,
-    const GstTensorsInfo * in_info)
+    const GstTensorsInfo * in_info, GstTensorsInfo * out_info)
 {
   GstTensorFilterPrivate *priv;
-  GstTensorsInfo out_info;
   int status;
   gboolean ret = FALSE;
 
@@ -404,9 +403,10 @@ g_tensor_filter_set_input_info (GTensorFilterSingle * self,
     return FALSE;
 
   status = priv->fw->setInputDimension (&priv->prop, &priv->privateData,
-      in_info, &out_info);
+      in_info, out_info);
   if (status == 0) {
-    gst_tensors_info_copy(&priv->out_config.info, &out_info);
+    gst_tensors_info_copy(&priv->prop.input_meta, in_info);
+    gst_tensors_info_copy(&priv->prop.output_meta, out_info);
     ret = TRUE;
   }
 
index c836d64..cf25949 100644 (file)
@@ -537,13 +537,6 @@ tflite_core_setInputDim (void * tflite, const GstTensorsInfo * in_info,
     return status;
   }
 
-  /** get output tensor info to be returned */
-  status = c->getOutputTensorDim (out_info);
-  if (status != 0) {
-    g_assert (c->setInputTensorDim (&cur_in_info) == 0);
-    return status;
-  }
-
   /** update input tensor info */
   if ((status = c->setInputTensorProp ()) != 0) {
     g_assert (c->setInputTensorDim (&cur_in_info) == 0);
@@ -559,6 +552,15 @@ tflite_core_setInputDim (void * tflite, const GstTensorsInfo * in_info,
     return status;
   }
 
+  /** get output tensor info to be returned */
+  status = c->getOutputTensorDim (out_info);
+  if (status != 0) {
+    g_assert (c->setInputTensorDim (&cur_in_info) == 0);
+    g_assert (c->setInputTensorProp () == 0);
+    g_assert (c->setOutputTensorProp () == 0);
+    return status;
+  }
+
   return 0;
 }
 
diff --git a/tests/test_models/models/add.tflite b/tests/test_models/models/add.tflite
new file mode 100644 (file)
index 0000000..e748b68
Binary files /dev/null and b/tests/test_models/models/add.tflite differ
index 31b4592..fa6b472 100644 (file)
@@ -2250,7 +2250,7 @@ TEST (nnstreamer_capi_singleshot, set_input_info_fail)
   ml_tensors_info_set_tensor_dimension (in_info, 0, in_dim);
 
   /** mobilenet model does not support setting different input dimension */
-  status = ml_single_set_input_info (single, &in_info);
+  status = ml_single_set_input_info (single, in_info);
   EXPECT_TRUE (status == ML_ERROR_NOT_SUPPORTED ||
       status == ML_ERROR_INVALID_PARAMETER);
 
@@ -2260,6 +2260,281 @@ TEST (nnstreamer_capi_singleshot, set_input_info_fail)
   g_free (test_model);
 }
 
+/**
+ * @brief Test NNStreamer single shot (tensorflow-lite)
+ * @detail Try setting number of input tensors and its type
+ */
+TEST (nnstreamer_capi_singleshot, set_input_info_fail_01)
+{
+  ml_single_h single;
+  ml_tensors_info_h in_info;
+  ml_tensor_type_e type = ML_TENSOR_TYPE_UNKNOWN;
+  unsigned int count = 0;
+  int status;
+
+  const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
+  gchar *test_model;
+
+  /* supposed to run test in build directory */
+  if (root_path == NULL)
+    root_path = "..";
+
+  /** add.tflite adds value 2 to all the values in the input */
+  test_model = g_build_filename (root_path, "tests", "test_models", "models",
+      "add.tflite", NULL);
+  ASSERT_TRUE (g_file_test (test_model, G_FILE_TEST_EXISTS));
+
+  status = ml_single_open (&single, test_model, NULL, NULL,
+      ML_NNFW_TYPE_TENSORFLOW_LITE, ML_NNFW_HW_ANY);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_single_get_input_info (single, &in_info);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_tensors_info_get_count (in_info, &count);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  /** changing the count of number of tensors is not allowed */
+  ml_tensors_info_set_count (in_info, count + 1);
+  status = ml_single_set_input_info (single, in_info);
+  EXPECT_NE (status, ML_ERROR_NONE);
+  ml_tensors_info_set_count (in_info, count);
+
+  status = ml_tensors_info_get_tensor_type (in_info, 0, &type);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_EQ (type, ML_TENSOR_TYPE_FLOAT32);
+
+  /** changing the type of input tensors is not allowed */
+  ml_tensors_info_set_tensor_type (in_info, 0, ML_TENSOR_TYPE_INT32);
+  status = ml_single_set_input_info (single, in_info);
+  EXPECT_NE (status, ML_ERROR_NONE);
+
+  status = ml_single_close (single);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  g_free (test_model);
+}
+
+/**
+ * @brief Test NNStreamer single shot (tensorflow-lite)
+ * @detail Try setting dimension to the same value. This model does not allow
+ *         changing the dimension to a different. However, setting the same
+ *         value for dimension should be successful.
+ */
+TEST (nnstreamer_capi_singleshot, set_input_info_success)
+{
+  int status;
+  ml_single_h single;
+  ml_tensors_info_h in_info;
+  ml_tensor_dimension in_dim;
+  ml_tensors_data_h input, output;
+
+  const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
+  gchar *test_model;
+
+  /* supposed to run test in build directory */
+  if (root_path == NULL)
+    root_path = "..";
+
+  test_model = g_build_filename (root_path, "tests", "test_models", "models",
+      "mobilenet_v1_1.0_224_quant.tflite", NULL);
+  ASSERT_TRUE (g_file_test (test_model, G_FILE_TEST_EXISTS));
+
+  status = ml_single_open (&single, test_model, NULL, NULL,
+      ML_NNFW_TYPE_TENSORFLOW_LITE, ML_NNFW_HW_ANY);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_single_set_input_info (single, NULL);
+  EXPECT_NE (status, ML_ERROR_NONE);
+
+  ml_tensors_info_create (&in_info);
+  in_dim[0] = 3;
+  in_dim[1] = 224;
+  in_dim[2] = 224;
+  in_dim[3] = 1;
+  ml_tensors_info_set_count (in_info, 1);
+  ml_tensors_info_set_tensor_type (in_info, 0, ML_TENSOR_TYPE_UINT8);
+  ml_tensors_info_set_tensor_dimension (in_info, 0, in_dim);
+
+  /** set the same original input dimension */
+  status = ml_single_set_input_info (single, in_info);
+  EXPECT_TRUE (status == ML_ERROR_NOT_SUPPORTED ||
+      status == ML_ERROR_NONE);
+
+  /* generate dummy data */
+  input = output = NULL;
+  status = ml_tensors_data_create (in_info, &input);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (input != NULL);
+
+  status = ml_single_invoke (single, input, &output);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+  EXPECT_TRUE (output != NULL);
+
+  ml_tensors_data_destroy (output);
+  ml_tensors_data_destroy (input);
+
+  status = ml_single_close (single);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  g_free (test_model);
+}
+
+/**
+ * @brief Test NNStreamer single shot (tensorflow-lite)
+ * @detail Change the number of input tensors, run the model and verify output
+ */
+TEST (nnstreamer_capi_singleshot, set_input_info_success_01)
+{
+  ml_single_h single;
+  ml_tensors_info_h in_info, out_info;
+  ml_tensors_info_h in_res, out_res;
+  ml_tensors_data_h input, output;
+  ml_tensor_dimension in_dim, out_dim, res_dim;
+  ml_tensor_type_e type = ML_TENSOR_TYPE_UNKNOWN;
+  unsigned int count = 0;
+  int status, tensor_size;
+  size_t data_size;
+  float *data;
+
+  const gchar *root_path = g_getenv ("NNSTREAMER_BUILD_ROOT_PATH");
+  gchar *test_model;
+
+  /* supposed to run test in build directory */
+  if (root_path == NULL)
+    root_path = "..";
+
+  /** add.tflite adds value 2 to all the values in the input */
+  test_model = g_build_filename (root_path, "tests", "test_models", "models",
+      "add.tflite", NULL);
+  ASSERT_TRUE (g_file_test (test_model, G_FILE_TEST_EXISTS));
+
+  ml_tensors_info_create (&in_info);
+  ml_tensors_info_create (&out_info);
+  ml_tensors_info_create (&in_res);
+  ml_tensors_info_create (&out_res);
+
+  tensor_size = 5;
+  in_dim[0] = tensor_size;
+  in_dim[1] = 1;
+  in_dim[2] = 1;
+  in_dim[3] = 1;
+  ml_tensors_info_set_count (in_info, 1);
+  ml_tensors_info_set_tensor_type (in_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (in_info, 0, in_dim);
+
+  out_dim[0] = tensor_size;
+  out_dim[1] = 1;
+  out_dim[2] = 1;
+  out_dim[3] = 1;
+  ml_tensors_info_set_count (out_info, 1);
+  ml_tensors_info_set_tensor_type (out_info, 0, ML_TENSOR_TYPE_FLOAT32);
+  ml_tensors_info_set_tensor_dimension (out_info, 0, out_dim);
+
+  status = ml_single_open (&single, test_model, NULL, NULL,
+      ML_NNFW_TYPE_TENSORFLOW_LITE, ML_NNFW_HW_ANY);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  status = ml_single_get_input_info (single, &in_res);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  /**
+   * 1. start with a model file with different input dimensions
+   * 2. change the input for the model file
+   * 3. run the model file with the updated input dimensions
+   * 4. verify the output
+   */
+
+  ml_tensors_info_get_tensor_dimension (in_res, 0, res_dim);
+  EXPECT_FALSE (in_dim[0] == res_dim[0]);
+  EXPECT_TRUE (in_dim[1] == res_dim[1]);
+  EXPECT_TRUE (in_dim[2] == res_dim[2]);
+  EXPECT_TRUE (in_dim[3] == res_dim[3]);
+
+  /** set the same original input dimension */
+  status = ml_single_set_input_info (single, in_info);
+  EXPECT_TRUE (status == ML_ERROR_NOT_SUPPORTED ||
+      status == ML_ERROR_NONE);
+  if (status == ML_ERROR_NONE) {
+    /* input tensor in filter */
+    status = ml_single_get_input_info (single, &in_res);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+
+    status = ml_tensors_info_get_count (in_res, &count);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+    EXPECT_EQ (count, 1U);
+
+    status = ml_tensors_info_get_tensor_type (in_res, 0, &type);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+    EXPECT_EQ (type, ML_TENSOR_TYPE_FLOAT32);
+
+    ml_tensors_info_get_tensor_dimension (in_res, 0, res_dim);
+    EXPECT_TRUE (in_dim[0] == res_dim[0]);
+    EXPECT_TRUE (in_dim[1] == res_dim[1]);
+    EXPECT_TRUE (in_dim[2] == res_dim[2]);
+    EXPECT_TRUE (in_dim[3] == res_dim[3]);
+
+    /* output tensor in filter */
+    status = ml_single_get_output_info (single, &out_res);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+
+    status = ml_tensors_info_get_count (out_res, &count);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+    EXPECT_EQ (count, 1U);
+
+    status = ml_tensors_info_get_tensor_type (out_res, 0, &type);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+    EXPECT_EQ (type, ML_TENSOR_TYPE_FLOAT32);
+
+    ml_tensors_info_get_tensor_dimension (out_res, 0, res_dim);
+    EXPECT_TRUE (out_dim[0] == res_dim[0]);
+    EXPECT_TRUE (out_dim[1] == res_dim[1]);
+    EXPECT_TRUE (out_dim[2] == res_dim[2]);
+    EXPECT_TRUE (out_dim[3] == res_dim[3]);
+
+    input = output = NULL;
+
+    /* generate dummy data */
+    status = ml_tensors_data_create (in_info, &input);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+    EXPECT_TRUE (input != NULL);
+
+    status = ml_tensors_data_get_tensor_data (input, 0, (void **) &data, &data_size);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+    EXPECT_EQ (data_size, tensor_size * sizeof (int));
+    for (int idx = 0; idx < tensor_size; idx++)
+      data[idx] = idx;
+
+    status = ml_single_invoke (single, input, &output);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+    EXPECT_TRUE (output != NULL);
+
+    status = ml_tensors_data_get_tensor_data (input, 0, (void **) &data, &data_size);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+    EXPECT_EQ (data_size, tensor_size * sizeof (int));
+    for (int idx = 0; idx < tensor_size; idx++)
+      EXPECT_EQ (data[idx], idx);
+
+    status = ml_tensors_data_get_tensor_data (output, 0, (void **) &data, &data_size);
+    EXPECT_EQ (status, ML_ERROR_NONE);
+    EXPECT_EQ (data_size, tensor_size * sizeof (int));
+    for (int idx = 0; idx < tensor_size; idx++)
+      EXPECT_EQ (data[idx], idx+2);
+
+    ml_tensors_data_destroy (output);
+    ml_tensors_data_destroy (input);
+  }
+
+  status = ml_single_close (single);
+  EXPECT_EQ (status, ML_ERROR_NONE);
+
+  g_free (test_model);
+  ml_tensors_info_destroy (in_info);
+  ml_tensors_info_destroy (out_info);
+  ml_tensors_info_destroy (in_res);
+  ml_tensors_info_destroy (out_res);
+}
+
 #endif /* ENABLE_TENSORFLOW_LITE */
 
 /**