[PythonFilter/Testcases] Add some testcases for Python filter
authorDongju Chae <dongju.chae@samsung.com>
Fri, 17 May 2019 08:23:22 +0000 (17:23 +0900)
committerwooksong <wook16.song@samsung.com>
Mon, 20 May 2019 02:23:40 +0000 (11:23 +0900)
I've added some existing testcases
- passthrough.py
- scaler.py

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
ext/nnstreamer/tensor_filter/tensor_filter_python_core.cc
meson.build
tests/nnstreamer_filter_python/checkScaledTensor.py [new file with mode: 0644]
tests/nnstreamer_filter_python/python/passthrough.py [new file with mode: 0644]
tests/nnstreamer_filter_python/python/scaler.py [new file with mode: 0644]
tests/nnstreamer_filter_python/runTest.sh [new file with mode: 0755]

index 7a9f60c..2422333 100644 (file)
@@ -47,11 +47,15 @@ PYCore::PYCore (const char* _script_path, const char* _custom)
    * (e.g., multiarray.x86_64-linux-gnu.so: undefined symbol: PyExc_SystemError)
    */
   gchar libname[32];
-#if PY_VERSION_HEX >= 0x03000000
-  g_snprintf (libname, sizeof(libname), "libpython%fm.so.1", PYTHON3_VERSION);
+
+  g_snprintf (libname, sizeof(libname), 
+#if PY_VERSION_HEX >= 0x03000000 
+              "libpython%d.%dm.so.1.0",
 #else
-  g_snprintf (libname, sizeof(libname), "libpython%f.so.1", PYTHON2_VERSION);
+              "libpython%d.%d.so.1.0",
 #endif
+              PY_MAJOR_VERSION, PY_MINOR_VERSION);
+
   handle = dlopen(libname, RTLD_LAZY | RTLD_GLOBAL);
   g_assert(handle);
 
@@ -85,11 +89,7 @@ PYCore::PYCore (const char* _script_path, const char* _custom)
   Py_XDECREF(sys_module);
 
   /** Find nnstreamer_api module */
-#if PY_VERSION_HEX >= 0x03000000
-  PyObject *api_module = PyImport_ImportModule("nnstreamer_python3");
-#else
-  PyObject *api_module = PyImport_ImportModule("nnstreamer_python2");
-#endif
+  PyObject *api_module = PyImport_ImportModule("nnstreamer_python");
   g_assert(api_module);
   shape_cls = PyObject_GetAttrString(api_module, "TensorShape"); 
   Py_XDECREF(api_module);
index 0e92526..063d3c2 100644 (file)
@@ -156,14 +156,13 @@ have_python2 = false
 have_python3 = false
 
 if get_option('enable-python')
-  prog_pkgconfig = find_program('pkg-config')
+  pg_pkgconfig = find_program('pkg-config')
 
   # Check python 2.7
   python2_dep = dependency('python-2.7', required: false)
   if python2_dep.found()
-    python2_incs = run_command(prog_pkgconfig, ['python-2.7', '--cflags']).stdout().strip().split()
+    python2_incs = run_command(pg_pkgconfig, ['python-2.7', '--cflags']).stdout().strip().split()
     if cc.has_header('numpy/arrayobject.h', args: python2_incs)
-      add_project_arguments('-DPYTHON2_VERSION=2.7', language: ['c', 'cpp'])
       have_python2 = true
     endif
   endif
@@ -171,10 +170,8 @@ if get_option('enable-python')
   # Check python 3.x
   python3_dep = dependency('python3', required: false)
   if python3_dep.found()
-    python3_version = run_command(prog_pkgconfig, ['python3', '--modversion']).stdout().strip()
-    python3_incs = run_command(prog_pkgconfig, ['python3', '--cflags']).stdout().strip().split()
+    python3_incs = run_command(pg_pkgconfig, ['python3', '--cflags']).stdout().strip().split()
     if cc.has_header('numpy/arrayobject.h', args: python3_incs)
-      add_project_arguments('-DPYTHON3_VERSION=' + python3_version, language: ['c', 'cpp'])
       have_python3 = true
     endif
   endif
diff --git a/tests/nnstreamer_filter_python/checkScaledTensor.py b/tests/nnstreamer_filter_python/checkScaledTensor.py
new file mode 100644 (file)
index 0000000..659b52c
--- /dev/null
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+##
+# Copyright (C) 2018 Samsung Electronics
+# License: LGPL-2.1
+#
+# @file   checkScaledTensor.py
+# @brief  Check if the scaled results are correct
+#         This script is imported from tests/nnstreamer_filter_custom/
+# @author MyungJoo Ham <myungjoo.ham@samsung.com>
+
+import sys
+
+## @brief Compare original content and scaled one
+def compare (data1, width1, height1, data2, width2, height2, innerdim):
+  if (len(data1) * width2 * height2) != (len(data2) * width1 * height1):
+    print(str(len(data1) * width2 * height2)+" / "+str(len(data2) * width1 * height1))
+    return 1
+
+  count = 0
+  count2 = 0
+  while (count < len(data1)):
+    # Terminated incorrectly
+    if (count + (innerdim * width1 * height1)) > len(data1):
+      return 2
+    if (count2 + (innerdim * width2 * height2)) > len(data2):
+      return 3
+    if count2 >= len(data2):
+      return 4
+
+    for y in range(0, height2):
+      for x in range(0, width2):
+        for c in range(0, innerdim):
+          ix = x * width1 / width2
+          iy = y * height1 / height2
+          if data1[count + c + ix * innerdim + iy * width1 * innerdim] != data2[count2 + c + x * innerdim + y * width2 * innerdim]:
+            print("At "+str(x)+","+str(y))
+            return 5
+    count = count + innerdim * width1 * height1
+    count2 = count2 + innerdim * width2 * height2
+
+  if count > len(data1):
+    return 6
+  if count2 > len(data2):
+    return 7
+  return 0
+
+## @brief Read file and return its content
+def readfile (filename):
+  F = open(filename, 'r')
+  readfile = F.read()
+  F.close()
+  return readfile
+
+if len(sys.argv) != 8:
+  exit(9)
+
+data1 = readfile(sys.argv[1])
+width1 = int(sys.argv[2])
+height1 = int(sys.argv[3])
+data2 = readfile(sys.argv[4])
+width2 = int(sys.argv[5])
+height2 = int(sys.argv[6])
+innerdim = int(sys.argv[7])
+
+exit(compare(data1, width1, height1, data2, width2, height2, innerdim))
diff --git a/tests/nnstreamer_filter_python/python/passthrough.py b/tests/nnstreamer_filter_python/python/passthrough.py
new file mode 100644 (file)
index 0000000..7652ff3
--- /dev/null
@@ -0,0 +1,42 @@
+##
+# Copyright (C) 2019 Samsung Electronics
+# License: LGPL-2.1
+#
+# @file    Passthrough.py
+# @brief   Python custom filter example: passthrough
+# @author  Dongju Chae <dongju.chae@samsung.com>
+
+import numpy as np
+import nnstreamer_python as nns
+
+D1 = 3
+D2 = 280
+D3 = 40
+
+## @brief  User-defined custom filter; DO NOT CHANGE CLASS NAME
+class CustomFilter(object):
+
+## @breif  The constructor for custom filter: passthrough
+#  @param  None
+  def __init__ (self, *args):
+    self.input_dims = [nns.TensorShape([D1, D2, D3], np.uint8)]
+    self.output_dims = [nns.TensorShape([D1, D2, D3], np.uint8)]
+
+## @breif  python callback: getInputDim
+#  @param  None
+#  @return user-assigned input dimensions 
+  def getInputDim (self):
+    return self.input_dims
+
+## @breif  Python callback: getOutputDim
+#  @param  None
+#  @return user-assigned output dimensions 
+  def getOutputDim (self):
+    return self.output_dims
+
+## @breif  Python callback: invoke
+#  @param  Input tensors: list of input numpy array
+#  @return output tensors: list of output numpy array
+  def invoke (self, input_array):
+    # passthrough; just return
+    return input_array;
diff --git a/tests/nnstreamer_filter_python/python/scaler.py b/tests/nnstreamer_filter_python/python/scaler.py
new file mode 100644 (file)
index 0000000..4a415b9
--- /dev/null
@@ -0,0 +1,64 @@
+##
+# Copyright (C) 2019 Samsung Electronics
+# License: LGPL-2.1
+#
+# @file    Scaler.py
+# @brief   Python custom filter example: scaler
+# @author  Dongju Chae <dongju.chae@samsung.com>
+
+import numpy as np
+import nnstreamer_python as nns
+
+## @brief  User-defined custom filter; DO NOT CHANGE CLASS NAME
+class CustomFilter(object):
+  new_x = 0
+  new_y = 0
+
+## @breif  The constructor for custom filter: scaler
+#  @param  Dimensions to scale
+  def __init__ (self, *args):
+    if len (args) == 1: 
+      try:
+        (self.new_x, self.new_y) = [int (dim) for dim in args[0].split('x')]
+      except:
+        print ("Dimension should have this format: AAAxBBB (e.g., 640x480)")
+
+## @breif  Python callback: setInputDim
+#  @param  Input dimensions: list of nns.TensorShape
+  def setInputDim (self, input_dims):
+    if len(input_dims) != 1:
+      print ("One input tensor is allowed")
+      return None
+    self.input_dims = input_dims
+    self.output_dims = [nns.TensorShape(self.input_dims[0].getDims(), 
+                                        self.input_dims[0].getType())]
+
+    dims = self.output_dims[0].getDims()
+    if (self.new_x > 0):
+      dims[1] = self.new_x
+    if (self.new_y > 0):
+      dims[2] = self.new_y
+
+    return self.output_dims
+
+## @breif  Python callback: invoke
+#  @param  Input tensors: list of input numpy array
+#  @return Output tensors: list of output numpy array
+  def invoke (self, input_array):
+    # reshape to n-D array (in reverse order)
+    input_tensor = np.reshape(input_array[0], self.input_dims[0].getDims()[::-1]) 
+    output_tensor = np.empty(self.output_dims[0].getDims()[::-1],
+                             dtype=self.output_dims[0].getType())
+    in_dims = self.input_dims[0].getDims()
+    out_dims = self.output_dims[0].getDims()
+    for z in range(out_dims[3]):
+      for y in range(out_dims[2]):
+        for x in range(out_dims[1]):
+          for c in range(out_dims[0]):
+            ix = int(x * in_dims[1] / out_dims[1]);
+            iy = int(y * in_dims[2] / out_dims[2]);
+            output_tensor[z][y][x][c] = input_tensor[z][iy][ix][c]
+
+    # to 1-D array
+    return [np.ravel(output_tensor)]
diff --git a/tests/nnstreamer_filter_python/runTest.sh b/tests/nnstreamer_filter_python/runTest.sh
new file mode 100755 (executable)
index 0000000..fbfe78e
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+##
+## @file runTest.sh
+## @author Dongju Chae <dongju.chae@samsung.com>
+## @date Apr 3 2019
+## @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
+
+PATH_TO_PLUGIN="../../build"
+# Check python libraies are built
+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 python2.so)
+    if [[ ! $check ]]; then
+      echo "Cannot find python shared lib"
+      report
+      exit
+    fi
+  else
+    echo "Cannot find ${ini_path}"
+    report exit
+  fi
+else
+  echo "No build directory"
+  report
+  exit
+fi
+
+FRAMEWORK="python2"
+# This symlink is necessary only for testcases; when installed, symlinks will be made
+pushd ../../build/ext/nnstreamer/tensor_filter
+ln -s nnstreamer_python2.so nnstreamer_python.so
+popd
+
+# Passthrough test
+export PYTHONPATH=../../build/ext/nnstreamer/tensor_filter/:$PYTHONPATH
+PATH_TO_SCRIPT="python/passthrough.py"
+gstTest "--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=RGB ! tensor_converter ! tee name=t ! queue ! tensor_filter framework=\"${FRAMEWORK}\" model=\"${PATH_TO_SCRIPT}\" input=\"3:280:40\" inputtype=\"uint8\" output=\"3:280:40\" outputtype=\"uint8\" ! filesink location=\"testcase1.passthrough.log\" sync=true t. ! queue ! filesink location=\"testcase1.direct.log\" sync=true" 1 0 0 $PERFORMANCE
+callCompareTest testcase1.direct.log testcase1.passthrough.log 1 "Compare 1" 0 0
+
+# Scaler test
+# 1) 640x480 --> 320x240
+PATH_TO_SCRIPT="python/scaler.py"
+ARGUMENTS="320x240"
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=RGB,width=640,height=480,framerate=0/1 ! videoconvert ! video/x-raw, format=RGB ! tensor_converter ! tee name=t ! queue ! tensor_filter framework=\"${FRAMEWORK}\" model=\"${PATH_TO_SCRIPT}\" custom=\"${ARGUMENTS}\" ! filesink location=\"testcase2.scaled.log\" sync=true t. ! queue ! filesink location=\"testcase2.direct.log\" sync=true" 2 0 0 $PERFORMANCE
+python checkScaledTensor.py testcase2.direct.log 640 480 testcase2.scaled.log 320 240 3
+testResult $? 2 "Golden test comparison" 0 1
+
+# 2) 640x480 --> 1280x960
+ARGUMENTS="1280x960"
+gstTest "--gst-plugin-path=${PATH_TO_PLUGIN} videotestsrc num-buffers=1 ! video/x-raw,format=RGB,width=640,height=480,framerate=0/1 ! videoconvert ! video/x-raw, format=RGB ! tensor_converter ! tee name=t ! queue ! tensor_filter framework=\"${FRAMEWORK}\" model=\"${PATH_TO_SCRIPT}\" custom=\"${ARGUMENTS}\" ! filesink location=\"testcase3.scaled.log\" sync=true t. ! queue ! filesink location=\"testcase3.direct.log\" sync=true" 3 0 0 $PERFORMANCE
+python checkScaledTensor.py testcase3.direct.log 640 480 testcase3.scaled.log 1280 960 3
+testResult $? 3 "Golden test comparison" 0 1
+
+report