From: MyungJoo Ham Date: Fri, 1 Jun 2018 04:46:27 +0000 (+0900) Subject: [Filter/Custom] Custom tensor postprocessing support X-Git-Tag: v0.0.1~193 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b594a591f4cd4fd3e05790c3579b2e9b3cbf6723;p=platform%2Fupstream%2Fnnstreamer.git [Filter/Custom] Custom tensor postprocessing support NN developers may define their own custom tensor operations in the format given byu tensor_common to let NNStreamer do any custom operations on the tensor stream. Signed-off-by: MyungJoo Ham --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 51dcf4a..c13b2d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,12 @@ ELSE(TIZEN OR GTEST_LIB) SET(gtestInc /usr/src/gtest) ENDIF(TIZEN OR GTEST_LIB) +IF (NOT INCLUDE_INSTALL_DIR) + # We need to define includedir path + SET (INCLUDE_INSTALL_DIR /usr/include) + MESSAGE ("Warning: INCLUDE_INSTALL_DIR not defined. Using /usr/include") +ENDIF (NOT INCLUDE_INSTALL_DIR) + pkg_check_modules(pkgs REQUIRED ${PKG_MODULES}) INCLUDE_DIRECTORIES( @@ -69,3 +75,12 @@ TARGET_LINK_LIBRARIES(unittest_common ${pkgs_LIBRARIES} ${gtestLink}) ADD_SUBDIRECTORY(tensor_converter) ADD_SUBDIRECTORY(tensor_filter) + +CONFIGURE_FILE(nnstreamer.pc.in nnstreamer.pc @ONLY) + +INSTALL(FILES include/tensor_typedef.h + DESTINATION ${INCLUDE_INSTALL_DIR}/nnstreamer + ) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/nnstreamer.pc + DESTINATION ${LIB_INSTALL_DIR}/pkgconfig + ) diff --git a/include/tensor_common.h b/include/tensor_common.h index a2b8b84..06e06de 100644 --- a/include/tensor_common.h +++ b/include/tensor_common.h @@ -54,29 +54,10 @@ #include #include +#include "tensor_typedef.h" G_BEGIN_DECLS -#define NNS_TENSOR_RANK_LIMIT (4) -/** - * @brief Possible data element types of other/tensor. - * - * The current version supports NNS_UINT8 only as video-input. - * There is no restrictions for inter-NN or sink-to-app. - */ -typedef enum _nns_tensor_type { - _NNS_INT32 = 0, - _NNS_UINT32, - _NNS_INT16, - _NNS_UINT16, - _NNS_INT8, - _NNS_UINT8, - _NNS_FLOAT64, - _NNS_FLOAT32, - - _NNS_END, -} tensor_type; - /** * @brief Possible input stream types for other/tensor. * diff --git a/include/tensor_typedef.h b/include/tensor_typedef.h new file mode 100644 index 0000000..9cd16df --- /dev/null +++ b/include/tensor_typedef.h @@ -0,0 +1,78 @@ +/* + * NNStreamer Common Header, Typedef part, for export as devel package. + * Copyright (C) 2018 MyungJoo Ham + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * @file tensor_common_typedef.h + * @date 01 Jun 2018 + * @brief Common header file for NNStreamer, the GStreamer plugin for neural networks + * @see http://github.com/TO-BE-DETERMINED-SOON + * @see https://github.sec.samsung.net/STAR/nnstreamer + * @author MyungJoo Ham + * + * To Packagers: + * + * This fils it to be packaged as "devel" package for NN developers. + */ + +#ifndef __GST_TENSOR_TYPEDEF_H__ +#define __GST_TENSOR_TYPEDEF_H__ + +#define NNS_TENSOR_RANK_LIMIT (4) +/** + * @brief Possible data element types of other/tensor. + * + * The current version supports NNS_UINT8 only as video-input. + * There is no restrictions for inter-NN or sink-to-app. + */ +typedef enum _nns_tensor_type { + _NNS_INT32 = 0, + _NNS_UINT32, + _NNS_INT16, + _NNS_UINT16, + _NNS_INT8, + _NNS_UINT8, + _NNS_FLOAT64, + _NNS_FLOAT32, + + _NNS_END, +} tensor_type; + +#endif /*__GST_TENSOR_TYPEDEF_H__*/ diff --git a/nnstreamer.pc.in b/nnstreamer.pc.in new file mode 100644 index 0000000..75a2f16 --- /dev/null +++ b/nnstreamer.pc.in @@ -0,0 +1,13 @@ +# Package Information for pkg-config + +prefix=@PREFIX@ +exec_prefix=@EXEC_PREFIX@ +libdir=@LIB_INSTALL_DIR@ +includedir=/usr/include/deviced + +Name: nnstreamer +Description: Neural Network Suite for GStreamer +Version: @VERSION@ +Requires: +Libs: +Cflags: -I${includedir} diff --git a/packaging/nnstreamer.spec b/packaging/nnstreamer.spec index 63a6fcf..3e6cee1 100644 --- a/packaging/nnstreamer.spec +++ b/packaging/nnstreamer.spec @@ -39,6 +39,13 @@ HTML pages of lcov results of NNStreamer generated during rpmbuild NNStreamer is a set of gstreamer plugins to support general neural networks and their plugins in a gstreamer stream. +%package devel +Summary: Development package for custom tensor operator developers (tensor_filter/custom) +Requires: nnstreamer = %{version}-%{release} +%description devel +Development package for custom tensor operator developers (tensor_filter/custom). +This contains corresponding header files and .pc pkgconfig file. + %prep %setup -q cp %{SOURCE1001} . @@ -102,7 +109,13 @@ cp -r result %{buildroot}%{_datadir}/nnstreamer/unittest/ %defattr(-,root,root,-) # The libraries are in LGPLv2.1 (testcases and non GST-plugin components are APL2) %license LICENSE.LGPLv2.1 -%{_libdir}/* +%{_libdir}/*.so +%{_libdir}/*.so* +# TODO generate .so files with version info. Migrate symbolic-link .so to devel. + +%files devel +%{_includedir}/nnstreamer/* +%{_libdir}/pkgconfig/nnstreamer.pc %if 0%{?testcoverage} %files unittest-coverage diff --git a/tensor_filter/CMakeLists.txt b/tensor_filter/CMakeLists.txt index 6fb9ff6..1db2d76 100644 --- a/tensor_filter/CMakeLists.txt +++ b/tensor_filter/CMakeLists.txt @@ -1,8 +1,12 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) -ADD_LIBRARY(tensor_filter SHARED tensor_filter.c tensor_filter_tensorflow_lite.c) +ADD_LIBRARY(tensor_filter SHARED + tensor_filter.c + tensor_filter_tensorflow_lite.c + tensor_filter_custom.c + ) -TARGET_LINK_LIBRARIES(tensor_filter ${pkgs_LIBRARIES}) +TARGET_LINK_LIBRARIES(tensor_filter dl ${pkgs_LIBRARIES}) TARGET_INCLUDE_DIRECTORIES(tensor_filter PUBLIC ${pkgs_INCLUDE_DIRS}) TARGET_COMPILE_OPTIONS(tensor_filter PUBLIC ${pkgs_CFLAGS_OTHER}) @@ -11,3 +15,6 @@ INSTALL(TARGETS tensor_filter LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} ) +INSTALL(FILES tensor_filter_custom.h + DESTINATION ${INCLUDE_INSTALL_DIR}/nnstreamer + ) diff --git a/tensor_filter/README.md b/tensor_filter/README.md index ffee3be..c5cf42a 100644 --- a/tensor_filter/README.md +++ b/tensor_filter/README.md @@ -22,7 +22,11 @@ This should fill in ```GstTensor_Filter_Framework``` supporting tensorflow_lite. ### Custom function support, ```tensor_filter_custom.c``` -This should fill in ```GstTensor_Filter_Framework``` supporting dlopen'ed custom shared objects, requiring such shared objects to provide its own defined functions. +Neural network and streameline developers may define their own tensor postprocessing operations with tensor_filter_custom. + +With ```nnstreamer-devel``` package installed at build time (e.g., ```BuildRequires: pkgconfig(nnstreamer)``` in .spec file), develerops can implement their own functions and expose their functions via ```NNStreamer_custom_class``` defined in ```tensor_fitler_custom.h```. The resulting custom developer plugin should exist as a shared library (.so) with the symbol NNStreamer_custom exposed with all the func defined in NNStreamer_custom_class. + +@TODO Write an example custom filter for novice developers. ### We may add other NNFW as well (tensorflow, caffe, ...) diff --git a/tensor_filter/tensor_filter.c b/tensor_filter/tensor_filter.c index c414a8a..ca4f15f 100644 --- a/tensor_filter/tensor_filter.c +++ b/tensor_filter/tensor_filter.c @@ -85,7 +85,7 @@ GstTensor_Filter_Framework *tensor_filter_supported[] = { [_T_F_UNDEFINED] = NULL, - [_T_F_CUSTOM] = NULL, + [_T_F_CUSTOM] = &NNS_support_custom, [_T_F_TENSORFLOW_LITE] = &NNS_support_tensorflow_lite, [_T_F_TENSORFLOW] = NULL, [_T_F_CAFFE2] = NULL, @@ -285,6 +285,8 @@ gst_tensor_filter_init (GstTensor_Filter * filter) filter->outputDimension[2] = 1; filter->outputDimension[3] = 1; // out filter->outputType = _NNS_END; // not initialized + + filter->privateData = NULL; // mark not initialized. } /** diff --git a/tensor_filter/tensor_filter.h b/tensor_filter/tensor_filter.h index 2552dde..81e6dc2 100644 --- a/tensor_filter/tensor_filter.h +++ b/tensor_filter/tensor_filter.h @@ -104,8 +104,8 @@ extern const char* nnfw_names[]; static const gboolean nnfw_support_status[] = { FALSE, - FALSE, - FALSE, + TRUE, + TRUE, FALSE, FALSE, @@ -162,9 +162,11 @@ struct _GstTensor_Filter_Framework int (*invoke_NN)(GstTensor_Filter *filter, void *inputptr, void *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! */ }; extern GstTensor_Filter_Framework NNS_support_tensorflow_lite; +extern GstTensor_Filter_Framework NNS_support_custom; extern GstTensor_Filter_Framework *tensor_filter_supported[]; diff --git a/tensor_filter/tensor_filter_custom.c b/tensor_filter/tensor_filter_custom.c new file mode 100644 index 0000000..8f6ba91 --- /dev/null +++ b/tensor_filter/tensor_filter_custom.c @@ -0,0 +1,182 @@ +/** + * GStreamer Tensor_Filter, Tensorflow-Lite Module + * Copyright (C) 2018 MyungJoo Ham + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * @file tensor_filter_custom.c + * @date 01 Jun 2018 + * @brief Custom tensor post-processing interface for NNStreamer suite between NN developer-plugins and NNstreamer. + * @see http://github.com/TO-BE-DETERMINED-SOON + * @see https://github.sec.samsung.net/STAR/nnstreamer + * @author MyungJoo Ham + * + * This is the per-NN-framework plugin (custom) for tensor_filter. + * Fill in "GstTensor_Filter_Framework" for tensor_filter.h/c + * + */ + +#include "tensor_filter.h" +#include "tensor_filter_custom.h" +#include +#include + +struct _internal_data { + GstTensor_Filter *parent; + + void *handle; + NNStreamer_custom_class *methods; + + void* customFW_private_data; +}; +typedef struct _internal_data internal_data; + +/** + * @brief Load the custom library. Will skip loading if it's already loaded. + * @return 0 if successfully loaded. 1 if skipped (already loaded). -1 if error + */ +static int custom_loadlib(GstTensor_Filter *filter) { + internal_data *ptr; + char *dlsym_error; + + if (filter->privateData != NULL) { + /* @TODO : Check the integrity of filter->data and filter->modelFilename, nnfw */ + return 1; + } + + ptr = g_new0(internal_data, 0); /* Fill Zero! */ + filter->privateData = ptr; + ptr->parent = filter; + + /* Load .so if this is the first time for this instance. */ + ptr->handle = dlopen(filter->modelFilename, RTLD_NOW); + if (!ptr->handle) { + g_free(ptr); + filter->privateData = NULL; + return -1; + } + + dlerror(); + ptr->methods = (NNStreamer_custom_class *) dlsym(ptr->handle, "NNStreamer_custom"); + dlsym_error = dlerror(); + if (dlsym_error) { + g_printerr("tensor_filter_custom:loadlib error: %s\n", dlsym_error); + dlclose(ptr->handle); + g_free(ptr); + filter->privateData = NULL; + return -1; + } + + ptr->customFW_private_data = ptr->methods->initfunc(); + return 0; +} + +/** + * @brief The mandatory callback for GstTensor_Filter_Framework + * @param filter The parent object + * @param[in] inptr The input tensor + * @param[out] outptr The output tensor + */ +static int custom_invoke(GstTensor_Filter *filter, void *inptr, void *outptr) { + int retval = custom_loadlib(filter); + internal_data *ptr; + + /* Actually, tensor_filter must have called getInput/OotputDim first. */ + g_assert(retval != 0); + + if (retval < 0) + return retval; + + g_assert(filter->privateData); + ptr = filter->privateData; + + return ptr->methods->invoke(ptr->customFW_private_data, inptr, outptr); +} + +/** + * @brief The optional callback for GstTensor_Filter_Framework + */ +static int custom_getInputDim(GstTensor_Filter *filter, uint32_t *inputDimension, tensor_type *type) { + int retval = custom_loadlib(filter); + internal_data *ptr; + + if (retval < 0) + return retval; + + g_assert(filter->privateData); + ptr = filter->privateData; + + return ptr->methods->getInputDim(ptr->customFW_private_data, inputDimension, type); +} + +/** + * @brief The optional callback for GstTensor_Filter_Framework + */ +static int custom_getOutputDim(GstTensor_Filter *filter, uint32_t *outputDimension, tensor_type *type) { + int retval = custom_loadlib(filter); + internal_data *ptr; + + if (retval < 0) + return retval; + + g_assert(filter->privateData); + ptr = filter->privateData; + + return ptr->methods->getOutputDim(ptr->customFW_private_data, outputDimension, type); +} + +/** + * @brief Free privateData and move on. + */ +static void custom_close(GstTensor_Filter *filter) { + internal_data *ptr = filter->privateData; + + ptr->methods->exitfunc(ptr->customFW_private_data); + g_free(ptr); + filter->privateData = NULL; +} + +GstTensor_Filter_Framework NNS_support_custom = { + .name = "custom", + .allow_in_place = FALSE, // custom cannot support in-place (outptr == inptr). + .invoke_NN = custom_invoke, + .getInputDimension = custom_getInputDim, + .getOutputDimension = custom_getOutputDim, + .close = custom_close, +}; diff --git a/tensor_filter/tensor_filter_custom.h b/tensor_filter/tensor_filter_custom.h new file mode 100644 index 0000000..ee85c8d --- /dev/null +++ b/tensor_filter/tensor_filter_custom.h @@ -0,0 +1,119 @@ +/** + * GStreamer Tensor_Filter, Tensorflow-Lite Module + * Copyright (C) 2018 MyungJoo Ham + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * @file tensor_filter_custom.h + * @date 01 Jun 2018 + * @brief Custom tensor post-processing interface for NNStreamer suite for post-processing code developers. + * @see http://github.com/TO-BE-DETERMINED-SOON + * @see https://github.sec.samsung.net/STAR/nnstreamer + * @author MyungJoo Ham + * + * How To for NNdevelopers: + * + * 1. Define struct, "NNStreamer_custom", with the functions defined. + * 2. Compile as a shared object. (.so in Linux) + * 3. Use NNStreamer (tensor_filter framework=custom, model=FILEPATH_OF_YOUR_SO.so, ...) + * + * To Packagers: + * + * This file is to be packaged as "devel" package for NN developers. + */ +#ifndef __NNS_TENSOR_FILTER_CUSTOM_H__ +#define __NNS_TENSOR_FILTER_CUSTOM_H__ + +#include + +/** + * @brief A function that is called before calling other functions. + * @return The returned pointer will be passed to other functions as "private_data". + */ +typedef void *(*NNS_custom_init_func)(void); + +/** + * @brief A function that is called after calling other functions, when it's ready to close. + * @param[in] private_data If you have allocated *private_data at init, free it here. + */ +typedef void (*NNS_custom_exit_func)(void *private_data); + +/** + * @brief Get input tensor type. + * @param[in] private_data The pointer returned by NNStreamer_custom_exit. + * @param[out] inputDimension uint32_t[NNS_TENSOR_RANK_LIMIT] + * @param[out] type Type of each element in the input tensor + */ +typedef int (*NNS_custom_get_input_dimension)(void *private_data, + uint32_t *inputDimension, tensor_type *type); + +/** + * @brief Get output tensor type. + * @param[in] private_data The pointer returned by NNStreamer_custom_exit. + * @param[out] outputDimension uint32_t[NNS_TENSOR_RANK_LIMIT] + * @param[out] type Type of each element in the output tensor + */ +typedef int (*NNS_custom_get_output_dimension)(void *private_data, + uint32_t *outputDimension, tensor_type *type); + +/** + * @brief Invoke the "main function". + * @param[in] private_data The pointer returned by NNStreamer_custom_exit. + * @param[in] inputPtr pointer to input tensor, size = dim1 x dim2 x dim3 x dim4 x typesize, allocated by caller + * @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); + +/** + * @brief Custom Filter Class + * + * Note that exery function pointer is MANDATORY! + */ +struct _NNStreamer_custom_class { + NNS_custom_init_func initfunc; + NNS_custom_exit_func exitfunc; + NNS_custom_get_input_dimension getInputDim; + NNS_custom_get_output_dimension getOutputDim; + NNS_custom_invoke invoke; +}; +typedef struct _NNStreamer_custom_class NNStreamer_custom_class; +extern NNStreamer_custom_class *NNStreamer_custom; + +#endif /*__NNS_TENSOR_FILTER_CUSTOM_H__*/