[filter/deepview-rt] Add filter subplugin for DeepViewRT
authorJulien Vuillaumier <julien.vuillaumier@nxp.com>
Mon, 16 May 2022 16:59:52 +0000 (18:59 +0200)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Thu, 9 Jun 2022 06:12:27 +0000 (15:12 +0900)
This change implements a tensor filter subplugin to interface
with DeepViewRT inference engine:
https://www.embeddedml.com/deepviewrt

Plugin can be instanciated using NNStreamer filter element
with relevant properties:

tensor_filter framework=deepview-rt model=<rtm model file>
              [custom=[Engine:<path to engine shared lib>]
                      [Cache:<cache size in MiB>]
                      [MemPool:<memory pool size in MiB>]]

SSAT test suite addition for DeepView RT tensor filter plugin.

Sample model (mobilenet_v1_0.25_224.rtm) is derived from public
tflite model, converted to rtm format per procedure below:
$ wget http://download.tensorflow.org/models/mobilenet_v1_2018_08_02/mobilenet_v1_0.25_224.tgz
$ tar xvf mobilenet_v1_0.25_224.tgz
$ deepview-converter  ./mobilenet_v1_0.25_224.tflite ./mobilenet_v1_0.25_224.rtm

Signed-off-by: Julien Vuillaumier <julien.vuillaumier@nxp.com>
ext/nnstreamer/tensor_filter/meson.build
ext/nnstreamer/tensor_filter/tensor_filter_deepview_rt.cc [new file with mode: 0644]
meson.build
meson_options.txt
tests/meson.build
tests/nnstreamer_filter_deepview_rt/runTest.sh [new file with mode: 0644]
tests/test_models/models/mobilenet_v1_0.25_224.rtm [new file with mode: 0755]

index dc746d8..eff3f14 100644 (file)
@@ -337,6 +337,31 @@ if caffe2_support_is_available
   )
 endif
 
+if deepview_rt_support_is_available
+  nnstreamer_filter_deepview_rt_deps = deepview_rt_support_deps + [glib_dep, gst_dep, nnstreamer_dep]
+
+  filter_sub_deepview_rt_sources = ['tensor_filter_deepview_rt.cc']
+
+  nnstreamer_filter_deepview_rt_sources = []
+  foreach s : filter_sub_deepview_rt_sources
+    nnstreamer_filter_deepview_rt_sources += join_paths(meson.current_source_dir(), s)
+  endforeach
+
+  shared_library('nnstreamer_filter_deepview-rt',
+    nnstreamer_filter_deepview_rt_sources,
+    dependencies: nnstreamer_filter_deepview_rt_deps,
+    install: true,
+    install_dir: filter_subplugin_install_dir
+  )
+
+  static_library('nnstreamer_filter_deepview-rt',
+    nnstreamer_filter_deepview_rt_sources,
+    dependencies: nnstreamer_filter_deepview_rt_deps,
+    install: true,
+    install_dir: nnstreamer_libdir
+  )
+endif
+
 if have_python3
   filter_sub_python3_sources = ['tensor_filter_python3.cc']
 
diff --git a/ext/nnstreamer/tensor_filter/tensor_filter_deepview_rt.cc b/ext/nnstreamer/tensor_filter/tensor_filter_deepview_rt.cc
new file mode 100644 (file)
index 0000000..9fce6ab
--- /dev/null
@@ -0,0 +1,591 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+/**
+ * NNStreamer tensor_filter, sub-plugin for DeepView RT
+ * Copyright 2022 NXP
+ */
+/**
+ * @file    tensor_filter_deepview_rt.cc
+ * @date    7 Jun 2022
+ * @brief   NNStreamer tensor-filter sub-plugin for DeepView RT engine
+ * @see     http://github.com/nnstreamer/nnstreamer
+ * @author  Julien Vuillaumier <julien.vuillaumier@nxp.com>
+ * @bug     No known bugs except for NYI items
+ */
+#include <fstream>
+#include <limits.h>
+#include <memory>
+#include <stdexcept>
+#include <thread>
+#include <unistd.h>
+#include <vector>
+
+
+#define DVRT_SUBPLUGIN_NAME "deepview-rt"
+
+
+/**
+ * @brief Glib logging domain
+ *        set environment variable G_MESSAGES_DEBUG=deepview-rt for debug/info logs
+ */
+#define G_LOG_DOMAIN DVRT_SUBPLUGIN_NAME
+
+#include <glib.h>
+#include <nnstreamer_cppplugin_api_filter.hh>
+#include <nnstreamer_log.h>
+#include <nnstreamer_util.h>
+#include <tensor_common.h>
+
+#include <deepview_rt.h>
+
+using namespace std;
+
+
+namespace nnstreamer
+{
+namespace tensorfilter_dvrt
+{
+
+extern "C" {
+void _init_filter_dvrt (void) __attribute__ ((constructor));
+void _fini_filter_dvrt (void) __attribute__ ((destructor));
+}
+
+/**
+ * @brief Default context cache size in MiB
+ */
+#define DVRT_CONTEXT_CACHE_SIZE_MB_DEFAULT 8
+
+/**
+ * @brief Default context mempool size in MiB
+ */
+#define DVRT_CONTEXT_MEMPOOL_SIZE_MB_DEFAULT 16
+
+
+/**
+ * @brief tensor_filter_subplugin concrete class for DeepViewRT.
+ */
+class dvrt_subplugin final : public tensor_filter_subplugin
+{
+  private:
+  struct dvrt_options_s;
+
+  GstTensorsInfo inputInfo; /**< The tensor info of input tensors */
+  GstTensorsInfo outputInfo; /**< The tensor info of output tensors */
+  GMappedFile *modelMap; /**< Model file mmaped to memory */
+
+  NNContext *context; /**< Context for model load and runtime */
+  NNEngine *engine; /**< Engine path for context acceleration */
+  const NNModel *model; /**< Model blob pointer */
+  vector<NNTensor *> inputTensors; /**< Input tensor from model */
+  vector<NNTensor *> outputTensors; /**< Output tensor from model */
+
+  static const char *name;
+  static dvrt_subplugin *registeredRepresentation;
+
+  void cleanup ();
+  int parseCustomOptions (const GstTensorFilterProperties *props,
+                          struct dvrt_options_s *opts);
+  int initContext (dvrt_options_s *options);
+  int initTensorsMeta ();
+  int setInputTensorProp ();
+  int setOutputTensorProp ();
+  int setTensorProp (gint isInput);
+  int getTensorDim (gsize index, tensor_dim dim);
+  int getTensorType (gsize index, tensor_type *type);
+
+  public:
+  static void init_filter_dvrt ();
+  static void fini_filter_dvrt ();
+
+  dvrt_subplugin ();
+  ~dvrt_subplugin ();
+
+  tensor_filter_subplugin &getEmptyInstance ();
+  void configure_instance (const GstTensorFilterProperties *prop);
+  void invoke (const GstTensorMemory *input, GstTensorMemory *output);
+  void getFrameworkInfo (GstTensorFilterFrameworkInfo &info);
+  int getModelInfo (model_info_ops ops, GstTensorsInfo &in_info,
+                                        GstTensorsInfo &out_info);
+  int eventHandler (event_ops ops, GstTensorFilterFrameworkEventData &data);
+};
+
+const char *dvrt_subplugin::name = DVRT_SUBPLUGIN_NAME;
+
+/**
+ * @brief Options for dvrt context.
+ */
+struct dvrt_subplugin::dvrt_options_s {
+  const gchar *modelPath; /**< rtm model path */
+  const gchar *enginePath; /**< Engine path for context acceleration */
+  guint cacheMb; /**< Context cache size in MiB */
+  guint memPoolMb; /**< Context mempool size in MiB */
+};
+
+/**
+ * @brief dvrt_subplugin class constructor.
+ */
+dvrt_subplugin::dvrt_subplugin ()
+{
+  gst_tensors_info_init (std::addressof (inputInfo));
+  gst_tensors_info_init (std::addressof (outputInfo));
+
+  modelMap = nullptr;
+  context  = nullptr;
+  engine   = nullptr;
+}
+
+/** @brief cleanup resources used by dvrt subplugin */
+void
+dvrt_subplugin::cleanup ()
+{
+  gst_tensors_info_free (std::addressof (inputInfo));
+  gst_tensors_info_free (std::addressof (outputInfo));
+
+  if (context) {
+    nn_context_model_unload (context);
+    nn_context_release (context);
+    context = nullptr;
+  }
+
+  if (engine) {
+    nn_engine_unload (engine);
+    g_free (engine);
+    engine = nullptr;
+  }
+
+  if (modelMap) {
+    g_mapped_file_unref (modelMap);
+    modelMap = nullptr;
+  }
+}
+
+/**
+ * @brief dvrt_subplugin class destructor.
+ */
+dvrt_subplugin::~dvrt_subplugin ()
+{
+  cleanup ();
+}
+
+/** @brief get empty instance of deepviewrt subplugin */
+tensor_filter_subplugin &
+dvrt_subplugin::getEmptyInstance ()
+{
+  return *(new dvrt_subplugin ());
+}
+
+
+/**
+ * @brief Internal function to get the option for dvrt context.
+ * @return 0 if OK. non-zero if error.
+ */
+int
+dvrt_subplugin::parseCustomOptions (const GstTensorFilterProperties *props,
+                                    struct dvrt_options_s *opts)
+{
+  if (props->num_models == 1)
+    opts->modelPath = props->model_files[0];
+  else
+    opts->modelPath = nullptr;
+
+  opts->enginePath = nullptr;
+  opts->cacheMb    = DVRT_CONTEXT_CACHE_SIZE_MB_DEFAULT;
+  opts->memPoolMb  = DVRT_CONTEXT_MEMPOOL_SIZE_MB_DEFAULT;
+
+  if (props->custom_properties) {
+    gchar **strv;
+    guint i, len;
+
+    strv = g_strsplit (props->custom_properties, ",", -1);
+    len = g_strv_length (strv);
+
+    for (i = 0; i < len; ++i) {
+      gchar **pair = g_strsplit (strv[i], ":", -1);
+
+      if (g_strv_length (pair) > 1) {
+        g_strstrip (pair[0]);
+        g_strstrip (pair[1]);
+
+        if (g_ascii_strcasecmp (pair[0], "Engine") == 0)
+          opts->enginePath = g_strdup (pair[1]);
+        else if (g_ascii_strcasecmp (pair[0], "Cache") == 0)
+          opts->cacheMb = g_ascii_strtoll (pair[1], nullptr, 10);
+        else if (g_ascii_strcasecmp (pair[0], "MemPool") == 0)
+          opts->memPoolMb = g_ascii_strtoll (pair[1], nullptr, 10);
+      }
+
+      g_strfreev (pair);
+    }
+
+    g_strfreev (strv);
+  }
+
+  return 0;
+}
+
+/**
+ * @brief return the type of tensor
+ * @return 0 if OK. non-zero if error.
+ */
+int
+dvrt_subplugin::getTensorType (gsize index, tensor_type *type)
+{
+  NNTensorType _type;
+  tensor_type res;
+
+  *type = _NNS_END;
+
+  _type = nn_model_layer_datatype_id (model, index);
+  switch (_type) {
+  case NNTensorType_I8:
+    res = _NNS_INT8;
+    break;
+  case NNTensorType_U8:
+    res = _NNS_UINT8;
+    break;
+  case NNTensorType_I16:
+    res = _NNS_INT16;
+    break;
+  case NNTensorType_U16:
+    res = _NNS_UINT16;
+    break;
+  case NNTensorType_I32:
+    res = _NNS_INT32;
+    break;
+  case NNTensorType_U32:
+    res = _NNS_UINT32;
+    break;
+  case NNTensorType_I64:
+    res = _NNS_INT64;
+    break;
+  case NNTensorType_U64:
+    res = _NNS_UINT64;
+    break;
+  case NNTensorType_F32:
+    res = _NNS_FLOAT32;
+    break;
+  case NNTensorType_F64:
+    res = _NNS_FLOAT64;
+    break;
+  case NNTensorType_F16:
+  default:
+    nns_logw ("Tensor type not supported: %d", (gint)_type);
+    return -EINVAL;
+  }
+
+  *type = res;
+  return 0;
+}
+
+/**
+ * @brief return the shape of tensor
+ * @return 0 if OK. non-zero if error.
+ */
+int
+dvrt_subplugin::getTensorDim (gsize index, tensor_dim dim)
+{
+  gsize dims;
+  const gint32 *shape;
+  gsize i;
+
+  shape = nn_model_layer_shape (model, index, &dims);
+  if (dims > NNS_TENSOR_RANK_LIMIT) {
+    nns_logw ("Shape rank too high: %zu max: %d", dims, NNS_TENSOR_RANK_LIMIT);
+    return -EINVAL;
+  }
+
+  /* the order of dimension is reversed at CAPS negotiation */
+  for (i = 0; i < dims; i++)
+    dim[dims - i - 1] = shape[i];
+
+  /* fill remaining entries with 1 */
+  for (i = dims; i < NNS_TENSOR_RANK_LIMIT; ++i) {
+    dim[i] = 1;
+  }
+
+  return 0;
+}
+
+/**
+ * @brief fetch and setup input/ouput tensors metadata
+ * @return 0 if OK. non-zero if error.
+ */
+int
+dvrt_subplugin::setTensorProp (gint isInput)
+{
+  GstTensorsInfo *tensorMeta;
+  const guint32 *indices;
+  gsize num;
+  vector<NNTensor *> *tensors;
+  const gchar *tag = isInput ? "input" : "output";
+
+  if (isInput) {
+    tensorMeta = std::addressof (inputInfo);
+    indices = nn_model_inputs (model, &num);
+    tensors = &inputTensors;
+  } else {
+    tensorMeta = std::addressof (outputInfo);
+    indices = nn_model_outputs (model, &num);
+    tensors = &outputTensors;
+  }
+
+  if (num > NNS_TENSOR_SIZE_LIMIT) {
+    nns_logw ("Too many %s tensors: %zu max: %d",
+    tag, num, NNS_TENSOR_SIZE_LIMIT);
+    return -EINVAL;
+  }
+  tensorMeta->num_tensors = num;
+  tensors->clear ();
+  tensors->reserve (num);
+
+  for (size_t i = 0; i < num; i++) {
+    gsize index = indices[i];
+    NNTensor *tensor = nn_context_tensor_index (context, index);
+    tensors->push_back (tensor);
+
+    const gchar *name = nn_model_layer_name (model, index);
+    tensorMeta->info[i].name = g_strdup (name);
+    if (getTensorDim (index, tensorMeta->info[i].dimension))
+      return -EINVAL;
+
+    if (getTensorType (index, &tensorMeta->info[i].type))
+      return -EINVAL;
+
+    gchar *dim;
+    dim = gst_tensor_get_dimension_string (tensorMeta->info[i].dimension);
+    ml_logd ("tensorMeta[%zu] >> name[%s], type[%d], dim[%s]", i,
+             tensorMeta->info[i].name, tensorMeta->info[i].type, dim);
+    g_free (dim);
+  }
+
+  return 0;
+}
+
+/**
+ * @brief fetch and setup ouput tensors metadata
+ * @return 0 if OK. non-zero if error.
+ */
+int
+dvrt_subplugin::setOutputTensorProp ()
+{
+  return setTensorProp (FALSE);
+}
+
+/**
+ * @brief fetch and setup input tensors metadata
+ * @return 0 if OK. non-zero if error.
+ */
+int
+dvrt_subplugin::setInputTensorProp ()
+{
+  return setTensorProp (TRUE);
+}
+
+/**
+ * @brief create context for rtm model.
+ * @return 0 if OK. non-zero if error.
+ */
+int
+dvrt_subplugin::initContext (dvrt_options_s *options)
+{
+  GError *err = nullptr;
+  int nerr;
+  NNError nnerror;
+
+  if (!options->modelPath)
+    return -ENOENT;
+
+  GMappedFile *modelMap = g_mapped_file_new (options->modelPath, FALSE, &err);
+  if (!modelMap || err) {
+    nns_logw ("Could not map model file %s %s",
+              options->modelPath, err->message);
+    g_clear_error (&err);
+    return -ENOENT;
+  }
+
+  gsize size = g_mapped_file_get_length (modelMap);
+  model = g_mapped_file_get_contents (modelMap);
+  nerr = nn_model_validate (model, size);
+  if (nerr) {
+    nns_logw ("Model validation failed %s", nn_model_validate_error (nerr));
+    return -EINVAL;
+  }
+
+  if (options->enginePath) {
+    NNEngine *e = (NNEngine *) g_malloc (nn_engine_sizeof ());
+    engine = nn_engine_init (e);
+    nnerror = nn_engine_load (engine, options->enginePath);
+    if (nnerror) {
+      nns_logw ("Engine load failed %s %s",
+                options->enginePath, nn_strerror (nnerror));
+      return -ENOENT;
+    }
+  }
+
+  context = nn_context_init (engine,
+            (size_t) (options->memPoolMb * 1024UL * 1024UL),
+            nullptr,
+            (size_t) (options->cacheMb * 1024UL * 1024UL),
+            nullptr);
+  if (!context) {
+    nns_logw ("Context init failed");
+    return -ENOMEM;
+  }
+
+  nnerror = nn_context_model_load (context, size, model);
+  if (nnerror) {
+    nns_logw ("Context model load failed %s", nn_strerror (nnerror));
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+/** @brief configure dvrt instance */
+void
+dvrt_subplugin::configure_instance (const GstTensorFilterProperties *prop)
+{
+  struct dvrt_options_s options = {};
+  int ret;
+
+  ret = parseCustomOptions (prop, &options);
+  if (ret)
+    goto done;
+
+  ret = initContext (&options);
+  if (ret)
+    goto done;
+
+  ret = setInputTensorProp ();
+  if (ret)
+    goto done;
+
+  ret = setOutputTensorProp ();
+
+done:
+  g_free ((gpointer) options.enginePath);
+  if (ret) {
+    cleanup ();
+    throw std::invalid_argument ("Instance configuration failed");
+  }
+}
+
+/** @brief invoke using dvrt */
+void
+dvrt_subplugin::invoke (const GstTensorMemory *input, GstTensorMemory *output)
+{
+  g_assert (inputTensors.size () == inputInfo.num_tensors);
+  for (gsize i = 0; i < inputInfo.num_tensors; ++i) {
+    NNTensor *tensor = inputTensors[i];
+    g_assert (tensor);
+    gsize size = nn_tensor_size (tensor);
+    g_assert (size == input[i].size);
+
+    void *data = nn_tensor_mapwo (tensor);
+    g_assert (data);
+    memcpy (data, input[i].data, input[i].size);
+    nn_tensor_unmap (tensor);
+
+    nns_logd ("Invoke Input copy to (%p) (%zu) bytes", data, input[i].size);
+  }
+
+  NNError err = nn_context_run (context);
+  if (err) {
+    nns_logw ("Context run failed %s", nn_strerror (err));
+    throw std::runtime_error ("Invoking DeepView RT failed.");
+  }
+
+  g_assert (outputTensors.size () == outputInfo.num_tensors);
+  for (gsize i = 0; i < outputInfo.num_tensors; ++i) {
+    NNTensor *tensor = outputTensors[i];
+    g_assert (tensor);
+    gsize size = nn_tensor_size (tensor);
+    g_assert (size == output[i].size);
+
+    const void *data = nn_tensor_mapro (tensor);
+    g_assert (data);
+    memcpy (output[i].data, data, output[i].size);
+    nn_tensor_unmap (tensor);
+
+    nns_logd ("Invoke Output copy from (%p) (%zu) bytes",
+              data, output[i].size);
+  }
+}
+
+/** @brief Get framework information */
+void
+dvrt_subplugin::getFrameworkInfo (GstTensorFilterFrameworkInfo &info)
+{
+  info.name = name;
+  info.allow_in_place = 0;
+  info.allocate_in_invoke = 0;
+  info.run_without_model = 0;
+  info.verify_model_path = 1;
+}
+
+/**
+ * @brief Method to get the model information.
+ */
+int
+dvrt_subplugin::getModelInfo (
+    model_info_ops ops, GstTensorsInfo &in_info, GstTensorsInfo &out_info)
+{
+  if (ops == GET_IN_OUT_INFO) {
+    gst_tensors_info_copy (std::addressof (in_info), std::addressof (inputInfo));
+    gst_tensors_info_copy (std::addressof (out_info), std::addressof (outputInfo));
+    return 0;
+  }
+
+  return -ENOENT;
+}
+
+/**
+ * @brief Method to handle events.
+ */
+int
+dvrt_subplugin::eventHandler (event_ops ops, GstTensorFilterFrameworkEventData &data)
+{
+  UNUSED (ops);
+  UNUSED (data);
+  return -ENOENT;
+}
+
+dvrt_subplugin *dvrt_subplugin::registeredRepresentation = nullptr;
+
+/** @brief Initialize this object for tensor_filter subplugin runtime register */
+void
+dvrt_subplugin::init_filter_dvrt (void)
+{
+  registeredRepresentation
+      = tensor_filter_subplugin::register_subplugin<dvrt_subplugin> ();
+  nnstreamer_filter_set_custom_property_desc (name,
+      "Cache",   "Context cache size in MiB",
+      "MemPool", "Context mempool size in MiB",
+      "Engine",  "Engine plugin path for context acceleration",
+      nullptr);
+}
+
+/** @brief initializer */
+void
+_init_filter_dvrt ()
+{
+  dvrt_subplugin::init_filter_dvrt ();
+}
+
+/** @brief Destruct the subplugin */
+void
+dvrt_subplugin::fini_filter_dvrt (void)
+{
+  assert (registeredRepresentation != nullptr);
+  tensor_filter_subplugin::unregister_subplugin (registeredRepresentation);
+}
+
+/** @brief finalizer */
+void
+_fini_filter_dvrt ()
+{
+  dvrt_subplugin::fini_filter_dvrt ();
+}
+
+}  /* namespace nnstreamer::tensorfilter_dvrt */
+} /* namespace nnstreamer */
index badc23b..5576502 100644 (file)
@@ -283,6 +283,21 @@ if not get_option('mqtt-support').disabled()
   endif
 endif
 
+# deepview-rt
+deepview_rt_dep = dependency('', required: false)
+if not get_option('deepview-rt-support').disabled()
+  deepview_rt_dep = dependency('deepview-rt', required: false)
+  if (not deepview_rt_dep.found())
+    deepview_rt_lib = cxx.find_library('deepview-rt', required: false)
+    if (deepview_rt_lib.found() and cxx.check_header('deepview_rt.h'))
+      deepview_rt_dep = declare_dependency(dependencies: [ deepview_rt_lib ])
+    endif
+  endif
+endif
+if get_option('deepview-rt-support').enabled() and not deepview_rt_dep.found()
+  error('deepview-rt enabled but dependencies are not found.')
+endif
+
 # armnn
 armnn_dep = dependency('', required: false)
 if not get_option('armnn-support').disabled()
@@ -342,6 +357,10 @@ features = {
     'target': 'caffe2',
     'project_args': { 'ENABLE_CAFFE2': 1 }
   },
+  'deepview-rt-support': {
+    'extra_deps': [ deepview_rt_dep ],
+    'project_args': { 'ENABLE_DEEPVIEW_RT': 1 }
+  },
   'mvncsdk2-support': {
     'target': 'libmvnc',
     'project_args': { 'ENABLE_MOVIDIUS_NCSDK2' : 1}
index f9bf17f..8ca7f8b 100644 (file)
@@ -6,6 +6,7 @@ option('tflite-support', type: 'feature', value: 'auto')
 option('tflite2-support', type: 'feature', value: 'auto')
 option('pytorch-support', type: 'feature', value: 'auto')
 option('caffe2-support', type: 'feature', value: 'auto')
+option('deepview-rt-support', type: 'feature', value: 'auto')
 option('python3-support', type: 'feature', value: 'auto')
 option('mvncsdk2-support', type: 'feature', value: 'auto')
 # nnfw-runtime ( details in https://review.tizen.org/gerrit/p/platform/core/ml/nnfw )
index 4a1a3f3..e42d76c 100644 (file)
@@ -358,6 +358,9 @@ if get_option('install-test')
   if caffe2_support_is_available
     install_subdir('nnstreamer_filter_caffe2', install_dir: unittest_install_dir)
   endif
+  if deepview_rt_support_is_available
+    install_subdir('nnstreamer_filter_deepview_rt', install_dir: unittest_install_dir)
+  endif
   if flatbuf_support_is_available
     install_subdir('nnstreamer_flatbuf', install_dir: unittest_install_dir)
     install_subdir('nnstreamer_flexbuf', install_dir: unittest_install_dir)
diff --git a/tests/nnstreamer_filter_deepview_rt/runTest.sh b/tests/nnstreamer_filter_deepview_rt/runTest.sh
new file mode 100644 (file)
index 0000000..a4f7c75
--- /dev/null
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+##
+## SPDX-License-Identifier: LGPL-2.1-only
+##
+## @file runTest.sh
+## Copyright 2022 NXP
+## @brief SSAT Test Cases for NNStreamer
+##
+if [[ "$SSATAPILOADED" != "1" ]]; then
+    SILENT=0
+    INDEPENDENT=1
+    search="ssat-api.sh"
+    source $search
+    printf "${Blue}Independent Mode${NC}
+"
+fi
+
+# This is compatible with SSAT (https://github.com/myungjoo/SSAT)
+testInit $1
+
+# NNStreamer and plugins path for test
+PATH_TO_PLUGIN="../../build"
+
+if [[ -d $PATH_TO_PLUGIN ]]; then
+    ini_path="${PATH_TO_PLUGIN}/ext/nnstreamer/tensor_filter"
+    if [[ -d ${ini_path} ]]; then
+        check=$(ls ${ini_path} | grep deepview-rt.so)
+        if [[ ! $check ]]; then
+            echo "Cannot find deepview-rt shared lib"
+            report
+            exit
+        fi
+    else
+        echo "Cannot find ${ini_path}"
+    fi
+else
+    ini_file="/etc/nnstreamer.ini"
+    if [[ -f ${ini_file} ]]; then
+        path=$(grep "^filters" ${ini_file})
+        key=${path%=*}
+        value=${path##*=}
+
+        if [[ $key != "filters" ]]; then
+            echo "String Error"
+            report
+            exit
+        fi
+
+        if [[ -d ${value} ]]; then
+            check=$(ls ${value} | grep deepview-rt.so)
+            if [[ ! $check ]]; then
+                echo "Cannot find deepview-rt lib"
+                report
+                exit
+            fi
+        else
+            echo "Cannot file ${value}"
+            report
+            exit
+        fi
+    else
+        echo "Cannot identify nnstreamer.ini"
+        report
+        exit
+    fi
+fi
+
+PATH_TO_MODEL="../test_models/models/mobilenet_v1_0.25_224.rtm"
+PATH_TO_LABEL="../test_models/labels/labels.txt"
+PATH_TO_IMAGE="../test_models/data/orange.png"
+PATH_TO_CLASS="class.out.log"
+
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} filesrc location=${PATH_TO_IMAGE} ! pngdec ! videoscale ! imagefreeze ! videoconvert ! video/x-raw,format=RGB,framerate=0/1 ! tensor_converter ! tensor_transform mode=arithmetic option=typecast:float32,div:127.5,add:-1.0 ! tensor_filter framework=deepview-rt model=${PATH_TO_MODEL} ! tensor_decoder mode=image_labeling option1=${PATH_TO_LABEL} ! filesink location=${PATH_TO_CLASS} " 1 0 0 $PERFORMANCE
+class=`cat ${PATH_TO_CLASS}`
+[ "$class" = "orange" ]
+testResult $? 1 "Golden test comparison" 0 1
+
+# Negative tests: wrong input dimensions 1:480:640:3 instead of 1:224:224:3
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} filesrc location=${PATH_TO_IMAGE} ! pngdec ! videoscale ! imagefreeze ! videoconvert ! video/x-raw,,width=640,height=480,format=RGB,framerate=0/1 ! tensor_converter ! tensor_transform mode=arithmetic option=typecast:float32,div:127.5,add:-1.0 ! tensor_filter framework=deepview-rt model=${PATH_TO_MODEL} ! tensor_decoder mode=image_labeling option1=${PATH_TO_LABEL} ! filesink location=${PATH_TO_CLASS} " 1_n 0 1 $PERFORMANCE
+
+# Cleanup
+rm *.log
+
+report
diff --git a/tests/test_models/models/mobilenet_v1_0.25_224.rtm b/tests/test_models/models/mobilenet_v1_0.25_224.rtm
new file mode 100755 (executable)
index 0000000..9406bb4
Binary files /dev/null and b/tests/test_models/models/mobilenet_v1_0.25_224.rtm differ