Update fluid (#9007)

This commit is contained in:
Orest Chura 2021-12-08 14:43:31 +03:00 committed by GitHub
parent 97f6dbf1f2
commit f734e7679b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
272 changed files with 30542 additions and 3888 deletions

View File

@ -1 +1 @@
91e7c0aaa00be504e8e6692d0b3b86c1
52e7351a888ee42076be4db81d9ac992

View File

@ -23,25 +23,20 @@ ocv_add_module(gapi
REQUIRED
opencv_imgproc
OPTIONAL
opencv_video
opencv_video opencv_calib3d
WRAP
python
)
if(MSVC)
# Disable obsollete warning C4503 popping up on MSVC <<2017
# https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-1-c4503?view=vs-2019
ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4503)
if (OPENCV_GAPI_INF_ENGINE AND NOT INF_ENGINE_RELEASE VERSION_GREATER "2021000000")
# Disable IE deprecated code warning C4996 for releases < 2021.1
ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4996)
if(MSVC_VERSION LESS 1910)
# Disable obsolete warning C4503 popping up on MSVC << 15 2017
# https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-1-c4503?view=vs-2019
# and IE deprecated code warning C4996
ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4503 /wd4996)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # don't add Clang here: issue should be investigated and fixed (workaround for Apple only)
ocv_warnings_disable(CMAKE_CXX_FLAGS -Wrange-loop-analysis) # https://github.com/opencv/opencv/issues/18928
endif()
file(GLOB gapi_ext_hdrs
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/*.hpp"
@ -52,10 +47,13 @@ file(GLOB gapi_ext_hdrs
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/infer/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/ocl/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/own/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/plaidml/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/python/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/render/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/s11n/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/streaming/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/plaidml/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/streaming/gstreamer/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/streaming/onevpl/*.hpp"
"${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/util/*.hpp"
)
@ -80,6 +78,7 @@ set(gapi_srcs
src/api/kernels_video.cpp
src/api/kernels_nnparsers.cpp
src/api/kernels_streaming.cpp
src/api/kernels_stereo.cpp
src/api/render.cpp
src/api/render_ocv.cpp
src/api/ginfer.cpp
@ -115,6 +114,7 @@ set(gapi_srcs
src/backends/cpu/gcpubackend.cpp
src/backends/cpu/gcpukernel.cpp
src/backends/cpu/gcpuimgproc.cpp
src/backends/cpu/gcpustereo.cpp
src/backends/cpu/gcpuvideo.cpp
src/backends/cpu/gcpucore.cpp
src/backends/cpu/gnnparsers.cpp
@ -125,6 +125,7 @@ set(gapi_srcs
src/backends/fluid/gfluidimgproc.cpp
src/backends/fluid/gfluidimgproc_func.dispatch.cpp
src/backends/fluid/gfluidcore.cpp
src/backends/fluid/gfluidcore_func.dispatch.cpp
# OCL Backend (currently built-in)
src/backends/ocl/goclbackend.cpp
@ -162,9 +163,45 @@ set(gapi_srcs
# Python bridge
src/backends/ie/bindings_ie.cpp
src/backends/python/gpythonbackend.cpp
# OpenVPL Streaming source
src/streaming/onevpl/source.cpp
src/streaming/onevpl/source_priv.cpp
src/streaming/onevpl/file_data_provider.cpp
src/streaming/onevpl/cfg_params.cpp
src/streaming/onevpl/cfg_params_parser.cpp
src/streaming/onevpl/utils.cpp
src/streaming/onevpl/data_provider_interface_exception.cpp
src/streaming/onevpl/accelerators/surface/cpu_frame_adapter.cpp
src/streaming/onevpl/accelerators/surface/surface.cpp
src/streaming/onevpl/accelerators/surface/surface_pool.cpp
src/streaming/onevpl/accelerators/accel_policy_cpu.cpp
src/streaming/onevpl/accelerators/accel_policy_dx11.cpp
src/streaming/onevpl/engine/engine_session.cpp
src/streaming/onevpl/engine/processing_engine_base.cpp
src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp
src/streaming/onevpl/engine/decode/decode_session.cpp
src/streaming/onevpl/demux/async_mfp_demux_data_provider.cpp
src/streaming/onevpl/data_provider_dispatcher.cpp
src/streaming/onevpl/cfg_param_device_selector.cpp
src/streaming/onevpl/device_selector_interface.cpp
# GStreamer Streaming source
src/streaming/gstreamer/gstreamer_pipeline_facade.cpp
src/streaming/gstreamer/gstreamerpipeline.cpp
src/streaming/gstreamer/gstreamersource.cpp
src/streaming/gstreamer/gstreamer_buffer_utils.cpp
src/streaming/gstreamer/gstreamer_media_adapter.cpp
src/streaming/gstreamer/gstreamerenv.cpp
# Utils (ITT tracing)
src/utils/itt.cpp
)
ocv_add_dispatched_file(backends/fluid/gfluidimgproc_func SSE4_1 AVX2)
ocv_add_dispatched_file(backends/fluid/gfluidcore_func SSE4_1 AVX2)
ocv_list_add_prefix(gapi_srcs "${CMAKE_CURRENT_LIST_DIR}/")
@ -178,17 +215,33 @@ ocv_module_include_directories("${CMAKE_CURRENT_LIST_DIR}/src")
ocv_create_module()
ocv_target_link_libraries(${the_module} PRIVATE ade)
if(OPENCV_GAPI_INF_ENGINE)
ocv_target_link_libraries(${the_module} PRIVATE ${INF_ENGINE_TARGET})
endif()
if (HAVE_NGRAPH)
ocv_target_link_libraries(${the_module} PRIVATE ngraph::ngraph)
endif()
if(HAVE_TBB)
ocv_target_link_libraries(${the_module} PRIVATE tbb)
endif()
# TODO: Consider support of ITT in G-API standalone mode.
if(CV_TRACE AND HAVE_ITT)
ocv_target_compile_definitions(${the_module} PRIVATE -DOPENCV_WITH_ITT=1)
ocv_module_include_directories(${ITT_INCLUDE_DIRS})
ocv_target_link_libraries(${the_module} PRIVATE ${ITT_LIBRARIES})
endif()
set(__test_extra_deps "")
if(OPENCV_GAPI_INF_ENGINE)
list(APPEND __test_extra_deps ${INF_ENGINE_TARGET})
endif()
if(HAVE_NGRAPH)
list(APPEND __test_extra_deps ngraph::ngraph)
endif()
ocv_add_accuracy_tests(${__test_extra_deps})
# FIXME: test binary is linked with ADE directly since ADE symbols
@ -198,6 +251,9 @@ ocv_add_accuracy_tests(${__test_extra_deps})
if(TARGET opencv_test_gapi)
target_include_directories(opencv_test_gapi PRIVATE "${CMAKE_CURRENT_LIST_DIR}/src")
target_link_libraries(opencv_test_gapi PRIVATE ade)
if (HAVE_NGRAPH)
ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_NGRAPH)
endif()
endif()
if(HAVE_TBB AND TARGET opencv_test_gapi)
@ -222,6 +278,29 @@ if(HAVE_PLAIDML)
ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${PLAIDML_INCLUDE_DIRS})
endif()
if(HAVE_GAPI_ONEVPL)
if(TARGET opencv_test_gapi)
ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_ONEVPL)
ocv_target_link_libraries(opencv_test_gapi PRIVATE ${VPL_IMPORTED_TARGETS})
if(HAVE_D3D11 AND HAVE_OPENCL)
ocv_target_include_directories(opencv_test_gapi SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS})
endif()
endif()
ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_ONEVPL)
ocv_target_link_libraries(${the_module} PRIVATE ${VPL_IMPORTED_TARGETS})
if(HAVE_D3D11 AND HAVE_OPENCL)
ocv_target_include_directories(${the_module} SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS})
endif()
endif()
if(HAVE_GSTREAMER)
if(TARGET opencv_test_gapi)
ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_GSTREAMER)
ocv_target_link_libraries(opencv_test_gapi PRIVATE ocv.3rdparty.gstreamer)
endif()
ocv_target_compile_definitions(${the_module} PRIVATE -DHAVE_GSTREAMER)
ocv_target_link_libraries(${the_module} PRIVATE ocv.3rdparty.gstreamer)
endif()
if(WIN32)
# Required for htonl/ntohl on Windows
@ -239,3 +318,27 @@ endif()
ocv_add_perf_tests()
ocv_add_samples()
# Required for sample with inference on host
if (TARGET example_gapi_onevpl_infer_single_roi)
if(OPENCV_GAPI_INF_ENGINE)
ocv_target_link_libraries(example_gapi_onevpl_infer_single_roi PRIVATE ${INF_ENGINE_TARGET})
ocv_target_compile_definitions(example_gapi_onevpl_infer_single_roi PRIVATE -DHAVE_INF_ENGINE)
endif()
if(HAVE_D3D11 AND HAVE_OPENCL)
ocv_target_include_directories(example_gapi_onevpl_infer_single_roi SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS})
endif()
endif()
# perf test dependencies postprocessing
if(HAVE_GAPI_ONEVPL)
# NB: TARGET opencv_perf_gapi doesn't exist before `ocv_add_perf_tests`
if(TARGET opencv_perf_gapi)
ocv_target_compile_definitions(opencv_perf_gapi PRIVATE -DHAVE_ONEVPL)
ocv_target_link_libraries(opencv_perf_gapi PRIVATE ${VPL_IMPORTED_TARGETS})
if(HAVE_D3D11 AND HAVE_OPENCL)
ocv_target_include_directories(opencv_perf_gapi SYSTEM PRIVATE ${OPENCL_INCLUDE_DIRS})
endif()
endif()
endif()

View File

@ -20,12 +20,26 @@ endif()
set(ADE_root "${ade_src_dir}/${ade_subdir}/sources/ade")
file(GLOB_RECURSE ADE_sources "${ADE_root}/source/*.cpp")
file(GLOB_RECURSE ADE_include "${ADE_root}/include/ade/*.hpp")
add_library(ade STATIC ${ADE_include} ${ADE_sources})
add_library(ade STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL}
${ADE_include}
${ADE_sources}
)
target_include_directories(ade PUBLIC $<BUILD_INTERFACE:${ADE_root}/include>)
set_target_properties(ade PROPERTIES POSITION_INDEPENDENT_CODE True)
set_target_properties(ade PROPERTIES
POSITION_INDEPENDENT_CODE True
OUTPUT_NAME ade
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
COMPILE_PDB_NAME ade
COMPILE_PDB_NAME_DEBUG "ade${OPENCV_DEBUG_POSTFIX}"
ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}
)
if(ENABLE_SOLUTION_FOLDERS)
set_target_properties(ade PROPERTIES FOLDER "3rdparty")
endif()
if(NOT BUILD_SHARED_LIBS)
ocv_install_target(ade EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
ocv_install_target(ade EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
endif()
ocv_install_3rdparty_licenses(ade "${ade_src_dir}/${ade_subdir}/LICENSE")

View File

@ -32,3 +32,10 @@ if(WITH_PLAIDML)
set(HAVE_PLAIDML TRUE)
endif()
endif()
if(WITH_GAPI_ONEVPL)
find_package(VPL)
if(VPL_FOUND)
set(HAVE_GAPI_ONEVPL TRUE)
endif()
endif()

View File

@ -6,6 +6,13 @@ if (NOT TARGET ade )
find_package(ade 0.1.0 REQUIRED)
endif()
if (WITH_GAPI_ONEVPL)
find_package(VPL)
if(VPL_FOUND)
set(HAVE_GAPI_ONEVPL TRUE)
endif()
endif()
set(FLUID_TARGET fluid)
set(FLUID_ROOT "${CMAKE_CURRENT_LIST_DIR}/../")

View File

@ -47,7 +47,7 @@ an external parameter.
G-API provides a macro to define a new kernel interface --
G_TYPED_KERNEL():
@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_api
@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp filter2d_api
This macro is a shortcut to a new type definition. It takes three
arguments to register a new type, and requires type body to be present
@ -81,18 +81,18 @@ Once a kernel is defined, it can be used in pipelines with special,
G-API-supplied method "::on()". This method has the same signature as
defined in kernel, so this code:
@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_on
@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp filter2d_on
is a perfectly legal construction. This example has some verbosity,
though, so usually a kernel declaration comes with a C++ function
wrapper ("factory method") which enables optional parameters, more
compact syntax, Doxygen comments, etc:
@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_wrap
@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp filter2d_wrap
so now it can be used like:
@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_wrap_call
@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp filter2d_wrap_call
# Extra information {#gapi_kernel_supp_info}
@ -143,7 +143,7 @@ For example, the aforementioned `Filter2D` is implemented in
"reference" CPU (OpenCV) plugin this way (*NOTE* -- this is a
simplified form with improper border handling):
@snippet modules/gapi/samples/kernel_api_snippets.cpp filter2d_ocv
@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp filter2d_ocv
Note how CPU (OpenCV) plugin has transformed the original kernel
signature:
@ -174,7 +174,7 @@ point extraction to an STL vector:
A compound kernel _implementation_ can be defined using a generic
macro GAPI_COMPOUND_KERNEL():
@snippet modules/gapi/samples/kernel_api_snippets.cpp compound
@snippet samples/cpp/tutorial_code/gapi/doc_snippets/kernel_api_snippets.cpp compound
<!-- TODO: ADD on how Compound kernels may simplify dispatching -->
<!-- TODO: Add details on when expand() is called! -->

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018 Intel Corporation
// Copyright (C) 2018-2021 Intel Corporation
#ifndef OPENCV_GAPI_HPP
@ -19,6 +19,7 @@
@}
@defgroup gapi_std_backends G-API Standard Backends
@defgroup gapi_compile_args G-API Graph Compilation Arguments
@defgroup gapi_serialization G-API Serialization functionality
@}
*/

View File

@ -29,6 +29,10 @@
*/
namespace cv { namespace gapi {
/**
* @brief This namespace contains G-API Operation Types for OpenCV
* Core module functionality.
*/
namespace core {
using GMat2 = std::tuple<GMat,GMat>;
using GMat3 = std::tuple<GMat,GMat,GMat>; // FIXME: how to avoid this?
@ -53,6 +57,7 @@ namespace core {
G_TYPED_KERNEL(GAddC, <GMat(GMat, GScalar, int)>, "org.opencv.core.math.addC") {
static GMatDesc outMeta(GMatDesc a, GScalarDesc, int ddepth) {
GAPI_Assert(a.chan <= 4);
return a.withDepth(ddepth);
}
};
@ -298,8 +303,8 @@ namespace core {
}
};
G_TYPED_KERNEL(GAbsDiffC, <GMat(GMat, GScalar)>, "org.opencv.core.matrixop.absdiffC") {
static GMatDesc outMeta(GMatDesc a, GScalarDesc) {
G_TYPED_KERNEL(GAbsDiffC, <GMat(GMat,GScalar)>, "org.opencv.core.matrixop.absdiffC") {
static GMatDesc outMeta(const GMatDesc& a, const GScalarDesc&) {
return a;
}
};
@ -394,7 +399,7 @@ namespace core {
};
G_TYPED_KERNEL(GResize, <GMat(GMat,Size,double,double,int)>, "org.opencv.core.transform.resize") {
static GMatDesc outMeta(GMatDesc in, Size sz, double fx, double fy, int) {
static GMatDesc outMeta(GMatDesc in, Size sz, double fx, double fy, int /*interp*/) {
if (sz.width != 0 && sz.height != 0)
{
return in.withSize(sz);
@ -575,6 +580,12 @@ namespace core {
return std::make_tuple(empty_gopaque_desc(), empty_array_desc(), empty_array_desc());
}
};
G_TYPED_KERNEL(GTranspose, <GMat(GMat)>, "org.opencv.core.transpose") {
static GMatDesc outMeta(GMatDesc in) {
return in.withSize({in.size.height, in.size.width});
}
};
} // namespace core
namespace streaming {
@ -591,6 +602,12 @@ G_TYPED_KERNEL(GSizeR, <GOpaque<Size>(GOpaque<Rect>)>, "org.opencv.streaming.siz
return empty_gopaque_desc();
}
};
G_TYPED_KERNEL(GSizeMF, <GOpaque<Size>(GFrame)>, "org.opencv.streaming.sizeMF") {
static GOpaqueDesc outMeta(const GFrameDesc&) {
return empty_gopaque_desc();
}
};
} // namespace streaming
//! @addtogroup gapi_math
@ -639,7 +656,7 @@ Supported matrix data types are @ref CV_8UC1, @ref CV_8UC3, @ref CV_16UC1, @ref
@param ddepth optional depth of the output matrix.
@sa sub, addWeighted
*/
GAPI_EXPORTS GMat addC(const GMat& src1, const GScalar& c, int ddepth = -1);
GAPI_EXPORTS_W GMat addC(const GMat& src1, const GScalar& c, int ddepth = -1);
//! @overload
GAPI_EXPORTS GMat addC(const GScalar& c, const GMat& src1, int ddepth = -1);
@ -754,7 +771,10 @@ GAPI_EXPORTS GMat mulC(const GScalar& multiplier, const GMat& src, int ddepth =
The function divides one matrix by another:
\f[\texttt{dst(I) = saturate(src1(I)*scale/src2(I))}\f]
When src2(I) is zero, dst(I) will also be zero. Different channels of
For integer types when src2(I) is zero, dst(I) will also be zero.
Floating point case returns Inf/NaN (according to IEEE).
Different channels of
multi-channel matrices are processed independently.
The matrices can be single or multi channel. Output matrix must have the same size and depth as src.
@ -1484,7 +1504,7 @@ enlarge an image, it will generally look best with cv::INTER_CUBIC (slow) or cv:
@sa warpAffine, warpPerspective, remap, resizeP
*/
GAPI_EXPORTS GMat resize(const GMat& src, const Size& dsize, double fx = 0, double fy = 0, int interpolation = INTER_LINEAR);
GAPI_EXPORTS_W GMat resize(const GMat& src, const Size& dsize, double fx = 0, double fy = 0, int interpolation = INTER_LINEAR);
/** @brief Resizes a planar image.
@ -1903,14 +1923,14 @@ kmeans(const GMat& data, const int K, const GMat& bestLabels,
- Function textual ID is "org.opencv.core.kmeansNDNoInit"
- #KMEANS_USE_INITIAL_LABELS flag must not be set while using this overload.
*/
GAPI_EXPORTS std::tuple<GOpaque<double>,GMat,GMat>
GAPI_EXPORTS_W std::tuple<GOpaque<double>,GMat,GMat>
kmeans(const GMat& data, const int K, const TermCriteria& criteria, const int attempts,
const KmeansFlags flags);
/** @overload
@note Function textual ID is "org.opencv.core.kmeans2D"
*/
GAPI_EXPORTS std::tuple<GOpaque<double>,GArray<int>,GArray<Point2f>>
GAPI_EXPORTS_W std::tuple<GOpaque<double>,GArray<int>,GArray<Point2f>>
kmeans(const GArray<Point2f>& data, const int K, const GArray<int>& bestLabels,
const TermCriteria& criteria, const int attempts, const KmeansFlags flags);
@ -1921,6 +1941,21 @@ GAPI_EXPORTS std::tuple<GOpaque<double>,GArray<int>,GArray<Point3f>>
kmeans(const GArray<Point3f>& data, const int K, const GArray<int>& bestLabels,
const TermCriteria& criteria, const int attempts, const KmeansFlags flags);
/** @brief Transposes a matrix.
The function transposes the matrix:
\f[\texttt{dst} (i,j) = \texttt{src} (j,i)\f]
@note
- Function textual ID is "org.opencv.core.transpose"
- No complex conjugation is done in case of a complex matrix. It should be done separately if needed.
@param src input array.
*/
GAPI_EXPORTS GMat transpose(const GMat& src);
namespace streaming {
/** @brief Gets dimensions from Mat.
@ -1929,7 +1964,7 @@ namespace streaming {
@param src Input tensor
@return Size (tensor dimensions).
*/
GAPI_EXPORTS GOpaque<Size> size(const GMat& src);
GAPI_EXPORTS_W GOpaque<Size> size(const GMat& src);
/** @overload
Gets dimensions from rectangle.
@ -1939,7 +1974,16 @@ Gets dimensions from rectangle.
@param r Input rectangle.
@return Size (rectangle dimensions).
*/
GAPI_EXPORTS GOpaque<Size> size(const GOpaque<Rect>& r);
GAPI_EXPORTS_W GOpaque<Size> size(const GOpaque<Rect>& r);
/** @brief Gets dimensions from MediaFrame.
@note Function textual ID is "org.opencv.streaming.sizeMF"
@param src Input frame
@return Size (frame dimensions).
*/
GAPI_EXPORTS GOpaque<Size> size(const GFrame& src);
} //namespace streaming
} //namespace gapi
} //namespace cv

View File

@ -28,18 +28,14 @@ namespace gimpl
{
// Forward-declare an internal class
class GCPUExecutable;
namespace render
{
namespace ocv
{
class GRenderExecutable;
}
}
} // namespace gimpl
namespace gapi
{
/**
* @brief This namespace contains G-API CPU backend functions,
* structures, and symbols.
*/
namespace cpu
{
/**
@ -129,7 +125,6 @@ protected:
std::unordered_map<std::size_t, GRunArgP> m_results;
friend class gimpl::GCPUExecutable;
friend class gimpl::render::ocv::GRenderExecutable;
};
class GAPI_EXPORTS GCPUKernel
@ -190,6 +185,11 @@ template<> struct get_in<cv::GArray<cv::GScalar> >: public get_in<cv::GArray<cv:
{
};
// FIXME(dm): GArray<vector<U>>/GArray<GArray<U>> conversion should be done more gracefully in the system
template<typename U> struct get_in<cv::GArray<cv::GArray<U>> >: public get_in<cv::GArray<std::vector<U>> >
{
};
//FIXME(dm): GOpaque<Mat>/GOpaque<GMat> conversion should be done more gracefully in the system
template<> struct get_in<cv::GOpaque<cv::GMat> >: public get_in<cv::GOpaque<cv::Mat> >
{
@ -487,7 +487,7 @@ public:
#define GAPI_OCV_KERNEL_ST(Name, API, State) \
struct Name: public cv::GCPUStKernelImpl<Name, API, State> \
/// @private
class gapi::cpu::GOCVFunctor : public gapi::GFunctor
{
public:

View File

@ -0,0 +1,48 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_CPU_STEREO_API_HPP
#define OPENCV_GAPI_CPU_STEREO_API_HPP
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
namespace cv {
namespace gapi {
namespace calib3d {
namespace cpu {
GAPI_EXPORTS GKernelPackage kernels();
/** @brief Structure for the Stereo operation initialization parameters.*/
struct GAPI_EXPORTS StereoInitParam {
StereoInitParam(int nD, int bS, double bL, double f):
numDisparities(nD), blockSize(bS), baseline(bL), focus(f) {}
StereoInitParam() = default;
int numDisparities = 0;
int blockSize = 21;
double baseline = 63.5;
double focus = 3.6;
};
} // namespace cpu
} // namespace calib3d
} // namespace gapi
namespace detail {
template<> struct CompileArgTag<cv::gapi::calib3d::cpu::StereoInitParam> {
static const char* tag() {
return "org.opencv.stereoInit";
}
};
} // namespace detail
} // namespace cv
#endif // OPENCV_GAPI_CPU_STEREO_API_HPP

View File

@ -25,7 +25,7 @@ namespace fluid {
struct Border
{
// This constructor is required to support existing kernels which are part of G-API
Border(int _type, cv::Scalar _val) : type(_type), value(_val) {};
Border(int _type, cv::Scalar _val) : type(_type), value(_val) {}
int type;
cv::Scalar value;

View File

@ -25,6 +25,9 @@ namespace cv {
namespace gapi
{
/**
* @brief This namespace contains G-API Fluid backend functions, structures, and symbols.
*/
namespace fluid
{
/**

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2020 Intel Corporation
// Copyright (C) 2018-2021 Intel Corporation
#ifndef OPENCV_GAPI_GARG_HPP
@ -171,7 +171,7 @@ using GRunArgs = std::vector<GRunArg>;
* It's an ordinary overload of addition assignment operator.
*
* Example of usage:
* @snippet dynamic_graph.cpp GRunArgs usage
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/dynamic_graph_snippets.cpp GRunArgs usage
*
*/
inline GRunArgs& operator += (GRunArgs &lhs, const GRunArgs &rhs)
@ -223,7 +223,7 @@ using GRunArgsP = std::vector<GRunArgP>;
* It's an ordinary overload of addition assignment operator.
*
* Example of usage:
* @snippet dynamic_graph.cpp GRunArgsP usage
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/dynamic_graph_snippets.cpp GRunArgsP usage
*
*/
inline GRunArgsP& operator += (GRunArgsP &lhs, const GRunArgsP &rhs)
@ -235,8 +235,39 @@ inline GRunArgsP& operator += (GRunArgsP &lhs, const GRunArgsP &rhs)
namespace gapi
{
GAPI_EXPORTS cv::GRunArgsP bind(cv::GRunArgs &results);
GAPI_EXPORTS cv::GRunArg bind(cv::GRunArgP &out); // FIXME: think more about it
/**
* \addtogroup gapi_serialization
* @{
*
* @brief G-API functions and classes for serialization and deserialization.
*/
/** @brief Wraps deserialized output GRunArgs to GRunArgsP which can be used by GCompiled.
*
* Since it's impossible to get modifiable output arguments from deserialization
* it needs to be wrapped by this function.
*
* Example of usage:
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp bind after deserialization
*
* @param out_args deserialized GRunArgs.
* @return the same GRunArgs wrapped in GRunArgsP.
* @see deserialize
*/
GAPI_EXPORTS cv::GRunArgsP bind(cv::GRunArgs &out_args);
/** @brief Wraps output GRunArgsP available during graph execution to GRunArgs which can be serialized.
*
* GRunArgsP is pointer-to-value, so to be serialized they need to be binded to real values
* which this function does.
*
* Example of usage:
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp bind before serialization
*
* @param out output GRunArgsP available during graph execution.
* @return the same GRunArgsP wrapped in serializable GRunArgs.
* @see serialize
*/
GAPI_EXPORTS cv::GRunArg bind(cv::GRunArgP &out); // FIXME: think more about it
/** @} */
}
template<typename... Ts> inline GRunArgs gin(const Ts&... args)
@ -249,6 +280,30 @@ template<typename... Ts> inline GRunArgsP gout(Ts&... args)
return GRunArgsP{ GRunArgP(detail::wrap_host_helper<Ts>::wrap_out(args))... };
}
struct GTypeInfo;
using GTypesInfo = std::vector<GTypeInfo>;
// FIXME: Needed for python bridge, must be moved to more appropriate header
namespace detail {
struct ExtractArgsCallback
{
cv::GRunArgs operator()(const cv::GTypesInfo& info) const { return c(info); }
using CallBackT = std::function<cv::GRunArgs(const cv::GTypesInfo& info)>;
CallBackT c;
};
struct ExtractMetaCallback
{
cv::GMetaArgs operator()(const cv::GTypesInfo& info) const { return c(info); }
using CallBackT = std::function<cv::GMetaArgs(const cv::GTypesInfo& info)>;
CallBackT c;
};
void constructGraphOutputs(const cv::GTypesInfo &out_info,
cv::GRunArgs &args,
cv::GRunArgsP &outs);
} // namespace detail
} // namespace cv
#endif // OPENCV_GAPI_GARG_HPP

View File

@ -35,14 +35,14 @@ template<typename T> class GArray;
* \addtogroup gapi_meta_args
* @{
*/
struct GArrayDesc
struct GAPI_EXPORTS_W_SIMPLE GArrayDesc
{
// FIXME: Body
// FIXME: Also implement proper operator== then
bool operator== (const GArrayDesc&) const { return true; }
};
template<typename U> GArrayDesc descr_of(const std::vector<U> &) { return {};}
static inline GArrayDesc empty_array_desc() {return {}; }
GAPI_EXPORTS_W inline GArrayDesc empty_array_desc() {return {}; }
/** @} */
std::ostream& operator<<(std::ostream& os, const cv::GArrayDesc &desc);
@ -236,7 +236,7 @@ namespace detail
class VectorRef
{
std::shared_ptr<BasicVectorRef> m_ref;
cv::detail::OpaqueKind m_kind;
cv::detail::OpaqueKind m_kind = cv::detail::OpaqueKind::CV_UNKNOWN;
template<typename T> inline void check() const
{
@ -246,12 +246,18 @@ namespace detail
public:
VectorRef() = default;
template<typename T> explicit VectorRef(const std::vector<T>& vec) :
m_ref(new VectorRefT<T>(vec)), m_kind(GOpaqueTraits<T>::kind) {}
template<typename T> explicit VectorRef(std::vector<T>& vec) :
m_ref(new VectorRefT<T>(vec)), m_kind(GOpaqueTraits<T>::kind) {}
template<typename T> explicit VectorRef(std::vector<T>&& vec) :
m_ref(new VectorRefT<T>(std::move(vec))), m_kind(GOpaqueTraits<T>::kind) {}
template<typename T> explicit VectorRef(const std::vector<T>& vec)
: m_ref(new VectorRefT<T>(vec))
, m_kind(GOpaqueTraits<T>::kind)
{}
template<typename T> explicit VectorRef(std::vector<T>& vec)
: m_ref(new VectorRefT<T>(vec))
, m_kind(GOpaqueTraits<T>::kind)
{}
template<typename T> explicit VectorRef(std::vector<T>&& vec)
: m_ref(new VectorRefT<T>(std::move(vec)))
, m_kind(GOpaqueTraits<T>::kind)
{}
cv::detail::OpaqueKind getKind() const
{
@ -321,9 +327,10 @@ namespace detail
# define FLATTEN_NS cv
#endif
template<class T> struct flatten_g;
template<> struct flatten_g<cv::GMat> { using type = FLATTEN_NS::Mat; };
template<> struct flatten_g<cv::GScalar> { using type = FLATTEN_NS::Scalar; };
template<class T> struct flatten_g { using type = T; };
template<> struct flatten_g<cv::GMat> { using type = FLATTEN_NS::Mat; };
template<> struct flatten_g<cv::GScalar> { using type = FLATTEN_NS::Scalar; };
template<class T> struct flatten_g<GArray<T>> { using type = std::vector<T>; };
template<class T> struct flatten_g { using type = T; };
#undef FLATTEN_NS
// FIXME: the above mainly duplicates "ProtoToParam" thing from gtyped.hpp
// but I decided not to include gtyped here - probably worth moving that stuff
@ -333,21 +340,79 @@ namespace detail
/** \addtogroup gapi_data_objects
* @{
*/
/**
* @brief `cv::GArray<T>` template class represents a list of objects
* of class `T` in the graph.
*
* `cv::GArray<T>` describes a functional relationship between
* operations consuming and producing arrays of objects of class
* `T`. The primary purpose of `cv::GArray<T>` is to represent a
* dynamic list of objects -- where the size of the list is not known
* at the graph construction or compile time. Examples include: corner
* and feature detectors (`cv::GArray<cv::Point>`), object detection
* and tracking results (`cv::GArray<cv::Rect>`). Programmers can use
* their own types with `cv::GArray<T>` in the custom operations.
*
* Similar to `cv::GScalar`, `cv::GArray<T>` may be value-initialized
* -- in this case a graph-constant value is associated with the object.
*
* `GArray<T>` is a virtual counterpart of `std::vector<T>`, which is
* usually used to represent the `GArray<T>` data in G-API during the
* execution.
*
* @sa `cv::GOpaque<T>`
*/
template<typename T> class GArray
{
public:
// Host type (or Flat type) - the type this GArray is actually
// specified to.
/// @private
using HT = typename detail::flatten_g<typename std::decay<T>::type>::type;
/**
* @brief Constructs a value-initialized `cv::GArray<T>`
*
* `cv::GArray<T>` objects may have their values
* be associated at graph construction time. It is useful when
* some operation has a `cv::GArray<T>` input which doesn't change during
* the program execution, and is set only once. In this case,
* there is no need to declare such `cv::GArray<T>` as a graph input.
*
* @note The value of `cv::GArray<T>` may be overwritten by assigning some
* other `cv::GArray<T>` to the object using `operator=` -- on the
* assigment, the old association or value is discarded.
*
* @param v a std::vector<T> to associate with this
* `cv::GArray<T>` object. Vector data is copied into the
* `cv::GArray<T>` (no reference to the passed data is held).
*/
explicit GArray(const std::vector<HT>& v) // Constant value constructor
: m_ref(detail::GArrayU(detail::VectorRef(v))) { putDetails(); }
/**
* @overload
* @brief Constructs a value-initialized `cv::GArray<T>`
*
* @param v a std::vector<T> to associate with this
* `cv::GArray<T>` object. Vector data is moved into the `cv::GArray<T>`.
*/
explicit GArray(std::vector<HT>&& v) // Move-constructor
: m_ref(detail::GArrayU(detail::VectorRef(std::move(v)))) { putDetails(); }
GArray() { putDetails(); } // Empty constructor
explicit GArray(detail::GArrayU &&ref) // GArrayU-based constructor
: m_ref(ref) { putDetails(); } // (used by GCall, not for users)
/**
* @brief Constructs an empty `cv::GArray<T>`
*
* Normally, empty G-API data objects denote a starting point of
* the graph. When an empty `cv::GArray<T>` is assigned to a result
* of some operation, it obtains a functional link to this
* operation (and is not empty anymore).
*/
GArray() { putDetails(); } // Empty constructor
/// @private
explicit GArray(detail::GArrayU &&ref) // GArrayU-based constructor
: m_ref(ref) { putDetails(); } // (used by GCall, not for users)
/// @private
detail::GArrayU strip() const {
@ -368,8 +433,6 @@ private:
detail::GArrayU m_ref;
};
using GArrayP2f = GArray<cv::Point2f>;
/** @} */
} // namespace cv

View File

@ -17,6 +17,13 @@
namespace cv {
namespace gapi{
/**
* @brief This namespace contains experimental G-API functionality,
* functions or structures in this namespace are subjects to change or
* removal in the future releases. This namespace also contains
* functions which API is not stabilized yet.
*/
namespace wip {
/**

View File

@ -44,6 +44,7 @@ namespace detail
CV_UNKNOWN, // Unknown, generic, opaque-to-GAPI data type unsupported in graph seriallization
CV_BOOL, // bool user G-API data
CV_INT, // int user G-API data
CV_INT64, // int64_t user G-API data
CV_DOUBLE, // double user G-API data
CV_FLOAT, // float user G-API data
CV_UINT64, // uint64_t user G-API data
@ -61,6 +62,7 @@ namespace detail
template<typename T> struct GOpaqueTraits;
template<typename T> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_UNKNOWN; };
template<> struct GOpaqueTraits<int> { static constexpr const OpaqueKind kind = OpaqueKind::CV_INT; };
template<> struct GOpaqueTraits<int64_t> { static constexpr const OpaqueKind kind = OpaqueKind::CV_INT64; };
template<> struct GOpaqueTraits<double> { static constexpr const OpaqueKind kind = OpaqueKind::CV_DOUBLE; };
template<> struct GOpaqueTraits<float> { static constexpr const OpaqueKind kind = OpaqueKind::CV_FLOAT; };
template<> struct GOpaqueTraits<uint64_t> { static constexpr const OpaqueKind kind = OpaqueKind::CV_UINT64; };
@ -132,12 +134,12 @@ namespace detail {
*
* For example, if an example computation is executed like this:
*
* @snippet modules/gapi/samples/api_ref_snippets.cpp graph_decl_apply
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp graph_decl_apply
*
* Extra parameter specifying which kernels to compile with can be
* passed like this:
*
* @snippet modules/gapi/samples/api_ref_snippets.cpp apply_with_param
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp apply_with_param
*/
/**
@ -195,6 +197,14 @@ private:
using GCompileArgs = std::vector<GCompileArg>;
inline cv::GCompileArgs& operator += ( cv::GCompileArgs &lhs,
const cv::GCompileArgs &rhs)
{
lhs.reserve(lhs.size() + rhs.size());
lhs.insert(lhs.end(), rhs.begin(), rhs.end());
return lhs;
}
/**
* @brief Wraps a list of arguments (a parameter pack) into a vector of
* compilation arguments (cv::GCompileArg).

View File

@ -61,11 +61,11 @@ namespace s11n {
* executed. The below example expresses calculation of Sobel operator
* for edge detection (\f$G = \sqrt{G_x^2 + G_y^2}\f$):
*
* @snippet modules/gapi/samples/api_ref_snippets.cpp graph_def
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp graph_def
*
* Full pipeline can be now captured with this object declaration:
*
* @snippet modules/gapi/samples/api_ref_snippets.cpp graph_cap_full
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp graph_cap_full
*
* Input/output data objects on which a call graph should be
* reconstructed are passed using special wrappers cv::GIn and
@ -78,7 +78,7 @@ namespace s11n {
* expects that image gradients are already pre-calculated may be
* defined like this:
*
* @snippet modules/gapi/samples/api_ref_snippets.cpp graph_cap_sub
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp graph_cap_sub
*
* The resulting graph would expect two inputs and produce one
* output. In this case, it doesn't matter if gx/gy data objects are
@ -130,7 +130,7 @@ public:
* Graph can be defined in-place directly at the moment of its
* construction with a lambda:
*
* @snippet modules/gapi/samples/api_ref_snippets.cpp graph_gen
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp graph_gen
*
* This may be useful since all temporary objects (cv::GMats) and
* namespaces can be localized to scope of lambda, without
@ -258,7 +258,8 @@ public:
void apply(GRunArgs &&ins, GRunArgsP &&outs, GCompileArgs &&args = {}); // Arg-to-arg overload
/// @private -- Exclude this function from OpenCV documentation
GAPI_WRAP GRunArgs apply(GRunArgs &&ins, GCompileArgs &&args = {});
GAPI_WRAP GRunArgs apply(const cv::detail::ExtractArgsCallback &callback,
GCompileArgs &&args = {});
/// @private -- Exclude this function from OpenCV documentation
void apply(const std::vector<cv::Mat>& ins, // Compatibility overload
@ -459,6 +460,10 @@ public:
*/
GAPI_WRAP GStreamingCompiled compileStreaming(GCompileArgs &&args = {});
/// @private -- Exclude this function from OpenCV documentation
GAPI_WRAP GStreamingCompiled compileStreaming(const cv::detail::ExtractMetaCallback &callback,
GCompileArgs &&args = {});
// 2. Direct metadata version
/**
* @overload

View File

@ -28,14 +28,54 @@ struct GOrigin;
/** \addtogroup gapi_data_objects
* @{
*/
/**
* @brief GFrame class represents an image or media frame in the graph.
*
* GFrame doesn't store any data itself, instead it describes a
* functional relationship between operations consuming and producing
* GFrame objects.
*
* GFrame is introduced to handle various media formats (e.g., NV12 or
* I420) under the same type. Various image formats may differ in the
* number of planes (e.g. two for NV12, three for I420) and the pixel
* layout inside. GFrame type allows to handle these media formats in
* the graph uniformly -- the graph structure will not change if the
* media format changes, e.g. a different camera or decoder is used
* with the same graph. G-API provides a number of operations which
* operate directly on GFrame, like `infer<>()` or
* renderFrame(); these operations are expected to handle different
* media formats inside. There is also a number of accessor
* operations like BGR(), Y(), UV() -- these operations provide
* access to frame's data in the familiar cv::GMat form, which can be
* used with the majority of the existing G-API operations. These
* accessor functions may perform color space converion on the fly if
* the image format of the GFrame they are applied to differs from the
* operation's semantic (e.g. the BGR() accessor is called on an NV12
* image frame).
*
* GFrame is a virtual counterpart of cv::MediaFrame.
*
* @sa cv::MediaFrame, cv::GFrameDesc, BGR(), Y(), UV(), infer<>().
*/
class GAPI_EXPORTS_W_SIMPLE GFrame
{
public:
GAPI_WRAP GFrame(); // Empty constructor
GFrame(const GNode &n, std::size_t out); // Operation result constructor
/**
* @brief Constructs an empty GFrame
*
* Normally, empty G-API data objects denote a starting point of
* the graph. When an empty GFrame is assigned to a result of some
* operation, it obtains a functional link to this operation (and
* is not empty anymore).
*/
GAPI_WRAP GFrame(); // Empty constructor
GOrigin& priv(); // Internal use only
const GOrigin& priv() const; // Internal use only
/// @private
GFrame(const GNode &n, std::size_t out); // Operation result constructor
/// @private
GOrigin& priv(); // Internal use only
/// @private
const GOrigin& priv() const; // Internal use only
private:
std::shared_ptr<GOrigin> m_priv;

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2020 Intel Corporation
// Copyright (C) 2018-2021 Intel Corporation
#ifndef OPENCV_GAPI_GKERNEL_HPP
@ -30,6 +30,7 @@ struct GTypeInfo
{
GShape shape;
cv::detail::OpaqueKind kind;
detail::HostCtor ctor;
};
using GShapes = std::vector<GShape>;
@ -371,6 +372,7 @@ namespace gapi
{
// Prework: model "Device" API before it gets to G-API headers.
// FIXME: Don't mix with internal Backends class!
/// @private
class GAPI_EXPORTS GBackend
{
public:
@ -411,6 +413,7 @@ namespace std
namespace cv {
namespace gapi {
/// @private
class GFunctor
{
public:
@ -516,6 +519,13 @@ namespace gapi {
*/
const std::vector<GTransform>& get_transformations() const;
/**
* @brief Returns vector of kernel ids included in the package
*
* @return vector of kernel ids included in the package
*/
std::vector<std::string> get_kernel_ids() const;
/**
* @brief Test if a particular kernel _implementation_ KImpl is
* included in this kernel package.
@ -605,6 +615,18 @@ namespace gapi {
includeHelper<KImpl>();
}
/**
* @brief Adds a new kernel based on it's backend and id into the kernel package
*
* @param backend backend associated with the kernel
* @param kernel_id a name/id of the kernel
*/
void include(const cv::gapi::GBackend& backend, const std::string& kernel_id)
{
removeAPI(kernel_id);
m_id_kernels[kernel_id] = std::make_pair(backend, GKernelImpl{{}, {}});
}
/**
* @brief Lists all backends which are included into package
*
@ -637,7 +659,7 @@ namespace gapi {
* Use this function to pass kernel implementations (defined in
* either way) and transformations to the system. Example:
*
* @snippet modules/gapi/samples/api_ref_snippets.cpp kernels_snippet
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp kernels_snippet
*
* Note that kernels() itself is a function returning object, not
* a type, so having `()` at the end is important -- it must be a
@ -697,7 +719,7 @@ namespace gapi {
* @{
*/
/**
* @brief cv::use_only() is a special combinator which hints G-API to use only
* @brief cv::gapi::use_only() is a special combinator which hints G-API to use only
* kernels specified in cv::GComputation::compile() (and not to extend kernels available by
* default with that package).
*/

View File

@ -30,29 +30,57 @@ struct GOrigin;
* @brief G-API data objects used to build G-API expressions.
*
* These objects do not own any particular data (except compile-time
* associated values like with cv::GScalar) and are used to construct
* graphs.
* associated values like with cv::GScalar or `cv::GArray<T>`) and are
* used only to construct graphs.
*
* Every graph in G-API starts and ends with data objects.
*
* Once constructed and compiled, G-API operates with regular host-side
* data instead. Refer to the below table to find the mapping between
* G-API and regular data types.
* G-API and regular data types when passing input and output data
* structures to G-API:
*
* G-API data type | I/O data type
* ------------------ | -------------
* cv::GMat | cv::Mat
* cv::GMat | cv::Mat, cv::UMat, cv::RMat
* cv::GScalar | cv::Scalar
* `cv::GArray<T>` | std::vector<T>
* `cv::GOpaque<T>` | T
* cv::GFrame | cv::MediaFrame
*/
/**
* @brief GMat class represents image or tensor data in the
* graph.
*
* GMat doesn't store any data itself, instead it describes a
* functional relationship between operations consuming and producing
* GMat objects.
*
* GMat is a virtual counterpart of Mat and UMat, but it
* doesn't mean G-API use Mat or UMat objects internally to represent
* GMat objects -- the internal data representation may be
* backend-specific or optimized out at all.
*
* @sa Mat, GMatDesc
*/
class GAPI_EXPORTS_W_SIMPLE GMat
{
public:
/**
* @brief Constructs an empty GMat
*
* Normally, empty G-API data objects denote a starting point of
* the graph. When an empty GMat is assigned to a result of some
* operation, it obtains a functional link to this operation (and
* is not empty anymore).
*/
GAPI_WRAP GMat(); // Empty constructor
GMat(const GNode &n, std::size_t out); // Operation result constructor
/// @private
GMat(const GNode &n, std::size_t out); // Operation result constructor
/// @private
GOrigin& priv(); // Internal use only
/// @private
const GOrigin& priv() const; // Internal use only
private:
@ -73,25 +101,25 @@ class RMat;
* \addtogroup gapi_meta_args
* @{
*/
struct GAPI_EXPORTS GMatDesc
struct GAPI_EXPORTS_W_SIMPLE GMatDesc
{
// FIXME: Default initializers in C++14
int depth;
int chan;
cv::Size size; // NB.: no multi-dimensional cases covered yet
bool planar;
std::vector<int> dims; // FIXME: Maybe it's real questionable to have it here
GAPI_PROP int depth;
GAPI_PROP int chan;
GAPI_PROP cv::Size size; // NB.: no multi-dimensional cases covered yet
GAPI_PROP bool planar;
GAPI_PROP std::vector<int> dims; // FIXME: Maybe it's real questionable to have it here
GMatDesc(int d, int c, cv::Size s, bool p = false)
GAPI_WRAP GMatDesc(int d, int c, cv::Size s, bool p = false)
: depth(d), chan(c), size(s), planar(p) {}
GMatDesc(int d, const std::vector<int> &dd)
GAPI_WRAP GMatDesc(int d, const std::vector<int> &dd)
: depth(d), chan(-1), size{-1,-1}, planar(false), dims(dd) {}
GMatDesc(int d, std::vector<int> &&dd)
GAPI_WRAP GMatDesc(int d, std::vector<int> &&dd)
: depth(d), chan(-1), size{-1,-1}, planar(false), dims(std::move(dd)) {}
GMatDesc() : GMatDesc(-1, -1, {-1,-1}) {}
GAPI_WRAP GMatDesc() : GMatDesc(-1, -1, {-1,-1}) {}
inline bool operator== (const GMatDesc &rhs) const
{
@ -120,7 +148,7 @@ struct GAPI_EXPORTS GMatDesc
// Meta combinator: return a new GMatDesc which differs in size by delta
// (all other fields are taken unchanged from this GMatDesc)
// FIXME: a better name?
GMatDesc withSizeDelta(cv::Size delta) const
GAPI_WRAP GMatDesc withSizeDelta(cv::Size delta) const
{
GMatDesc desc(*this);
desc.size += delta;
@ -130,12 +158,12 @@ struct GAPI_EXPORTS GMatDesc
// (all other fields are taken unchanged from this GMatDesc)
//
// This is an overload.
GMatDesc withSizeDelta(int dx, int dy) const
GAPI_WRAP GMatDesc withSizeDelta(int dx, int dy) const
{
return withSizeDelta(cv::Size{dx,dy});
}
GMatDesc withSize(cv::Size sz) const
GAPI_WRAP GMatDesc withSize(cv::Size sz) const
{
GMatDesc desc(*this);
desc.size = sz;
@ -144,7 +172,7 @@ struct GAPI_EXPORTS GMatDesc
// Meta combinator: return a new GMatDesc with specified data depth.
// (all other fields are taken unchanged from this GMatDesc)
GMatDesc withDepth(int ddepth) const
GAPI_WRAP GMatDesc withDepth(int ddepth) const
{
GAPI_Assert(CV_MAT_CN(ddepth) == 1 || ddepth == -1);
GMatDesc desc(*this);
@ -155,7 +183,7 @@ struct GAPI_EXPORTS GMatDesc
// Meta combinator: return a new GMatDesc with specified data depth
// and number of channels.
// (all other fields are taken unchanged from this GMatDesc)
GMatDesc withType(int ddepth, int dchan) const
GAPI_WRAP GMatDesc withType(int ddepth, int dchan) const
{
GAPI_Assert(CV_MAT_CN(ddepth) == 1 || ddepth == -1);
GMatDesc desc = withDepth(ddepth);
@ -166,7 +194,7 @@ struct GAPI_EXPORTS GMatDesc
// Meta combinator: return a new GMatDesc with planar flag set
// (no size changes are performed, only channel interpretation is changed
// (interleaved -> planar)
GMatDesc asPlanar() const
GAPI_WRAP GMatDesc asPlanar() const
{
GAPI_Assert(planar == false);
GMatDesc desc(*this);
@ -177,7 +205,7 @@ struct GAPI_EXPORTS GMatDesc
// Meta combinator: return a new GMatDesc
// reinterpreting 1-channel input as planar image
// (size height is divided by plane number)
GMatDesc asPlanar(int planes) const
GAPI_WRAP GMatDesc asPlanar(int planes) const
{
GAPI_Assert(planar == false);
GAPI_Assert(chan == 1);
@ -192,7 +220,7 @@ struct GAPI_EXPORTS GMatDesc
// Meta combinator: return a new GMatDesc with planar flag set to false
// (no size changes are performed, only channel interpretation is changed
// (planar -> interleaved)
GMatDesc asInterleaved() const
GAPI_WRAP GMatDesc asInterleaved() const
{
GAPI_Assert(planar == true);
GMatDesc desc(*this);

View File

@ -21,6 +21,9 @@
#include <opencv2/gapi/util/type_traits.hpp>
#include <opencv2/gapi/own/assert.hpp>
#include <opencv2/gapi/gcommon.hpp> // OpaqueKind
#include <opencv2/gapi/garray.hpp> // TypeHintBase
namespace cv
{
// Forward declaration; GNode and GOrigin are an internal
@ -33,14 +36,14 @@ template<typename T> class GOpaque;
* \addtogroup gapi_meta_args
* @{
*/
struct GOpaqueDesc
struct GAPI_EXPORTS_W_SIMPLE GOpaqueDesc
{
// FIXME: Body
// FIXME: Also implement proper operator== then
bool operator== (const GOpaqueDesc&) const { return true; }
};
template<typename U> GOpaqueDesc descr_of(const U &) { return {};}
static inline GOpaqueDesc empty_gopaque_desc() {return {}; }
GAPI_EXPORTS_W inline GOpaqueDesc empty_gopaque_desc() {return {}; }
/** @} */
std::ostream& operator<<(std::ostream& os, const cv::GOpaqueDesc &desc);
@ -229,7 +232,7 @@ namespace detail
class OpaqueRef
{
std::shared_ptr<BasicOpaqueRef> m_ref;
cv::detail::OpaqueKind m_kind;
cv::detail::OpaqueKind m_kind = cv::detail::OpaqueKind::CV_UNKNOWN;
template<typename T> inline void check() const
{
@ -304,15 +307,40 @@ namespace detail
/** \addtogroup gapi_data_objects
* @{
*/
/**
* @brief `cv::GOpaque<T>` template class represents an object of
* class `T` in the graph.
*
* `cv::GOpaque<T>` describes a functional relationship between operations
* consuming and producing object of class `T`. `cv::GOpaque<T>` is
* designed to extend G-API with user-defined data types, which are
* often required with user-defined operations. G-API can't apply any
* optimizations to user-defined types since these types are opaque to
* the framework. However, there is a number of G-API operations
* declared with `cv::GOpaque<T>` as a return type,
* e.g. cv::gapi::streaming::timestamp() or cv::gapi::streaming::size().
*
* @sa `cv::GArray<T>`
*/
template<typename T> class GOpaque
{
public:
// Host type (or Flat type) - the type this GOpaque is actually
// specified to.
/// @private
using HT = typename detail::flatten_g<util::decay_t<T>>::type;
/**
* @brief Constructs an empty `cv::GOpaque<T>`
*
* Normally, empty G-API data objects denote a starting point of
* the graph. When an empty `cv::GOpaque<T>` is assigned to a result
* of some operation, it obtains a functional link to this
* operation (and is not empty anymore).
*/
GOpaque() { putDetails(); } // Empty constructor
/// @private
explicit GOpaque(detail::GOpaqueU &&ref) // GOpaqueU-based constructor
: m_ref(ref) { putDetails(); } // (used by GCall, not for users)

View File

@ -71,7 +71,7 @@ public:
* It's an ordinary overload of addition assignment operator.
*
* Example of usage:
* @snippet dynamic_graph.cpp GIOProtoArgs usage
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/dynamic_graph_snippets.cpp GIOProtoArgs usage
*
*/
template<typename Tg>
@ -135,7 +135,7 @@ GRunArg value_of(const GOrigin &origin);
// Transform run-time computation arguments into a collection of metadata
// extracted from that arguments
GMetaArg GAPI_EXPORTS descr_of(const GRunArg &arg );
GMetaArgs GAPI_EXPORTS_W descr_of(const GRunArgs &args);
GMetaArgs GAPI_EXPORTS descr_of(const GRunArgs &args);
// Transform run-time operation result argument into metadata extracted from that argument
// Used to compare the metadata, which generated at compile time with the metadata result operation in run time

View File

@ -25,18 +25,83 @@ struct GOrigin;
/** \addtogroup gapi_data_objects
* @{
*/
/**
* @brief GScalar class represents cv::Scalar data in the graph.
*
* GScalar may be associated with a cv::Scalar value, which becomes
* its constant value bound in graph compile time. cv::GScalar describes a
* functional relationship between operations consuming and producing
* GScalar objects.
*
* GScalar is a virtual counterpart of cv::Scalar, which is usually used
* to represent the GScalar data in G-API during the execution.
*
* @sa Scalar
*/
class GAPI_EXPORTS_W_SIMPLE GScalar
{
public:
GAPI_WRAP GScalar(); // Empty constructor
explicit GScalar(const cv::Scalar& s); // Constant value constructor from cv::Scalar
/**
* @brief Constructs an empty GScalar
*
* Normally, empty G-API data objects denote a starting point of
* the graph. When an empty GScalar is assigned to a result of some
* operation, it obtains a functional link to this operation (and
* is not empty anymore).
*/
GAPI_WRAP GScalar();
/**
* @brief Constructs a value-initialized GScalar
*
* In contrast with GMat (which can be either an explicit graph input
* or a result of some operation), GScalars may have their values
* be associated at graph construction time. It is useful when
* some operation has a GScalar input which doesn't change during
* the program execution, and is set only once. In this case,
* there is no need to declare such GScalar as a graph input.
*
* @note The value of GScalar may be overwritten by assigning some
* other GScalar to the object using `operator=` -- on the
* assigment, the old GScalar value is discarded.
*
* @param s a cv::Scalar value to associate with this GScalar object.
*/
explicit GScalar(const cv::Scalar& s);
/**
* @overload
* @brief Constructs a value-initialized GScalar
*
* @param s a cv::Scalar value to associate with this GScalar object.
*/
explicit GScalar(cv::Scalar&& s); // Constant value move-constructor from cv::Scalar
/**
* @overload
* @brief Constructs a value-initialized GScalar
*
* @param v0 A `double` value to associate with this GScalar. Note
* that only the first component of a four-component cv::Scalar is
* set to this value, with others remain zeros.
*
* This constructor overload is not marked `explicit` and can be
* used in G-API expression code like this:
*
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp gscalar_implicit
*
* Here operator+(GMat,GScalar) is used to wrap cv::gapi::addC()
* and a value-initialized GScalar is created on the fly.
*
* @overload
*/
GScalar(double v0); // Constant value constructor from double
GScalar(const GNode &n, std::size_t out); // Operation result constructor
/// @private
GScalar(const GNode &n, std::size_t out); // Operation result constructor
/// @private
GOrigin& priv(); // Internal use only
/// @private
const GOrigin& priv() const; // Internal use only
private:
@ -49,7 +114,7 @@ private:
* \addtogroup gapi_meta_args
* @{
*/
struct GScalarDesc
struct GAPI_EXPORTS_W_SIMPLE GScalarDesc
{
// NB.: right now it is empty
@ -64,9 +129,9 @@ struct GScalarDesc
}
};
static inline GScalarDesc empty_scalar_desc() { return GScalarDesc(); }
GAPI_EXPORTS_W inline GScalarDesc empty_scalar_desc() { return GScalarDesc(); }
GAPI_EXPORTS GScalarDesc descr_of(const cv::Scalar &scalar);
GAPI_EXPORTS GScalarDesc descr_of(const cv::Scalar &scalar);
std::ostream& operator<<(std::ostream& os, const cv::GScalarDesc &desc);

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018 Intel Corporation
// Copyright (C) 2018-2021 Intel Corporation
#ifndef OPENCV_GAPI_GSTREAMING_COMPILED_HPP
@ -65,12 +65,23 @@ using OptionalOpaqueRef = OptRef<cv::detail::OpaqueRef>;
using GOptRunArgP = util::variant<
optional<cv::Mat>*,
optional<cv::RMat>*,
optional<cv::MediaFrame>*,
optional<cv::Scalar>*,
cv::detail::OptionalVectorRef,
cv::detail::OptionalOpaqueRef
>;
using GOptRunArgsP = std::vector<GOptRunArgP>;
using GOptRunArg = util::variant<
optional<cv::Mat>,
optional<cv::RMat>,
optional<cv::MediaFrame>,
optional<cv::Scalar>,
optional<cv::detail::VectorRef>,
optional<cv::detail::OpaqueRef>
>;
using GOptRunArgs = std::vector<GOptRunArg>;
namespace detail {
template<typename T> inline GOptRunArgP wrap_opt_arg(optional<T>& arg) {
@ -86,6 +97,14 @@ template<> inline GOptRunArgP wrap_opt_arg(optional<cv::Mat> &m) {
return GOptRunArgP{&m};
}
template<> inline GOptRunArgP wrap_opt_arg(optional<cv::RMat> &m) {
return GOptRunArgP{&m};
}
template<> inline GOptRunArgP wrap_opt_arg(optional<cv::MediaFrame> &f) {
return GOptRunArgP{&f};
}
template<> inline GOptRunArgP wrap_opt_arg(optional<cv::Scalar> &s) {
return GOptRunArgP{&s};
}
@ -180,7 +199,10 @@ public:
* @param ins vector of inputs to process.
* @sa gin
*/
GAPI_WRAP void setSource(GRunArgs &&ins);
void setSource(GRunArgs &&ins);
/// @private -- Exclude this function from OpenCV documentation
GAPI_WRAP void setSource(const cv::detail::ExtractArgsCallback& callback);
/**
* @brief Specify an input video stream for a single-input
@ -193,7 +215,7 @@ public:
* @param s a shared pointer to IStreamSource representing the
* input video stream.
*/
GAPI_WRAP void setSource(const gapi::wip::IStreamSource::Ptr& s);
void setSource(const gapi::wip::IStreamSource::Ptr& s);
/**
* @brief Constructs and specifies an input video stream for a
@ -251,7 +273,8 @@ public:
bool pull(cv::GRunArgsP &&outs);
// NB: Used from python
GAPI_WRAP std::tuple<bool, cv::GRunArgs> pull();
/// @private -- Exclude this function from OpenCV documentation
GAPI_WRAP std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> pull();
/**
* @brief Get some next available data from the pipeline.
@ -367,6 +390,41 @@ protected:
};
/** @} */
namespace gapi {
/**
* @brief This namespace contains G-API functions, structures, and
* symbols related to the Streaming execution mode.
*
* Some of the operations defined in this namespace (e.g. size(),
* BGR(), etc.) can be used in the traditional execution mode too.
*/
namespace streaming {
/**
* @brief Specify queue capacity for streaming execution.
*
* In the streaming mode the pipeline steps are connected with queues
* and this compile argument controls every queue's size.
*/
struct GAPI_EXPORTS_W_SIMPLE queue_capacity
{
GAPI_WRAP
explicit queue_capacity(size_t cap = 1) : capacity(cap) { };
GAPI_PROP_RW
size_t capacity;
};
/** @} */
} // namespace streaming
} // namespace gapi
namespace detail
{
template<> struct CompileArgTag<cv::gapi::streaming::queue_capacity>
{
static const char* tag() { return "gapi.queue_capacity"; }
};
}
}
#endif // OPENCV_GAPI_GSTREAMING_COMPILED_HPP

View File

@ -31,7 +31,7 @@ struct GAPI_EXPORTS GTransform
F pattern;
F substitute;
GTransform(const std::string& d, const F &p, const F &s) : description(d), pattern(p), substitute(s){};
GTransform(const std::string& d, const F &p, const F &s) : description(d), pattern(p), substitute(s) {}
};
namespace detail

View File

@ -19,11 +19,25 @@
#include <opencv2/gapi/streaming/source.hpp>
#include <opencv2/gapi/media.hpp>
#include <opencv2/gapi/gcommon.hpp>
#include <opencv2/gapi/util/util.hpp>
#include <opencv2/gapi/own/convert.hpp>
namespace cv
{
namespace detail
{
template<typename, typename = void>
struct contains_shape_field : std::false_type {};
template<typename TaggedTypeCandidate>
struct contains_shape_field<TaggedTypeCandidate,
void_t<decltype(TaggedTypeCandidate::shape)>> :
std::is_same<typename std::decay<decltype(TaggedTypeCandidate::shape)>::type, GShape>
{};
template<typename Type>
struct has_gshape : contains_shape_field<Type> {};
// FIXME: These traits and enum and possible numerous switch(kind)
// block may be replaced with a special Handler<T> object or with
// a double dispatch
@ -181,10 +195,16 @@ namespace detail
}
template<typename U> static auto wrap_in (const U &u) -> typename GTypeTraits<T>::strip_type
{
static_assert(!(cv::detail::has_gshape<GTypeTraits<U>>::value
|| cv::detail::contains<typename std::decay<U>::type, GAPI_OWN_TYPES_LIST>::value),
"gin/gout must not be used with G* classes or cv::gapi::own::*");
return GTypeTraits<T>::wrap_in(u);
}
template<typename U> static auto wrap_out(U &u) -> typename GTypeTraits<T>::strip_type
{
static_assert(!(cv::detail::has_gshape<GTypeTraits<U>>::value
|| cv::detail::contains<typename std::decay<U>::type, GAPI_OWN_TYPES_LIST>::value),
"gin/gout must not be used with G* classses or cv::gapi::own::*");
return GTypeTraits<T>::wrap_out(u);
}
};

View File

@ -57,7 +57,7 @@ namespace detail
*
* Refer to the following example. Regular (untyped) code is written this way:
*
* @snippet modules/gapi/samples/api_ref_snippets.cpp Untyped_Example
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp Untyped_Example
*
* Here:
*
@ -71,7 +71,7 @@ namespace detail
*
* Now the same code written with typed API:
*
* @snippet modules/gapi/samples/api_ref_snippets.cpp Typed_Example
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp Typed_Example
*
* The key difference is:
*

View File

@ -47,6 +47,10 @@ void validateFindingContoursMeta(const int depth, const int chan, const int mode
namespace cv { namespace gapi {
/**
* @brief This namespace contains G-API Operation Types for OpenCV
* ImgProc module functionality.
*/
namespace imgproc {
using GMat2 = std::tuple<GMat,GMat>;
using GMat3 = std::tuple<GMat,GMat,GMat>; // FIXME: how to avoid this?
@ -1158,7 +1162,7 @@ if there are 2 channels, or have 2 columns if there is a single channel. Mat sho
@param src Input gray-scale image @ref CV_8UC1; or input set of @ref CV_32S or @ref CV_32F
2D points stored in Mat.
*/
GAPI_EXPORTS GOpaque<Rect> boundingRect(const GMat& src);
GAPI_EXPORTS_W GOpaque<Rect> boundingRect(const GMat& src);
/** @overload
@ -1168,7 +1172,7 @@ Calculates the up-right bounding rectangle of a point set.
@param src Input 2D point set, stored in std::vector<cv::Point2i>.
*/
GAPI_EXPORTS GOpaque<Rect> boundingRect(const GArray<Point2i>& src);
GAPI_EXPORTS_W GOpaque<Rect> boundingRect(const GArray<Point2i>& src);
/** @overload
@ -1341,7 +1345,7 @@ Output image is 8-bit unsigned 3-channel image @ref CV_8UC3.
@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3.
@sa RGB2BGR
*/
GAPI_EXPORTS GMat BGR2RGB(const GMat& src);
GAPI_EXPORTS_W GMat BGR2RGB(const GMat& src);
/** @brief Converts an image from RGB color space to gray-scaled.

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
#ifndef OPENCV_GAPI_INFER_HPP
@ -76,6 +76,113 @@ struct valid_infer2_types< std::tuple<cv::GMat,Ns...>, std::tuple<T,Ts...> > {
valid_infer2_types< std::tuple<cv::GMat>, std::tuple<T> >::value
&& valid_infer2_types< std::tuple<Ns...>, std::tuple<Ts...> >::value;
};
// Struct stores network input/output names.
// Used by infer<Generic>
struct InOutInfo
{
std::vector<std::string> in_names;
std::vector<std::string> out_names;
};
template <typename OutT>
class GInferOutputsTyped
{
public:
GInferOutputsTyped() = default;
GInferOutputsTyped(std::shared_ptr<cv::GCall> call)
: m_priv(std::make_shared<Priv>(std::move(call)))
{
}
OutT at(const std::string& name)
{
auto it = m_priv->blobs.find(name);
if (it == m_priv->blobs.end()) {
// FIXME: Avoid modifying GKernel
auto shape = cv::detail::GTypeTraits<OutT>::shape;
m_priv->call->kernel().outShapes.push_back(shape);
m_priv->call->kernel().outCtors.emplace_back(cv::detail::GObtainCtor<OutT>::get());
auto out_idx = static_cast<int>(m_priv->blobs.size());
it = m_priv->blobs.emplace(name,
cv::detail::Yield<OutT>::yield(*(m_priv->call), out_idx)).first;
m_priv->info->out_names.push_back(name);
}
return it->second;
}
private:
struct Priv
{
Priv(std::shared_ptr<cv::GCall> c)
: call(std::move(c)), info(cv::util::any_cast<InOutInfo>(&call->params()))
{
}
std::shared_ptr<cv::GCall> call;
InOutInfo* info = nullptr;
std::unordered_map<std::string, OutT> blobs;
};
std::shared_ptr<Priv> m_priv;
};
template <typename... Ts>
class GInferInputsTyped
{
public:
GInferInputsTyped()
: m_priv(std::make_shared<Priv>())
{
}
template <typename U>
GInferInputsTyped<Ts...>& setInput(const std::string& name, U in)
{
m_priv->blobs.emplace(std::piecewise_construct,
std::forward_as_tuple(name),
std::forward_as_tuple(in));
return *this;
}
using StorageT = cv::util::variant<Ts...>;
StorageT& operator[](const std::string& name) {
return m_priv->blobs[name];
}
using Map = std::unordered_map<std::string, StorageT>;
const Map& getBlobs() const {
return m_priv->blobs;
}
private:
struct Priv
{
std::unordered_map<std::string, StorageT> blobs;
};
std::shared_ptr<Priv> m_priv;
};
template<typename InferT>
std::shared_ptr<cv::GCall> makeCall(const std::string &tag,
std::vector<cv::GArg> &&args,
std::vector<std::string> &&names,
cv::GKinds &&kinds) {
auto call = std::make_shared<cv::GCall>(GKernel{
InferT::id(),
tag,
InferT::getOutMeta,
{}, // outShape will be filled later
std::move(kinds),
{}, // outCtors will be filled later
});
call->setArgs(std::move(args));
call->params() = cv::detail::InOutInfo{std::move(names), {}};
return call;
}
} // namespace detail
// TODO: maybe tuple_wrap_helper from util.hpp may help with this.
@ -166,49 +273,6 @@ struct GInferBase {
}
};
// Struct stores network input/output names.
// Used by infer<Generic>
struct InOutInfo
{
std::vector<std::string> in_names;
std::vector<std::string> out_names;
};
/**
* @{
* @brief G-API object used to collect network inputs
*/
class GAPI_EXPORTS_W_SIMPLE GInferInputs
{
using Map = std::unordered_map<std::string, GMat>;
public:
GAPI_WRAP GInferInputs();
GAPI_WRAP void setInput(const std::string& name, const cv::GMat& value);
cv::GMat& operator[](const std::string& name);
const Map& getBlobs() const;
private:
std::shared_ptr<Map> in_blobs;
};
/** @} */
/**
* @{
* @brief G-API object used to collect network outputs
*/
struct GAPI_EXPORTS_W_SIMPLE GInferOutputs
{
public:
GAPI_WRAP GInferOutputs() = default;
GInferOutputs(std::shared_ptr<cv::GCall> call);
GAPI_WRAP cv::GMat at(const std::string& name);
private:
struct Priv;
std::shared_ptr<Priv> m_priv;
};
/** @} */
// Base "InferROI" kernel.
// All notes from "Infer" kernel apply here as well.
struct GInferROIBase {
@ -295,6 +359,90 @@ struct GInferList2 final
static constexpr const char* tag() { return Net::tag(); }
};
/**
* @brief G-API object used to collect network inputs
*/
using GInferInputs = cv::detail::GInferInputsTyped<cv::GMat, cv::GFrame>;
/**
* @brief G-API object used to collect the list of network inputs
*/
using GInferListInputs = cv::detail::GInferInputsTyped<cv::GArray<cv::GMat>, cv::GArray<cv::Rect>>;
/**
* @brief G-API object used to collect network outputs
*/
using GInferOutputs = cv::detail::GInferOutputsTyped<cv::GMat>;
/**
* @brief G-API object used to collect the list of network outputs
*/
using GInferListOutputs = cv::detail::GInferOutputsTyped<cv::GArray<cv::GMat>>;
namespace detail {
void inline unpackBlobs(const cv::GInferInputs::Map& blobs,
std::vector<cv::GArg>& args,
std::vector<std::string>& names,
cv::GKinds& kinds)
{
for (auto&& p : blobs) {
names.emplace_back(p.first);
switch (p.second.index()) {
case cv::GInferInputs::StorageT::index_of<cv::GMat>():
args.emplace_back(cv::util::get<cv::GMat>(p.second));
kinds.emplace_back(cv::detail::OpaqueKind::CV_MAT);
break;
case cv::GInferInputs::StorageT::index_of<cv::GFrame>():
args.emplace_back(cv::util::get<cv::GFrame>(p.second));
kinds.emplace_back(cv::detail::OpaqueKind::CV_UNKNOWN);
break;
default:
GAPI_Assert(false);
}
}
}
template <typename InferType>
struct InferROITraits;
template <>
struct InferROITraits<GInferROIBase>
{
using outType = cv::GInferOutputs;
using inType = cv::GOpaque<cv::Rect>;
};
template <>
struct InferROITraits<GInferListBase>
{
using outType = cv::GInferListOutputs;
using inType = cv::GArray<cv::Rect>;
};
template<typename InferType>
typename InferROITraits<InferType>::outType
inferGenericROI(const std::string& tag,
const typename InferROITraits<InferType>::inType& in,
const cv::GInferInputs& inputs)
{
std::vector<cv::GArg> args;
std::vector<std::string> names;
cv::GKinds kinds;
args.emplace_back(in);
kinds.emplace_back(cv::detail::OpaqueKind::CV_RECT);
unpackBlobs(inputs.getBlobs(), args, names, kinds);
auto call = cv::detail::makeCall<InferType>(tag,
std::move(args),
std::move(names),
std::move(kinds));
return {std::move(call)};
}
} // namespace detail
} // namespace cv
// FIXME: Probably the <API> signature makes a function/tuple/function round-trip
@ -384,7 +532,11 @@ typename Net::Result infer(Args&&... args) {
}
/**
* @brief Special network type
* @brief Generic network type: input and output layers are configured dynamically at runtime
*
* Unlike the network types defined with G_API_NET macro, this one
* doesn't fix number of network inputs and outputs at the compilation stage
* thus providing user with an opportunity to program them in runtime.
*/
struct Generic { };
@ -395,38 +547,98 @@ struct Generic { };
* @param inputs networks's inputs
* @return a GInferOutputs
*/
template<typename T = Generic> GInferOutputs
infer(const std::string& tag, const GInferInputs& inputs)
template<typename T = Generic> cv::GInferOutputs
infer(const std::string& tag, const cv::GInferInputs& inputs)
{
std::vector<GArg> input_args;
std::vector<std::string> input_names;
std::vector<cv::GArg> args;
std::vector<std::string> names;
cv::GKinds kinds;
const auto& blobs = inputs.getBlobs();
for (auto&& p : blobs)
{
input_names.push_back(p.first);
input_args.emplace_back(p.second);
}
cv::detail::unpackBlobs(inputs.getBlobs(), args, names, kinds);
GKinds kinds(blobs.size(), cv::detail::OpaqueKind::CV_MAT);
auto call = std::make_shared<cv::GCall>(GKernel{
GInferBase::id(),
tag,
GInferBase::getOutMeta,
{}, // outShape will be filled later
std::move(kinds),
{}, // outCtors will be filled later
});
auto call = cv::detail::makeCall<GInferBase>(tag,
std::move(args),
std::move(names),
std::move(kinds));
call->setArgs(std::move(input_args));
call->params() = InOutInfo{input_names, {}};
return GInferOutputs{std::move(call)};
return cv::GInferOutputs{std::move(call)};
}
GAPI_EXPORTS_W inline GInferOutputs infer(const String& name, const GInferInputs& inputs)
/** @brief Calculates response for the generic network
* for the specified region in the source image.
* Currently expects a single-input network only.
*
* @param tag a network tag
* @param roi a an object describing the region of interest
* in the source image. May be calculated in the same graph dynamically.
* @param inputs networks's inputs
* @return a cv::GInferOutputs
*/
template<typename T = Generic> cv::GInferOutputs
infer(const std::string& tag, const cv::GOpaque<cv::Rect>& roi, const cv::GInferInputs& inputs)
{
return infer<Generic>(name, inputs);
return cv::detail::inferGenericROI<GInferROIBase>(tag, roi, inputs);
}
/** @brief Calculates responses for the specified network
* for every region in the source image.
*
* @param tag a network tag
* @param rois a list of rectangles describing regions of interest
* in the source image. Usually an output of object detector or tracker.
* @param inputs networks's inputs
* @return a cv::GInferListOutputs
*/
template<typename T = Generic> cv::GInferListOutputs
infer(const std::string& tag, const cv::GArray<cv::Rect>& rois, const cv::GInferInputs& inputs)
{
return cv::detail::inferGenericROI<GInferListBase>(tag, rois, inputs);
}
/** @brief Calculates responses for the specified network
* for every region in the source image, extended version.
*
* @param tag a network tag
* @param in a source image containing regions of interest.
* @param inputs networks's inputs
* @return a cv::GInferListOutputs
*/
template<typename T = Generic, typename Input>
typename std::enable_if<cv::detail::accepted_infer_types<Input>::value, cv::GInferListOutputs>::type
infer2(const std::string& tag,
const Input& in,
const cv::GInferListInputs& inputs)
{
std::vector<cv::GArg> args;
std::vector<std::string> names;
cv::GKinds kinds;
args.emplace_back(in);
auto k = cv::detail::GOpaqueTraits<Input>::kind;
kinds.emplace_back(k);
for (auto&& p : inputs.getBlobs()) {
names.emplace_back(p.first);
switch (p.second.index()) {
case cv::GInferListInputs::StorageT::index_of<cv::GArray<cv::GMat>>():
args.emplace_back(cv::util::get<cv::GArray<cv::GMat>>(p.second));
kinds.emplace_back(cv::detail::OpaqueKind::CV_MAT);
break;
case cv::GInferListInputs::StorageT::index_of<cv::GArray<cv::Rect>>():
args.emplace_back(cv::util::get<cv::GArray<cv::Rect>>(p.second));
kinds.emplace_back(cv::detail::OpaqueKind::CV_RECT);
break;
default:
GAPI_Assert(false);
}
}
auto call = cv::detail::makeCall<GInferList2Base>(tag,
std::move(args),
std::move(names),
std::move(kinds));
return cv::GInferListOutputs{std::move(call)};
}
} // namespace gapi
@ -442,7 +654,8 @@ namespace gapi {
// A type-erased form of network parameters.
// Similar to how a type-erased GKernel is represented and used.
struct GAPI_EXPORTS GNetParam {
/// @private
struct GAPI_EXPORTS_W_SIMPLE GNetParam {
std::string tag; // FIXME: const?
GBackend backend; // Specifies the execution model
util::any params; // Backend-interpreted parameter structure
@ -453,12 +666,13 @@ struct GAPI_EXPORTS GNetParam {
*/
/**
* @brief A container class for network configurations. Similar to
* GKernelPackage.Use cv::gapi::networks() to construct this object.
* GKernelPackage. Use cv::gapi::networks() to construct this object.
*
* @sa cv::gapi::networks
*/
struct GAPI_EXPORTS_W_SIMPLE GNetPackage {
GAPI_WRAP GNetPackage() = default;
GAPI_WRAP explicit GNetPackage(std::vector<GNetParam> nets);
explicit GNetPackage(std::initializer_list<GNetParam> ii);
std::vector<GBackend> backends() const;
std::vector<GNetParam> networks;
@ -486,6 +700,14 @@ template<typename... Args>
cv::gapi::GNetPackage networks(Args&&... args) {
return cv::gapi::GNetPackage({ cv::detail::strip(args)... });
}
inline cv::gapi::GNetPackage& operator += ( cv::gapi::GNetPackage& lhs,
const cv::gapi::GNetPackage& rhs) {
lhs.networks.reserve(lhs.networks.size() + rhs.networks.size());
lhs.networks.insert(lhs.networks.end(), rhs.networks.begin(), rhs.networks.end());
return lhs;
}
} // namespace gapi
} // namespace cv

View File

@ -22,17 +22,31 @@ namespace ie {
// This class can be marked as SIMPLE, because it's implemented as pimpl
class GAPI_EXPORTS_W_SIMPLE PyParams {
public:
GAPI_WRAP
PyParams() = default;
GAPI_WRAP
PyParams(const std::string &tag,
const std::string &model,
const std::string &weights,
const std::string &device);
GAPI_WRAP
PyParams(const std::string &tag,
const std::string &model,
const std::string &device);
GAPI_WRAP
PyParams& constInput(const std::string &layer_name,
const cv::Mat &data,
TraitAs hint = TraitAs::TENSOR);
GAPI_WRAP
PyParams& cfgNumRequests(size_t nireq);
GAPI_WRAP
PyParams& cfgBatchSize(const size_t size);
GBackend backend() const;
std::string tag() const;
cv::util::any params() const;

View File

@ -2,12 +2,13 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2019 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
#ifndef OPENCV_GAPI_INFER_IE_HPP
#define OPENCV_GAPI_INFER_IE_HPP
#include <unordered_map>
#include <unordered_set>
#include <string>
#include <array>
#include <tuple> // tuple, tuple_size
@ -23,12 +24,17 @@
namespace cv {
namespace gapi {
// FIXME: introduce a new sub-namespace for NN?
/**
* @brief This namespace contains G-API OpenVINO backend functions,
* structures, and symbols.
*/
namespace ie {
GAPI_EXPORTS cv::gapi::GBackend backend();
/**
* Specify how G-API and IE should trait input data
* Specifies how G-API and IE should trait input data
*
* In OpenCV, the same cv::Mat is used to represent both
* image and tensor data. Sometimes those are hardly distinguishable,
@ -46,28 +52,39 @@ enum class TraitAs: int
using IEConfig = std::map<std::string, std::string>;
namespace detail {
struct ParamDesc {
std::string model_path;
std::string weights_path;
std::string device_id;
struct ParamDesc {
std::string model_path;
std::string weights_path;
std::string device_id;
// NB: Here order follows the `Net` API
std::vector<std::string> input_names;
std::vector<std::string> output_names;
std::vector<std::string> input_names;
std::vector<std::string> output_names;
using ConstInput = std::pair<cv::Mat, TraitAs>;
std::unordered_map<std::string, ConstInput> const_inputs;
using ConstInput = std::pair<cv::Mat, TraitAs>;
std::unordered_map<std::string, ConstInput> const_inputs;
// NB: nun_* may differ from topology's real input/output port numbers
// (e.g. topology's partial execution)
std::size_t num_in; // How many inputs are defined in the operation
std::size_t num_out; // How many outputs are defined in the operation
std::size_t num_in;
std::size_t num_out;
enum class Kind { Load, Import };
Kind kind;
bool is_generic;
IEConfig config;
};
enum class Kind {Load, Import};
Kind kind;
bool is_generic;
IEConfig config;
std::map<std::string, std::vector<std::size_t>> reshape_table;
std::unordered_set<std::string> layer_names_to_reshape;
// NB: Number of asyncrhonious infer requests
size_t nireq;
// NB: An optional config to setup RemoteContext for IE
cv::util::any context_config;
// NB: batch_size can't be equal to 1 by default, because some of models
// have 2D (Layout::NC) input and if the first dimension not equal to 1
// net.setBatchSize(1) will overwrite it.
cv::optional<size_t> batch_size;
};
} // namespace detail
// FIXME: this is probably a shared (reusable) thing
@ -81,8 +98,21 @@ struct PortCfg {
, std::tuple_size<typename Net::OutArgs>::value >;
};
/**
* @brief This structure provides functions
* that fill inference parameters for "OpenVINO Toolkit" model.
*/
template<typename Net> class Params {
public:
/** @brief Class constructor.
Constructs Params based on model information and specifies default values for other
inference description parameters. Model is loaded and compiled using "OpenVINO Toolkit".
@param model Path to topology IR (.xml file).
@param weights Path to weights (.bin file).
@param device target device to use.
*/
Params(const std::string &model,
const std::string &weights,
const std::string &device)
@ -91,9 +121,21 @@ public:
, std::tuple_size<typename Net::OutArgs>::value // num_out
, detail::ParamDesc::Kind::Load
, false
, {}
, {}
, {}
, 1u
, {}
, {}} {
};
/** @overload
Use this constructor to work with pre-compiled network.
Model is imported from a pre-compiled blob.
@param model Path to model.
@param device target device to use.
*/
Params(const std::string &model,
const std::string &device)
: desc{ model, {}, device, {}, {}, {}
@ -101,25 +143,61 @@ public:
, std::tuple_size<typename Net::OutArgs>::value // num_out
, detail::ParamDesc::Kind::Import
, false
, {}
, {}
, {}
, 1u
, {}
, {}} {
};
Params<Net>& cfgInputLayers(const typename PortCfg<Net>::In &ll) {
/** @brief Specifies sequence of network input layers names for inference.
The function is used to associate cv::gapi::infer<> inputs with the model inputs.
Number of names has to match the number of network inputs as defined in G_API_NET().
In case a network has only single input layer, there is no need to specify name manually.
@param layer_names std::array<std::string, N> where N is the number of inputs
as defined in the @ref G_API_NET. Contains names of input layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgInputLayers(const typename PortCfg<Net>::In &layer_names) {
desc.input_names.clear();
desc.input_names.reserve(ll.size());
std::copy(ll.begin(), ll.end(),
desc.input_names.reserve(layer_names.size());
std::copy(layer_names.begin(), layer_names.end(),
std::back_inserter(desc.input_names));
return *this;
}
Params<Net>& cfgOutputLayers(const typename PortCfg<Net>::Out &ll) {
/** @brief Specifies sequence of network output layers names for inference.
The function is used to associate cv::gapi::infer<> outputs with the model outputs.
Number of names has to match the number of network outputs as defined in G_API_NET().
In case a network has only single output layer, there is no need to specify name manually.
@param layer_names std::array<std::string, N> where N is the number of outputs
as defined in the @ref G_API_NET. Contains names of output layers.
@return reference to this parameter structure.
*/
Params<Net>& cfgOutputLayers(const typename PortCfg<Net>::Out &layer_names) {
desc.output_names.clear();
desc.output_names.reserve(ll.size());
std::copy(ll.begin(), ll.end(),
desc.output_names.reserve(layer_names.size());
std::copy(layer_names.begin(), layer_names.end(),
std::back_inserter(desc.output_names));
return *this;
}
/** @brief Specifies a constant input.
The function is used to set a constant input. This input has to be
a preprocessed tensor if its type is TENSOR. Need to provide name of the
network layer which will receive provided data.
@param layer_name Name of network layer.
@param data cv::Mat that contains data which will be associated with network layer.
@param hint Input type @sa cv::gapi::ie::TraitAs.
@return reference to this parameter structure.
*/
Params<Net>& constInput(const std::string &layer_name,
const cv::Mat &data,
TraitAs hint = TraitAs::TENSOR) {
@ -127,13 +205,134 @@ public:
return *this;
}
/** @brief Specifies OpenVINO plugin configuration.
The function is used to set configuration for OpenVINO plugin. Some parameters
can be different for each plugin. Please follow https://docs.openvinotoolkit.org/latest/index.html
to check information about specific plugin.
@param cfg Map of pairs: (config parameter name, config parameter value).
@return reference to this parameter structure.
*/
Params& pluginConfig(const IEConfig& cfg) {
desc.config = cfg;
return *this;
}
/** @overload
Function with a rvalue parameter.
@param cfg rvalue map of pairs: (config parameter name, config parameter value).
@return reference to this parameter structure.
*/
Params& pluginConfig(IEConfig&& cfg) {
desc.config = std::move(cfg);
return *this;
}
Params& pluginConfig(const IEConfig& cfg) {
desc.config = cfg;
/** @brief Specifies configuration for RemoteContext in InferenceEngine.
When RemoteContext is configured the backend imports the networks using the context.
It also expects cv::MediaFrames to be actually remote, to operate with blobs via the context.
@param ctx_cfg cv::util::any value which holds InferenceEngine::ParamMap.
@return reference to this parameter structure.
*/
Params& cfgContextParams(const cv::util::any& ctx_cfg) {
desc.context_config = ctx_cfg;
return *this;
}
/** @overload
Function with an rvalue parameter.
@param ctx_cfg cv::util::any value which holds InferenceEngine::ParamMap.
@return reference to this parameter structure.
*/
Params& cfgContextParams(cv::util::any&& ctx_cfg) {
desc.context_config = std::move(ctx_cfg);
return *this;
}
/** @brief Specifies number of asynchronous inference requests.
@param nireq Number of inference asynchronous requests.
@return reference to this parameter structure.
*/
Params& cfgNumRequests(size_t nireq) {
GAPI_Assert(nireq > 0 && "Number of infer requests must be greater than zero!");
desc.nireq = nireq;
return *this;
}
/** @brief Specifies new input shapes for the network inputs.
The function is used to specify new input shapes for the network inputs.
Follow https://docs.openvinotoolkit.org/latest/classInferenceEngine_1_1networkNetwork.html
for additional information.
@param reshape_table Map of pairs: name of corresponding data and its dimension.
@return reference to this parameter structure.
*/
Params<Net>& cfgInputReshape(const std::map<std::string, std::vector<std::size_t>>& reshape_table) {
desc.reshape_table = reshape_table;
return *this;
}
/** @overload */
Params<Net>& cfgInputReshape(std::map<std::string, std::vector<std::size_t>>&& reshape_table) {
desc.reshape_table = std::move(reshape_table);
return *this;
}
/** @overload
@param layer_name Name of layer.
@param layer_dims New dimensions for this layer.
@return reference to this parameter structure.
*/
Params<Net>& cfgInputReshape(const std::string& layer_name, const std::vector<size_t>& layer_dims) {
desc.reshape_table.emplace(layer_name, layer_dims);
return *this;
}
/** @overload */
Params<Net>& cfgInputReshape(std::string&& layer_name, std::vector<size_t>&& layer_dims) {
desc.reshape_table.emplace(layer_name, layer_dims);
return *this;
}
/** @overload
@param layer_names set of names of network layers that will be used for network reshape.
@return reference to this parameter structure.
*/
Params<Net>& cfgInputReshape(const std::unordered_set<std::string>& layer_names) {
desc.layer_names_to_reshape = layer_names;
return *this;
}
/** @overload
@param layer_names rvalue set of the selected layers will be reshaped automatically
its input image size.
@return reference to this parameter structure.
*/
Params<Net>& cfgInputReshape(std::unordered_set<std::string>&& layer_names) {
desc.layer_names_to_reshape = std::move(layer_names);
return *this;
}
/** @brief Specifies the inference batch size.
The function is used to specify inference batch size.
Follow https://docs.openvinotoolkit.org/latest/classInferenceEngine_1_1CNNNetwork.html#a8e9d19270a48aab50cb5b1c43eecb8e9 for additional information
@param size batch size which will be used.
@return reference to this parameter structure.
*/
Params<Net>& cfgBatchSize(const size_t size) {
desc.batch_size = cv::util::make_optional(size);
return *this;
}
@ -147,29 +346,118 @@ protected:
detail::ParamDesc desc;
};
/*
* @brief This structure provides functions for generic network type that
* fill inference parameters.
* @see struct Generic
*/
template<>
class Params<cv::gapi::Generic> {
public:
/** @brief Class constructor.
Constructs Params based on model information and sets default values for other
inference description parameters. Model is loaded and compiled using OpenVINO Toolkit.
@param tag string tag of the network for which these parameters are intended.
@param model path to topology IR (.xml file).
@param weights path to weights (.bin file).
@param device target device to use.
*/
Params(const std::string &tag,
const std::string &model,
const std::string &weights,
const std::string &device)
: desc{ model, weights, device, {}, {}, {}, 0u, 0u, detail::ParamDesc::Kind::Load, true, {}}, m_tag(tag) {
: desc{ model, weights, device, {}, {}, {}, 0u, 0u,
detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u,
{}, {}},
m_tag(tag) {
};
/** @overload
This constructor for pre-compiled networks. Model is imported from pre-compiled
blob.
@param tag string tag of the network for which these parameters are intended.
@param model path to model.
@param device target device to use.
*/
Params(const std::string &tag,
const std::string &model,
const std::string &device)
: desc{ model, {}, device, {}, {}, {}, 0u, 0u, detail::ParamDesc::Kind::Import, true, {}}, m_tag(tag) {
: desc{ model, {}, device, {}, {}, {}, 0u, 0u,
detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u,
{}, {}},
m_tag(tag) {
};
/** @see ie::Params::pluginConfig. */
Params& pluginConfig(const IEConfig& cfg) {
desc.config = cfg;
return *this;
}
/** @overload */
Params& pluginConfig(IEConfig&& cfg) {
desc.config = std::move(cfg);
return *this;
}
Params& pluginConfig(const IEConfig& cfg) {
desc.config = cfg;
/** @see ie::Params::constInput. */
Params& constInput(const std::string &layer_name,
const cv::Mat &data,
TraitAs hint = TraitAs::TENSOR) {
desc.const_inputs[layer_name] = {data, hint};
return *this;
}
/** @see ie::Params::cfgNumRequests. */
Params& cfgNumRequests(size_t nireq) {
GAPI_Assert(nireq > 0 && "Number of infer requests must be greater than zero!");
desc.nireq = nireq;
return *this;
}
/** @see ie::Params::cfgInputReshape */
Params& cfgInputReshape(const std::map<std::string, std::vector<std::size_t>>&reshape_table) {
desc.reshape_table = reshape_table;
return *this;
}
/** @overload */
Params& cfgInputReshape(std::map<std::string, std::vector<std::size_t>> && reshape_table) {
desc.reshape_table = std::move(reshape_table);
return *this;
}
/** @overload */
Params& cfgInputReshape(std::string && layer_name, std::vector<size_t> && layer_dims) {
desc.reshape_table.emplace(layer_name, layer_dims);
return *this;
}
/** @overload */
Params& cfgInputReshape(const std::string & layer_name, const std::vector<size_t>&layer_dims) {
desc.reshape_table.emplace(layer_name, layer_dims);
return *this;
}
/** @overload */
Params& cfgInputReshape(std::unordered_set<std::string> && layer_names) {
desc.layer_names_to_reshape = std::move(layer_names);
return *this;
}
/** @overload */
Params& cfgInputReshape(const std::unordered_set<std::string>&layer_names) {
desc.layer_names_to_reshape = layer_names;
return *this;
}
/** @see ie::Params::cfgBatchSize */
Params& cfgBatchSize(const size_t size) {
desc.batch_size = cv::util::make_optional(size);
return *this;
}

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
#ifndef OPENCV_GAPI_INFER_ONNX_HPP
#define OPENCV_GAPI_INFER_ONNX_HPP
@ -20,6 +20,10 @@
namespace cv {
namespace gapi {
/**
* @brief This namespace contains G-API ONNX Runtime backend functions, structures, and symbols.
*/
namespace onnx {
GAPI_EXPORTS cv::gapi::GBackend backend();
@ -34,30 +38,35 @@ enum class TraitAs: int {
using PostProc = std::function<void(const std::unordered_map<std::string, cv::Mat> &,
std::unordered_map<std::string, cv::Mat> &)>;
namespace detail {
/**
* @brief This structure contains description of inference parameters
* which is specific to ONNX models.
*/
struct ParamDesc {
std::string model_path;
std::string model_path; //!< Path to model.
// NB: nun_* may differ from topology's real input/output port numbers
// (e.g. topology's partial execution)
std::size_t num_in; // How many inputs are defined in the operation
std::size_t num_out; // How many outputs are defined in the operation
std::size_t num_in; //!< How many inputs are defined in the operation
std::size_t num_out; //!< How many outputs are defined in the operation
// NB: Here order follows the `Net` API
std::vector<std::string> input_names;
std::vector<std::string> output_names;
std::vector<std::string> input_names; //!< Names of input network layers.
std::vector<std::string> output_names; //!< Names of output network layers.
using ConstInput = std::pair<cv::Mat, TraitAs>;
std::unordered_map<std::string, ConstInput> const_inputs;
std::unordered_map<std::string, ConstInput> const_inputs; //!< Map with pair of name of network layer and ConstInput which will be associated with this.
std::vector<cv::Scalar> mean;
std::vector<cv::Scalar> stdev;
std::vector<cv::Scalar> mean; //!< Mean values for preprocessing.
std::vector<cv::Scalar> stdev; //!< Standard deviation values for preprocessing.
std::vector<cv::GMatDesc> out_metas;
PostProc custom_post_proc;
std::vector<cv::GMatDesc> out_metas; //!< Out meta information about your output (type, dimension).
PostProc custom_post_proc; //!< Post processing function.
std::vector<bool> normalize;
std::vector<bool> normalize; //!< Vector of bool values that enabled or disabled normalize of input data.
std::vector<std::string> names_to_remap; //!< Names of output layers that will be processed in PostProc function.
};
} // namespace detail
@ -77,30 +86,71 @@ struct PortCfg {
, std::tuple_size<typename Net::InArgs>::value >;
};
/**
* Contains description of inference parameters and kit of functions that
* fill this parameters.
*/
template<typename Net> class Params {
public:
/** @brief Class constructor.
Constructs Params based on model information and sets default values for other
inference description parameters.
@param model Path to model (.onnx file).
*/
Params(const std::string &model) {
desc.model_path = model;
desc.num_in = std::tuple_size<typename Net::InArgs>::value;
desc.num_out = std::tuple_size<typename Net::OutArgs>::value;
};
// BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::onnx::backend(); }
std::string tag() const { return Net::tag(); }
cv::util::any params() const { return { desc }; }
// END(G-API's network parametrization API)
/** @brief Specifies sequence of network input layers names for inference.
Params<Net>& cfgInputLayers(const typename PortCfg<Net>::In &ll) {
desc.input_names.assign(ll.begin(), ll.end());
The function is used to associate data of graph inputs with input layers of
network topology. Number of names has to match the number of network inputs. If a network
has only one input layer, there is no need to call it as the layer is
associated with input automatically but this doesn't prevent you from
doing it yourself. Count of names has to match to number of network inputs.
@param layer_names std::array<std::string, N> where N is the number of inputs
as defined in the @ref G_API_NET. Contains names of input layers.
@return the reference on modified object.
*/
Params<Net>& cfgInputLayers(const typename PortCfg<Net>::In &layer_names) {
desc.input_names.assign(layer_names.begin(), layer_names.end());
return *this;
}
Params<Net>& cfgOutputLayers(const typename PortCfg<Net>::Out &ll) {
desc.output_names.assign(ll.begin(), ll.end());
/** @brief Specifies sequence of output layers names for inference.
The function is used to associate data of graph outputs with output layers of
network topology. If a network has only one output layer, there is no need to call it
as the layer is associated with ouput automatically but this doesn't prevent
you from doing it yourself. Count of names has to match to number of network
outputs or you can set your own output but for this case you have to
additionally use @ref cfgPostProc function.
@param layer_names std::array<std::string, N> where N is the number of outputs
as defined in the @ref G_API_NET. Contains names of output layers.
@return the reference on modified object.
*/
Params<Net>& cfgOutputLayers(const typename PortCfg<Net>::Out &layer_names) {
desc.output_names.assign(layer_names.begin(), layer_names.end());
return *this;
}
/** @brief Sets a constant input.
The function is used to set constant input. This input has to be
a prepared tensor since preprocessing is disabled for this case. You should
provide name of network layer which will receive provided data.
@param layer_name Name of network layer.
@param data cv::Mat that contains data which will be associated with network layer.
@param hint Type of input (TENSOR).
@return the reference on modified object.
*/
Params<Net>& constInput(const std::string &layer_name,
const cv::Mat &data,
TraitAs hint = TraitAs::TENSOR) {
@ -108,6 +158,17 @@ public:
return *this;
}
/** @brief Specifies mean value and standard deviation for preprocessing.
The function is used to set mean value and standard deviation for preprocessing
of input data.
@param m std::array<cv::Scalar, N> where N is the number of inputs
as defined in the @ref G_API_NET. Contains mean values.
@param s std::array<cv::Scalar, N> where N is the number of inputs
as defined in the @ref G_API_NET. Contains standard deviation values.
@return the reference on modified object.
*/
Params<Net>& cfgMeanStd(const typename PortCfg<Net>::NormCoefs &m,
const typename PortCfg<Net>::NormCoefs &s) {
desc.mean.assign(m.begin(), m.end());
@ -115,18 +176,103 @@ public:
return *this;
}
Params<Net>& cfgPostProc(const std::vector<cv::GMatDesc> &outs,
const PostProc &pp) {
desc.out_metas = outs;
desc.custom_post_proc = pp;
/** @brief Configures graph output and provides the post processing function from user.
The function is used when you work with networks with dynamic outputs.
Since we can't know dimensions of inference result needs provide them for
construction of graph output. This dimensions can differ from inference result.
So you have to provide @ref PostProc function that gets information from inference
result and fill output which is constructed by dimensions from out_metas.
@param out_metas Out meta information about your output (type, dimension).
@param remap_function Post processing function, which has two parameters. First is onnx
result, second is graph output. Both parameters is std::map that contain pair of
layer's name and cv::Mat.
@return the reference on modified object.
*/
Params<Net>& cfgPostProc(const std::vector<cv::GMatDesc> &out_metas,
const PostProc &remap_function) {
desc.out_metas = out_metas;
desc.custom_post_proc = remap_function;
return *this;
}
Params<Net>& cfgNormalize(const typename PortCfg<Net>::Normalize &n) {
desc.normalize.assign(n.begin(), n.end());
/** @overload
Function with a rvalue parameters.
@param out_metas rvalue out meta information about your output (type, dimension).
@param remap_function rvalue post processing function, which has two parameters. First is onnx
result, second is graph output. Both parameters is std::map that contain pair of
layer's name and cv::Mat.
@return the reference on modified object.
*/
Params<Net>& cfgPostProc(std::vector<cv::GMatDesc> &&out_metas,
PostProc &&remap_function) {
desc.out_metas = std::move(out_metas);
desc.custom_post_proc = std::move(remap_function);
return *this;
}
/** @overload
The function has additional parameter names_to_remap. This parameter provides
information about output layers which will be used for inference and post
processing function.
@param out_metas Out meta information.
@param remap_function Post processing function.
@param names_to_remap Names of output layers. network's inference will
be done on these layers. Inference's result will be processed in post processing
function using these names.
@return the reference on modified object.
*/
Params<Net>& cfgPostProc(const std::vector<cv::GMatDesc> &out_metas,
const PostProc &remap_function,
const std::vector<std::string> &names_to_remap) {
desc.out_metas = out_metas;
desc.custom_post_proc = remap_function;
desc.names_to_remap = names_to_remap;
return *this;
}
/** @overload
Function with a rvalue parameters and additional parameter names_to_remap.
@param out_metas rvalue out meta information.
@param remap_function rvalue post processing function.
@param names_to_remap rvalue names of output layers. network's inference will
be done on these layers. Inference's result will be processed in post processing
function using these names.
@return the reference on modified object.
*/
Params<Net>& cfgPostProc(std::vector<cv::GMatDesc> &&out_metas,
PostProc &&remap_function,
std::vector<std::string> &&names_to_remap) {
desc.out_metas = std::move(out_metas);
desc.custom_post_proc = std::move(remap_function);
desc.names_to_remap = std::move(names_to_remap);
return *this;
}
/** @brief Specifies normalize parameter for preprocessing.
The function is used to set normalize parameter for preprocessing of input data.
@param normalizations std::array<cv::Scalar, N> where N is the number of inputs
as defined in the @ref G_API_NET. Сontains bool values that enabled or disabled
normalize of input data.
@return the reference on modified object.
*/
Params<Net>& cfgNormalize(const typename PortCfg<Net>::Normalize &normalizations) {
desc.normalize.assign(normalizations.begin(), normalizations.end());
return *this;
}
// BEGIN(G-API's network parametrization API)
GBackend backend() const { return cv::gapi::onnx::backend(); }
std::string tag() const { return Net::tag(); }
cv::util::any params() const { return { desc }; }
// END(G-API's network parametrization API)
protected:
detail::ParamDesc desc;
};

View File

@ -64,12 +64,13 @@ detection is smaller than confidence threshold, detection is rejected.
given label will get to the output.
@return a tuple with a vector of detected boxes and a vector of appropriate labels.
*/
GAPI_EXPORTS std::tuple<GArray<Rect>, GArray<int>> parseSSD(const GMat& in,
const GOpaque<Size>& inSz,
const float confidenceThreshold = 0.5f,
const int filterLabel = -1);
GAPI_EXPORTS_W std::tuple<GArray<Rect>, GArray<int>> parseSSD(const GMat& in,
const GOpaque<Size>& inSz,
const float confidenceThreshold = 0.5f,
const int filterLabel = -1);
/** @brief Parses output of SSD network.
/** @overload
Extracts detection information (box, confidence) from SSD output and
filters it by given confidence and by going out of bounds.
@ -85,11 +86,11 @@ the larger side of the rectangle.
@param filterOutOfBounds If provided true, out-of-frame boxes are filtered.
@return a vector of detected bounding boxes.
*/
GAPI_EXPORTS GArray<Rect> parseSSD(const GMat& in,
const GOpaque<Size>& inSz,
const float confidenceThreshold = 0.5f,
const bool alignmentToSquare = false,
const bool filterOutOfBounds = false);
GAPI_EXPORTS_W GArray<Rect> parseSSD(const GMat& in,
const GOpaque<Size>& inSz,
const float confidenceThreshold,
const bool alignmentToSquare,
const bool filterOutOfBounds);
/** @brief Parses output of Yolo network.
@ -112,12 +113,12 @@ If 1.f, nms is not performed and no boxes are rejected.
<a href="https://github.com/openvinotoolkit/open_model_zoo/blob/master/models/public/yolo-v2-tiny-tf/yolo-v2-tiny-tf.md">documentation</a>.
@return a tuple with a vector of detected boxes and a vector of appropriate labels.
*/
GAPI_EXPORTS std::tuple<GArray<Rect>, GArray<int>> parseYolo(const GMat& in,
const GOpaque<Size>& inSz,
const float confidenceThreshold = 0.5f,
const float nmsThreshold = 0.5f,
const std::vector<float>& anchors
= nn::parsers::GParseYolo::defaultAnchors());
GAPI_EXPORTS_W std::tuple<GArray<Rect>, GArray<int>> parseYolo(const GMat& in,
const GOpaque<Size>& inSz,
const float confidenceThreshold = 0.5f,
const float nmsThreshold = 0.5f,
const std::vector<float>& anchors
= nn::parsers::GParseYolo::defaultAnchors());
} // namespace gapi
} // namespace cv

View File

@ -13,26 +13,142 @@
#include <utility> // forward<>()
#include <opencv2/gapi/gframe.hpp>
#include <opencv2/gapi/util/any.hpp>
// Forward declaration
namespace cv {
namespace gapi {
namespace s11n {
struct IOStream;
struct IIStream;
} // namespace s11n
} // namespace gapi
} // namespace cv
namespace cv {
/** \addtogroup gapi_data_structures
* @{
*
* @brief Extra G-API data structures used to pass input/output data
* to the graph for processing.
*/
/**
* @brief cv::MediaFrame class represents an image/media frame
* obtained from an external source.
*
* cv::MediaFrame represents image data as specified in
* cv::MediaFormat. cv::MediaFrame is designed to be a thin wrapper over some
* external memory of buffer; the class itself provides an uniform
* interface over such types of memory. cv::MediaFrame wraps data from
* a camera driver or from a media codec and provides an abstraction
* layer over this memory to G-API. MediaFrame defines a compact interface
* to access and manage the underlying data; the implementation is
* fully defined by the associated Adapter (which is usually
* user-defined).
*
* @sa cv::RMat
*/
class GAPI_EXPORTS MediaFrame {
public:
enum class Access { R, W };
/// This enum defines different types of cv::MediaFrame provided
/// access to the underlying data. Note that different flags can't
/// be combined in this version.
enum class Access {
R, ///< Access data for reading
W, ///< Access data for writing
};
class IAdapter;
class View;
using AdapterPtr = std::unique_ptr<IAdapter>;
/**
* @brief Constructs an empty MediaFrame
*
* The constructed object has no any data associated with it.
*/
MediaFrame();
explicit MediaFrame(AdapterPtr &&);
template<class T, class... Args> static cv::MediaFrame Create(Args&&...);
View access(Access) const;
/**
* @brief Constructs a MediaFrame with the given
* Adapter. MediaFrame takes ownership over the passed adapter.
*
* @param p an unique pointer to instance of IAdapter derived class.
*/
explicit MediaFrame(AdapterPtr &&p);
/**
* @overload
* @brief Constructs a MediaFrame with the given parameters for
* the Adapter. The adapter of type `T` is costructed on the fly.
*
* @param args list of arguments to construct an adapter of type
* `T`.
*/
template<class T, class... Args> static cv::MediaFrame Create(Args&&... args);
/**
* @brief Obtain access to the underlying data with the given
* mode.
*
* Depending on the associated Adapter and the data wrapped, this
* method may be cheap (e.g., the underlying memory is local) or
* costly (if the underlying memory is external or device
* memory).
*
* @param mode an access mode flag
* @return a MediaFrame::View object. The views should be handled
* carefully, refer to the MediaFrame::View documentation for details.
*/
View access(Access mode) const;
/**
* @brief Returns a media frame descriptor -- the information
* about the media format, dimensions, etc.
* @return a cv::GFrameDesc
*/
cv::GFrameDesc desc() const;
// FIXME: design a better solution
// Should be used only if the actual adapter provides implementation
/// @private -- exclude from the OpenCV documentation for now.
cv::util::any blobParams() const;
/**
* @brief Casts and returns the associated MediaFrame adapter to
* the particular adapter type `T`, returns nullptr if the type is
* different.
*
* This method may be useful if the adapter type is known by the
* caller, and some lower level access to the memory is required.
* Depending on the memory type, it may be more efficient than
* access().
*
* @return a pointer to the adapter object, nullptr if the adapter
* type is different.
*/
template<typename T> T* get() const {
static_assert(std::is_base_of<IAdapter, T>::value,
"T is not derived from cv::MediaFrame::IAdapter!");
auto* adapter = getAdapter();
GAPI_Assert(adapter != nullptr);
return dynamic_cast<T*>(adapter);
}
/**
* @brief Serialize MediaFrame's data to a byte array.
*
* @note The actual logic is implemented by frame's adapter class.
* Does nothing by default.
*
* @param os Bytestream to store serialized MediaFrame data in.
*/
void serialize(cv::gapi::s11n::IOStream& os) const;
private:
struct Priv;
std::shared_ptr<Priv> m;
IAdapter* getAdapter() const;
};
template<class T, class... Args>
@ -41,6 +157,43 @@ inline cv::MediaFrame cv::MediaFrame::Create(Args&&... args) {
return cv::MediaFrame(std::move(ptr));
}
/**
* @brief Provides access to the MediaFrame's underlying data.
*
* This object contains the necessary information to access the pixel
* data of the associated MediaFrame: arrays of pointers and strides
* (distance between every plane row, in bytes) for every image
* plane, as defined in cv::MediaFormat.
* There may be up to four image planes in MediaFrame.
*
* Depending on the MediaFrame::Access flag passed in
* MediaFrame::access(), a MediaFrame::View may be read- or
* write-only.
*
* Depending on the MediaFrame::IAdapter implementation associated
* with the parent MediaFrame, writing to memory with
* MediaFrame::Access::R flag may have no effect or lead to
* undefined behavior. Same applies to reading the memory with
* MediaFrame::Access::W flag -- again, depending on the IAdapter
* implementation, the host-side buffer the view provides access to
* may have no current data stored in (so in-place editing of the
* buffer contents may not be possible).
*
* MediaFrame::View objects must be handled carefully, as an external
* resource associated with MediaFrame may be locked for the time the
* MediaFrame::View object exists. Obtaining MediaFrame::View should
* be seen as "map" and destroying it as "unmap" in the "map/unmap"
* idiom (applicable to OpenCL, device memory, remote
* memory).
*
* When a MediaFrame buffer is accessed for writing, and the memory
* under MediaFrame::View::Ptrs is altered, the data synchronization
* of a host-side and device/remote buffer is not guaranteed until the
* MediaFrame::View is destroyed. In other words, the real data on the
* device or in a remote target may be updated at the MediaFrame::View
* destruction only -- but it depends on the associated
* MediaFrame::IAdapter implementation.
*/
class GAPI_EXPORTS MediaFrame::View final {
public:
static constexpr const size_t MAX_PLANES = 4;
@ -48,25 +201,56 @@ public:
using Strides = std::array<std::size_t, MAX_PLANES>; // in bytes
using Callback = std::function<void()>;
/// @private
View(Ptrs&& ptrs, Strides&& strs, Callback &&cb = [](){});
/// @private
View(const View&) = delete;
/// @private
View(View&&) = default;
/// @private
View& operator = (const View&) = delete;
~View();
Ptrs ptr;
Strides stride;
Ptrs ptr; ///< Array of image plane pointers
Strides stride; ///< Array of image plane strides, in bytes.
private:
Callback m_cb;
};
/**
* @brief An interface class for MediaFrame data adapters.
*
* Implement this interface to wrap media data in the MediaFrame. It
* makes sense to implement this class if there is a custom
* cv::gapi::wip::IStreamSource defined -- in this case, a stream
* source can produce MediaFrame objects with this adapter and the
* media data may be passed to graph without any copy. For example, a
* GStreamer-based stream source can implement an adapter over
* `GstBuffer` and G-API will transparently use it in the graph.
*/
class GAPI_EXPORTS MediaFrame::IAdapter {
public:
virtual ~IAdapter() = 0;
virtual cv::GFrameDesc meta() const = 0;
virtual MediaFrame::View access(MediaFrame::Access) = 0;
// FIXME: design a better solution
// The default implementation does nothing
virtual cv::util::any blobParams() const;
virtual void serialize(cv::gapi::s11n::IOStream&) {
GAPI_Assert(false && "Generic serialize method of MediaFrame::IAdapter does nothing by default. "
"Please, implement it in derived class to properly serialize the object.");
}
virtual void deserialize(cv::gapi::s11n::IIStream&) {
GAPI_Assert(false && "Generic deserialize method of MediaFrame::IAdapter does nothing by default. "
"Please, implement it in derived class to properly deserialize the object.");
}
};
/** @} */
} //namespace cv

View File

@ -29,6 +29,9 @@ namespace gimpl
namespace gapi
{
/**
* @brief This namespace contains G-API OpenCL backend functions, structures, and symbols.
*/
namespace ocl
{
/**

View File

@ -14,6 +14,12 @@
# include <opencv2/core/cvdef.h>
# include <opencv2/core/types.hpp>
# include <opencv2/core/base.hpp>
#define GAPI_OWN_TYPES_LIST cv::gapi::own::Rect, \
cv::gapi::own::Size, \
cv::gapi::own::Point, \
cv::gapi::own::Point2f, \
cv::gapi::own::Scalar, \
cv::gapi::own::Mat
#else // Without OpenCV
# include <opencv2/gapi/own/cvdefs.hpp>
# include <opencv2/gapi/own/types.hpp> // cv::gapi::own::Rect/Size/Point
@ -28,6 +34,8 @@ namespace cv {
using Scalar = gapi::own::Scalar;
using Mat = gapi::own::Mat;
} // namespace cv
#define GAPI_OWN_TYPES_LIST cv::gapi::own::VoidType
#endif // !defined(GAPI_STANDALONE)
#endif // OPENCV_GAPI_OPENCV_INCLUDES_HPP

View File

@ -20,11 +20,12 @@ typedef char schar;
typedef unsigned short ushort;
#define CV_USRTYPE1 (void)"CV_USRTYPE1 support has been dropped in OpenCV 4.0"
#define CV_CN_MAX 512
#define CV_CN_SHIFT 3
#define CV_DEPTH_MAX (1 << CV_CN_SHIFT)
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
@ -33,7 +34,6 @@ typedef unsigned short ushort;
#define CV_32F 5
#define CV_64F 6
#define CV_16F 7
#define CV_USRTYPE1 8
#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK)
@ -71,7 +71,6 @@ typedef unsigned short ushort;
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))
#define CV_16FC1 CV_MAKETYPE(CV_16F,1)
#define CV_16FC2 CV_MAKETYPE(CV_16F,2)
#define CV_16FC3 CV_MAKETYPE(CV_16F,3)
@ -92,6 +91,16 @@ typedef unsigned short ushort;
// cvdef.h:
#ifndef CV_ALWAYS_INLINE
# if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
# define CV_ALWAYS_INLINE inline __attribute__((always_inline))
# elif defined(_MSC_VER)
# define CV_ALWAYS_INLINE __forceinline
# else
# define CV_ALWAYS_INLINE inline
# endif
#endif
#define CV_MAT_CN_MASK ((CV_CN_MAX - 1) << CV_CN_SHIFT)
#define CV_MAT_CN(flags) ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1)
#define CV_MAT_TYPE_MASK (CV_DEPTH_MAX*CV_CN_MAX - 1)

View File

@ -12,10 +12,14 @@
# include <opencv2/core/base.hpp>
# define GAPI_EXPORTS CV_EXPORTS
/* special informative macros for wrapper generators */
# define GAPI_PROP CV_PROP
# define GAPI_PROP_RW CV_PROP_RW
# define GAPI_WRAP CV_WRAP
# define GAPI_EXPORTS_W_SIMPLE CV_EXPORTS_W_SIMPLE
# define GAPI_EXPORTS_W CV_EXPORTS_W
# else
# define GAPI_PROP
# define GAPI_PROP_RW
# define GAPI_WRAP
# define GAPI_EXPORTS
# define GAPI_EXPORTS_W_SIMPLE

View File

@ -11,9 +11,9 @@
#include <math.h>
#include <limits>
#include <type_traits>
#include <opencv2/gapi/own/assert.hpp>
#include <opencv2/gapi/util/type_traits.hpp>
namespace cv { namespace gapi { namespace own {
//-----------------------------
@ -22,16 +22,12 @@ namespace cv { namespace gapi { namespace own {
//
//-----------------------------
template<typename DST, typename SRC>
static inline DST saturate(SRC x)
template<typename DST, typename SRC,
typename = cv::util::enable_if_t<!std::is_same<DST, SRC>::value &&
std::is_integral<DST>::value &&
std::is_integral<SRC>::value> >
static CV_ALWAYS_INLINE DST saturate(SRC x)
{
// only integral types please!
GAPI_DbgAssert(std::is_integral<DST>::value &&
std::is_integral<SRC>::value);
if (std::is_same<DST, SRC>::value)
return static_cast<DST>(x);
if (sizeof(DST) > sizeof(SRC))
return static_cast<DST>(x);
@ -44,38 +40,35 @@ static inline DST saturate(SRC x)
std::numeric_limits<DST>::max():
static_cast<DST>(x);
}
template<typename T>
static CV_ALWAYS_INLINE T saturate(T x)
{
return x;
}
template<typename DST, typename SRC, typename R,
cv::util::enable_if_t<std::is_floating_point<DST>::value, bool> = true >
static CV_ALWAYS_INLINE DST saturate(SRC x, R)
{
return static_cast<DST>(x);
}
template<typename DST, typename SRC, typename R,
cv::util::enable_if_t<std::is_integral<DST>::value &&
std::is_integral<SRC>::value , bool> = true >
static CV_ALWAYS_INLINE DST saturate(SRC x, R)
{
return saturate<DST>(x);
}
// Note, that OpenCV rounds differently:
// - like std::round() for add, subtract
// - like std::rint() for multiply, divide
template<typename DST, typename SRC, typename R>
static inline DST saturate(SRC x, R round)
template<typename DST, typename SRC, typename R,
cv::util::enable_if_t<std::is_integral<DST>::value &&
std::is_floating_point<SRC>::value, bool> = true >
static CV_ALWAYS_INLINE DST saturate(SRC x, R round)
{
if (std::is_floating_point<DST>::value)
{
return static_cast<DST>(x);
}
else if (std::is_integral<SRC>::value)
{
GAPI_DbgAssert(std::is_integral<DST>::value &&
std::is_integral<SRC>::value);
return saturate<DST>(x);
}
else
{
GAPI_DbgAssert(std::is_integral<DST>::value &&
std::is_floating_point<SRC>::value);
#ifdef _WIN32
// Suppress warning about converting x to floating-point
// Note that x is already floating-point at this point
#pragma warning(disable: 4244)
#endif
int ix = static_cast<int>(round(x));
#ifdef _WIN32
#pragma warning(default: 4244)
#endif
return saturate<DST>(ix);
}
int ix = static_cast<int>(round(x));
return saturate<DST>(ix);
}
// explicit suffix 'd' for double type

View File

@ -15,6 +15,11 @@ namespace cv
{
namespace gapi
{
/**
* @brief This namespace contains G-API own data structures used in
* its standalone mode build.
*/
namespace own
{
@ -22,7 +27,7 @@ class Point
{
public:
Point() = default;
Point(int _x, int _y) : x(_x), y(_y) {};
Point(int _x, int _y) : x(_x), y(_y) {}
int x = 0;
int y = 0;
@ -32,7 +37,7 @@ class Point2f
{
public:
Point2f() = default;
Point2f(float _x, float _y) : x(_x), y(_y) {};
Point2f(float _x, float _y) : x(_x), y(_y) {}
float x = 0.f;
float y = 0.f;
@ -42,9 +47,9 @@ class Rect
{
public:
Rect() = default;
Rect(int _x, int _y, int _width, int _height) : x(_x), y(_y), width(_width), height(_height) {};
Rect(int _x, int _y, int _width, int _height) : x(_x), y(_y), width(_width), height(_height) {}
#if !defined(GAPI_STANDALONE)
Rect(const cv::Rect& other) : x(other.x), y(other.y), width(other.width), height(other.height) {};
Rect(const cv::Rect& other) : x(other.x), y(other.y), width(other.width), height(other.height) {}
inline Rect& operator=(const cv::Rect& other)
{
x = other.x;
@ -99,9 +104,9 @@ class Size
{
public:
Size() = default;
Size(int _width, int _height) : width(_width), height(_height) {};
Size(int _width, int _height) : width(_width), height(_height) {}
#if !defined(GAPI_STANDALONE)
Size(const cv::Size& other) : width(other.width), height(other.height) {};
Size(const cv::Size& other) : width(other.width), height(other.height) {}
inline Size& operator=(const cv::Size& rhs)
{
width = rhs.width;
@ -138,6 +143,7 @@ inline std::ostream& operator<<(std::ostream& o, const Size& s)
return o;
}
struct VoidType {};
} // namespace own
} // namespace gapi
} // namespace cv

View File

@ -59,7 +59,7 @@ public:
using F = std::function<void(GPlaidMLContext &)>;
GPlaidMLKernel() = default;
explicit GPlaidMLKernel(const F& f) : m_f(f) {};
explicit GPlaidMLKernel(const F& f) : m_f(f) {}
void apply(GPlaidMLContext &ctx) const
{

View File

@ -15,6 +15,11 @@ namespace cv
{
namespace gapi
{
/**
* @brief This namespace contains G-API PlaidML backend functions,
* structures, and symbols.
*/
namespace plaidml
{

View File

@ -0,0 +1,67 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_PYTHON_API_HPP
#define OPENCV_GAPI_PYTHON_API_HPP
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
namespace cv {
namespace gapi {
/**
* @brief This namespace contains G-API Python backend functions,
* structures, and symbols.
*
* This functionality is required to enable G-API custom operations
* and kernels when using G-API from Python, no need to use it in the
* C++ form.
*/
namespace python {
GAPI_EXPORTS cv::gapi::GBackend backend();
struct GPythonContext
{
const cv::GArgs &ins;
const cv::GMetaArgs &in_metas;
const cv::GTypesInfo &out_info;
};
using Impl = std::function<cv::GRunArgs(const GPythonContext&)>;
class GAPI_EXPORTS GPythonKernel
{
public:
GPythonKernel() = default;
GPythonKernel(Impl run);
cv::GRunArgs operator()(const GPythonContext& ctx);
private:
Impl m_run;
};
class GAPI_EXPORTS GPythonFunctor : public cv::gapi::GFunctor
{
public:
using Meta = cv::GKernel::M;
GPythonFunctor(const char* id, const Meta &meta, const Impl& impl);
GKernelImpl impl() const override;
gapi::GBackend backend() const override;
private:
GKernelImpl impl_;
};
} // namespace python
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_PYTHON_API_HPP

View File

@ -81,9 +81,9 @@ using GMatDesc2 = std::tuple<cv::GMatDesc,cv::GMatDesc>;
@param prims vector of drawing primitivies
@param args graph compile time parameters
*/
void GAPI_EXPORTS render(cv::Mat& bgr,
const Prims& prims,
cv::GCompileArgs&& args = {});
void GAPI_EXPORTS_W render(cv::Mat& bgr,
const Prims& prims,
cv::GCompileArgs&& args = {});
/** @brief The function renders on two NV12 planes passed drawing primitivies
@ -92,11 +92,22 @@ void GAPI_EXPORTS render(cv::Mat& bgr,
@param prims vector of drawing primitivies
@param args graph compile time parameters
*/
void GAPI_EXPORTS render(cv::Mat& y_plane,
cv::Mat& uv_plane,
void GAPI_EXPORTS_W render(cv::Mat& y_plane,
cv::Mat& uv_plane,
const Prims& prims,
cv::GCompileArgs&& args = {});
/** @brief The function renders on the input media frame passed drawing primitivies
@param frame input Media Frame : @ref cv::MediaFrame.
@param prims vector of drawing primitivies
@param args graph compile time parameters
*/
void GAPI_EXPORTS render(cv::MediaFrame& frame,
const Prims& prims,
cv::GCompileArgs&& args = {});
G_TYPED_KERNEL_M(GRenderNV12, <GMat2(cv::GMat,cv::GMat,cv::GArray<wip::draw::Prim>)>, "org.opencv.render.nv12")
{
static GMatDesc2 outMeta(GMatDesc y_plane, GMatDesc uv_plane, GArrayDesc)
@ -113,6 +124,14 @@ G_TYPED_KERNEL(GRenderBGR, <cv::GMat(cv::GMat,cv::GArray<wip::draw::Prim>)>, "or
}
};
G_TYPED_KERNEL(GRenderFrame, <cv::GFrame(cv::GFrame, cv::GArray<wip::draw::Prim>)>, "org.opencv.render.frame")
{
static GFrameDesc outMeta(GFrameDesc desc, GArrayDesc)
{
return desc;
}
};
/** @brief Renders on 3 channels input
Output image must be 8-bit unsigned planar 3-channel image
@ -120,7 +139,7 @@ Output image must be 8-bit unsigned planar 3-channel image
@param src input image: 8-bit unsigned 3-channel image @ref CV_8UC3
@param prims draw primitives
*/
GAPI_EXPORTS GMat render3ch(const GMat& src, const GArray<Prim>& prims);
GAPI_EXPORTS_W GMat render3ch(const GMat& src, const GArray<Prim>& prims);
/** @brief Renders on two planes
@ -131,19 +150,34 @@ uv image must be 8-bit unsigned planar 2-channel image @ref CV_8UC2
@param uv input image: 8-bit unsigned 2-channel image @ref CV_8UC2
@param prims draw primitives
*/
GAPI_EXPORTS GMat2 renderNV12(const GMat& y,
const GMat& uv,
const GArray<Prim>& prims);
GAPI_EXPORTS_W GMat2 renderNV12(const GMat& y,
const GMat& uv,
const GArray<Prim>& prims);
/** @brief Renders Media Frame
Output media frame frame cv::MediaFrame
@param m_frame input image: cv::MediaFrame @ref cv::MediaFrame
@param prims draw primitives
*/
GAPI_EXPORTS GFrame renderFrame(const GFrame& m_frame,
const GArray<Prim>& prims);
//! @} gapi_draw_api
} // namespace draw
} // namespace wip
/**
* @brief This namespace contains G-API CPU rendering backend functions,
* structures, and symbols. See @ref gapi_draw for details.
*/
namespace render
{
namespace ocv
{
GAPI_EXPORTS cv::gapi::GKernelPackage kernels();
GAPI_EXPORTS_W cv::gapi::GKernelPackage kernels();
} // namespace ocv
} // namespace render

View File

@ -41,7 +41,7 @@ struct freetype_font
*
* Parameters match cv::putText().
*/
struct Text
struct GAPI_EXPORTS_W_SIMPLE Text
{
/**
* @brief Text constructor
@ -55,6 +55,7 @@ struct Text
* @param lt_ The line type. See #LineTypes
* @param bottom_left_origin_ When true, the image data origin is at the bottom-left corner. Otherwise, it is at the top-left corner
*/
GAPI_WRAP
Text(const std::string& text_,
const cv::Point& org_,
int ff_,
@ -68,17 +69,18 @@ struct Text
{
}
GAPI_WRAP
Text() = default;
/*@{*/
std::string text; //!< The text string to be drawn
cv::Point org; //!< The bottom-left corner of the text string in the image
int ff; //!< The font type, see #HersheyFonts
double fs; //!< The font scale factor that is multiplied by the font-specific base size
cv::Scalar color; //!< The text color
int thick; //!< The thickness of the lines used to draw a text
int lt; //!< The line type. See #LineTypes
bool bottom_left_origin; //!< When true, the image data origin is at the bottom-left corner. Otherwise, it is at the top-left corner
GAPI_PROP_RW std::string text; //!< The text string to be drawn
GAPI_PROP_RW cv::Point org; //!< The bottom-left corner of the text string in the image
GAPI_PROP_RW int ff; //!< The font type, see #HersheyFonts
GAPI_PROP_RW double fs; //!< The font scale factor that is multiplied by the font-specific base size
GAPI_PROP_RW cv::Scalar color; //!< The text color
GAPI_PROP_RW int thick; //!< The thickness of the lines used to draw a text
GAPI_PROP_RW int lt; //!< The line type. See #LineTypes
GAPI_PROP_RW bool bottom_left_origin; //!< When true, the image data origin is at the bottom-left corner. Otherwise, it is at the top-left corner
/*@{*/
};
@ -122,7 +124,7 @@ struct FText
*
* Parameters match cv::rectangle().
*/
struct Rect
struct GAPI_EXPORTS_W_SIMPLE Rect
{
/**
* @brief Rect constructor
@ -142,14 +144,15 @@ struct Rect
{
}
GAPI_WRAP
Rect() = default;
/*@{*/
cv::Rect rect; //!< Coordinates of the rectangle
cv::Scalar color; //!< The rectangle color or brightness (grayscale image)
int thick; //!< The thickness of lines that make up the rectangle. Negative values, like #FILLED, mean that the function has to draw a filled rectangle
int lt; //!< The type of the line. See #LineTypes
int shift; //!< The number of fractional bits in the point coordinates
GAPI_PROP_RW cv::Rect rect; //!< Coordinates of the rectangle
GAPI_PROP_RW cv::Scalar color; //!< The rectangle color or brightness (grayscale image)
GAPI_PROP_RW int thick; //!< The thickness of lines that make up the rectangle. Negative values, like #FILLED, mean that the function has to draw a filled rectangle
GAPI_PROP_RW int lt; //!< The type of the line. See #LineTypes
GAPI_PROP_RW int shift; //!< The number of fractional bits in the point coordinates
/*@{*/
};
@ -158,7 +161,7 @@ struct Rect
*
* Parameters match cv::circle().
*/
struct Circle
struct GAPI_EXPORTS_W_SIMPLE Circle
{
/**
* @brief Circle constructor
@ -170,6 +173,7 @@ struct Circle
* @param lt_ The Type of the circle boundary. See #LineTypes
* @param shift_ The Number of fractional bits in the coordinates of the center and in the radius value
*/
GAPI_WRAP
Circle(const cv::Point& center_,
int radius_,
const cv::Scalar& color_,
@ -180,15 +184,16 @@ struct Circle
{
}
GAPI_WRAP
Circle() = default;
/*@{*/
cv::Point center; //!< The center of the circle
int radius; //!< The radius of the circle
cv::Scalar color; //!< The color of the circle
int thick; //!< The thickness of the circle outline, if positive. Negative values, like #FILLED, mean that a filled circle is to be drawn
int lt; //!< The Type of the circle boundary. See #LineTypes
int shift; //!< The Number of fractional bits in the coordinates of the center and in the radius value
GAPI_PROP_RW cv::Point center; //!< The center of the circle
GAPI_PROP_RW int radius; //!< The radius of the circle
GAPI_PROP_RW cv::Scalar color; //!< The color of the circle
GAPI_PROP_RW int thick; //!< The thickness of the circle outline, if positive. Negative values, like #FILLED, mean that a filled circle is to be drawn
GAPI_PROP_RW int lt; //!< The Type of the circle boundary. See #LineTypes
GAPI_PROP_RW int shift; //!< The Number of fractional bits in the coordinates of the center and in the radius value
/*@{*/
};
@ -197,7 +202,7 @@ struct Circle
*
* Parameters match cv::line().
*/
struct Line
struct GAPI_EXPORTS_W_SIMPLE Line
{
/**
* @brief Line constructor
@ -209,6 +214,7 @@ struct Line
* @param lt_ The Type of the line. See #LineTypes
* @param shift_ The number of fractional bits in the point coordinates
*/
GAPI_WRAP
Line(const cv::Point& pt1_,
const cv::Point& pt2_,
const cv::Scalar& color_,
@ -219,15 +225,16 @@ struct Line
{
}
GAPI_WRAP
Line() = default;
/*@{*/
cv::Point pt1; //!< The first point of the line segment
cv::Point pt2; //!< The second point of the line segment
cv::Scalar color; //!< The line color
int thick; //!< The thickness of line
int lt; //!< The Type of the line. See #LineTypes
int shift; //!< The number of fractional bits in the point coordinates
GAPI_PROP_RW cv::Point pt1; //!< The first point of the line segment
GAPI_PROP_RW cv::Point pt2; //!< The second point of the line segment
GAPI_PROP_RW cv::Scalar color; //!< The line color
GAPI_PROP_RW int thick; //!< The thickness of line
GAPI_PROP_RW int lt; //!< The Type of the line. See #LineTypes
GAPI_PROP_RW int shift; //!< The number of fractional bits in the point coordinates
/*@{*/
};
@ -236,7 +243,7 @@ struct Line
*
* Mosaicing is a very basic method to obfuscate regions in the image.
*/
struct Mosaic
struct GAPI_EXPORTS_W_SIMPLE Mosaic
{
/**
* @brief Mosaic constructor
@ -252,12 +259,13 @@ struct Mosaic
{
}
GAPI_WRAP
Mosaic() : cellSz(0), decim(0) {}
/*@{*/
cv::Rect mos; //!< Coordinates of the mosaic
int cellSz; //!< Cell size (same for X, Y)
int decim; //!< Decimation (0 stands for no decimation)
GAPI_PROP_RW cv::Rect mos; //!< Coordinates of the mosaic
GAPI_PROP_RW int cellSz; //!< Cell size (same for X, Y)
GAPI_PROP_RW int decim; //!< Decimation (0 stands for no decimation)
/*@{*/
};
@ -266,7 +274,7 @@ struct Mosaic
*
* Image is blended on a frame using the specified mask.
*/
struct Image
struct GAPI_EXPORTS_W_SIMPLE Image
{
/**
* @brief Mosaic constructor
@ -275,6 +283,7 @@ struct Image
* @param img_ Image to draw
* @param alpha_ Alpha channel for image to draw (same size and number of channels)
*/
GAPI_WRAP
Image(const cv::Point& org_,
const cv::Mat& img_,
const cv::Mat& alpha_) :
@ -282,19 +291,20 @@ struct Image
{
}
GAPI_WRAP
Image() = default;
/*@{*/
cv::Point org; //!< The bottom-left corner of the image
cv::Mat img; //!< Image to draw
cv::Mat alpha; //!< Alpha channel for image to draw (same size and number of channels)
GAPI_PROP_RW cv::Point org; //!< The bottom-left corner of the image
GAPI_PROP_RW cv::Mat img; //!< Image to draw
GAPI_PROP_RW cv::Mat alpha; //!< Alpha channel for image to draw (same size and number of channels)
/*@{*/
};
/**
* @brief This structure represents a polygon to draw.
*/
struct Poly
struct GAPI_EXPORTS_W_SIMPLE Poly
{
/**
* @brief Mosaic constructor
@ -305,6 +315,7 @@ struct Poly
* @param lt_ The Type of the line. See #LineTypes
* @param shift_ The number of fractional bits in the point coordinate
*/
GAPI_WRAP
Poly(const std::vector<cv::Point>& points_,
const cv::Scalar& color_,
int thick_ = 1,
@ -314,14 +325,15 @@ struct Poly
{
}
GAPI_WRAP
Poly() = default;
/*@{*/
std::vector<cv::Point> points; //!< Points to connect
cv::Scalar color; //!< The line color
int thick; //!< The thickness of line
int lt; //!< The Type of the line. See #LineTypes
int shift; //!< The number of fractional bits in the point coordinate
GAPI_PROP_RW std::vector<cv::Point> points; //!< Points to connect
GAPI_PROP_RW cv::Scalar color; //!< The line color
GAPI_PROP_RW int thick; //!< The thickness of line
GAPI_PROP_RW int lt; //!< The Type of the line. See #LineTypes
GAPI_PROP_RW int shift; //!< The number of fractional bits in the point coordinate
/*@{*/
};
@ -336,7 +348,7 @@ using Prim = util::variant
, Poly
>;
using Prims = std::vector<Prim>;
using Prims = std::vector<Prim>;
//! @} gapi_draw_prims
} // namespace draw

View File

@ -14,8 +14,8 @@
namespace cv {
namespace gapi {
namespace s11n {
struct IOStream;
struct IIStream;
struct IOStream;
struct IIStream;
} // namespace s11n
} // namespace gapi
} // namespace cv
@ -25,11 +25,11 @@ namespace cv {
// "Remote Mat", a general class which provides an abstraction layer over the data
// storage and placement (host, remote device etc) and allows to access this data.
//
// The device specific implementation is hidden in the RMat::Adapter class
// The device specific implementation is hidden in the RMat::IAdapter class
//
// The basic flow is the following:
// * Backend which is aware of the remote device:
// - Implements own AdapterT class which is derived from RMat::Adapter
// - Implements own AdapterT class which is derived from RMat::IAdapter
// - Wraps device memory into RMat via make_rmat utility function:
// cv::RMat rmat = cv::make_rmat<AdapterT>(args);
//
@ -42,6 +42,9 @@ namespace cv {
// performCalculations(in_view, out_view);
// // data from out_view is transferred to the device when out_view is destroyed
// }
/** \addtogroup gapi_data_structures
* @{
*/
class GAPI_EXPORTS RMat
{
public:
@ -98,23 +101,27 @@ public:
};
enum class Access { R, W };
class Adapter
class IAdapter
// Adapter class is going to be deleted and renamed as IAdapter
{
public:
virtual ~Adapter() = default;
virtual ~IAdapter() = default;
virtual GMatDesc desc() const = 0;
// Implementation is responsible for setting the appropriate callback to
// the view when accessed for writing, to ensure that the data from the view
// is transferred to the device when the view is destroyed
virtual View access(Access) = 0;
virtual void serialize(cv::gapi::s11n::IOStream&) {
GAPI_Assert(false && "Generic serialize method should never be called for RMat adapter");
GAPI_Assert(false && "Generic serialize method of RMat::IAdapter does nothing by default. "
"Please, implement it in derived class to properly serialize the object.");
}
virtual void deserialize(cv::gapi::s11n::IIStream&) {
GAPI_Assert(false && "Generic deserialize method should never be called for RMat adapter");
GAPI_Assert(false && "Generic deserialize method of RMat::IAdapter does nothing by default. "
"Please, implement it in derived class to properly deserialize the object.");
}
};
using AdapterP = std::shared_ptr<Adapter>;
using Adapter = IAdapter; // Keep backward compatibility
using AdapterP = std::shared_ptr<IAdapter>;
RMat() = default;
RMat(AdapterP&& a) : m_adapter(std::move(a)) {}
@ -131,7 +138,7 @@ public:
// return nullptr if underlying type is different
template<typename T> T* get() const
{
static_assert(std::is_base_of<Adapter, T>::value, "T is not derived from Adapter!");
static_assert(std::is_base_of<IAdapter, T>::value, "T is not derived from IAdapter!");
GAPI_Assert(m_adapter != nullptr);
return dynamic_cast<T*>(m_adapter.get());
}
@ -146,6 +153,7 @@ private:
template<typename T, typename... Ts>
RMat make_rmat(Ts&&... args) { return { std::make_shared<T>(std::forward<Ts>(args)...) }; }
/** @} */
} //namespace cv

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
#ifndef OPENCV_GAPI_S11N_HPP
#define OPENCV_GAPI_S11N_HPP
@ -13,61 +13,145 @@
#include <opencv2/gapi/s11n/base.hpp>
#include <opencv2/gapi/gcomputation.hpp>
#include <opencv2/gapi/rmat.hpp>
#include <opencv2/gapi/media.hpp>
#include <opencv2/gapi/util/util.hpp>
// FIXME: caused by deserialize_runarg
#if (defined _WIN32 || defined _WIN64) && defined _MSC_VER
#pragma warning(disable: 4702)
#endif
namespace cv {
namespace gapi {
/**
* \addtogroup gapi_serialization
* @{
*/
namespace detail {
GAPI_EXPORTS cv::GComputation getGraph(const std::vector<char> &p);
GAPI_EXPORTS cv::GComputation getGraph(const std::vector<char> &bytes);
GAPI_EXPORTS cv::GMetaArgs getMetaArgs(const std::vector<char> &p);
GAPI_EXPORTS cv::GMetaArgs getMetaArgs(const std::vector<char> &bytes);
GAPI_EXPORTS cv::GRunArgs getRunArgs(const std::vector<char> &p);
GAPI_EXPORTS cv::GRunArgs getRunArgs(const std::vector<char> &bytes);
GAPI_EXPORTS std::vector<std::string> getVectorOfStrings(const std::vector<char> &bytes);
template<typename... Types>
cv::GCompileArgs getCompileArgs(const std::vector<char> &p);
cv::GCompileArgs getCompileArgs(const std::vector<char> &bytes);
template<typename RMatAdapterType>
cv::GRunArgs getRunArgsWithRMats(const std::vector<char> &p);
template<typename... AdapterType>
cv::GRunArgs getRunArgsWithAdapters(const std::vector<char> &bytes);
} // namespace detail
/** @brief Serialize a graph represented by GComputation into an array of bytes.
*
* Check different overloads for more examples.
* @param c GComputation to serialize.
* @return serialized vector of bytes.
*/
GAPI_EXPORTS std::vector<char> serialize(const cv::GComputation &c);
//namespace{
/** @overload
* @param ca GCompileArgs to serialize.
*/
GAPI_EXPORTS std::vector<char> serialize(const cv::GCompileArgs& ca);
/** @overload
* @param ma GMetaArgs to serialize.
*/
GAPI_EXPORTS std::vector<char> serialize(const cv::GMetaArgs& ma);
/** @overload
* @param ra GRunArgs to serialize.
*/
GAPI_EXPORTS std::vector<char> serialize(const cv::GRunArgs& ra);
/** @overload
* @param vs std::vector<std::string> to serialize.
*/
GAPI_EXPORTS std::vector<char> serialize(const std::vector<std::string>& vs);
/**
* @private
*/
template<typename T> static inline
T deserialize(const std::vector<char> &p);
//} //ananymous namespace
GAPI_EXPORTS std::vector<char> serialize(const cv::GCompileArgs&);
GAPI_EXPORTS std::vector<char> serialize(const cv::GMetaArgs&);
GAPI_EXPORTS std::vector<char> serialize(const cv::GRunArgs&);
T deserialize(const std::vector<char> &bytes);
/** @brief Deserialize GComputation from a byte array.
*
* Check different overloads for more examples.
* @param bytes serialized vector of bytes.
* @return deserialized GComputation object.
*/
template<> inline
cv::GComputation deserialize(const std::vector<char> &p) {
return detail::getGraph(p);
cv::GComputation deserialize(const std::vector<char> &bytes) {
return detail::getGraph(bytes);
}
/** @brief Deserialize GMetaArgs from a byte array.
*
* Check different overloads for more examples.
* @param bytes serialized vector of bytes.
* @return deserialized GMetaArgs object.
*/
template<> inline
cv::GMetaArgs deserialize(const std::vector<char> &p) {
return detail::getMetaArgs(p);
cv::GMetaArgs deserialize(const std::vector<char> &bytes) {
return detail::getMetaArgs(bytes);
}
/** @brief Deserialize GRunArgs from a byte array.
*
* Check different overloads for more examples.
* @param bytes serialized vector of bytes.
* @return deserialized GRunArgs object.
*/
template<> inline
cv::GRunArgs deserialize(const std::vector<char> &p) {
return detail::getRunArgs(p);
cv::GRunArgs deserialize(const std::vector<char> &bytes) {
return detail::getRunArgs(bytes);
}
/** @brief Deserialize std::vector<std::string> from a byte array.
*
* Check different overloads for more examples.
* @param bytes serialized vector of bytes.
* @return deserialized std::vector<std::string> object.
*/
template<> inline
std::vector<std::string> deserialize(const std::vector<char> &bytes) {
return detail::getVectorOfStrings(bytes);
}
/**
* @brief Deserialize GCompileArgs which types were specified in the template from a byte array.
*
* @note cv::gapi::s11n::detail::S11N template specialization must be provided to make a custom type
* in GCompileArgs deserializable.
*
* @param bytes vector of bytes to deserialize GCompileArgs object from.
* @return GCompileArgs object.
* @see GCompileArgs cv::gapi::s11n::detail::S11N
*/
template<typename T, typename... Types> inline
typename std::enable_if<std::is_same<T, GCompileArgs>::value, GCompileArgs>::
type deserialize(const std::vector<char> &p) {
return detail::getCompileArgs<Types...>(p);
type deserialize(const std::vector<char> &bytes) {
return detail::getCompileArgs<Types...>(bytes);
}
template<typename T, typename RMatAdapterType> inline
/**
* @brief Deserialize GRunArgs including RMat and MediaFrame objects if any from a byte array.
*
* Adapter types are specified in the template.
* @note To be used properly specified adapter types must overload their deserialize() method.
* @param bytes vector of bytes to deserialize GRunArgs object from.
* @return GRunArgs including RMat and MediaFrame objects if any.
* @see RMat MediaFrame
*/
template<typename T, typename AtLeastOneAdapterT, typename... AdapterTypes> inline
typename std::enable_if<std::is_same<T, GRunArgs>::value, GRunArgs>::
type deserialize(const std::vector<char> &p) {
return detail::getRunArgsWithRMats<RMatAdapterType>(p);
type deserialize(const std::vector<char> &bytes) {
return detail::getRunArgsWithAdapters<AtLeastOneAdapterT, AdapterTypes...>(bytes);
}
} // namespace gapi
} // namespace cv
@ -75,6 +159,17 @@ type deserialize(const std::vector<char> &p) {
namespace cv {
namespace gapi {
namespace s11n {
/** @brief This structure is an interface for serialization routines.
*
* It's main purpose is to provide multiple overloads for operator<<()
* with basic C++ in addition to OpenCV/G-API types.
*
* This sctructure can be inherited and further extended with additional types.
*
* For example, it is utilized in cv::gapi::s11n::detail::S11N as input parameter
* in serialize() method.
*/
struct GAPI_EXPORTS IOStream {
virtual ~IOStream() = default;
// Define the native support for basic C++ types at the API level:
@ -91,6 +186,16 @@ struct GAPI_EXPORTS IOStream {
virtual IOStream& operator<< (const std::string&) = 0;
};
/** @brief This structure is an interface for deserialization routines.
*
* It's main purpose is to provide multiple overloads for operator>>()
* with basic C++ in addition to OpenCV/G-API types.
*
* This structure can be inherited and further extended with additional types.
*
* For example, it is utilized in cv::gapi::s11n::detail::S11N as input parameter
* in deserialize() method.
*/
struct GAPI_EXPORTS IIStream {
virtual ~IIStream() = default;
virtual IIStream& operator>> (bool &) = 0;
@ -108,7 +213,7 @@ struct GAPI_EXPORTS IIStream {
};
namespace detail {
GAPI_EXPORTS std::unique_ptr<IIStream> getInStream(const std::vector<char> &p);
GAPI_EXPORTS std::unique_ptr<IIStream> getInStream(const std::vector<char> &bytes);
} // namespace detail
////////////////////////////////////////////////////////////////////////////////
@ -138,24 +243,26 @@ GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::Mat &m);
// FIXME: for GRunArgs serailization
#if !defined(GAPI_STANDALONE)
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::UMat &);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::UMat &);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::UMat & um);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::UMat & um);
#endif // !defined(GAPI_STANDALONE)
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::RMat &r);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::RMat &r);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::gapi::wip::IStreamSource::Ptr &);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::gapi::wip::IStreamSource::Ptr &);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::gapi::wip::IStreamSource::Ptr &issptr);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::gapi::wip::IStreamSource::Ptr &issptr);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::detail::VectorRef &);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::detail::VectorRef &);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::detail::VectorRef &vr);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::detail::VectorRef &vr);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::detail::OpaqueRef &);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::detail::OpaqueRef &);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::detail::OpaqueRef &opr);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::detail::OpaqueRef &opr);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::MediaFrame &);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::MediaFrame &);
/// @private -- Exclude this function from OpenCV documentation
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::MediaFrame &mf);
/// @private -- Exclude this function from OpenCV documentation
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::MediaFrame &mf);
// Generic STL types ////////////////////////////////////////////////////////////////
template<typename K, typename V>
@ -178,6 +285,7 @@ IIStream& operator>> (IIStream& is, std::map<K, V> &m) {
}
return is;
}
template<typename K, typename V>
IOStream& operator<< (IOStream& os, const std::unordered_map<K, V> &m) {
const uint32_t sz = static_cast<uint32_t>(m.size());
@ -198,6 +306,7 @@ IIStream& operator>> (IIStream& is, std::unordered_map<K, V> &m) {
}
return is;
}
template<typename T>
IOStream& operator<< (IOStream& os, const std::vector<T> &ts) {
const uint32_t sz = static_cast<uint32_t>(ts.size());
@ -225,16 +334,19 @@ template<typename V>
IOStream& put_v(IOStream&, const V&, std::size_t) {
GAPI_Assert(false && "variant>>: requested index is invalid");
};
template<typename V, typename X, typename... Xs>
IOStream& put_v(IOStream& os, const V& v, std::size_t x) {
return (x == 0u)
? os << cv::util::get<X>(v)
: put_v<V, Xs...>(os, v, x-1);
}
template<typename V>
IIStream& get_v(IIStream&, V&, std::size_t, std::size_t) {
GAPI_Assert(false && "variant<<: requested index is invalid");
}
template<typename V, typename X, typename... Xs>
IIStream& get_v(IIStream& is, V& v, std::size_t i, std::size_t gi) {
if (i == gi) {
@ -246,11 +358,13 @@ IIStream& get_v(IIStream& is, V& v, std::size_t i, std::size_t gi) {
}
} // namespace detail
//! @overload
template<typename... Ts>
IOStream& operator<< (IOStream& os, const cv::util::variant<Ts...> &v) {
os << static_cast<uint32_t>(v.index());
return detail::put_v<cv::util::variant<Ts...>, Ts...>(os, v, v.index());
}
//! @overload
template<typename... Ts>
IIStream& operator>> (IIStream& is, cv::util::variant<Ts...> &v) {
int idx = -1;
@ -260,6 +374,7 @@ IIStream& operator>> (IIStream& is, cv::util::variant<Ts...> &v) {
}
// FIXME: consider a better solution
/// @private -- Exclude this function from OpenCV documentation
template<typename... Ts>
void getRunArgByIdx (IIStream& is, cv::util::variant<Ts...> &v, uint32_t idx) {
is = detail::get_v<cv::util::variant<Ts...>, Ts...>(is, v, 0u, idx);
@ -290,16 +405,39 @@ static cv::util::optional<GCompileArg> exec(const std::string& tag, cv::gapi::s1
}
};
template<typename T> struct deserialize_runarg;
template<typename ...T>
struct deserialize_arg_with_adapter;
template<typename RMatAdapterType>
template<typename RA, typename TA>
struct deserialize_arg_with_adapter<RA, TA> {
static GRunArg exec(cv::gapi::s11n::IIStream& is) {
std::unique_ptr<TA> ptr(new TA);
ptr->deserialize(is);
return GRunArg { RA(std::move(ptr)) };
}
};
template<typename RA>
struct deserialize_arg_with_adapter<RA, void> {
static GRunArg exec(cv::gapi::s11n::IIStream&) {
GAPI_Assert(false && "No suitable adapter class found during RMat/MediaFrame deserialization. "
"Please, make sure you've passed them in cv::gapi::deserialize() template");
return GRunArg{};
}
};
template<typename... Types>
struct deserialize_runarg {
static GRunArg exec(cv::gapi::s11n::IIStream& is, uint32_t idx) {
if (idx == GRunArg::index_of<RMat>()) {
auto ptr = std::make_shared<RMatAdapterType>();
ptr->deserialize(is);
return GRunArg { RMat(std::move(ptr)) };
} else { // non-RMat arg - use default deserialization
// Type or void (if not found)
using TA = typename cv::util::find_adapter_impl<RMat::IAdapter, Types...>::type;
return deserialize_arg_with_adapter<RMat, TA>::exec(is);
} else if (idx == GRunArg::index_of<MediaFrame>()) {
// Type or void (if not found)
using TA = typename cv::util::find_adapter_impl<MediaFrame::IAdapter, Types...>::type;
return deserialize_arg_with_adapter<MediaFrame, TA>::exec(is);
} else { // not an adapter holding type runarg - use default deserialization
GRunArg arg;
getRunArgByIdx(is, arg, idx);
return arg;
@ -342,9 +480,9 @@ cv::GCompileArgs getCompileArgs(const std::vector<char> &sArgs) {
return args;
}
template<typename RMatAdapterType>
cv::GRunArgs getRunArgsWithRMats(const std::vector<char> &p) {
std::unique_ptr<cv::gapi::s11n::IIStream> pIs = cv::gapi::s11n::detail::getInStream(p);
template<typename... AdapterTypes>
cv::GRunArgs getRunArgsWithAdapters(const std::vector<char> &bytes) {
std::unique_ptr<cv::gapi::s11n::IIStream> pIs = cv::gapi::s11n::detail::getInStream(bytes);
cv::gapi::s11n::IIStream& is = *pIs;
cv::GRunArgs args;
@ -353,12 +491,14 @@ cv::GRunArgs getRunArgsWithRMats(const std::vector<char> &p) {
for (uint32_t i = 0; i < sz; ++i) {
uint32_t idx = 0;
is >> idx;
args.push_back(cv::gapi::detail::deserialize_runarg<RMatAdapterType>::exec(is, idx));
args.push_back(cv::gapi::detail::deserialize_runarg<AdapterTypes...>::exec(is, idx));
}
return args;
}
} // namespace detail
/** @} */
} // namespace gapi
} // namespace cv

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
#ifndef OPENCV_GAPI_S11N_BASE_HPP
#define OPENCV_GAPI_S11N_BASE_HPP
@ -12,31 +12,65 @@
namespace cv {
namespace gapi {
/**
* @brief This namespace contains G-API serialization and
* deserialization functions and data structures.
*/
namespace s11n {
struct IOStream;
struct IIStream;
namespace detail {
//! @addtogroup gapi_serialization
//! @{
struct NotImplemented {
};
// The default S11N for custom types is NotImplemented
// Don't! sublass from NotImplemented if you actually implement S11N.
/** @brief This structure allows to implement serialization routines for custom types.
*
* The default S11N for custom types is not implemented.
*
* @note When providing an overloaded implementation for S11N with your type
* don't inherit it from NotImplemented structure.
*
* @note There are lots of overloaded >> and << operators for basic and OpenCV/G-API types
* which can be utilized when serializing a custom type.
*
* Example of usage:
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp S11N usage
*
*/
template<typename T>
struct S11N: public NotImplemented {
/**
* @brief This function allows user to serialize their custom type.
*
* @note The default overload throws an exception if called. User need to
* properly overload the function to use it.
*/
static void serialize(IOStream &, const T &) {
GAPI_Assert(false && "No serialization routine is provided!");
}
/**
* @brief This function allows user to deserialize their custom type.
*
* @note The default overload throws an exception if called. User need to
* properly overload the function to use it.
*/
static T deserialize(IIStream &) {
GAPI_Assert(false && "No deserialization routine is provided!");
}
};
/// @private -- Exclude this struct from OpenCV documentation
template<typename T> struct has_S11N_spec {
static constexpr bool value = !std::is_base_of<NotImplemented,
S11N<typename std::decay<T>::type>>::value;
};
//! @} gapi_serialization
} // namespace detail
} // namespace s11n

View File

@ -0,0 +1,85 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distereoibution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_STEREO_HPP
#define OPENCV_GAPI_STEREO_HPP
#include <opencv2/gapi/gmat.hpp>
#include <opencv2/gapi/gscalar.hpp>
#include <opencv2/gapi/gkernel.hpp>
namespace cv {
namespace gapi {
/**
* The enum specified format of result that you get from @ref cv::gapi::stereo.
*/
enum class StereoOutputFormat {
DEPTH_FLOAT16, ///< Floating point 16 bit value, CV_16FC1.
///< This identifier is deprecated, use DEPTH_16F instead.
DEPTH_FLOAT32, ///< Floating point 32 bit value, CV_32FC1
///< This identifier is deprecated, use DEPTH_16F instead.
DISPARITY_FIXED16_11_5, ///< 16 bit signed: first bit for sign,
///< 10 bits for integer part,
///< 5 bits for fractional part.
///< This identifier is deprecated,
///< use DISPARITY_16Q_10_5 instead.
DISPARITY_FIXED16_12_4, ///< 16 bit signed: first bit for sign,
///< 11 bits for integer part,
///< 4 bits for fractional part.
///< This identifier is deprecated,
///< use DISPARITY_16Q_11_4 instead.
DEPTH_16F = DEPTH_FLOAT16, ///< Same as DEPTH_FLOAT16
DEPTH_32F = DEPTH_FLOAT32, ///< Same as DEPTH_FLOAT32
DISPARITY_16Q_10_5 = DISPARITY_FIXED16_11_5, ///< Same as DISPARITY_FIXED16_11_5
DISPARITY_16Q_11_4 = DISPARITY_FIXED16_12_4 ///< Same as DISPARITY_FIXED16_12_4
};
/**
* @brief This namespace contains G-API Operation Types for Stereo and
* related functionality.
*/
namespace calib3d {
G_TYPED_KERNEL(GStereo, <GMat(GMat, GMat, const StereoOutputFormat)>, "org.opencv.stereo") {
static GMatDesc outMeta(const GMatDesc &left, const GMatDesc &right, const StereoOutputFormat of) {
GAPI_Assert(left.chan == 1);
GAPI_Assert(left.depth == CV_8U);
GAPI_Assert(right.chan == 1);
GAPI_Assert(right.depth == CV_8U);
switch(of) {
case StereoOutputFormat::DEPTH_FLOAT16:
return left.withDepth(CV_16FC1);
case StereoOutputFormat::DEPTH_FLOAT32:
return left.withDepth(CV_32FC1);
case StereoOutputFormat::DISPARITY_FIXED16_11_5:
case StereoOutputFormat::DISPARITY_FIXED16_12_4:
return left.withDepth(CV_16SC1);
default:
GAPI_Assert(false && "Unknown output format!");
}
}
};
} // namespace calib3d
/** @brief Computes disparity/depth map for the specified stereo-pair.
The function computes disparity or depth map depending on passed StereoOutputFormat argument.
@param left 8-bit single-channel left image of @ref CV_8UC1 type.
@param right 8-bit single-channel right image of @ref CV_8UC1 type.
@param of enum to specified output kind: depth or disparity and corresponding type
*/
GAPI_EXPORTS GMat stereo(const GMat& left,
const GMat& right,
const StereoOutputFormat of = StereoOutputFormat::DEPTH_FLOAT32);
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_STEREO_HPP

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
#ifndef OPENCV_GAPI_GSTREAMING_DESYNC_HPP
@ -73,9 +73,10 @@ G desync(const G &g) {
* which produces an array of cv::util::optional<> objects.
*
* @note This feature is highly experimental now and is currently
* limited to a single GMat argument only.
* limited to a single GMat/GFrame argument only.
*/
GAPI_EXPORTS GMat desync(const GMat &g);
GAPI_EXPORTS GFrame desync(const GFrame &f);
} // namespace streaming
} // namespace gapi

View File

@ -20,6 +20,19 @@ G_API_OP(GBGR, <GMat(GFrame)>, "org.opencv.streaming.BGR")
static GMatDesc outMeta(const GFrameDesc& in) { return GMatDesc{CV_8U, 3, in.size}; }
};
G_API_OP(GY, <GMat(GFrame)>, "org.opencv.streaming.Y") {
static GMatDesc outMeta(const GFrameDesc& frameDesc) {
return GMatDesc { CV_8U, 1, frameDesc.size , false };
}
};
G_API_OP(GUV, <GMat(GFrame)>, "org.opencv.streaming.UV") {
static GMatDesc outMeta(const GFrameDesc& frameDesc) {
return GMatDesc { CV_8U, 2, cv::Size(frameDesc.size.width / 2, frameDesc.size.height / 2),
false };
}
};
/** @brief Gets bgr plane from input frame
@note Function textual ID is "org.opencv.streaming.BGR"
@ -29,6 +42,25 @@ G_API_OP(GBGR, <GMat(GFrame)>, "org.opencv.streaming.BGR")
*/
GAPI_EXPORTS cv::GMat BGR(const cv::GFrame& in);
/** @brief Extracts Y plane from media frame.
Output image is 8-bit 1-channel image of @ref CV_8UC1.
@note Function textual ID is "org.opencv.streaming.Y"
@param frame input media frame.
*/
GAPI_EXPORTS GMat Y(const cv::GFrame& frame);
/** @brief Extracts UV plane from media frame.
Output image is 8-bit 2-channel image of @ref CV_8UC2.
@note Function textual ID is "org.opencv.streaming.UV"
@param frame input media frame.
*/
GAPI_EXPORTS GMat UV(const cv::GFrame& frame);
} // namespace streaming
//! @addtogroup gapi_transform
@ -42,7 +74,7 @@ e.g when graph's input needs to be passed directly to output, like in Streaming
@param in Input image
@return Copy of the input
*/
GAPI_EXPORTS GMat copy(const GMat& in);
GAPI_EXPORTS_W GMat copy(const GMat& in);
/** @brief Makes a copy of the input frame. Note that this copy may be not real
(no actual data copied). Use this function to maintain graph contracts,

View File

@ -0,0 +1,47 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERPIPELINE_HPP
#define OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERPIPELINE_HPP
#include <opencv2/gapi/streaming/gstreamer/gstreamersource.hpp>
#include <opencv2/gapi/own/exports.hpp>
#include <string>
#include <unordered_map>
#include <memory>
namespace cv {
namespace gapi {
namespace wip {
namespace gst {
class GAPI_EXPORTS GStreamerPipeline
{
public:
class Priv;
explicit GStreamerPipeline(const std::string& pipeline);
IStreamSource::Ptr getStreamingSource(const std::string& appsinkName,
const GStreamerSource::OutputType outputType =
GStreamerSource::OutputType::MAT);
virtual ~GStreamerPipeline();
protected:
explicit GStreamerPipeline(std::unique_ptr<Priv> priv);
std::unique_ptr<Priv> m_priv;
};
} // namespace gst
using GStreamerPipeline = gst::GStreamerPipeline;
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERPIPELINE_HPP

View File

@ -0,0 +1,89 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERSOURCE_HPP
#define OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERSOURCE_HPP
#include <opencv2/gapi/streaming/source.hpp>
#include <opencv2/gapi/garg.hpp>
#include <memory>
namespace cv {
namespace gapi {
namespace wip {
namespace gst {
/**
* @brief OpenCV's GStreamer streaming source.
* Streams cv::Mat-s/cv::MediaFrame from passed GStreamer pipeline.
*
* This class implements IStreamSource interface.
*
* To create GStreamerSource instance you need to pass 'pipeline' and, optionally, 'outputType'
* arguments into constructor.
* 'pipeline' should represent GStreamer pipeline in form of textual description.
* Almost any custom pipeline is supported which can be successfully ran via gst-launch.
* The only two limitations are:
* - there should be __one__ appsink element in the pipeline to pass data to OpenCV app.
* Pipeline can actually contain many sink elements, but it must have one and only one
* appsink among them.
*
* - data passed to appsink should be video-frame in NV12 format.
*
* 'outputType' is used to select type of output data to produce: 'cv::MediaFrame' or 'cv::Mat'.
* To produce 'cv::MediaFrame'-s you need to pass 'GStreamerSource::OutputType::FRAME' and,
* correspondingly, 'GStreamerSource::OutputType::MAT' to produce 'cv::Mat'-s.
* Please note, that in the last case, output 'cv::Mat' will be of BGR format, internal conversion
* from NV12 GStreamer data will happen.
* Default value for 'outputType' is 'GStreamerSource::OutputType::MAT'.
*
* @note Stream sources are passed to G-API via shared pointers, so please use gapi::make_src<>
* to create objects and ptr() to pass a GStreamerSource to cv::gin().
*
* @note You need to build OpenCV with GStreamer support to use this class.
*/
class GStreamerPipelineFacade;
class GAPI_EXPORTS GStreamerSource : public IStreamSource
{
public:
class Priv;
// Indicates what type of data should be produced by GStreamerSource: cv::MediaFrame or cv::Mat
enum class OutputType {
FRAME,
MAT
};
GStreamerSource(const std::string& pipeline,
const GStreamerSource::OutputType outputType =
GStreamerSource::OutputType::MAT);
GStreamerSource(std::shared_ptr<GStreamerPipelineFacade> pipeline,
const std::string& appsinkName,
const GStreamerSource::OutputType outputType =
GStreamerSource::OutputType::MAT);
bool pull(cv::gapi::wip::Data& data) override;
GMetaArg descr_of() const override;
~GStreamerSource() override;
protected:
explicit GStreamerSource(std::unique_ptr<Priv> priv);
std::unique_ptr<Priv> m_priv;
};
} // namespace gst
using GStreamerSource = gst::GStreamerSource;
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_STREAMING_GSTREAMER_GSTREAMERSOURCE_HPP

View File

@ -0,0 +1,88 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_STREAMING_ONEVPL_CFG_PARAMS_HPP
#define OPENCV_GAPI_STREAMING_ONEVPL_CFG_PARAMS_HPP
#include <map>
#include <memory>
#include <string>
#include <opencv2/gapi/streaming/source.hpp>
#include <opencv2/gapi/util/variant.hpp>
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
/**
* @brief Public class is using for creation of onevpl::GSource instances.
*
* Class members availaible through methods @ref CfgParam::get_name() and @ref CfgParam::get_value() are used by
* onevpl::GSource inner logic to create or find oneVPL particular implementation
* (software/hardware, specific API version and etc.).
*
* @note Because oneVPL may provide several implementations which are satisfying with multiple (or single one) @ref CfgParam
* criteria therefore it is possible to configure `preferred` parameters. This kind of CfgParams are created
* using `is_major = false` argument in @ref CfgParam::create method and are not used by creating oneVPL particular implementations.
* Instead they fill out a "score table" to select preferrable implementation from available list. Implementation are satisfying
* with most of these optional params would be chosen.
* If no one optional CfgParam params were present then first of available oneVPL implementation would be applied.
* Please get on https://spec.oneapi.io/versions/latest/elements/oneVPL/source/API_ref/VPL_disp_api_func.html?highlight=mfxcreateconfig#mfxsetconfigfilterproperty
* for using OneVPL configuration. In this schema `mfxU8 *name` represents @ref CfgParam::get_name() and
* `mfxVariant value` is @ref CfgParam::get_value()
*/
struct GAPI_EXPORTS CfgParam {
using name_t = std::string;
using value_t = cv::util::variant<uint8_t, int8_t,
uint16_t, int16_t,
uint32_t, int32_t,
uint64_t, int64_t,
float_t,
double_t,
void*,
std::string>;
/**
* Create onevp::GSource configuration parameter.
*
*@param name name of parameter.
*@param value value of parameter.
*@param is_major TRUE if parameter MUST be provided by OneVPL inner implementation, FALSE for optional (for resolve multiple available implementations).
*
*/
template<typename ValueType>
static CfgParam create(const std::string& name, ValueType&& value, bool is_major = true) {
CfgParam param(name, CfgParam::value_t(std::forward<ValueType>(value)), is_major);
return param;
}
struct Priv;
const name_t& get_name() const;
const value_t& get_value() const;
bool is_major() const;
bool operator==(const CfgParam& rhs) const;
bool operator< (const CfgParam& rhs) const;
bool operator!=(const CfgParam& rhs) const;
CfgParam& operator=(const CfgParam& src);
CfgParam& operator=(CfgParam&& src);
CfgParam(const CfgParam& src);
CfgParam(CfgParam&& src);
~CfgParam();
private:
CfgParam(const std::string& param_name, value_t&& param_value, bool is_major_param);
std::shared_ptr<Priv> m_priv;
};
} //namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_STREAMING_ONEVPL_CFG_PARAMS_HPP

View File

@ -0,0 +1,105 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef GAPI_STREAMING_ONEVPL_ONEVPL_DATA_PROVIDER_INTERFACE_HPP
#define GAPI_STREAMING_ONEVPL_ONEVPL_DATA_PROVIDER_INTERFACE_HPP
#include <exception>
#include <memory>
#include <string>
#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
struct GAPI_EXPORTS DataProviderException : public std::exception {
DataProviderException(const std::string& descr);
DataProviderException(std::string&& descr);
virtual ~DataProviderException() = default;
virtual const char* what() const noexcept override;
private:
std::string reason;
};
struct GAPI_EXPORTS DataProviderSystemErrorException final : public DataProviderException {
DataProviderSystemErrorException(int error_code, const std::string& desription = std::string());
~DataProviderSystemErrorException() = default;
};
struct GAPI_EXPORTS DataProviderUnsupportedException final : public DataProviderException {
DataProviderUnsupportedException(const std::string& description);
~DataProviderUnsupportedException() = default;
};
struct GAPI_EXPORTS DataProviderImplementationException : public DataProviderException {
DataProviderImplementationException(const std::string& description);
~DataProviderImplementationException() = default;
};
/**
* @brief Public interface allows to customize extraction of video stream data
* used by onevpl::GSource instead of reading stream from file (by default).
*
* Interface implementation constructor MUST provide consistency and creates fully operable object.
* If error happened implementation MUST throw `DataProviderException` kind exceptions
*
* @note Interface implementation MUST manage stream and other constructed resources by itself to avoid any kind of leak.
* For simple interface implementation example please see `StreamDataProvider` in `tests/streaming/gapi_streaming_tests.cpp`
*/
struct GAPI_EXPORTS IDataProvider {
using Ptr = std::shared_ptr<IDataProvider>;
using mfx_codec_id_type = uint32_t;
/**
* NB: here is supposed to be forward declaration of mfxBitstream
* But according to current oneVPL implementation it is impossible to forward
* declare untagged struct mfxBitstream.
*
* IDataProvider makes sense only for HAVE_VPL is ON and to keep IDataProvider
* interface API/ABI compliant between core library and user application layer
* let's introduce wrapper mfx_bitstream which inherits mfxBitstream in private
* G-API code section and declare forward for wrapper mfx_bitstream here
*/
struct mfx_bitstream;
virtual ~IDataProvider() = default;
/**
* The function is used by onevpl::GSource to extract codec id from data
*
*/
virtual mfx_codec_id_type get_mfx_codec_id() const = 0;
/**
* The function is used by onevpl::GSource to extract binary data stream from @ref IDataProvider
* implementation.
*
* It MUST throw `DataProviderException` kind exceptions in fail cases.
* It MUST return MFX_ERR_MORE_DATA in EOF which considered as not-fail case.
*
* @param in_out_bitsream the input-output reference on MFX bitstream buffer which MUST be empty at the first request
* to allow implementation to allocate it by itself and to return back. Subsequent invocation of `fetch_bitstream_data`
* MUST use the previously used in_out_bitsream to avoid skipping rest of frames which haven't been consumed
* @return true for fetched data, false on EOF and throws exception on error
*/
virtual bool fetch_bitstream_data(std::shared_ptr<mfx_bitstream> &in_out_bitsream) = 0;
/**
* The function is used by onevpl::GSource to check more binary data availability.
*
* It MUST return TRUE in case of EOF and NO_THROW exceptions.
*
* @return boolean value which detects end of stream
*/
virtual bool empty() const = 0;
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // GAPI_STREAMING_ONEVPL_ONEVPL_DATA_PROVIDER_INTERFACE_HPP

View File

@ -0,0 +1,102 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef GAPI_STREAMING_ONEVPL_DEVICE_SELECTOR_INTERFACE_HPP
#define GAPI_STREAMING_ONEVPL_DEVICE_SELECTOR_INTERFACE_HPP
#include <limits>
#include <map>
#include <string>
#include <vector>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
enum class AccelType: uint8_t {
HOST,
DX11,
LAST_VALUE = std::numeric_limits<uint8_t>::max()
};
GAPI_EXPORTS const char* to_cstring(AccelType type);
struct IDeviceSelector;
struct GAPI_EXPORTS Device {
friend struct IDeviceSelector;
using Ptr = void*;
~Device();
const std::string& get_name() const;
Ptr get_ptr() const;
AccelType get_type() const;
private:
Device(Ptr device_ptr, const std::string& device_name,
AccelType device_type);
std::string name;
Ptr ptr;
AccelType type;
};
struct GAPI_EXPORTS Context {
friend struct IDeviceSelector;
using Ptr = void*;
~Context();
Ptr get_ptr() const;
AccelType get_type() const;
private:
Context(Ptr ctx_ptr, AccelType ctx_type);
Ptr ptr;
AccelType type;
};
struct GAPI_EXPORTS IDeviceSelector {
using Ptr = std::shared_ptr<IDeviceSelector>;
struct GAPI_EXPORTS Score {
friend struct IDeviceSelector;
using Type = int16_t;
static constexpr Type MaxActivePriority = std::numeric_limits<Type>::max();
static constexpr Type MinActivePriority = 0;
static constexpr Type MaxPassivePriority = MinActivePriority - 1;
static constexpr Type MinPassivePriority = std::numeric_limits<Type>::min();
Score(Type val);
~Score();
operator Type () const;
Type get() const;
friend bool operator< (Score lhs, Score rhs) {
return lhs.get() < rhs.get();
}
private:
Type value;
};
using DeviceScoreTable = std::map<Score, Device>;
using DeviceContexts = std::vector<Context>;
virtual ~IDeviceSelector();
virtual DeviceScoreTable select_devices() const = 0;
virtual DeviceContexts select_context() = 0;
protected:
template<typename Entity, typename ...Args>
static Entity create(Args &&...args) {
return Entity(std::forward<Args>(args)...);
}
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // GAPI_STREAMING_ONEVPL_DEVICE_SELECTOR_INTERFACE_HPP

View File

@ -0,0 +1,90 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_STREAMING_ONEVPL_ONEVPL_SOURCE_HPP
#define OPENCV_GAPI_STREAMING_ONEVPL_ONEVPL_SOURCE_HPP
#include <opencv2/gapi/garg.hpp>
#include <opencv2/gapi/streaming/meta.hpp>
#include <opencv2/gapi/streaming/source.hpp>
#include <opencv2/gapi/streaming/onevpl/cfg_params.hpp>
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp>
#include <opencv2/gapi/streaming/onevpl/device_selector_interface.hpp>
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
using CfgParams = std::vector<CfgParam>;
/**
* @brief G-API streaming source based on OneVPL implementation.
*
* This class implements IStreamSource interface.
* Its constructor takes source file path (in usual way) or @ref onevpl::IDataProvider
* interface implementation (for not file-based sources). It also allows to pass-through
* oneVPL configuration parameters by using several @ref onevpl::CfgParam.
*
* @note stream sources are passed to G-API via shared pointers, so
* please gapi::make_onevpl_src<> to create objects and ptr() to pass a
* GSource to cv::gin().
*/
class GAPI_EXPORTS GSource : public IStreamSource
{
public:
struct Priv;
GSource(const std::string& filePath,
const CfgParams& cfg_params = CfgParams{});
GSource(const std::string& filePath,
const CfgParams& cfg_params,
const std::string& device_id,
void* accel_device_ptr,
void* accel_ctx_ptr);
GSource(const std::string& filePath,
const CfgParams& cfg_params,
std::shared_ptr<IDeviceSelector> selector);
GSource(std::shared_ptr<IDataProvider> source,
const CfgParams& cfg_params = CfgParams{});
GSource(std::shared_ptr<IDataProvider> source,
const CfgParams& cfg_params,
const std::string& device_id,
void* accel_device_ptr,
void* accel_ctx_ptr);
GSource(std::shared_ptr<IDataProvider> source,
const CfgParams& cfg_params,
std::shared_ptr<IDeviceSelector> selector);
~GSource() override;
bool pull(cv::gapi::wip::Data& data) override;
GMetaArg descr_of() const override;
private:
explicit GSource(std::unique_ptr<Priv>&& impl);
std::unique_ptr<Priv> m_priv;
};
} // namespace onevpl
using GVPLSource = onevpl::GSource;
template<class... Args>
GAPI_EXPORTS_W cv::Ptr<IStreamSource> inline make_onevpl_src(Args&&... args)
{
return make_src<onevpl::GSource>(std::forward<Args>(args)...);
}
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_STREAMING_ONEVPL_ONEVPL_SOURCE_HPP

View File

@ -0,0 +1,30 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_STREAMING_SYNC_HPP
#define OPENCV_GAPI_STREAMING_SYNC_HPP
namespace cv {
namespace gapi {
namespace streaming {
enum class sync_policy {
dont_sync,
drop
};
} // namespace streaming
} // namespace gapi
namespace detail {
template<> struct CompileArgTag<gapi::streaming::sync_policy> {
static const char* tag() { return "gapi.streaming.sync_policy"; }
};
} // namespace detail
} // namespace cv
#endif // OPENCV_GAPI_STREAMING_SYNC_HPP

View File

@ -31,7 +31,11 @@ namespace internal
#if defined(__GXX_RTTI) || defined(_CPPRTTI)
return dynamic_cast<T>(operand);
#else
#warning used static cast instead of dynamic because RTTI is disabled
#ifdef __GNUC__
#warning used static cast instead of dynamic because RTTI is disabled
#else
#pragma message("WARNING: used static cast instead of dynamic because RTTI is disabled")
#endif
return static_cast<T>(operand);
#endif
}

View File

@ -33,7 +33,7 @@ namespace util
// Constructors
// NB.: there were issues with Clang 3.8 when =default() was used
// instead {}
optional() {};
optional() {}
optional(const optional&) = default;
explicit optional(T&&) noexcept;
explicit optional(const T&) noexcept;
@ -84,7 +84,7 @@ namespace util
// Implementation //////////////////////////////////////////////////////////
template<class T> optional<T>::optional(T &&v) noexcept
: m_holder(v)
: m_holder(std::move(v))
{
}

View File

@ -116,7 +116,73 @@ namespace detail
using type = std::tuple<Objs...>;
static type get(std::tuple<Objs...>&& objs) { return std::forward<std::tuple<Objs...>>(objs); }
};
template<typename... Ts>
struct make_void { typedef void type;};
template<typename... Ts>
using void_t = typename make_void<Ts...>::type;
} // namespace detail
namespace util
{
template<typename ...L>
struct overload_lamba_set;
template<typename L1>
struct overload_lamba_set<L1> : public L1
{
overload_lamba_set(L1&& lambda) : L1(std::move(lambda)) {}
overload_lamba_set(const L1& lambda) : L1(lambda) {}
using L1::operator();
};
template<typename L1, typename ...L>
struct overload_lamba_set<L1, L...> : public L1, public overload_lamba_set<L...>
{
using base_type = overload_lamba_set<L...>;
overload_lamba_set(L1 &&lambda1, L&& ...lambdas):
L1(std::move(lambda1)),
base_type(std::forward<L>(lambdas)...) {}
overload_lamba_set(const L1 &lambda1, L&& ...lambdas):
L1(lambda1),
base_type(std::forward<L>(lambdas)...) {}
using L1::operator();
using base_type::operator();
};
template<typename... L>
overload_lamba_set<L...> overload_lambdas(L&& ...lambdas)
{
return overload_lamba_set<L...>(std::forward<L>(lambdas)...);
}
template<typename ...T>
struct find_adapter_impl;
template<typename AdapterT, typename T>
struct find_adapter_impl<AdapterT, T>
{
using type = typename std::conditional<std::is_base_of<AdapterT, T>::value,
T,
void>::type;
static constexpr bool found = std::is_base_of<AdapterT, T>::value;
};
template<typename AdapterT, typename T, typename... Types>
struct find_adapter_impl<AdapterT, T, Types...>
{
using type = typename std::conditional<std::is_base_of<AdapterT, T>::value,
T,
typename find_adapter_impl<AdapterT, Types...>::type>::type;
static constexpr bool found = std::is_base_of<AdapterT, T>::value ||
find_adapter_impl<AdapterT, Types...>::found;
};
} // namespace util
} // namespace cv
// \endcond

View File

@ -11,6 +11,7 @@
#include <array>
#include <type_traits>
#include <opencv2/gapi/util/compiler_hints.hpp>
#include <opencv2/gapi/util/throw.hpp>
#include <opencv2/gapi/util/util.hpp> // max_of_t
#include <opencv2/gapi/util/type_traits.hpp>
@ -44,6 +45,12 @@ namespace util
static const constexpr std::size_t value = detail::type_list_index_helper<0, Target, Types...>::value;
};
template<std::size_t Index, class... Types >
struct type_list_element
{
using type = typename std::tuple_element<Index, std::tuple<Types...> >::type;
};
class bad_variant_access: public std::exception
{
public:
@ -233,9 +240,87 @@ namespace util
template<typename T, typename... Types>
const T& get(const util::variant<Types...> &v);
template<std::size_t Index, typename... Types>
typename util::type_list_element<Index, Types...>::type& get(util::variant<Types...> &v);
template<std::size_t Index, typename... Types>
const typename util::type_list_element<Index, Types...>::type& get(const util::variant<Types...> &v);
template<typename T, typename... Types>
bool holds_alternative(const util::variant<Types...> &v) noexcept;
// Visitor
namespace detail
{
struct visitor_interface {};
// Class `visitor_return_type_deduction_helper`
// introduces solution for deduction `return_type` in `visit` function in common way
// for both Lambda and class Visitor and keep one interface invocation point: `visit` only
// his helper class is required to unify return_type deduction mechanism because
// for Lambda it is possible to take type of `decltype(visitor(get<0>(var)))`
// but for class Visitor there is no operator() in base case,
// because it provides `operator() (std::size_t index, ...)`
// So `visitor_return_type_deduction_helper` expose `operator()`
// uses only for class Visitor only for deduction `return type` in visit()
template<typename R>
struct visitor_return_type_deduction_helper
{
using return_type = R;
// to be used in Lambda return type deduction context only
template<typename T>
return_type operator() (T&&);
};
}
// Special purpose `static_visitor` can receive additional arguments
template<typename R, typename Impl>
struct static_visitor : public detail::visitor_interface,
public detail::visitor_return_type_deduction_helper<R> {
// assign responsibility for return type deduction to helper class
using return_type = typename detail::visitor_return_type_deduction_helper<R>::return_type;
using detail::visitor_return_type_deduction_helper<R>::operator();
friend Impl;
template<typename VariantValue, typename ...Args>
return_type operator() (std::size_t index, VariantValue&& value, Args&& ...args)
{
suppress_unused_warning(index);
return static_cast<Impl*>(this)-> visit(
std::forward<VariantValue>(value),
std::forward<Args>(args)...);
}
};
// Special purpose `static_indexed_visitor` can receive additional arguments
// And make forwarding current variant index as runtime function argument to its `Impl`
template<typename R, typename Impl>
struct static_indexed_visitor : public detail::visitor_interface,
public detail::visitor_return_type_deduction_helper<R> {
// assign responsibility for return type deduction to helper class
using return_type = typename detail::visitor_return_type_deduction_helper<R>::return_type;
using detail::visitor_return_type_deduction_helper<R>::operator();
friend Impl;
template<typename VariantValue, typename ...Args>
return_type operator() (std::size_t Index, VariantValue&& value, Args&& ...args)
{
return static_cast<Impl*>(this)-> visit(Index,
std::forward<VariantValue>(value),
std::forward<Args>(args)...);
}
};
template <class T>
struct variant_size;
template <class... Types>
struct variant_size<util::variant<Types...>>
: std::integral_constant<std::size_t, sizeof...(Types)> { };
// FIXME: T&&, const TT&& versions.
// Implementation //////////////////////////////////////////////////////////
@ -402,6 +487,22 @@ namespace util
throw_error(bad_variant_access());
}
template<std::size_t Index, typename... Types>
typename util::type_list_element<Index, Types...>::type& get(util::variant<Types...> &v)
{
using ReturnType = typename util::type_list_element<Index, Types...>::type;
return const_cast<ReturnType&>(get<Index, Types...>(static_cast<const util::variant<Types...> &>(v)));
}
template<std::size_t Index, typename... Types>
const typename util::type_list_element<Index, Types...>::type& get(const util::variant<Types...> &v)
{
static_assert(Index < sizeof...(Types),
"`Index` it out of bound of `util::variant` type list");
using ReturnType = typename util::type_list_element<Index, Types...>::type;
return get<ReturnType>(v);
}
template<typename T, typename... Types>
bool holds_alternative(const util::variant<Types...> &v) noexcept
{
@ -428,7 +529,130 @@ namespace util
{
return !(lhs == rhs);
}
} // namespace cv
namespace detail
{
// terminate recursion implementation for `non-void` ReturnType
template<typename ReturnType, std::size_t CurIndex, std::size_t ElemCount,
typename Visitor, typename Variant, typename... VisitorArgs>
ReturnType apply_visitor_impl(Visitor&&, Variant&,
std::true_type, std::false_type,
VisitorArgs&& ...)
{
return {};
}
// terminate recursion implementation for `void` ReturnType
template<typename ReturnType, std::size_t CurIndex, std::size_t ElemCount,
typename Visitor, typename Variant, typename... VisitorArgs>
void apply_visitor_impl(Visitor&&, Variant&,
std::true_type, std::true_type,
VisitorArgs&& ...)
{
}
// Intermediate resursion processor for Lambda Visitors
template<typename ReturnType, std::size_t CurIndex, std::size_t ElemCount,
typename Visitor, typename Variant, bool no_return_value, typename... VisitorArgs>
typename std::enable_if<!std::is_base_of<visitor_interface, typename std::decay<Visitor>::type>::value, ReturnType>::type
apply_visitor_impl(Visitor&& visitor, Variant&& v, std::false_type not_processed,
std::integral_constant<bool, no_return_value> should_no_return,
VisitorArgs&& ...args)
{
static_assert(std::is_same<ReturnType, decltype(visitor(get<CurIndex>(v)))>::value,
"Different `ReturnType`s detected! All `Visitor::visit` or `overload_lamba_set`"
" must return the same type");
suppress_unused_warning(not_processed);
if (v.index() == CurIndex)
{
return visitor.operator()(get<CurIndex>(v), std::forward<VisitorArgs>(args)... );
}
using is_variant_processed_t = std::integral_constant<bool, CurIndex + 1 >= ElemCount>;
return apply_visitor_impl<ReturnType, CurIndex +1, ElemCount>(
std::forward<Visitor>(visitor),
std::forward<Variant>(v),
is_variant_processed_t{},
should_no_return,
std::forward<VisitorArgs>(args)...);
}
//Visual Studio 2014 compilation fix: cast visitor to base class before invoke operator()
template<std::size_t CurIndex, typename ReturnType, typename Visitor, class Value, typename... VisitorArgs>
typename std::enable_if<std::is_base_of<static_visitor<ReturnType, typename std::decay<Visitor>::type>,
typename std::decay<Visitor>::type>::value, ReturnType>::type
invoke_class_visitor(Visitor& visitor, Value&& v, VisitorArgs&&...args)
{
return static_cast<static_visitor<ReturnType, typename std::decay<Visitor>::type>&>(visitor).operator() (CurIndex, std::forward<Value>(v), std::forward<VisitorArgs>(args)... );
}
//Visual Studio 2014 compilation fix: cast visitor to base class before invoke operator()
template<std::size_t CurIndex, typename ReturnType, typename Visitor, class Value, typename... VisitorArgs>
typename std::enable_if<std::is_base_of<static_indexed_visitor<ReturnType, typename std::decay<Visitor>::type>,
typename std::decay<Visitor>::type>::value, ReturnType>::type
invoke_class_visitor(Visitor& visitor, Value&& v, VisitorArgs&&...args)
{
return static_cast<static_indexed_visitor<ReturnType, typename std::decay<Visitor>::type>&>(visitor).operator() (CurIndex, std::forward<Value>(v), std::forward<VisitorArgs>(args)... );
}
// Intermediate recursion processor for special case `visitor_interface` derived Visitors
template<typename ReturnType, std::size_t CurIndex, std::size_t ElemCount,
typename Visitor, typename Variant, bool no_return_value, typename... VisitorArgs>
typename std::enable_if<std::is_base_of<visitor_interface, typename std::decay<Visitor>::type>::value, ReturnType>::type
apply_visitor_impl(Visitor&& visitor, Variant&& v, std::false_type not_processed,
std::integral_constant<bool, no_return_value> should_no_return,
VisitorArgs&& ...args)
{
static_assert(std::is_same<ReturnType, decltype(visitor(get<CurIndex>(v)))>::value,
"Different `ReturnType`s detected! All `Visitor::visit` or `overload_lamba_set`"
" must return the same type");
suppress_unused_warning(not_processed);
if (v.index() == CurIndex)
{
return invoke_class_visitor<CurIndex, ReturnType>(visitor, get<CurIndex>(v), std::forward<VisitorArgs>(args)... );
}
using is_variant_processed_t = std::integral_constant<bool, CurIndex + 1 >= ElemCount>;
return apply_visitor_impl<ReturnType, CurIndex +1, ElemCount>(
std::forward<Visitor>(visitor),
std::forward<Variant>(v),
is_variant_processed_t{},
should_no_return,
std::forward<VisitorArgs>(args)...);
}
} // namespace detail
template<typename Visitor, typename Variant, typename... VisitorArg>
auto visit(Visitor &visitor, const Variant& var, VisitorArg &&...args) -> decltype(visitor(get<0>(var)))
{
constexpr std::size_t varsize = util::variant_size<Variant>::value;
static_assert(varsize != 0, "utils::variant must contains one type at least ");
using is_variant_processed_t = std::false_type;
using ReturnType = decltype(visitor(get<0>(var)));
using return_t = std::is_same<ReturnType, void>;
return detail::apply_visitor_impl<ReturnType, 0, varsize, Visitor>(
std::forward<Visitor>(visitor),
var, is_variant_processed_t{},
return_t{},
std::forward<VisitorArg>(args)...);
}
template<typename Visitor, typename Variant>
auto visit(Visitor&& visitor, const Variant& var) -> decltype(visitor(get<0>(var)))
{
constexpr std::size_t varsize = util::variant_size<Variant>::value;
static_assert(varsize != 0, "utils::variant must contains one type at least ");
using is_variant_processed_t = std::false_type;
using ReturnType = decltype(visitor(get<0>(var)));
using return_t = std::is_same<ReturnType, void>;
return detail::apply_visitor_impl<ReturnType, 0, varsize, Visitor>(
std::forward<Visitor>(visitor),
var, is_variant_processed_t{},
return_t{});
}
} // namespace util
} // namespace cv
#endif // OPENCV_GAPI_UTIL_VARIANT_HPP

View File

@ -42,6 +42,10 @@ struct GAPI_EXPORTS KalmanParams
Mat controlMatrix;
};
/**
* @brief This namespace contains G-API Operations and functions for
* video-oriented algorithms, like optical flow and background subtraction.
*/
namespace video
{
using GBuildPyrOutput = std::tuple<GArray<GMat>, GScalar>;

View File

@ -0,0 +1,299 @@
__all__ = ['op', 'kernel']
import sys
import cv2 as cv
# NB: Register function in specific module
def register(mname):
def parameterized(func):
sys.modules[mname].__dict__[func.__name__] = func
return func
return parameterized
@register('cv2.gapi')
def networks(*args):
return cv.gapi_GNetPackage(list(map(cv.detail.strip, args)))
@register('cv2.gapi')
def compile_args(*args):
return list(map(cv.GCompileArg, args))
@register('cv2')
def GIn(*args):
return [*args]
@register('cv2')
def GOut(*args):
return [*args]
@register('cv2')
def gin(*args):
return [*args]
@register('cv2.gapi')
def descr_of(*args):
return [*args]
@register('cv2')
class GOpaque():
# NB: Inheritance from c++ class cause segfault.
# So just aggregate cv.GOpaqueT instead of inheritance
def __new__(cls, argtype):
return cv.GOpaqueT(argtype)
class Bool():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_BOOL)
class Int():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_INT)
class Double():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_DOUBLE)
class Float():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_FLOAT)
class String():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_STRING)
class Point():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_POINT)
class Point2f():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_POINT2F)
class Size():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_SIZE)
class Rect():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_RECT)
class Prim():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_DRAW_PRIM)
class Any():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_ANY)
@register('cv2')
class GArray():
# NB: Inheritance from c++ class cause segfault.
# So just aggregate cv.GArrayT instead of inheritance
def __new__(cls, argtype):
return cv.GArrayT(argtype)
class Bool():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_BOOL)
class Int():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_INT)
class Double():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_DOUBLE)
class Float():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_FLOAT)
class String():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_STRING)
class Point():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_POINT)
class Point2f():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_POINT2F)
class Size():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_SIZE)
class Rect():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_RECT)
class Scalar():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_SCALAR)
class Mat():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_MAT)
class GMat():
def __new__(self):
return cv.GArrayT(cv.gapi.CV_GMAT)
class Prim():
def __new__(self):
return cv.GArray(cv.gapi.CV_DRAW_PRIM)
class Any():
def __new__(self):
return cv.GArray(cv.gapi.CV_ANY)
# NB: Top lvl decorator takes arguments
def op(op_id, in_types, out_types):
garray_types= {
cv.GArray.Bool: cv.gapi.CV_BOOL,
cv.GArray.Int: cv.gapi.CV_INT,
cv.GArray.Double: cv.gapi.CV_DOUBLE,
cv.GArray.Float: cv.gapi.CV_FLOAT,
cv.GArray.String: cv.gapi.CV_STRING,
cv.GArray.Point: cv.gapi.CV_POINT,
cv.GArray.Point2f: cv.gapi.CV_POINT2F,
cv.GArray.Size: cv.gapi.CV_SIZE,
cv.GArray.Rect: cv.gapi.CV_RECT,
cv.GArray.Scalar: cv.gapi.CV_SCALAR,
cv.GArray.Mat: cv.gapi.CV_MAT,
cv.GArray.GMat: cv.gapi.CV_GMAT,
cv.GArray.Prim: cv.gapi.CV_DRAW_PRIM,
cv.GArray.Any: cv.gapi.CV_ANY
}
gopaque_types= {
cv.GOpaque.Size: cv.gapi.CV_SIZE,
cv.GOpaque.Rect: cv.gapi.CV_RECT,
cv.GOpaque.Bool: cv.gapi.CV_BOOL,
cv.GOpaque.Int: cv.gapi.CV_INT,
cv.GOpaque.Double: cv.gapi.CV_DOUBLE,
cv.GOpaque.Float: cv.gapi.CV_FLOAT,
cv.GOpaque.String: cv.gapi.CV_STRING,
cv.GOpaque.Point: cv.gapi.CV_POINT,
cv.GOpaque.Point2f: cv.gapi.CV_POINT2F,
cv.GOpaque.Size: cv.gapi.CV_SIZE,
cv.GOpaque.Rect: cv.gapi.CV_RECT,
cv.GOpaque.Prim: cv.gapi.CV_DRAW_PRIM,
cv.GOpaque.Any: cv.gapi.CV_ANY
}
type2str = {
cv.gapi.CV_BOOL: 'cv.gapi.CV_BOOL' ,
cv.gapi.CV_INT: 'cv.gapi.CV_INT' ,
cv.gapi.CV_DOUBLE: 'cv.gapi.CV_DOUBLE' ,
cv.gapi.CV_FLOAT: 'cv.gapi.CV_FLOAT' ,
cv.gapi.CV_STRING: 'cv.gapi.CV_STRING' ,
cv.gapi.CV_POINT: 'cv.gapi.CV_POINT' ,
cv.gapi.CV_POINT2F: 'cv.gapi.CV_POINT2F' ,
cv.gapi.CV_SIZE: 'cv.gapi.CV_SIZE',
cv.gapi.CV_RECT: 'cv.gapi.CV_RECT',
cv.gapi.CV_SCALAR: 'cv.gapi.CV_SCALAR',
cv.gapi.CV_MAT: 'cv.gapi.CV_MAT',
cv.gapi.CV_GMAT: 'cv.gapi.CV_GMAT',
cv.gapi.CV_DRAW_PRIM: 'cv.gapi.CV_DRAW_PRIM'
}
# NB: Second lvl decorator takes class to decorate
def op_with_params(cls):
if not in_types:
raise Exception('{} operation should have at least one input!'.format(cls.__name__))
if not out_types:
raise Exception('{} operation should have at least one output!'.format(cls.__name__))
for i, t in enumerate(out_types):
if t not in [cv.GMat, cv.GScalar, *garray_types, *gopaque_types]:
raise Exception('{} unsupported output type: {} in possition: {}'
.format(cls.__name__, t.__name__, i))
def on(*args):
if len(in_types) != len(args):
raise Exception('Invalid number of input elements!\nExpected: {}, Actual: {}'
.format(len(in_types), len(args)))
for i, (t, a) in enumerate(zip(in_types, args)):
if t in garray_types:
if not isinstance(a, cv.GArrayT):
raise Exception("{} invalid type for argument {}.\nExpected: {}, Actual: {}"
.format(cls.__name__, i, cv.GArrayT.__name__, type(a).__name__))
elif a.type() != garray_types[t]:
raise Exception("{} invalid GArrayT type for argument {}.\nExpected: {}, Actual: {}"
.format(cls.__name__, i, type2str[garray_types[t]], type2str[a.type()]))
elif t in gopaque_types:
if not isinstance(a, cv.GOpaqueT):
raise Exception("{} invalid type for argument {}.\nExpected: {}, Actual: {}"
.format(cls.__name__, i, cv.GOpaqueT.__name__, type(a).__name__))
elif a.type() != gopaque_types[t]:
raise Exception("{} invalid GOpaque type for argument {}.\nExpected: {}, Actual: {}"
.format(cls.__name__, i, type2str[gopaque_types[t]], type2str[a.type()]))
else:
if t != type(a):
raise Exception('{} invalid input type for argument {}.\nExpected: {}, Actual: {}'
.format(cls.__name__, i, t.__name__, type(a).__name__))
op = cv.gapi.__op(op_id, cls.outMeta, *args)
out_protos = []
for i, out_type in enumerate(out_types):
if out_type == cv.GMat:
out_protos.append(op.getGMat())
elif out_type == cv.GScalar:
out_protos.append(op.getGScalar())
elif out_type in gopaque_types:
out_protos.append(op.getGOpaque(gopaque_types[out_type]))
elif out_type in garray_types:
out_protos.append(op.getGArray(garray_types[out_type]))
else:
raise Exception("""In {}: G-API operation can't produce the output with type: {} in position: {}"""
.format(cls.__name__, out_type.__name__, i))
return tuple(out_protos) if len(out_protos) != 1 else out_protos[0]
# NB: Extend operation class
cls.id = op_id
cls.on = staticmethod(on)
return cls
return op_with_params
def kernel(op_cls):
# NB: Second lvl decorator takes class to decorate
def kernel_with_params(cls):
# NB: Add new members to kernel class
cls.id = op_cls.id
cls.outMeta = op_cls.outMeta
return cls
return kernel_with_params
# FIXME: On the c++ side every class is placed in cv2 module.
cv.gapi.wip.draw.Rect = cv.gapi_wip_draw_Rect
cv.gapi.wip.draw.Text = cv.gapi_wip_draw_Text
cv.gapi.wip.draw.Circle = cv.gapi_wip_draw_Circle
cv.gapi.wip.draw.Line = cv.gapi_wip_draw_Line
cv.gapi.wip.draw.Mosaic = cv.gapi_wip_draw_Mosaic
cv.gapi.wip.draw.Image = cv.gapi_wip_draw_Image
cv.gapi.wip.draw.Poly = cv.gapi_wip_draw_Poly
cv.gapi.streaming.queue_capacity = cv.gapi_streaming_queue_capacity

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,338 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_PYTHON_BRIDGE_HPP
#define OPENCV_GAPI_PYTHON_BRIDGE_HPP
#include <opencv2/gapi.hpp>
#include <opencv2/gapi/garg.hpp>
#include <opencv2/gapi/gopaque.hpp>
#include <opencv2/gapi/render/render_types.hpp> // Prim
#define ID(T, E) T
#define ID_(T, E) ID(T, E),
#define WRAP_ARGS(T, E, G) \
G(T, E)
#define SWITCH(type, LIST_G, HC) \
switch(type) { \
LIST_G(HC, HC) \
default: \
GAPI_Assert(false && "Unsupported type"); \
}
using cv::gapi::wip::draw::Prim;
#define GARRAY_TYPE_LIST_G(G, G2) \
WRAP_ARGS(bool , cv::gapi::ArgType::CV_BOOL, G) \
WRAP_ARGS(int , cv::gapi::ArgType::CV_INT, G) \
WRAP_ARGS(int64_t , cv::gapi::ArgType::CV_INT64, G) \
WRAP_ARGS(double , cv::gapi::ArgType::CV_DOUBLE, G) \
WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \
WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \
WRAP_ARGS(cv::Point , cv::gapi::ArgType::CV_POINT, G) \
WRAP_ARGS(cv::Point2f , cv::gapi::ArgType::CV_POINT2F, G) \
WRAP_ARGS(cv::Size , cv::gapi::ArgType::CV_SIZE, G) \
WRAP_ARGS(cv::Rect , cv::gapi::ArgType::CV_RECT, G) \
WRAP_ARGS(cv::Scalar , cv::gapi::ArgType::CV_SCALAR, G) \
WRAP_ARGS(cv::Mat , cv::gapi::ArgType::CV_MAT, G) \
WRAP_ARGS(Prim , cv::gapi::ArgType::CV_DRAW_PRIM, G) \
WRAP_ARGS(cv::GArg , cv::gapi::ArgType::CV_ANY, G) \
WRAP_ARGS(cv::GMat , cv::gapi::ArgType::CV_GMAT, G2) \
#define GOPAQUE_TYPE_LIST_G(G, G2) \
WRAP_ARGS(bool , cv::gapi::ArgType::CV_BOOL, G) \
WRAP_ARGS(int , cv::gapi::ArgType::CV_INT, G) \
WRAP_ARGS(int64_t , cv::gapi::ArgType::CV_INT64, G) \
WRAP_ARGS(double , cv::gapi::ArgType::CV_DOUBLE, G) \
WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \
WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \
WRAP_ARGS(cv::Point , cv::gapi::ArgType::CV_POINT, G) \
WRAP_ARGS(cv::Point2f , cv::gapi::ArgType::CV_POINT2F, G) \
WRAP_ARGS(cv::Size , cv::gapi::ArgType::CV_SIZE, G) \
WRAP_ARGS(cv::GArg , cv::gapi::ArgType::CV_ANY, G) \
WRAP_ARGS(cv::Rect , cv::gapi::ArgType::CV_RECT, G2) \
namespace cv {
namespace gapi {
// NB: cv.gapi.CV_BOOL in python
enum ArgType {
CV_BOOL,
CV_INT,
CV_INT64,
CV_DOUBLE,
CV_FLOAT,
CV_STRING,
CV_POINT,
CV_POINT2F,
CV_SIZE,
CV_RECT,
CV_SCALAR,
CV_MAT,
CV_GMAT,
CV_DRAW_PRIM,
CV_ANY,
};
GAPI_EXPORTS_W inline cv::GInferOutputs infer(const String& name, const cv::GInferInputs& inputs)
{
return infer<Generic>(name, inputs);
}
GAPI_EXPORTS_W inline GInferOutputs infer(const std::string& name,
const cv::GOpaque<cv::Rect>& roi,
const GInferInputs& inputs)
{
return infer<Generic>(name, roi, inputs);
}
GAPI_EXPORTS_W inline GInferListOutputs infer(const std::string& name,
const cv::GArray<cv::Rect>& rois,
const GInferInputs& inputs)
{
return infer<Generic>(name, rois, inputs);
}
GAPI_EXPORTS_W inline GInferListOutputs infer2(const std::string& name,
const cv::GMat in,
const GInferListInputs& inputs)
{
return infer2<Generic>(name, in, inputs);
}
} // namespace gapi
namespace detail {
template <template <typename> class Wrapper, typename T>
struct WrapType { using type = Wrapper<T>; };
template <template <typename> class T, typename... Types>
using MakeVariantType = cv::util::variant<typename WrapType<T, Types>::type...>;
template<typename T> struct ArgTypeTraits;
#define DEFINE_TYPE_TRAITS(T, E) \
template <> \
struct ArgTypeTraits<T> { \
static constexpr const cv::gapi::ArgType type = E; \
}; \
GARRAY_TYPE_LIST_G(DEFINE_TYPE_TRAITS, DEFINE_TYPE_TRAITS)
} // namespace detail
class GAPI_EXPORTS_W_SIMPLE GOpaqueT
{
public:
GOpaqueT() = default;
using Storage = cv::detail::MakeVariantType<cv::GOpaque, GOPAQUE_TYPE_LIST_G(ID_, ID)>;
template<typename T>
GOpaqueT(cv::GOpaque<T> arg) : m_type(cv::detail::ArgTypeTraits<T>::type), m_arg(arg) { };
GAPI_WRAP GOpaqueT(gapi::ArgType type) : m_type(type)
{
#define HC(T, K) case K: \
m_arg = cv::GOpaque<T>(); \
break;
SWITCH(type, GOPAQUE_TYPE_LIST_G, HC)
#undef HC
}
cv::detail::GOpaqueU strip() {
#define HC(T, K) case Storage:: index_of<cv::GOpaque<T>>(): \
return cv::util::get<cv::GOpaque<T>>(m_arg).strip(); \
SWITCH(m_arg.index(), GOPAQUE_TYPE_LIST_G, HC)
#undef HC
GAPI_Assert(false);
}
GAPI_WRAP gapi::ArgType type() { return m_type; }
const Storage& arg() const { return m_arg; }
private:
gapi::ArgType m_type;
Storage m_arg;
};
class GAPI_EXPORTS_W_SIMPLE GArrayT
{
public:
GArrayT() = default;
using Storage = cv::detail::MakeVariantType<cv::GArray, GARRAY_TYPE_LIST_G(ID_, ID)>;
template<typename T>
GArrayT(cv::GArray<T> arg) : m_type(cv::detail::ArgTypeTraits<T>::type), m_arg(arg) { };
GAPI_WRAP GArrayT(gapi::ArgType type) : m_type(type)
{
#define HC(T, K) case K: \
m_arg = cv::GArray<T>(); \
break;
SWITCH(type, GARRAY_TYPE_LIST_G, HC)
#undef HC
}
cv::detail::GArrayU strip() {
#define HC(T, K) case Storage:: index_of<cv::GArray<T>>(): \
return cv::util::get<cv::GArray<T>>(m_arg).strip(); \
SWITCH(m_arg.index(), GARRAY_TYPE_LIST_G, HC)
#undef HC
GAPI_Assert(false);
}
GAPI_WRAP gapi::ArgType type() { return m_type; }
const Storage& arg() const { return m_arg; }
private:
gapi::ArgType m_type;
Storage m_arg;
};
namespace gapi {
namespace wip {
class GAPI_EXPORTS_W_SIMPLE GOutputs
{
public:
GOutputs() = default;
GOutputs(const std::string& id, cv::GKernel::M outMeta, cv::GArgs &&ins);
GAPI_WRAP cv::GMat getGMat();
GAPI_WRAP cv::GScalar getGScalar();
GAPI_WRAP cv::GArrayT getGArray(cv::gapi::ArgType type);
GAPI_WRAP cv::GOpaqueT getGOpaque(cv::gapi::ArgType type);
private:
class Priv;
std::shared_ptr<Priv> m_priv;
};
GOutputs op(const std::string& id, cv::GKernel::M outMeta, cv::GArgs&& args);
template <typename... T>
GOutputs op(const std::string& id, cv::GKernel::M outMeta, T&&... args)
{
return op(id, outMeta, cv::GArgs{cv::GArg(std::forward<T>(args))... });
}
} // namespace wip
} // namespace gapi
} // namespace cv
cv::gapi::wip::GOutputs cv::gapi::wip::op(const std::string& id,
cv::GKernel::M outMeta,
cv::GArgs&& args)
{
cv::gapi::wip::GOutputs outputs{id, outMeta, std::move(args)};
return outputs;
}
class cv::gapi::wip::GOutputs::Priv
{
public:
Priv(const std::string& id, cv::GKernel::M outMeta, cv::GArgs &&ins);
cv::GMat getGMat();
cv::GScalar getGScalar();
cv::GArrayT getGArray(cv::gapi::ArgType);
cv::GOpaqueT getGOpaque(cv::gapi::ArgType);
private:
int output = 0;
std::unique_ptr<cv::GCall> m_call;
};
cv::gapi::wip::GOutputs::Priv::Priv(const std::string& id, cv::GKernel::M outMeta, cv::GArgs &&args)
{
cv::GKinds kinds;
kinds.reserve(args.size());
std::transform(args.begin(), args.end(), std::back_inserter(kinds),
[](const cv::GArg& arg) { return arg.opaque_kind; });
m_call.reset(new cv::GCall{cv::GKernel{id, {}, outMeta, {}, std::move(kinds), {}}});
m_call->setArgs(std::move(args));
}
cv::GMat cv::gapi::wip::GOutputs::Priv::getGMat()
{
m_call->kernel().outShapes.push_back(cv::GShape::GMAT);
// ...so _empty_ constructor is passed here.
m_call->kernel().outCtors.emplace_back(cv::util::monostate{});
return m_call->yield(output++);
}
cv::GScalar cv::gapi::wip::GOutputs::Priv::getGScalar()
{
m_call->kernel().outShapes.push_back(cv::GShape::GSCALAR);
// ...so _empty_ constructor is passed here.
m_call->kernel().outCtors.emplace_back(cv::util::monostate{});
return m_call->yieldScalar(output++);
}
cv::GArrayT cv::gapi::wip::GOutputs::Priv::getGArray(cv::gapi::ArgType type)
{
m_call->kernel().outShapes.push_back(cv::GShape::GARRAY);
#define HC(T, K) \
case K: \
m_call->kernel().outCtors.emplace_back(cv::detail::GObtainCtor<cv::GArray<T>>::get()); \
return cv::GArrayT(m_call->yieldArray<T>(output++)); \
SWITCH(type, GARRAY_TYPE_LIST_G, HC)
#undef HC
}
cv::GOpaqueT cv::gapi::wip::GOutputs::Priv::getGOpaque(cv::gapi::ArgType type)
{
m_call->kernel().outShapes.push_back(cv::GShape::GOPAQUE);
#define HC(T, K) \
case K: \
m_call->kernel().outCtors.emplace_back(cv::detail::GObtainCtor<cv::GOpaque<T>>::get()); \
return cv::GOpaqueT(m_call->yieldOpaque<T>(output++)); \
SWITCH(type, GOPAQUE_TYPE_LIST_G, HC)
#undef HC
}
cv::gapi::wip::GOutputs::GOutputs(const std::string& id,
cv::GKernel::M outMeta,
cv::GArgs &&ins) :
m_priv(new cv::gapi::wip::GOutputs::Priv(id, outMeta, std::move(ins)))
{
}
cv::GMat cv::gapi::wip::GOutputs::getGMat()
{
return m_priv->getGMat();
}
cv::GScalar cv::gapi::wip::GOutputs::getGScalar()
{
return m_priv->getGScalar();
}
cv::GArrayT cv::gapi::wip::GOutputs::getGArray(cv::gapi::ArgType type)
{
return m_priv->getGArray(type);
}
cv::GOpaqueT cv::gapi::wip::GOutputs::getGOpaque(cv::gapi::ArgType type)
{
return m_priv->getGOpaque(type);
}
#endif // OPENCV_GAPI_PYTHON_BRIDGE_HPP

View File

@ -0,0 +1,458 @@
import argparse
import time
import numpy as np
import cv2 as cv
# ------------------------Service operations------------------------
def weight_path(model_path):
""" Get path of weights based on path to IR
Params:
model_path: the string contains path to IR file
Return:
Path to weights file
"""
assert model_path.endswith('.xml'), "Wrong topology path was provided"
return model_path[:-3] + 'bin'
def build_argparser():
""" Parse arguments from command line
Return:
Pack of arguments from command line
"""
parser = argparse.ArgumentParser(description='This is an OpenCV-based version of Gaze Estimation example')
parser.add_argument('--input',
help='Path to the input video file')
parser.add_argument('--out',
help='Path to the output video file')
parser.add_argument('--facem',
default='face-detection-retail-0005.xml',
help='Path to OpenVINO face detection model (.xml)')
parser.add_argument('--faced',
default='CPU',
help='Target device for the face detection' +
'(e.g. CPU, GPU, VPU, ...)')
parser.add_argument('--headm',
default='head-pose-estimation-adas-0001.xml',
help='Path to OpenVINO head pose estimation model (.xml)')
parser.add_argument('--headd',
default='CPU',
help='Target device for the head pose estimation inference ' +
'(e.g. CPU, GPU, VPU, ...)')
parser.add_argument('--landm',
default='facial-landmarks-35-adas-0002.xml',
help='Path to OpenVINO landmarks detector model (.xml)')
parser.add_argument('--landd',
default='CPU',
help='Target device for the landmarks detector (e.g. CPU, GPU, VPU, ...)')
parser.add_argument('--gazem',
default='gaze-estimation-adas-0002.xml',
help='Path to OpenVINO gaze vector estimaiton model (.xml)')
parser.add_argument('--gazed',
default='CPU',
help='Target device for the gaze vector estimation inference ' +
'(e.g. CPU, GPU, VPU, ...)')
parser.add_argument('--eyem',
default='open-closed-eye-0001.xml',
help='Path to OpenVINO open closed eye model (.xml)')
parser.add_argument('--eyed',
default='CPU',
help='Target device for the eyes state inference (e.g. CPU, GPU, VPU, ...)')
return parser
# ------------------------Support functions for custom kernels------------------------
def intersection(surface, rect):
""" Remove zone of out of bound from ROI
Params:
surface: image bounds is rect representation (top left coordinates and width and height)
rect: region of interest is also has rect representation
Return:
Modified ROI with correct bounds
"""
l_x = max(surface[0], rect[0])
l_y = max(surface[1], rect[1])
width = min(surface[0] + surface[2], rect[0] + rect[2]) - l_x
height = min(surface[1] + surface[3], rect[1] + rect[3]) - l_y
if width < 0 or height < 0:
return (0, 0, 0, 0)
return (l_x, l_y, width, height)
def process_landmarks(r_x, r_y, r_w, r_h, landmarks):
""" Create points from result of inference of facial-landmarks network and size of input image
Params:
r_x: x coordinate of top left corner of input image
r_y: y coordinate of top left corner of input image
r_w: width of input image
r_h: height of input image
landmarks: result of inference of facial-landmarks network
Return:
Array of landmarks points for one face
"""
lmrks = landmarks[0]
raw_x = lmrks[::2] * r_w + r_x
raw_y = lmrks[1::2] * r_h + r_y
return np.array([[int(x), int(y)] for x, y in zip(raw_x, raw_y)])
def eye_box(p_1, p_2, scale=1.8):
""" Get bounding box of eye
Params:
p_1: point of left edge of eye
p_2: point of right edge of eye
scale: change size of box with this value
Return:
Bounding box of eye and its midpoint
"""
size = np.linalg.norm(p_1 - p_2)
midpoint = (p_1 + p_2) / 2
width = scale * size
height = width
p_x = midpoint[0] - (width / 2)
p_y = midpoint[1] - (height / 2)
return (int(p_x), int(p_y), int(width), int(height)), list(map(int, midpoint))
# ------------------------Custom graph operations------------------------
@cv.gapi.op('custom.GProcessPoses',
in_types=[cv.GArray.GMat, cv.GArray.GMat, cv.GArray.GMat],
out_types=[cv.GArray.GMat])
class GProcessPoses:
@staticmethod
def outMeta(arr_desc0, arr_desc1, arr_desc2):
return cv.empty_array_desc()
@cv.gapi.op('custom.GParseEyes',
in_types=[cv.GArray.GMat, cv.GArray.Rect, cv.GOpaque.Size],
out_types=[cv.GArray.Rect, cv.GArray.Rect, cv.GArray.Point, cv.GArray.Point])
class GParseEyes:
@staticmethod
def outMeta(arr_desc0, arr_desc1, arr_desc2):
return cv.empty_array_desc(), cv.empty_array_desc(), \
cv.empty_array_desc(), cv.empty_array_desc()
@cv.gapi.op('custom.GGetStates',
in_types=[cv.GArray.GMat, cv.GArray.GMat],
out_types=[cv.GArray.Int, cv.GArray.Int])
class GGetStates:
@staticmethod
def outMeta(arr_desc0, arr_desc1):
return cv.empty_array_desc(), cv.empty_array_desc()
# ------------------------Custom kernels------------------------
@cv.gapi.kernel(GProcessPoses)
class GProcessPosesImpl:
""" Custom kernel. Processed poses of heads
"""
@staticmethod
def run(in_ys, in_ps, in_rs):
""" Сustom kernel executable code
Params:
in_ys: yaw angle of head
in_ps: pitch angle of head
in_rs: roll angle of head
Return:
Arrays with heads poses
"""
return [np.array([ys[0], ps[0], rs[0]]).T for ys, ps, rs in zip(in_ys, in_ps, in_rs)]
@cv.gapi.kernel(GParseEyes)
class GParseEyesImpl:
""" Custom kernel. Get information about eyes
"""
@staticmethod
def run(in_landm_per_face, in_face_rcs, frame_size):
""" Сustom kernel executable code
Params:
in_landm_per_face: landmarks from inference of facial-landmarks network for each face
in_face_rcs: bounding boxes for each face
frame_size: size of input image
Return:
Arrays of ROI for left and right eyes, array of midpoints and
array of landmarks points
"""
left_eyes = []
right_eyes = []
midpoints = []
lmarks = []
surface = (0, 0, *frame_size)
for landm_face, rect in zip(in_landm_per_face, in_face_rcs):
points = process_landmarks(*rect, landm_face)
lmarks.extend(points)
rect, midpoint_l = eye_box(points[0], points[1])
left_eyes.append(intersection(surface, rect))
rect, midpoint_r = eye_box(points[2], points[3])
right_eyes.append(intersection(surface, rect))
midpoints.append(midpoint_l)
midpoints.append(midpoint_r)
return left_eyes, right_eyes, midpoints, lmarks
@cv.gapi.kernel(GGetStates)
class GGetStatesImpl:
""" Custom kernel. Get state of eye - open or closed
"""
@staticmethod
def run(eyesl, eyesr):
""" Сustom kernel executable code
Params:
eyesl: result of inference of open-closed-eye network for left eye
eyesr: result of inference of open-closed-eye network for right eye
Return:
States of left eyes and states of right eyes
"""
out_l_st = [int(st) for eye_l in eyesl for st in (eye_l[:, 0] < eye_l[:, 1]).ravel()]
out_r_st = [int(st) for eye_r in eyesr for st in (eye_r[:, 0] < eye_r[:, 1]).ravel()]
return out_l_st, out_r_st
if __name__ == '__main__':
ARGUMENTS = build_argparser().parse_args()
# ------------------------Demo's graph------------------------
g_in = cv.GMat()
# Detect faces
face_inputs = cv.GInferInputs()
face_inputs.setInput('data', g_in)
face_outputs = cv.gapi.infer('face-detection', face_inputs)
faces = face_outputs.at('detection_out')
# Parse faces
sz = cv.gapi.streaming.size(g_in)
faces_rc = cv.gapi.parseSSD(faces, sz, 0.5, False, False)
# Detect poses
head_inputs = cv.GInferInputs()
head_inputs.setInput('data', g_in)
face_outputs = cv.gapi.infer('head-pose', faces_rc, head_inputs)
angles_y = face_outputs.at('angle_y_fc')
angles_p = face_outputs.at('angle_p_fc')
angles_r = face_outputs.at('angle_r_fc')
# Parse poses
heads_pos = GProcessPoses.on(angles_y, angles_p, angles_r)
# Detect landmarks
landmark_inputs = cv.GInferInputs()
landmark_inputs.setInput('data', g_in)
landmark_outputs = cv.gapi.infer('facial-landmarks', faces_rc,
landmark_inputs)
landmark = landmark_outputs.at('align_fc3')
# Parse landmarks
left_eyes, right_eyes, mids, lmarks = GParseEyes.on(landmark, faces_rc, sz)
# Detect eyes
eyes_inputs = cv.GInferInputs()
eyes_inputs.setInput('input.1', g_in)
eyesl_outputs = cv.gapi.infer('open-closed-eye', left_eyes, eyes_inputs)
eyesr_outputs = cv.gapi.infer('open-closed-eye', right_eyes, eyes_inputs)
eyesl = eyesl_outputs.at('19')
eyesr = eyesr_outputs.at('19')
# Process eyes states
l_eye_st, r_eye_st = GGetStates.on(eyesl, eyesr)
# Gaze estimation
gaze_inputs = cv.GInferListInputs()
gaze_inputs.setInput('left_eye_image', left_eyes)
gaze_inputs.setInput('right_eye_image', right_eyes)
gaze_inputs.setInput('head_pose_angles', heads_pos)
gaze_outputs = cv.gapi.infer2('gaze-estimation', g_in, gaze_inputs)
gaze_vectors = gaze_outputs.at('gaze_vector')
out = cv.gapi.copy(g_in)
# ------------------------End of graph------------------------
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(out,
faces_rc,
left_eyes,
right_eyes,
gaze_vectors,
angles_y,
angles_p,
angles_r,
l_eye_st,
r_eye_st,
mids,
lmarks))
# Networks
face_net = cv.gapi.ie.params('face-detection', ARGUMENTS.facem,
weight_path(ARGUMENTS.facem), ARGUMENTS.faced)
head_pose_net = cv.gapi.ie.params('head-pose', ARGUMENTS.headm,
weight_path(ARGUMENTS.headm), ARGUMENTS.headd)
landmarks_net = cv.gapi.ie.params('facial-landmarks', ARGUMENTS.landm,
weight_path(ARGUMENTS.landm), ARGUMENTS.landd)
gaze_net = cv.gapi.ie.params('gaze-estimation', ARGUMENTS.gazem,
weight_path(ARGUMENTS.gazem), ARGUMENTS.gazed)
eye_net = cv.gapi.ie.params('open-closed-eye', ARGUMENTS.eyem,
weight_path(ARGUMENTS.eyem), ARGUMENTS.eyed)
nets = cv.gapi.networks(face_net, head_pose_net, landmarks_net, gaze_net, eye_net)
# Kernels pack
kernels = cv.gapi.kernels(GParseEyesImpl, GProcessPosesImpl, GGetStatesImpl)
# ------------------------Execution part------------------------
ccomp = comp.compileStreaming(args=cv.gapi.compile_args(kernels, nets))
source = cv.gapi.wip.make_capture_src(ARGUMENTS.input)
ccomp.setSource(cv.gin(source))
ccomp.start()
frames = 0
fps = 0
print('Processing')
START_TIME = time.time()
while True:
start_time_cycle = time.time()
has_frame, (oimg,
outr,
l_eyes,
r_eyes,
outg,
out_y,
out_p,
out_r,
out_st_l,
out_st_r,
out_mids,
outl) = ccomp.pull()
if not has_frame:
break
# Draw
GREEN = (0, 255, 0)
RED = (0, 0, 255)
WHITE = (255, 255, 255)
BLUE = (255, 0, 0)
PINK = (255, 0, 255)
YELLOW = (0, 255, 255)
M_PI_180 = np.pi / 180
M_PI_2 = np.pi / 2
M_PI = np.pi
FACES_SIZE = len(outr)
for i, out_rect in enumerate(outr):
# Face box
cv.rectangle(oimg, out_rect, WHITE, 1)
rx, ry, rwidth, rheight = out_rect
# Landmarks
lm_radius = int(0.01 * rwidth + 1)
lmsize = int(len(outl) / FACES_SIZE)
for j in range(lmsize):
cv.circle(oimg, outl[j + i * lmsize], lm_radius, YELLOW, -1)
# Headposes
yaw = out_y[i]
pitch = out_p[i]
roll = out_r[i]
sin_y = np.sin(yaw[:] * M_PI_180)
sin_p = np.sin(pitch[:] * M_PI_180)
sin_r = np.sin(roll[:] * M_PI_180)
cos_y = np.cos(yaw[:] * M_PI_180)
cos_p = np.cos(pitch[:] * M_PI_180)
cos_r = np.cos(roll[:] * M_PI_180)
axis_length = 0.4 * rwidth
x_center = int(rx + rwidth / 2)
y_center = int(ry + rheight / 2)
# center to right
cv.line(oimg, [x_center, y_center],
[int(x_center + axis_length * (cos_r * cos_y + sin_y * sin_p * sin_r)),
int(y_center + axis_length * cos_p * sin_r)],
RED, 2)
# center to top
cv.line(oimg, [x_center, y_center],
[int(x_center + axis_length * (cos_r * sin_y * sin_p + cos_y * sin_r)),
int(y_center - axis_length * cos_p * cos_r)],
GREEN, 2)
# center to forward
cv.line(oimg, [x_center, y_center],
[int(x_center + axis_length * sin_y * cos_p),
int(y_center + axis_length * sin_p)],
PINK, 2)
scale_box = 0.002 * rwidth
cv.putText(oimg, "head pose: (y=%0.0f, p=%0.0f, r=%0.0f)" %
(np.round(yaw), np.round(pitch), np.round(roll)),
[int(rx), int(ry + rheight + 5 * rwidth / 100)],
cv.FONT_HERSHEY_PLAIN, scale_box * 2, WHITE, 1)
# Eyes boxes
color_l = GREEN if out_st_l[i] else RED
cv.rectangle(oimg, l_eyes[i], color_l, 1)
color_r = GREEN if out_st_r[i] else RED
cv.rectangle(oimg, r_eyes[i], color_r, 1)
# Gaze vectors
norm_gazes = np.linalg.norm(outg[i][0])
gaze_vector = outg[i][0] / norm_gazes
arrow_length = 0.4 * rwidth
gaze_arrow = [arrow_length * gaze_vector[0], -arrow_length * gaze_vector[1]]
left_arrow = [int(a+b) for a, b in zip(out_mids[0 + i * 2], gaze_arrow)]
right_arrow = [int(a+b) for a, b in zip(out_mids[1 + i * 2], gaze_arrow)]
if out_st_l[i]:
cv.arrowedLine(oimg, out_mids[0 + i * 2], left_arrow, BLUE, 2)
if out_st_r[i]:
cv.arrowedLine(oimg, out_mids[1 + i * 2], right_arrow, BLUE, 2)
v0, v1, v2 = outg[i][0]
gaze_angles = [180 / M_PI * (M_PI_2 + np.arctan2(v2, v0)),
180 / M_PI * (M_PI_2 - np.arccos(v1 / norm_gazes))]
cv.putText(oimg, "gaze angles: (h=%0.0f, v=%0.0f)" %
(np.round(gaze_angles[0]), np.round(gaze_angles[1])),
[int(rx), int(ry + rheight + 12 * rwidth / 100)],
cv.FONT_HERSHEY_PLAIN, scale_box * 2, WHITE, 1)
# Add FPS value to frame
cv.putText(oimg, "FPS: %0i" % (fps), [int(20), int(40)],
cv.FONT_HERSHEY_PLAIN, 2, RED, 2)
# Show result
cv.imshow('Gaze Estimation', oimg)
cv.waitKey(1)
fps = int(1. / (time.time() - start_time_cycle))
frames += 1
EXECUTION_TIME = time.time() - START_TIME
print('Execution successful')
print('Mean FPS is ', int(frames / EXECUTION_TIME))

View File

@ -3,30 +3,81 @@
namespace cv
{
struct GAPI_EXPORTS_W_SIMPLE GCompileArg { };
struct GAPI_EXPORTS_W_SIMPLE GCompileArg
{
GAPI_WRAP GCompileArg(gapi::GKernelPackage arg);
GAPI_WRAP GCompileArg(gapi::GNetPackage arg);
GAPI_WRAP GCompileArg(gapi::streaming::queue_capacity arg);
};
GAPI_EXPORTS_W GCompileArgs compile_args(gapi::GKernelPackage pkg);
GAPI_EXPORTS_W GCompileArgs compile_args(gapi::GNetPackage pkg);
class GAPI_EXPORTS_W_SIMPLE GInferInputs
{
public:
GAPI_WRAP GInferInputs();
GAPI_WRAP GInferInputs& setInput(const std::string& name, const cv::GMat& value);
GAPI_WRAP GInferInputs& setInput(const std::string& name, const cv::GFrame& value);
};
// NB: This classes doesn't exist in *.so
// HACK: Mark them as a class to force python wrapper generate code for this entities
class GAPI_EXPORTS_W_SIMPLE GProtoArg { };
class GAPI_EXPORTS_W_SIMPLE GProtoInputArgs { };
class GAPI_EXPORTS_W_SIMPLE GProtoOutputArgs { };
class GAPI_EXPORTS_W_SIMPLE GRunArg { };
class GAPI_EXPORTS_W_SIMPLE GMetaArg { };
class GAPI_EXPORTS_W_SIMPLE GInferListInputs
{
public:
GAPI_WRAP GInferListInputs();
GAPI_WRAP GInferListInputs setInput(const std::string& name, const cv::GArray<cv::GMat>& value);
GAPI_WRAP GInferListInputs setInput(const std::string& name, const cv::GArray<cv::Rect>& value);
};
class GAPI_EXPORTS_W_SIMPLE GArrayP2f { };
class GAPI_EXPORTS_W_SIMPLE GInferOutputs
{
public:
GAPI_WRAP GInferOutputs();
GAPI_WRAP cv::GMat at(const std::string& name);
};
using GProtoInputArgs = GIOProtoArgs<In_Tag>;
using GProtoOutputArgs = GIOProtoArgs<Out_Tag>;
class GAPI_EXPORTS_W_SIMPLE GInferListOutputs
{
public:
GAPI_WRAP GInferListOutputs();
GAPI_WRAP cv::GArray<cv::GMat> at(const std::string& name);
};
namespace gapi
{
GAPI_EXPORTS_W gapi::GNetPackage networks(const cv::gapi::ie::PyParams& params);
namespace wip
{
class GAPI_EXPORTS_W IStreamSource { };
} // namespace wip
} // namespace gapi
namespace gapi
{
namespace wip
{
class GAPI_EXPORTS_W IStreamSource { };
namespace draw
{
// NB: These render primitives are partially wrapped in shadow file
// because cv::Rect conflicts with cv::gapi::wip::draw::Rect in python generator
// and cv::Rect2i breaks standalone mode.
struct Rect
{
GAPI_WRAP Rect(const cv::Rect2i& rect_,
const cv::Scalar& color_,
int thick_ = 1,
int lt_ = 8,
int shift_ = 0);
};
struct Mosaic
{
GAPI_WRAP Mosaic(const cv::Rect2i& mos_, int cellSz_, int decim_);
};
} // namespace draw
} // namespace wip
namespace streaming
{
// FIXME: Extend to work with an arbitrary G-type.
cv::GOpaque<int64_t> GAPI_EXPORTS_W timestamp(cv::GMat);
cv::GOpaque<int64_t> GAPI_EXPORTS_W seqNo(cv::GMat);
cv::GOpaque<int64_t> GAPI_EXPORTS_W seq_id(cv::GMat);
GAPI_EXPORTS_W cv::GMat desync(const cv::GMat &g);
} // namespace streaming
} // namespace gapi
namespace detail
{
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ie::PyParams params);
} // namespace detail
} // namespace cv

View File

@ -3,129 +3,209 @@
import numpy as np
import cv2 as cv
import os
import sys
import unittest
from tests_common import NewOpenCVTests
# Plaidml is an optional backend
pkgs = [
('ocl' , cv.gapi.core.ocl.kernels()),
('cpu' , cv.gapi.core.cpu.kernels()),
('fluid' , cv.gapi.core.fluid.kernels())
# ('plaidml', cv.gapi.core.plaidml.kernels())
]
try:
if sys.version_info[:2] < (3, 0):
raise unittest.SkipTest('Python 2.x is not supported')
# Plaidml is an optional backend
pkgs = [
('ocl' , cv.gapi.core.ocl.kernels()),
('cpu' , cv.gapi.core.cpu.kernels()),
('fluid' , cv.gapi.core.fluid.kernels())
# ('plaidml', cv.gapi.core.plaidml.kernels())
]
class gapi_core_test(NewOpenCVTests):
class gapi_core_test(NewOpenCVTests):
def test_add(self):
# TODO: Extend to use any type and size here
sz = (720, 1280)
in1 = np.full(sz, 100)
in2 = np.full(sz, 50)
def test_add(self):
# TODO: Extend to use any type and size here
sz = (720, 1280)
in1 = np.full(sz, 100)
in2 = np.full(sz, 50)
# OpenCV
expected = cv.add(in1, in2)
# OpenCV
expected = cv.add(in1, in2)
# G-API
g_in1 = cv.GMat()
g_in2 = cv.GMat()
g_out = cv.gapi.add(g_in1, g_in2)
comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out))
# G-API
g_in1 = cv.GMat()
g_in2 = cv.GMat()
g_out = cv.gapi.add(g_in1, g_in2)
comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out))
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1, in2), args=cv.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
self.assertEqual(expected.dtype, actual.dtype, 'Failed on ' + pkg_name + ' backend')
def test_add_uint8(self):
sz = (720, 1280)
in1 = np.full(sz, 100, dtype=np.uint8)
in2 = np.full(sz, 50 , dtype=np.uint8)
# OpenCV
expected = cv.add(in1, in2)
# G-API
g_in1 = cv.GMat()
g_in2 = cv.GMat()
g_out = cv.gapi.add(g_in1, g_in2)
comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out))
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1, in2), args=cv.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
self.assertEqual(expected.dtype, actual.dtype, 'Failed on ' + pkg_name + ' backend')
def test_mean(self):
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in_mat = cv.imread(img_path)
# OpenCV
expected = cv.mean(in_mat)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.mean(g_in)
comp = cv.GComputation(g_in, g_out)
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in_mat), args=cv.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
def test_split3(self):
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in_mat = cv.imread(img_path)
# OpenCV
expected = cv.split(in_mat)
# G-API
g_in = cv.GMat()
b, g, r = cv.gapi.split3(g_in)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(b, g, r))
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in_mat), args=cv.compile_args(pkg))
# Comparison
for e, a in zip(expected, actual):
self.assertEqual(0.0, cv.norm(e, a, cv.NORM_INF),
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1, in2), args=cv.gapi.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
self.assertEqual(e.dtype, a.dtype, 'Failed on ' + pkg_name + ' backend')
self.assertEqual(expected.dtype, actual.dtype, 'Failed on ' + pkg_name + ' backend')
def test_threshold(self):
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in_mat = cv.cvtColor(cv.imread(img_path), cv.COLOR_RGB2GRAY)
maxv = (30, 30)
def test_add_uint8(self):
sz = (720, 1280)
in1 = np.full(sz, 100, dtype=np.uint8)
in2 = np.full(sz, 50 , dtype=np.uint8)
# OpenCV
expected_thresh, expected_mat = cv.threshold(in_mat, maxv[0], maxv[0], cv.THRESH_TRIANGLE)
# OpenCV
expected = cv.add(in1, in2)
# G-API
g_in = cv.GMat()
g_sc = cv.GScalar()
mat, threshold = cv.gapi.threshold(g_in, g_sc, cv.THRESH_TRIANGLE)
comp = cv.GComputation(cv.GIn(g_in, g_sc), cv.GOut(mat, threshold))
# G-API
g_in1 = cv.GMat()
g_in2 = cv.GMat()
g_out = cv.gapi.add(g_in1, g_in2)
comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out))
for pkg_name, pkg in pkgs:
actual_mat, actual_thresh = comp.apply(cv.gin(in_mat, maxv), args=cv.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected_mat, actual_mat, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
self.assertEqual(expected_mat.dtype, actual_mat.dtype,
'Failed on ' + pkg_name + ' backend')
self.assertEqual(expected_thresh, actual_thresh[0],
'Failed on ' + pkg_name + ' backend')
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1, in2), args=cv.gapi.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
self.assertEqual(expected.dtype, actual.dtype, 'Failed on ' + pkg_name + ' backend')
def test_mean(self):
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in_mat = cv.imread(img_path)
# OpenCV
expected = cv.mean(in_mat)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.mean(g_in)
comp = cv.GComputation(g_in, g_out)
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in_mat), args=cv.gapi.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
def test_split3(self):
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in_mat = cv.imread(img_path)
# OpenCV
expected = cv.split(in_mat)
# G-API
g_in = cv.GMat()
b, g, r = cv.gapi.split3(g_in)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(b, g, r))
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in_mat), args=cv.gapi.compile_args(pkg))
# Comparison
for e, a in zip(expected, actual):
self.assertEqual(0.0, cv.norm(e, a, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
self.assertEqual(e.dtype, a.dtype, 'Failed on ' + pkg_name + ' backend')
def test_threshold(self):
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in_mat = cv.cvtColor(cv.imread(img_path), cv.COLOR_RGB2GRAY)
maxv = (30, 30)
# OpenCV
expected_thresh, expected_mat = cv.threshold(in_mat, maxv[0], maxv[0], cv.THRESH_TRIANGLE)
# G-API
g_in = cv.GMat()
g_sc = cv.GScalar()
mat, threshold = cv.gapi.threshold(g_in, g_sc, cv.THRESH_TRIANGLE)
comp = cv.GComputation(cv.GIn(g_in, g_sc), cv.GOut(mat, threshold))
for pkg_name, pkg in pkgs:
actual_mat, actual_thresh = comp.apply(cv.gin(in_mat, maxv), args=cv.gapi.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected_mat, actual_mat, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
self.assertEqual(expected_mat.dtype, actual_mat.dtype,
'Failed on ' + pkg_name + ' backend')
self.assertEqual(expected_thresh, actual_thresh[0],
'Failed on ' + pkg_name + ' backend')
def test_kmeans(self):
# K-means params
count = 100
sz = (count, 2)
in_mat = np.random.random(sz).astype(np.float32)
K = 5
flags = cv.KMEANS_RANDOM_CENTERS
attempts = 1
criteria = (cv.TERM_CRITERIA_MAX_ITER + cv.TERM_CRITERIA_EPS, 30, 0)
# G-API
g_in = cv.GMat()
compactness, out_labels, centers = cv.gapi.kmeans(g_in, K, criteria, attempts, flags)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(compactness, out_labels, centers))
compact, labels, centers = comp.apply(cv.gin(in_mat))
# Assert
self.assertTrue(compact >= 0)
self.assertEqual(sz[0], labels.shape[0])
self.assertEqual(1, labels.shape[1])
self.assertTrue(labels.size != 0)
self.assertEqual(centers.shape[1], sz[1])
self.assertEqual(centers.shape[0], K)
self.assertTrue(centers.size != 0)
def generate_random_points(self, sz):
arr = np.random.random(sz).astype(np.float32).T
return list(zip(arr[0], arr[1]))
def test_kmeans_2d(self):
# K-means 2D params
count = 100
sz = (count, 2)
amount = sz[0]
K = 5
flags = cv.KMEANS_RANDOM_CENTERS
attempts = 1
criteria = (cv.TERM_CRITERIA_MAX_ITER + cv.TERM_CRITERIA_EPS, 30, 0)
in_vector = self.generate_random_points(sz)
in_labels = []
# G-API
data = cv.GArrayT(cv.gapi.CV_POINT2F)
best_labels = cv.GArrayT(cv.gapi.CV_INT)
compactness, out_labels, centers = cv.gapi.kmeans(data, K, best_labels, criteria, attempts, flags)
comp = cv.GComputation(cv.GIn(data, best_labels), cv.GOut(compactness, out_labels, centers))
compact, labels, centers = comp.apply(cv.gin(in_vector, in_labels))
# Assert
self.assertTrue(compact >= 0)
self.assertEqual(amount, len(labels))
self.assertEqual(K, len(centers))
except unittest.SkipTest as e:
message = str(e)
class TestSkip(unittest.TestCase):
def setUp(self):
self.skipTest('Skip tests: ' + message)
def test_skip():
pass
pass
if __name__ == '__main__':

View File

@ -3,76 +3,124 @@
import numpy as np
import cv2 as cv
import os
import sys
import unittest
from tests_common import NewOpenCVTests
# Plaidml is an optional backend
pkgs = [
('ocl' , cv.gapi.core.ocl.kernels()),
('cpu' , cv.gapi.core.cpu.kernels()),
('fluid' , cv.gapi.core.fluid.kernels())
# ('plaidml', cv.gapi.core.plaidml.kernels())
]
try:
if sys.version_info[:2] < (3, 0):
raise unittest.SkipTest('Python 2.x is not supported')
# Plaidml is an optional backend
pkgs = [
('ocl' , cv.gapi.core.ocl.kernels()),
('cpu' , cv.gapi.core.cpu.kernels()),
('fluid' , cv.gapi.core.fluid.kernels())
# ('plaidml', cv.gapi.core.plaidml.kernels())
]
class gapi_imgproc_test(NewOpenCVTests):
class gapi_imgproc_test(NewOpenCVTests):
def test_good_features_to_track(self):
# TODO: Extend to use any type and size here
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in1 = cv.cvtColor(cv.imread(img_path), cv.COLOR_RGB2GRAY)
def test_good_features_to_track(self):
# TODO: Extend to use any type and size here
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in1 = cv.cvtColor(cv.imread(img_path), cv.COLOR_RGB2GRAY)
# NB: goodFeaturesToTrack configuration
max_corners = 50
quality_lvl = 0.01
min_distance = 10
block_sz = 3
use_harris_detector = True
k = 0.04
mask = None
# NB: goodFeaturesToTrack configuration
max_corners = 50
quality_lvl = 0.01
min_distance = 10
block_sz = 3
use_harris_detector = True
k = 0.04
mask = None
# OpenCV
expected = cv.goodFeaturesToTrack(in1, max_corners, quality_lvl,
min_distance, mask=mask,
blockSize=block_sz, useHarrisDetector=use_harris_detector, k=k)
# OpenCV
expected = cv.goodFeaturesToTrack(in1, max_corners, quality_lvl,
min_distance, mask=mask,
blockSize=block_sz, useHarrisDetector=use_harris_detector, k=k)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.goodFeaturesToTrack(g_in, max_corners, quality_lvl,
min_distance, mask, block_sz, use_harris_detector, k)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.goodFeaturesToTrack(g_in, max_corners, quality_lvl,
min_distance, mask, block_sz, use_harris_detector, k)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out))
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out))
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1), args=cv.compile_args(pkg))
# NB: OpenCV & G-API have different output shapes:
# OpenCV - (num_points, 1, 2)
# G-API - (num_points, 2)
# Comparison
self.assertEqual(0.0, cv.norm(expected.flatten(), actual.flatten(), cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1), args=cv.gapi.compile_args(pkg))
# NB: OpenCV & G-API have different output shapes:
# OpenCV - (num_points, 1, 2)
# G-API - (num_points, 2)
# Comparison
self.assertEqual(0.0, cv.norm(expected.flatten(),
np.array(actual, dtype=np.float32).flatten(),
cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
def test_rgb2gray(self):
# TODO: Extend to use any type and size here
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in1 = cv.imread(img_path)
def test_rgb2gray(self):
# TODO: Extend to use any type and size here
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in1 = cv.imread(img_path)
# OpenCV
expected = cv.cvtColor(in1, cv.COLOR_RGB2GRAY)
# OpenCV
expected = cv.cvtColor(in1, cv.COLOR_RGB2GRAY)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.RGB2Gray(g_in)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.RGB2Gray(g_in)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out))
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out))
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1), args=cv.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1), args=cv.gapi.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
def test_bounding_rect(self):
sz = 1280
fscale = 256
def sample_value(fscale):
return np.random.uniform(0, 255 * fscale) / fscale
points = np.array([(sample_value(fscale), sample_value(fscale)) for _ in range(1280)], np.float32)
# OpenCV
expected = cv.boundingRect(points)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.boundingRect(g_in)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out))
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(points), args=cv.gapi.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
except unittest.SkipTest as e:
message = str(e)
class TestSkip(unittest.TestCase):
def setUp(self):
self.skipTest('Skip tests: ' + message)
def test_skip():
pass
pass
if __name__ == '__main__':

View File

@ -3,59 +3,338 @@
import numpy as np
import cv2 as cv
import os
import sys
import unittest
from tests_common import NewOpenCVTests
class test_gapi_infer(NewOpenCVTests):
try:
def test_getAvailableTargets(self):
targets = cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_OPENCV)
self.assertTrue(cv.dnn.DNN_TARGET_CPU in targets)
if sys.version_info[:2] < (3, 0):
raise unittest.SkipTest('Python 2.x is not supported')
def test_age_gender_infer(self):
class test_gapi_infer(NewOpenCVTests):
# NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return
def infer_reference_network(self, model_path, weights_path, img):
net = cv.dnn.readNetFromModelOptimizer(model_path, weights_path)
net.setPreferableBackend(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE)
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
weights_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
device_id = 'CPU'
img = cv.resize(cv.imread(img_path), (62,62))
blob = cv.dnn.blobFromImage(img)
# OpenCV DNN
net = cv.dnn.readNetFromModelOptimizer(model_path, weights_path)
net.setPreferableBackend(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE)
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)
net.setInput(blob)
return net.forward(net.getUnconnectedOutLayersNames())
blob = cv.dnn.blobFromImage(img)
net.setInput(blob)
dnn_age, dnn_gender = net.forward(net.getUnconnectedOutLayersNames())
def make_roi(self, img, roi):
return img[roi[1]:roi[1] + roi[3], roi[0]:roi[0] + roi[2], ...]
# OpenCV G-API
g_in = cv.GMat()
inputs = cv.GInferInputs()
inputs.setInput('data', g_in)
outputs = cv.gapi.infer("net", inputs)
age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob")
def test_age_gender_infer(self):
# NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(age_g, gender_g))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
weights_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
device_id = 'CPU'
nets = cv.gapi.networks(pp)
args = cv.compile_args(nets)
gapi_age, gapi_gender = comp.apply(cv.gin(img), args=cv.compile_args(cv.gapi.networks(pp)))
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img = cv.resize(cv.imread(img_path), (62,62))
# Check
self.assertEqual(0.0, cv.norm(dnn_gender, gapi_gender, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(dnn_age, gapi_age, cv.NORM_INF))
# OpenCV DNN
dnn_age, dnn_gender = self.infer_reference_network(model_path, weights_path, img)
# OpenCV G-API
g_in = cv.GMat()
inputs = cv.GInferInputs()
inputs.setInput('data', g_in)
outputs = cv.gapi.infer("net", inputs)
age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob")
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(age_g, gender_g))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
gapi_age, gapi_gender = comp.apply(cv.gin(img), args=cv.gapi.compile_args(cv.gapi.networks(pp)))
# Check
self.assertEqual(0.0, cv.norm(dnn_gender, gapi_gender, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(dnn_age, gapi_age, cv.NORM_INF))
def test_age_gender_infer_roi(self):
# NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
weights_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
device_id = 'CPU'
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img = cv.imread(img_path)
roi = (10, 10, 62, 62)
# OpenCV DNN
dnn_age, dnn_gender = self.infer_reference_network(model_path,
weights_path,
self.make_roi(img, roi))
# OpenCV G-API
g_in = cv.GMat()
g_roi = cv.GOpaqueT(cv.gapi.CV_RECT)
inputs = cv.GInferInputs()
inputs.setInput('data', g_in)
outputs = cv.gapi.infer("net", g_roi, inputs)
age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob")
comp = cv.GComputation(cv.GIn(g_in, g_roi), cv.GOut(age_g, gender_g))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
gapi_age, gapi_gender = comp.apply(cv.gin(img, roi), args=cv.gapi.compile_args(cv.gapi.networks(pp)))
# Check
self.assertEqual(0.0, cv.norm(dnn_gender, gapi_gender, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(dnn_age, gapi_age, cv.NORM_INF))
def test_age_gender_infer_roi_list(self):
# NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
weights_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
device_id = 'CPU'
rois = [(10, 15, 62, 62), (23, 50, 62, 62), (14, 100, 62, 62), (80, 50, 62, 62)]
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img = cv.imread(img_path)
# OpenCV DNN
dnn_age_list = []
dnn_gender_list = []
for roi in rois:
age, gender = self.infer_reference_network(model_path,
weights_path,
self.make_roi(img, roi))
dnn_age_list.append(age)
dnn_gender_list.append(gender)
# OpenCV G-API
g_in = cv.GMat()
g_rois = cv.GArrayT(cv.gapi.CV_RECT)
inputs = cv.GInferInputs()
inputs.setInput('data', g_in)
outputs = cv.gapi.infer("net", g_rois, inputs)
age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob")
comp = cv.GComputation(cv.GIn(g_in, g_rois), cv.GOut(age_g, gender_g))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
gapi_age_list, gapi_gender_list = comp.apply(cv.gin(img, rois),
args=cv.gapi.compile_args(cv.gapi.networks(pp)))
# Check
for gapi_age, gapi_gender, dnn_age, dnn_gender in zip(gapi_age_list,
gapi_gender_list,
dnn_age_list,
dnn_gender_list):
self.assertEqual(0.0, cv.norm(dnn_gender, gapi_gender, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(dnn_age, gapi_age, cv.NORM_INF))
def test_age_gender_infer2_roi(self):
# NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
weights_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
device_id = 'CPU'
rois = [(10, 15, 62, 62), (23, 50, 62, 62), (14, 100, 62, 62), (80, 50, 62, 62)]
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img = cv.imread(img_path)
# OpenCV DNN
dnn_age_list = []
dnn_gender_list = []
for roi in rois:
age, gender = self.infer_reference_network(model_path,
weights_path,
self.make_roi(img, roi))
dnn_age_list.append(age)
dnn_gender_list.append(gender)
# OpenCV G-API
g_in = cv.GMat()
g_rois = cv.GArrayT(cv.gapi.CV_RECT)
inputs = cv.GInferListInputs()
inputs.setInput('data', g_rois)
outputs = cv.gapi.infer2("net", g_in, inputs)
age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob")
comp = cv.GComputation(cv.GIn(g_in, g_rois), cv.GOut(age_g, gender_g))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
gapi_age_list, gapi_gender_list = comp.apply(cv.gin(img, rois),
args=cv.gapi.compile_args(cv.gapi.networks(pp)))
# Check
for gapi_age, gapi_gender, dnn_age, dnn_gender in zip(gapi_age_list,
gapi_gender_list,
dnn_age_list,
dnn_gender_list):
self.assertEqual(0.0, cv.norm(dnn_gender, gapi_gender, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(dnn_age, gapi_age, cv.NORM_INF))
def test_person_detection_retail_0013(self):
# NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return
root_path = '/omz_intel_models/intel/person-detection-retail-0013/FP32/person-detection-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
weights_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
img_path = self.find_file('gpu/lbpcascade/er.png', [os.environ.get('OPENCV_TEST_DATA_PATH')])
device_id = 'CPU'
img = cv.resize(cv.imread(img_path), (544, 320))
# OpenCV DNN
net = cv.dnn.readNetFromModelOptimizer(model_path, weights_path)
net.setPreferableBackend(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE)
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)
blob = cv.dnn.blobFromImage(img)
def parseSSD(detections, size):
h, w = size
bboxes = []
detections = detections.reshape(-1, 7)
for sample_id, class_id, confidence, xmin, ymin, xmax, ymax in detections:
if confidence >= 0.5:
x = int(xmin * w)
y = int(ymin * h)
width = int(xmax * w - x)
height = int(ymax * h - y)
bboxes.append((x, y, width, height))
return bboxes
net.setInput(blob)
dnn_detections = net.forward()
dnn_boxes = parseSSD(np.array(dnn_detections), img.shape[:2])
# OpenCV G-API
g_in = cv.GMat()
inputs = cv.GInferInputs()
inputs.setInput('data', g_in)
g_sz = cv.gapi.streaming.size(g_in)
outputs = cv.gapi.infer("net", inputs)
detections = outputs.at("detection_out")
bboxes = cv.gapi.parseSSD(detections, g_sz, 0.5, False, False)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(bboxes))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
gapi_boxes = comp.apply(cv.gin(img.astype(np.float32)),
args=cv.gapi.compile_args(cv.gapi.networks(pp)))
# Comparison
self.assertEqual(0.0, cv.norm(np.array(dnn_boxes).flatten(),
np.array(gapi_boxes).flatten(),
cv.NORM_INF))
def test_person_detection_retail_0013(self):
# NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return
root_path = '/omz_intel_models/intel/person-detection-retail-0013/FP32/person-detection-retail-0013'
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
weights_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
img_path = self.find_file('gpu/lbpcascade/er.png', [os.environ.get('OPENCV_TEST_DATA_PATH')])
device_id = 'CPU'
img = cv.resize(cv.imread(img_path), (544, 320))
# OpenCV DNN
net = cv.dnn.readNetFromModelOptimizer(model_path, weights_path)
net.setPreferableBackend(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE)
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)
blob = cv.dnn.blobFromImage(img)
def parseSSD(detections, size):
h, w = size
bboxes = []
detections = detections.reshape(-1, 7)
for sample_id, class_id, confidence, xmin, ymin, xmax, ymax in detections:
if confidence >= 0.5:
x = int(xmin * w)
y = int(ymin * h)
width = int(xmax * w - x)
height = int(ymax * h - y)
bboxes.append((x, y, width, height))
return bboxes
net.setInput(blob)
dnn_detections = net.forward()
dnn_boxes = parseSSD(np.array(dnn_detections), img.shape[:2])
# OpenCV G-API
g_in = cv.GMat()
inputs = cv.GInferInputs()
inputs.setInput('data', g_in)
g_sz = cv.gapi.streaming.size(g_in)
outputs = cv.gapi.infer("net", inputs)
detections = outputs.at("detection_out")
bboxes = cv.gapi.parseSSD(detections, g_sz, 0.5, False, False)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(bboxes))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
gapi_boxes = comp.apply(cv.gin(img.astype(np.float32)),
args=cv.gapi.compile_args(cv.gapi.networks(pp)))
# Comparison
self.assertEqual(0.0, cv.norm(np.array(dnn_boxes).flatten(),
np.array(gapi_boxes).flatten(),
cv.NORM_INF))
except unittest.SkipTest as e:
message = str(e)
class TestSkip(unittest.TestCase):
def setUp(self):
self.skipTest('Skip tests: ' + message)
def test_skip():
pass
pass
if __name__ == '__main__':

View File

@ -0,0 +1,227 @@
#!/usr/bin/env python
import numpy as np
import cv2 as cv
import os
import sys
import unittest
from tests_common import NewOpenCVTests
try:
if sys.version_info[:2] < (3, 0):
raise unittest.SkipTest('Python 2.x is not supported')
# FIXME: FText isn't supported yet.
class gapi_render_test(NewOpenCVTests):
def __init__(self, *args):
super().__init__(*args)
self.size = (300, 300, 3)
# Rect
self.rect = (30, 30, 50, 50)
self.rcolor = (0, 255, 0)
self.rlt = cv.LINE_4
self.rthick = 2
self.rshift = 3
# Text
self.text = 'Hello, world!'
self.org = (100, 100)
self.ff = cv.FONT_HERSHEY_SIMPLEX
self.fs = 1.0
self.tthick = 2
self.tlt = cv.LINE_8
self.tcolor = (255, 255, 255)
self.blo = False
# Circle
self.center = (200, 200)
self.radius = 200
self.ccolor = (255, 255, 0)
self.cthick = 2
self.clt = cv.LINE_4
self.cshift = 1
# Line
self.pt1 = (50, 50)
self.pt2 = (200, 200)
self.lcolor = (0, 255, 128)
self.lthick = 5
self.llt = cv.LINE_8
self.lshift = 2
# Poly
self.pts = [(50, 100), (100, 200), (25, 250)]
self.pcolor = (0, 0, 255)
self.pthick = 3
self.plt = cv.LINE_4
self.pshift = 1
# Image
self.iorg = (150, 150)
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
self.img = cv.resize(cv.imread(img_path), (50, 50))
self.alpha = np.full(self.img.shape[:2], 0.8, dtype=np.float32)
# Mosaic
self.mos = (100, 100, 100, 100)
self.cell_sz = 25
self.decim = 0
# Render primitives
self.prims = [cv.gapi.wip.draw.Rect(self.rect, self.rcolor, self.rthick, self.rlt, self.rshift),
cv.gapi.wip.draw.Text(self.text, self.org, self.ff, self.fs, self.tcolor, self.tthick, self.tlt, self.blo),
cv.gapi.wip.draw.Circle(self.center, self.radius, self.ccolor, self.cthick, self.clt, self.cshift),
cv.gapi.wip.draw.Line(self.pt1, self.pt2, self.lcolor, self.lthick, self.llt, self.lshift),
cv.gapi.wip.draw.Mosaic(self.mos, self.cell_sz, self.decim),
cv.gapi.wip.draw.Image(self.iorg, self.img, self.alpha),
cv.gapi.wip.draw.Poly(self.pts, self.pcolor, self.pthick, self.plt, self.pshift)]
def cvt_nv12_to_yuv(self, y, uv):
h,w,_ = uv.shape
upsample_uv = cv.resize(uv, (h * 2, w * 2))
return cv.merge([y, upsample_uv])
def cvt_yuv_to_nv12(self, yuv, y_out, uv_out):
chs = cv.split(yuv, [y_out, None, None])
uv = cv.merge([chs[1], chs[2]])
uv_out = cv.resize(uv, (uv.shape[0] // 2, uv.shape[1] // 2), dst=uv_out)
return y_out, uv_out
def cvt_bgr_to_yuv_color(self, bgr):
y = bgr[2] * 0.299000 + bgr[1] * 0.587000 + bgr[0] * 0.114000;
u = bgr[2] * -0.168736 + bgr[1] * -0.331264 + bgr[0] * 0.500000 + 128;
v = bgr[2] * 0.500000 + bgr[1] * -0.418688 + bgr[0] * -0.081312 + 128;
return (y, u, v)
def blend_img(self, background, org, img, alpha):
x, y = org
h, w, _ = img.shape
roi_img = background[x:x+w, y:y+h, :]
img32f_w = cv.merge([alpha] * 3).astype(np.float32)
roi32f_w = np.full(roi_img.shape, 1.0, dtype=np.float32)
roi32f_w -= img32f_w
img32f = (img / 255).astype(np.float32)
roi32f = (roi_img / 255).astype(np.float32)
cv.multiply(img32f, img32f_w, dst=img32f)
cv.multiply(roi32f, roi32f_w, dst=roi32f)
roi32f += img32f
roi_img[...] = np.round(roi32f * 255)
# This is quite naive implementations used as a simple reference
# doesn't consider corner cases.
def draw_mosaic(self, img, mos, cell_sz, decim):
x,y,w,h = mos
mosaic_area = img[x:x+w, y:y+h, :]
for i in range(0, mosaic_area.shape[0], cell_sz):
for j in range(0, mosaic_area.shape[1], cell_sz):
cell_roi = mosaic_area[j:j+cell_sz, i:i+cell_sz, :]
s0, s1, s2 = cv.mean(cell_roi)[:3]
mosaic_area[j:j+cell_sz, i:i+cell_sz] = (round(s0), round(s1), round(s2))
def render_primitives_bgr_ref(self, img):
cv.rectangle(img, self.rect, self.rcolor, self.rthick, self.rlt, self.rshift)
cv.putText(img, self.text, self.org, self.ff, self.fs, self.tcolor, self.tthick, self.tlt, self.blo)
cv.circle(img, self.center, self.radius, self.ccolor, self.cthick, self.clt, self.cshift)
cv.line(img, self.pt1, self.pt2, self.lcolor, self.lthick, self.llt, self.lshift)
cv.fillPoly(img, np.expand_dims(np.array([self.pts]), axis=0), self.pcolor, self.plt, self.pshift)
self.draw_mosaic(img, self.mos, self.cell_sz, self.decim)
self.blend_img(img, self.iorg, self.img, self.alpha)
def render_primitives_nv12_ref(self, y_plane, uv_plane):
yuv = self.cvt_nv12_to_yuv(y_plane, uv_plane)
cv.rectangle(yuv, self.rect, self.cvt_bgr_to_yuv_color(self.rcolor), self.rthick, self.rlt, self.rshift)
cv.putText(yuv, self.text, self.org, self.ff, self.fs, self.cvt_bgr_to_yuv_color(self.tcolor), self.tthick, self.tlt, self.blo)
cv.circle(yuv, self.center, self.radius, self.cvt_bgr_to_yuv_color(self.ccolor), self.cthick, self.clt, self.cshift)
cv.line(yuv, self.pt1, self.pt2, self.cvt_bgr_to_yuv_color(self.lcolor), self.lthick, self.llt, self.lshift)
cv.fillPoly(yuv, np.expand_dims(np.array([self.pts]), axis=0), self.cvt_bgr_to_yuv_color(self.pcolor), self.plt, self.pshift)
self.draw_mosaic(yuv, self.mos, self.cell_sz, self.decim)
self.blend_img(yuv, self.iorg, cv.cvtColor(self.img, cv.COLOR_BGR2YUV), self.alpha)
self.cvt_yuv_to_nv12(yuv, y_plane, uv_plane)
def test_render_primitives_on_bgr_graph(self):
expected = np.zeros(self.size, dtype=np.uint8)
actual = np.array(expected, copy=True)
# OpenCV
self.render_primitives_bgr_ref(expected)
# G-API
g_in = cv.GMat()
g_prims = cv.GArray.Prim()
g_out = cv.gapi.wip.draw.render3ch(g_in, g_prims)
comp = cv.GComputation(cv.GIn(g_in, g_prims), cv.GOut(g_out))
actual = comp.apply(cv.gin(actual, self.prims))
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
def test_render_primitives_on_bgr_function(self):
expected = np.zeros(self.size, dtype=np.uint8)
actual = np.array(expected, copy=True)
# OpenCV
self.render_primitives_bgr_ref(expected)
# G-API
cv.gapi.wip.draw.render(actual, self.prims)
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
def test_render_primitives_on_nv12_graph(self):
y_expected = np.zeros((self.size[0], self.size[1], 1), dtype=np.uint8)
uv_expected = np.zeros((self.size[0] // 2, self.size[1] // 2, 2), dtype=np.uint8)
y_actual = np.array(y_expected, copy=True)
uv_actual = np.array(uv_expected, copy=True)
# OpenCV
self.render_primitives_nv12_ref(y_expected, uv_expected)
# G-API
g_y = cv.GMat()
g_uv = cv.GMat()
g_prims = cv.GArray.Prim()
g_out_y, g_out_uv = cv.gapi.wip.draw.renderNV12(g_y, g_uv, g_prims)
comp = cv.GComputation(cv.GIn(g_y, g_uv, g_prims), cv.GOut(g_out_y, g_out_uv))
y_actual, uv_actual = comp.apply(cv.gin(y_actual, uv_actual, self.prims))
self.assertEqual(0.0, cv.norm(y_expected, y_actual, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(uv_expected, uv_actual, cv.NORM_INF))
def test_render_primitives_on_nv12_function(self):
y_expected = np.zeros((self.size[0], self.size[1], 1), dtype=np.uint8)
uv_expected = np.zeros((self.size[0] // 2, self.size[1] // 2, 2), dtype=np.uint8)
y_actual = np.array(y_expected, copy=True)
uv_actual = np.array(uv_expected, copy=True)
# OpenCV
self.render_primitives_nv12_ref(y_expected, uv_expected)
# G-API
cv.gapi.wip.draw.render(y_actual, uv_actual, self.prims)
self.assertEqual(0.0, cv.norm(y_expected, y_actual, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(uv_expected, uv_actual, cv.NORM_INF))
except unittest.SkipTest as e:
message = str(e)
class TestSkip(unittest.TestCase):
def setUp(self):
self.skipTest('Skip tests: ' + message)
def test_skip():
pass
pass
if __name__ == '__main__':
NewOpenCVTests.bootstrap()

View File

@ -3,41 +3,678 @@
import numpy as np
import cv2 as cv
import os
import sys
import unittest
from tests_common import NewOpenCVTests
# Plaidml is an optional backend
pkgs = [
('ocl' , cv.gapi.core.ocl.kernels()),
('cpu' , cv.gapi.core.cpu.kernels()),
('fluid' , cv.gapi.core.fluid.kernels())
# ('plaidml', cv.gapi.core.plaidml.kernels())
]
try:
if sys.version_info[:2] < (3, 0):
raise unittest.SkipTest('Python 2.x is not supported')
# Plaidml is an optional backend
pkgs = [
('ocl' , cv.gapi.core.ocl.kernels()),
('cpu' , cv.gapi.core.cpu.kernels()),
('fluid' , cv.gapi.core.fluid.kernels())
# ('plaidml', cv.gapi.core.plaidml.kernels())
]
class gapi_sample_pipelines(NewOpenCVTests):
@cv.gapi.op('custom.add', in_types=[cv.GMat, cv.GMat, int], out_types=[cv.GMat])
class GAdd:
"""Calculates sum of two matrices."""
# NB: This test check multiple outputs for operation
def test_mean_over_r(self):
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in_mat = cv.imread(img_path)
@staticmethod
def outMeta(desc1, desc2, depth):
return desc1
# # OpenCV
_, _, r_ch = cv.split(in_mat)
expected = cv.mean(r_ch)
# G-API
g_in = cv.GMat()
b, g, r = cv.gapi.split3(g_in)
g_out = cv.gapi.mean(r)
comp = cv.GComputation(g_in, g_out)
@cv.gapi.kernel(GAdd)
class GAddImpl:
"""Implementation for GAdd operation."""
@staticmethod
def run(img1, img2, dtype):
return cv.add(img1, img2)
@cv.gapi.op('custom.split3', in_types=[cv.GMat], out_types=[cv.GMat, cv.GMat, cv.GMat])
class GSplit3:
"""Divides a 3-channel matrix into 3 single-channel matrices."""
@staticmethod
def outMeta(desc):
out_desc = desc.withType(desc.depth, 1)
return out_desc, out_desc, out_desc
@cv.gapi.kernel(GSplit3)
class GSplit3Impl:
"""Implementation for GSplit3 operation."""
@staticmethod
def run(img):
# NB: cv.split return list but g-api requires tuple in multiple output case
return tuple(cv.split(img))
@cv.gapi.op('custom.mean', in_types=[cv.GMat], out_types=[cv.GScalar])
class GMean:
"""Calculates the mean value M of matrix elements."""
@staticmethod
def outMeta(desc):
return cv.empty_scalar_desc()
@cv.gapi.kernel(GMean)
class GMeanImpl:
"""Implementation for GMean operation."""
@staticmethod
def run(img):
# NB: cv.split return list but g-api requires tuple in multiple output case
return cv.mean(img)
@cv.gapi.op('custom.addC', in_types=[cv.GMat, cv.GScalar, int], out_types=[cv.GMat])
class GAddC:
"""Adds a given scalar value to each element of given matrix."""
@staticmethod
def outMeta(mat_desc, scalar_desc, dtype):
return mat_desc
@cv.gapi.kernel(GAddC)
class GAddCImpl:
"""Implementation for GAddC operation."""
@staticmethod
def run(img, sc, dtype):
# NB: dtype is just ignored in this implementation.
# Moreover from G-API kernel got scalar as tuples with 4 elements
# where the last element is equal to zero, just cut him for broadcasting.
return img + np.array(sc, dtype=np.uint8)[:-1]
@cv.gapi.op('custom.size', in_types=[cv.GMat], out_types=[cv.GOpaque.Size])
class GSize:
"""Gets dimensions from input matrix."""
@staticmethod
def outMeta(mat_desc):
return cv.empty_gopaque_desc()
@cv.gapi.kernel(GSize)
class GSizeImpl:
"""Implementation for GSize operation."""
@staticmethod
def run(img):
# NB: Take only H, W, because the operation should return cv::Size which is 2D.
return img.shape[:2]
@cv.gapi.op('custom.sizeR', in_types=[cv.GOpaque.Rect], out_types=[cv.GOpaque.Size])
class GSizeR:
"""Gets dimensions from rectangle."""
@staticmethod
def outMeta(opaq_desc):
return cv.empty_gopaque_desc()
@cv.gapi.kernel(GSizeR)
class GSizeRImpl:
"""Implementation for GSizeR operation."""
@staticmethod
def run(rect):
# NB: rect - is tuple (x, y, h, w)
return (rect[2], rect[3])
@cv.gapi.op('custom.boundingRect', in_types=[cv.GArray.Point], out_types=[cv.GOpaque.Rect])
class GBoundingRect:
"""Calculates minimal up-right bounding rectangle for the specified
9 point set or non-zero pixels of gray-scale image."""
@staticmethod
def outMeta(arr_desc):
return cv.empty_gopaque_desc()
@cv.gapi.kernel(GBoundingRect)
class GBoundingRectImpl:
"""Implementation for GBoundingRect operation."""
@staticmethod
def run(array):
# NB: OpenCV - numpy array (n_points x 2).
# G-API - array of tuples (n_points).
return cv.boundingRect(np.array(array))
@cv.gapi.op('custom.goodFeaturesToTrack',
in_types=[cv.GMat, int, float, float, int, bool, float],
out_types=[cv.GArray.Point2f])
class GGoodFeatures:
"""Finds the most prominent corners in the image
or in the specified image region."""
@staticmethod
def outMeta(desc, max_corners, quality_lvl,
min_distance, block_sz,
use_harris_detector, k):
return cv.empty_array_desc()
@cv.gapi.kernel(GGoodFeatures)
class GGoodFeaturesImpl:
"""Implementation for GGoodFeatures operation."""
@staticmethod
def run(img, max_corners, quality_lvl,
min_distance, block_sz,
use_harris_detector, k):
features = cv.goodFeaturesToTrack(img, max_corners, quality_lvl,
min_distance, mask=None,
blockSize=block_sz,
useHarrisDetector=use_harris_detector, k=k)
# NB: The operation output is cv::GArray<cv::Pointf>, so it should be mapped
# to python paramaters like this: [(1.2, 3.4), (5.2, 3.2)], because the cv::Point2f
# according to opencv rules mapped to the tuple and cv::GArray<> mapped to the list.
# OpenCV returns np.array with shape (n_features, 1, 2), so let's to convert it to list
# tuples with size == n_features.
features = list(map(tuple, features.reshape(features.shape[0], -1)))
return features
# To validate invalid cases
def create_op(in_types, out_types):
@cv.gapi.op('custom.op', in_types=in_types, out_types=out_types)
class Op:
"""Custom operation for testing."""
@staticmethod
def outMeta(desc):
raise NotImplementedError("outMeta isn't imlemented")
return Op
class gapi_sample_pipelines(NewOpenCVTests):
def test_custom_op_add(self):
sz = (3, 3)
in_mat1 = np.full(sz, 45, dtype=np.uint8)
in_mat2 = np.full(sz, 50, dtype=np.uint8)
# OpenCV
expected = cv.add(in_mat1, in_mat2)
# G-API
g_in1 = cv.GMat()
g_in2 = cv.GMat()
g_out = GAdd.on(g_in1, g_in2, cv.CV_8UC1)
comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out))
pkg = cv.gapi.kernels(GAddImpl)
actual = comp.apply(cv.gin(in_mat1, in_mat2), args=cv.gapi.compile_args(pkg))
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
def test_custom_op_split3(self):
sz = (4, 4)
in_ch1 = np.full(sz, 1, dtype=np.uint8)
in_ch2 = np.full(sz, 2, dtype=np.uint8)
in_ch3 = np.full(sz, 3, dtype=np.uint8)
# H x W x C
in_mat = np.stack((in_ch1, in_ch2, in_ch3), axis=2)
# G-API
g_in = cv.GMat()
g_ch1, g_ch2, g_ch3 = GSplit3.on(g_in)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_ch1, g_ch2, g_ch3))
pkg = cv.gapi.kernels(GSplit3Impl)
ch1, ch2, ch3 = comp.apply(cv.gin(in_mat), args=cv.gapi.compile_args(pkg))
self.assertEqual(0.0, cv.norm(in_ch1, ch1, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(in_ch2, ch2, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(in_ch3, ch3, cv.NORM_INF))
def test_custom_op_mean(self):
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in_mat = cv.imread(img_path)
# OpenCV
expected = cv.mean(in_mat)
# G-API
g_in = cv.GMat()
g_out = GMean.on(g_in)
comp = cv.GComputation(g_in, g_out)
pkg = cv.gapi.kernels(GMeanImpl)
actual = comp.apply(cv.gin(in_mat), args=cv.gapi.compile_args(pkg))
for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in_mat), args=cv.compile_args(pkg))
# Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend')
self.assertEqual(expected, actual)
def test_custom_op_addC(self):
sz = (3, 3, 3)
in_mat = np.full(sz, 45, dtype=np.uint8)
sc = (50, 10, 20)
# Numpy reference, make array from sc to keep uint8 dtype.
expected = in_mat + np.array(sc, dtype=np.uint8)
# G-API
g_in = cv.GMat()
g_sc = cv.GScalar()
g_out = GAddC.on(g_in, g_sc, cv.CV_8UC1)
comp = cv.GComputation(cv.GIn(g_in, g_sc), cv.GOut(g_out))
pkg = cv.gapi.kernels(GAddCImpl)
actual = comp.apply(cv.gin(in_mat, sc), args=cv.gapi.compile_args(pkg))
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
def test_custom_op_size(self):
sz = (100, 150, 3)
in_mat = np.full(sz, 45, dtype=np.uint8)
# Open_cV
expected = (100, 150)
# G-API
g_in = cv.GMat()
g_sz = GSize.on(g_in)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_sz))
pkg = cv.gapi.kernels(GSizeImpl)
actual = comp.apply(cv.gin(in_mat), args=cv.gapi.compile_args(pkg))
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
def test_custom_op_sizeR(self):
# x, y, h, w
roi = (10, 15, 100, 150)
expected = (100, 150)
# G-API
g_r = cv.GOpaque.Rect()
g_sz = GSizeR.on(g_r)
comp = cv.GComputation(cv.GIn(g_r), cv.GOut(g_sz))
pkg = cv.gapi.kernels(GSizeRImpl)
actual = comp.apply(cv.gin(roi), args=cv.gapi.compile_args(pkg))
# cv.norm works with tuples ?
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
def test_custom_op_boundingRect(self):
points = [(0,0), (0,1), (1,0), (1,1)]
# OpenCV
expected = cv.boundingRect(np.array(points))
# G-API
g_pts = cv.GArray.Point()
g_br = GBoundingRect.on(g_pts)
comp = cv.GComputation(cv.GIn(g_pts), cv.GOut(g_br))
pkg = cv.gapi.kernels(GBoundingRectImpl)
actual = comp.apply(cv.gin(points), args=cv.gapi.compile_args(pkg))
# cv.norm works with tuples ?
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
def test_custom_op_goodFeaturesToTrack(self):
# G-API
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in_mat = cv.cvtColor(cv.imread(img_path), cv.COLOR_RGB2GRAY)
# NB: goodFeaturesToTrack configuration
max_corners = 50
quality_lvl = 0.01
min_distance = 10.0
block_sz = 3
use_harris_detector = True
k = 0.04
# OpenCV
expected = cv.goodFeaturesToTrack(in_mat, max_corners, quality_lvl,
min_distance, mask=None,
blockSize=block_sz, useHarrisDetector=use_harris_detector, k=k)
# G-API
g_in = cv.GMat()
g_out = GGoodFeatures.on(g_in, max_corners, quality_lvl,
min_distance, block_sz, use_harris_detector, k)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out))
pkg = cv.gapi.kernels(GGoodFeaturesImpl)
actual = comp.apply(cv.gin(in_mat), args=cv.gapi.compile_args(pkg))
# NB: OpenCV & G-API have different output types.
# OpenCV - numpy array with shape (num_points, 1, 2)
# G-API - list of tuples with size - num_points
# Comparison
self.assertEqual(0.0, cv.norm(expected.flatten(),
np.array(actual, dtype=np.float32).flatten(), cv.NORM_INF))
def test_invalid_op(self):
# NB: Empty input types list
with self.assertRaises(Exception): create_op(in_types=[], out_types=[cv.GMat])
# NB: Empty output types list
with self.assertRaises(Exception): create_op(in_types=[cv.GMat], out_types=[])
# Invalid output types
with self.assertRaises(Exception): create_op(in_types=[cv.GMat], out_types=[int])
with self.assertRaises(Exception): create_op(in_types=[cv.GMat], out_types=[cv.GMat, int])
with self.assertRaises(Exception): create_op(in_types=[cv.GMat], out_types=[str, cv.GScalar])
def test_invalid_op_input(self):
# NB: Check GMat/GScalar
with self.assertRaises(Exception): create_op([cv.GMat] , [cv.GScalar]).on(cv.GScalar())
with self.assertRaises(Exception): create_op([cv.GScalar], [cv.GScalar]).on(cv.GMat())
# NB: Check GOpaque
op = create_op([cv.GOpaque.Rect], [cv.GMat])
with self.assertRaises(Exception): op.on(cv.GOpaque.Bool())
with self.assertRaises(Exception): op.on(cv.GOpaque.Int())
with self.assertRaises(Exception): op.on(cv.GOpaque.Double())
with self.assertRaises(Exception): op.on(cv.GOpaque.Float())
with self.assertRaises(Exception): op.on(cv.GOpaque.String())
with self.assertRaises(Exception): op.on(cv.GOpaque.Point())
with self.assertRaises(Exception): op.on(cv.GOpaque.Point2f())
with self.assertRaises(Exception): op.on(cv.GOpaque.Size())
# NB: Check GArray
op = create_op([cv.GArray.Rect], [cv.GMat])
with self.assertRaises(Exception): op.on(cv.GArray.Bool())
with self.assertRaises(Exception): op.on(cv.GArray.Int())
with self.assertRaises(Exception): op.on(cv.GArray.Double())
with self.assertRaises(Exception): op.on(cv.GArray.Float())
with self.assertRaises(Exception): op.on(cv.GArray.String())
with self.assertRaises(Exception): op.on(cv.GArray.Point())
with self.assertRaises(Exception): op.on(cv.GArray.Point2f())
with self.assertRaises(Exception): op.on(cv.GArray.Size())
# Check other possible invalid options
with self.assertRaises(Exception): op.on(cv.GMat())
with self.assertRaises(Exception): op.on(cv.GScalar())
with self.assertRaises(Exception): op.on(1)
with self.assertRaises(Exception): op.on('foo')
with self.assertRaises(Exception): op.on(False)
with self.assertRaises(Exception): create_op([cv.GMat, int], [cv.GMat]).on(cv.GMat(), 'foo')
with self.assertRaises(Exception): create_op([cv.GMat, int], [cv.GMat]).on(cv.GMat())
def test_stateful_kernel(self):
@cv.gapi.op('custom.sum', in_types=[cv.GArray.Int], out_types=[cv.GOpaque.Int])
class GSum:
@staticmethod
def outMeta(arr_desc):
return cv.empty_gopaque_desc()
@cv.gapi.kernel(GSum)
class GSumImpl:
last_result = 0
@staticmethod
def run(arr):
GSumImpl.last_result = sum(arr)
return GSumImpl.last_result
g_in = cv.GArray.Int()
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(GSum.on(g_in)))
s = comp.apply(cv.gin([1, 2, 3, 4]), args=cv.gapi.compile_args(cv.gapi.kernels(GSumImpl)))
self.assertEqual(10, s)
s = comp.apply(cv.gin([1, 2, 8, 7]), args=cv.gapi.compile_args(cv.gapi.kernels(GSumImpl)))
self.assertEqual(18, s)
self.assertEqual(18, GSumImpl.last_result)
def test_opaq_with_custom_type(self):
@cv.gapi.op('custom.op', in_types=[cv.GOpaque.Any, cv.GOpaque.String], out_types=[cv.GOpaque.Any])
class GLookUp:
@staticmethod
def outMeta(opaq_desc0, opaq_desc1):
return cv.empty_gopaque_desc()
@cv.gapi.kernel(GLookUp)
class GLookUpImpl:
@staticmethod
def run(table, key):
return table[key]
g_table = cv.GOpaque.Any()
g_key = cv.GOpaque.String()
g_out = GLookUp.on(g_table, g_key)
comp = cv.GComputation(cv.GIn(g_table, g_key), cv.GOut(g_out))
table = {
'int': 42,
'str': 'hello, world!',
'tuple': (42, 42)
}
out = comp.apply(cv.gin(table, 'int'), args=cv.gapi.compile_args(cv.gapi.kernels(GLookUpImpl)))
self.assertEqual(42, out)
out = comp.apply(cv.gin(table, 'str'), args=cv.gapi.compile_args(cv.gapi.kernels(GLookUpImpl)))
self.assertEqual('hello, world!', out)
out = comp.apply(cv.gin(table, 'tuple'), args=cv.gapi.compile_args(cv.gapi.kernels(GLookUpImpl)))
self.assertEqual((42, 42), out)
def test_array_with_custom_type(self):
@cv.gapi.op('custom.op', in_types=[cv.GArray.Any, cv.GArray.Any], out_types=[cv.GArray.Any])
class GConcat:
@staticmethod
def outMeta(arr_desc0, arr_desc1):
return cv.empty_array_desc()
@cv.gapi.kernel(GConcat)
class GConcatImpl:
@staticmethod
def run(arr0, arr1):
return arr0 + arr1
g_arr0 = cv.GArray.Any()
g_arr1 = cv.GArray.Any()
g_out = GConcat.on(g_arr0, g_arr1)
comp = cv.GComputation(cv.GIn(g_arr0, g_arr1), cv.GOut(g_out))
arr0 = ((2, 2), 2.0)
arr1 = (3, 'str')
out = comp.apply(cv.gin(arr0, arr1),
args=cv.gapi.compile_args(cv.gapi.kernels(GConcatImpl)))
self.assertEqual(arr0 + arr1, out)
def test_raise_in_kernel(self):
@cv.gapi.op('custom.op', in_types=[cv.GMat, cv.GMat], out_types=[cv.GMat])
class GAdd:
@staticmethod
def outMeta(desc0, desc1):
return desc0
@cv.gapi.kernel(GAdd)
class GAddImpl:
@staticmethod
def run(img0, img1):
raise Exception('Error')
return img0 + img1
g_in0 = cv.GMat()
g_in1 = cv.GMat()
g_out = GAdd.on(g_in0, g_in1)
comp = cv.GComputation(cv.GIn(g_in0, g_in1), cv.GOut(g_out))
img0 = np.array([1, 2, 3])
img1 = np.array([1, 2, 3])
with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1),
args=cv.gapi.compile_args(
cv.gapi.kernels(GAddImpl)))
def test_raise_in_outMeta(self):
@cv.gapi.op('custom.op', in_types=[cv.GMat, cv.GMat], out_types=[cv.GMat])
class GAdd:
@staticmethod
def outMeta(desc0, desc1):
raise NotImplementedError("outMeta isn't implemented")
@cv.gapi.kernel(GAdd)
class GAddImpl:
@staticmethod
def run(img0, img1):
return img0 + img1
g_in0 = cv.GMat()
g_in1 = cv.GMat()
g_out = GAdd.on(g_in0, g_in1)
comp = cv.GComputation(cv.GIn(g_in0, g_in1), cv.GOut(g_out))
img0 = np.array([1, 2, 3])
img1 = np.array([1, 2, 3])
with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1),
args=cv.gapi.compile_args(
cv.gapi.kernels(GAddImpl)))
def test_invalid_outMeta(self):
@cv.gapi.op('custom.op', in_types=[cv.GMat, cv.GMat], out_types=[cv.GMat])
class GAdd:
@staticmethod
def outMeta(desc0, desc1):
# Invalid outMeta
return cv.empty_gopaque_desc()
@cv.gapi.kernel(GAdd)
class GAddImpl:
@staticmethod
def run(img0, img1):
return img0 + img1
g_in0 = cv.GMat()
g_in1 = cv.GMat()
g_out = GAdd.on(g_in0, g_in1)
comp = cv.GComputation(cv.GIn(g_in0, g_in1), cv.GOut(g_out))
img0 = np.array([1, 2, 3])
img1 = np.array([1, 2, 3])
# FIXME: Cause Bad variant access.
# Need to provide more descriptive error messsage.
with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1),
args=cv.gapi.compile_args(
cv.gapi.kernels(GAddImpl)))
def test_pipeline_with_custom_kernels(self):
@cv.gapi.op('custom.resize', in_types=[cv.GMat, tuple], out_types=[cv.GMat])
class GResize:
@staticmethod
def outMeta(desc, size):
return desc.withSize(size)
@cv.gapi.kernel(GResize)
class GResizeImpl:
@staticmethod
def run(img, size):
return cv.resize(img, size)
@cv.gapi.op('custom.transpose', in_types=[cv.GMat, tuple], out_types=[cv.GMat])
class GTranspose:
@staticmethod
def outMeta(desc, order):
return desc
@cv.gapi.kernel(GTranspose)
class GTransposeImpl:
@staticmethod
def run(img, order):
return np.transpose(img, order)
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img = cv.imread(img_path)
size = (32, 32)
order = (1, 0, 2)
# Dummy pipeline just to validate this case:
# gapi -> custom -> custom -> gapi
# OpenCV
expected = cv.cvtColor(img, cv.COLOR_BGR2RGB)
expected = cv.resize(expected, size)
expected = np.transpose(expected, order)
expected = cv.mean(expected)
# G-API
g_bgr = cv.GMat()
g_rgb = cv.gapi.BGR2RGB(g_bgr)
g_resized = GResize.on(g_rgb, size)
g_transposed = GTranspose.on(g_resized, order)
g_mean = cv.gapi.mean(g_transposed)
comp = cv.GComputation(cv.GIn(g_bgr), cv.GOut(g_mean))
actual = comp.apply(cv.gin(img), args=cv.gapi.compile_args(
cv.gapi.kernels(GResizeImpl, GTransposeImpl)))
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
except unittest.SkipTest as e:
message = str(e)
class TestSkip(unittest.TestCase):
def setUp(self):
self.skipTest('Skip tests: ' + message)
def test_skip():
pass
pass
if __name__ == '__main__':

View File

@ -3,199 +3,366 @@
import numpy as np
import cv2 as cv
import os
import sys
import unittest
import time
from tests_common import NewOpenCVTests
class test_gapi_streaming(NewOpenCVTests):
def test_image_input(self):
sz = (1280, 720)
in_mat = np.random.randint(0, 100, sz).astype(np.uint8)
# OpenCV
expected = cv.medianBlur(in_mat, 3)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.medianBlur(g_in, 3)
c = cv.GComputation(g_in, g_out)
ccomp = c.compileStreaming(cv.descr_of(cv.gin(in_mat)))
ccomp.setSource(cv.gin(in_mat))
ccomp.start()
_, actual = ccomp.pull()
# Assert
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
try:
if sys.version_info[:2] < (3, 0):
raise unittest.SkipTest('Python 2.x is not supported')
def test_video_input(self):
ksize = 3
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
@cv.gapi.op('custom.delay', in_types=[cv.GMat], out_types=[cv.GMat])
class GDelay:
"""Delay for 10 ms."""
# OpenCV
cap = cv.VideoCapture(path)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.medianBlur(g_in, ksize)
c = cv.GComputation(g_in, g_out)
ccomp = c.compileStreaming()
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(source)
ccomp.start()
# Assert
max_num_frames = 10
proc_num_frames = 0
while cap.isOpened():
has_expected, expected = cap.read()
has_actual, actual = ccomp.pull()
self.assertEqual(has_expected, has_actual)
if not has_actual:
break
self.assertEqual(0.0, cv.norm(cv.medianBlur(expected, ksize), actual, cv.NORM_INF))
proc_num_frames += 1
if proc_num_frames == max_num_frames:
break;
@staticmethod
def outMeta(desc):
return desc
def test_video_split3(self):
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
@cv.gapi.kernel(GDelay)
class GDelayImpl:
"""Implementation for GDelay operation."""
# OpenCV
cap = cv.VideoCapture(path)
# G-API
g_in = cv.GMat()
b, g, r = cv.gapi.split3(g_in)
c = cv.GComputation(cv.GIn(g_in), cv.GOut(b, g, r))
ccomp = c.compileStreaming()
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(source)
ccomp.start()
# Assert
max_num_frames = 10
proc_num_frames = 0
while cap.isOpened():
has_expected, frame = cap.read()
has_actual, actual = ccomp.pull()
self.assertEqual(has_expected, has_actual)
if not has_actual:
break
expected = cv.split(frame)
for e, a in zip(expected, actual):
self.assertEqual(0.0, cv.norm(e, a, cv.NORM_INF))
proc_num_frames += 1
if proc_num_frames == max_num_frames:
break;
@staticmethod
def run(img):
time.sleep(0.01)
return img
def test_video_add(self):
sz = (576, 768, 3)
in_mat = np.random.randint(0, 100, sz).astype(np.uint8)
class test_gapi_streaming(NewOpenCVTests):
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
# OpenCV
cap = cv.VideoCapture(path)
# G-API
g_in1 = cv.GMat()
g_in2 = cv.GMat()
out = cv.gapi.add(g_in1, g_in2)
c = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(out))
ccomp = c.compileStreaming()
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(cv.gin(source, in_mat))
ccomp.start()
# Assert
max_num_frames = 10
proc_num_frames = 0
while cap.isOpened():
has_expected, frame = cap.read()
has_actual, actual = ccomp.pull()
self.assertEqual(has_expected, has_actual)
if not has_actual:
break
expected = cv.add(frame, in_mat)
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
proc_num_frames += 1
if proc_num_frames == max_num_frames:
break;
def test_video_good_features_to_track(self):
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
# NB: goodFeaturesToTrack configuration
max_corners = 50
quality_lvl = 0.01
min_distance = 10
block_sz = 3
use_harris_detector = True
k = 0.04
mask = None
# OpenCV
cap = cv.VideoCapture(path)
# G-API
g_in = cv.GMat()
g_gray = cv.gapi.RGB2Gray(g_in)
g_out = cv.gapi.goodFeaturesToTrack(g_gray, max_corners, quality_lvl,
min_distance, mask, block_sz, use_harris_detector, k)
c = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out))
ccomp = c.compileStreaming()
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(source)
ccomp.start()
# Assert
max_num_frames = 10
proc_num_frames = 0
while cap.isOpened():
has_expected, frame = cap.read()
has_actual, actual = ccomp.pull()
self.assertEqual(has_expected, has_actual)
if not has_actual:
break
def test_image_input(self):
sz = (1280, 720)
in_mat = np.random.randint(0, 100, sz).astype(np.uint8)
# OpenCV
frame = cv.cvtColor(frame, cv.COLOR_RGB2GRAY)
expected = cv.goodFeaturesToTrack(frame, max_corners, quality_lvl,
min_distance, mask=mask,
blockSize=block_sz, useHarrisDetector=use_harris_detector, k=k)
for e, a in zip(expected, actual):
# NB: OpenCV & G-API have different output shapes:
# OpenCV - (num_points, 1, 2)
# G-API - (num_points, 2)
self.assertEqual(0.0, cv.norm(e.flatten(), a.flatten(), cv.NORM_INF))
expected = cv.medianBlur(in_mat, 3)
proc_num_frames += 1
if proc_num_frames == max_num_frames:
break;
# G-API
g_in = cv.GMat()
g_out = cv.gapi.medianBlur(g_in, 3)
c = cv.GComputation(g_in, g_out)
ccomp = c.compileStreaming(cv.gapi.descr_of(in_mat))
ccomp.setSource(cv.gin(in_mat))
ccomp.start()
_, actual = ccomp.pull()
# Assert
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
def test_video_input(self):
ksize = 3
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
# OpenCV
cap = cv.VideoCapture(path)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.medianBlur(g_in, ksize)
c = cv.GComputation(g_in, g_out)
ccomp = c.compileStreaming()
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(cv.gin(source))
ccomp.start()
# Assert
max_num_frames = 10
proc_num_frames = 0
while cap.isOpened():
has_expected, expected = cap.read()
has_actual, actual = ccomp.pull()
self.assertEqual(has_expected, has_actual)
if not has_actual:
break
self.assertEqual(0.0, cv.norm(cv.medianBlur(expected, ksize), actual, cv.NORM_INF))
proc_num_frames += 1
if proc_num_frames == max_num_frames:
break
def test_video_split3(self):
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
# OpenCV
cap = cv.VideoCapture(path)
# G-API
g_in = cv.GMat()
b, g, r = cv.gapi.split3(g_in)
c = cv.GComputation(cv.GIn(g_in), cv.GOut(b, g, r))
ccomp = c.compileStreaming()
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(cv.gin(source))
ccomp.start()
# Assert
max_num_frames = 10
proc_num_frames = 0
while cap.isOpened():
has_expected, frame = cap.read()
has_actual, actual = ccomp.pull()
self.assertEqual(has_expected, has_actual)
if not has_actual:
break
expected = cv.split(frame)
for e, a in zip(expected, actual):
self.assertEqual(0.0, cv.norm(e, a, cv.NORM_INF))
proc_num_frames += 1
if proc_num_frames == max_num_frames:
break
def test_video_add(self):
sz = (576, 768, 3)
in_mat = np.random.randint(0, 100, sz).astype(np.uint8)
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
# OpenCV
cap = cv.VideoCapture(path)
# G-API
g_in1 = cv.GMat()
g_in2 = cv.GMat()
out = cv.gapi.add(g_in1, g_in2)
c = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(out))
ccomp = c.compileStreaming()
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(cv.gin(source, in_mat))
ccomp.start()
# Assert
max_num_frames = 10
proc_num_frames = 0
while cap.isOpened():
has_expected, frame = cap.read()
has_actual, actual = ccomp.pull()
self.assertEqual(has_expected, has_actual)
if not has_actual:
break
expected = cv.add(frame, in_mat)
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
proc_num_frames += 1
if proc_num_frames == max_num_frames:
break
def test_video_good_features_to_track(self):
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
# NB: goodFeaturesToTrack configuration
max_corners = 50
quality_lvl = 0.01
min_distance = 10
block_sz = 3
use_harris_detector = True
k = 0.04
mask = None
# OpenCV
cap = cv.VideoCapture(path)
# G-API
g_in = cv.GMat()
g_gray = cv.gapi.RGB2Gray(g_in)
g_out = cv.gapi.goodFeaturesToTrack(g_gray, max_corners, quality_lvl,
min_distance, mask, block_sz, use_harris_detector, k)
c = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out))
ccomp = c.compileStreaming()
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(cv.gin(source))
ccomp.start()
# Assert
max_num_frames = 10
proc_num_frames = 0
while cap.isOpened():
has_expected, frame = cap.read()
has_actual, actual = ccomp.pull()
self.assertEqual(has_expected, has_actual)
if not has_actual:
break
# OpenCV
frame = cv.cvtColor(frame, cv.COLOR_RGB2GRAY)
expected = cv.goodFeaturesToTrack(frame, max_corners, quality_lvl,
min_distance, mask=mask,
blockSize=block_sz, useHarrisDetector=use_harris_detector, k=k)
for e, a in zip(expected, actual):
# NB: OpenCV & G-API have different output shapes:
# OpenCV - (num_points, 1, 2)
# G-API - (num_points, 2)
self.assertEqual(0.0, cv.norm(e.flatten(),
np.array(a, np.float32).flatten(),
cv.NORM_INF))
proc_num_frames += 1
if proc_num_frames == max_num_frames:
break
def test_gapi_streaming_meta(self):
ksize = 3
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
# G-API
g_in = cv.GMat()
g_ts = cv.gapi.streaming.timestamp(g_in)
g_seqno = cv.gapi.streaming.seqNo(g_in)
g_seqid = cv.gapi.streaming.seq_id(g_in)
c = cv.GComputation(cv.GIn(g_in), cv.GOut(g_ts, g_seqno, g_seqid))
ccomp = c.compileStreaming()
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(cv.gin(source))
ccomp.start()
# Assert
max_num_frames = 10
curr_frame_number = 0
while True:
has_frame, (ts, seqno, seqid) = ccomp.pull()
if not has_frame:
break
self.assertEqual(curr_frame_number, seqno)
self.assertEqual(curr_frame_number, seqid)
curr_frame_number += 1
if curr_frame_number == max_num_frames:
break
def test_desync(self):
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']])
# G-API
g_in = cv.GMat()
g_out1 = cv.gapi.copy(g_in)
des = cv.gapi.streaming.desync(g_in)
g_out2 = GDelay.on(des)
c = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out1, g_out2))
kernels = cv.gapi.kernels(GDelayImpl)
ccomp = c.compileStreaming(args=cv.gapi.compile_args(kernels))
source = cv.gapi.wip.make_capture_src(path)
ccomp.setSource(cv.gin(source))
ccomp.start()
# Assert
max_num_frames = 10
proc_num_frames = 0
out_counter = 0
desync_out_counter = 0
none_counter = 0
while True:
has_frame, (out1, out2) = ccomp.pull()
if not has_frame:
break
if not out1 is None:
out_counter += 1
if not out2 is None:
desync_out_counter += 1
else:
none_counter += 1
proc_num_frames += 1
if proc_num_frames == max_num_frames:
ccomp.stop()
break
self.assertLess(0, proc_num_frames)
self.assertLess(desync_out_counter, out_counter)
self.assertLess(0, none_counter)
def test_compile_streaming_empty(self):
g_in = cv.GMat()
comp = cv.GComputation(g_in, cv.gapi.medianBlur(g_in, 3))
comp.compileStreaming()
def test_compile_streaming_args(self):
g_in = cv.GMat()
comp = cv.GComputation(g_in, cv.gapi.medianBlur(g_in, 3))
comp.compileStreaming(cv.gapi.compile_args(cv.gapi.streaming.queue_capacity(1)))
def test_compile_streaming_descr_of(self):
g_in = cv.GMat()
comp = cv.GComputation(g_in, cv.gapi.medianBlur(g_in, 3))
img = np.zeros((3,300,300), dtype=np.float32)
comp.compileStreaming(cv.gapi.descr_of(img))
def test_compile_streaming_descr_of_and_args(self):
g_in = cv.GMat()
comp = cv.GComputation(g_in, cv.gapi.medianBlur(g_in, 3))
img = np.zeros((3,300,300), dtype=np.float32)
comp.compileStreaming(cv.gapi.descr_of(img),
cv.gapi.compile_args(cv.gapi.streaming.queue_capacity(1)))
def test_compile_streaming_meta(self):
g_in = cv.GMat()
comp = cv.GComputation(g_in, cv.gapi.medianBlur(g_in, 3))
img = np.zeros((3,300,300), dtype=np.float32)
comp.compileStreaming([cv.GMatDesc(cv.CV_8U, 3, (300, 300))])
def test_compile_streaming_meta_and_args(self):
g_in = cv.GMat()
comp = cv.GComputation(g_in, cv.gapi.medianBlur(g_in, 3))
img = np.zeros((3,300,300), dtype=np.float32)
comp.compileStreaming([cv.GMatDesc(cv.CV_8U, 3, (300, 300))],
cv.gapi.compile_args(cv.gapi.streaming.queue_capacity(1)))
except unittest.SkipTest as e:
message = str(e)
class TestSkip(unittest.TestCase):
def setUp(self):
self.skipTest('Skip tests: ' + message)
def test_skip():
pass
pass
if __name__ == '__main__':

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
import numpy as np
import cv2 as cv
import os
import sys
import unittest
from tests_common import NewOpenCVTests
try:
if sys.version_info[:2] < (3, 0):
raise unittest.SkipTest('Python 2.x is not supported')
class gapi_types_test(NewOpenCVTests):
def test_garray_type(self):
types = [cv.gapi.CV_BOOL , cv.gapi.CV_INT , cv.gapi.CV_DOUBLE , cv.gapi.CV_FLOAT,
cv.gapi.CV_STRING, cv.gapi.CV_POINT , cv.gapi.CV_POINT2F, cv.gapi.CV_SIZE ,
cv.gapi.CV_RECT , cv.gapi.CV_SCALAR, cv.gapi.CV_MAT , cv.gapi.CV_GMAT]
for t in types:
g_array = cv.GArrayT(t)
self.assertEqual(t, g_array.type())
def test_gopaque_type(self):
types = [cv.gapi.CV_BOOL , cv.gapi.CV_INT , cv.gapi.CV_DOUBLE , cv.gapi.CV_FLOAT,
cv.gapi.CV_STRING, cv.gapi.CV_POINT , cv.gapi.CV_POINT2F, cv.gapi.CV_SIZE ,
cv.gapi.CV_RECT]
for t in types:
g_opaque = cv.GOpaqueT(t)
self.assertEqual(t, g_opaque.type())
except unittest.SkipTest as e:
message = str(e)
class TestSkip(unittest.TestCase):
def setUp(self):
self.skipTest('Skip tests: ' + message)
def test_skip():
pass
pass
if __name__ == '__main__':
NewOpenCVTests.bootstrap()

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2020 Intel Corporation
// Copyright (C) 2018-2021 Intel Corporation
#ifndef OPENCV_GAPI_CORE_PERF_TESTS_HPP
@ -28,14 +28,14 @@ namespace opencv_test
//------------------------------------------------------------------------------
class AddPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {};
class AddCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {};
class AddCPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, int, cv::GCompileArgs>> {};
class SubPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {};
class SubCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {};
class SubCPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, int, cv::GCompileArgs>> {};
class SubRCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {};
class MulPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {};
class MulDoublePerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {};
class MulCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {};
class DivPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, int, cv::GCompileArgs>> {};
class MulPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, int, double, cv::GCompileArgs>> {};
class MulDoublePerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, int, cv::GCompileArgs>> {};
class MulCPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, int, cv::GCompileArgs>> {};
class DivPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, int, double, cv::GCompileArgs>> {};
class DivCPerfTest : public TestPerfParams<tuple<cv::Size, MatType, int, cv::GCompileArgs>> {};
class DivRCPerfTest : public TestPerfParams<tuple<compare_f,cv::Size, MatType, int, cv::GCompileArgs>> {};
class MaskPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {};
@ -73,8 +73,17 @@ namespace opencv_test
class ConcatVertVecPerfTest : public TestPerfParams<tuple<cv::Size, MatType, cv::GCompileArgs>> {};
class LUTPerfTest : public TestPerfParams<tuple<MatType, MatType, cv::Size, cv::GCompileArgs>> {};
class ConvertToPerfTest : public TestPerfParams<tuple<compare_f, MatType, int, cv::Size, double, double, cv::GCompileArgs>> {};
class KMeansNDPerfTest : public TestPerfParams<tuple<cv::Size, CompareMats, int,
cv::KmeansFlags, cv::GCompileArgs>> {};
class KMeans2DPerfTest : public TestPerfParams<tuple<int, int, cv::KmeansFlags,
cv::GCompileArgs>> {};
class KMeans3DPerfTest : public TestPerfParams<tuple<int, int, cv::KmeansFlags,
cv::GCompileArgs>> {};
class TransposePerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, cv::GCompileArgs>> {};
class ResizePerfTest : public TestPerfParams<tuple<compare_f, MatType, int, cv::Size, cv::Size, cv::GCompileArgs>> {};
class BottleneckKernelsConstInputPerfTest : public TestPerfParams<tuple<compare_f, std::string, cv::GCompileArgs>> {};
class ResizeFxFyPerfTest : public TestPerfParams<tuple<compare_f, MatType, int, cv::Size, double, double, cv::GCompileArgs>> {};
class ResizeInSimpleGraphPerfTest : public TestPerfParams<tuple<compare_f, MatType, cv::Size, cv::GCompileArgs>> {};
class ParseSSDBLPerfTest : public TestPerfParams<tuple<cv::Size, float, int, cv::GCompileArgs>>, public ParserSSDTest {};
class ParseSSDPerfTest : public TestPerfParams<tuple<cv::Size, float, bool, bool, cv::GCompileArgs>>, public ParserSSDTest {};
class ParseYoloPerfTest : public TestPerfParams<tuple<cv::Size, float, float, int, cv::GCompileArgs>>, public ParserYoloTest {};

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2020 Intel Corporation
// Copyright (C) 2018-2021 Intel Corporation
#ifndef OPENCV_GAPI_CORE_PERF_TESTS_INL_HPP
@ -12,6 +12,8 @@
#include "gapi_core_perf_tests.hpp"
#include "../../test/common/gapi_core_tests_common.hpp"
namespace opencv_test
{
using namespace perf;
@ -59,10 +61,13 @@ PERF_TEST_P_(AddPerfTest, TestPerformance)
PERF_TEST_P_(AddCPerfTest, TestPerformance)
{
Size sz = get<0>(GetParam());
MatType type = get<1>(GetParam());
int dtype = get<2>(GetParam());
cv::GCompileArgs compile_args = get<3>(GetParam());
compare_f cmpF;
cv::Size sz;
MatType type = -1;
int dtype = -1;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, type, dtype, compile_args) = GetParam();
initMatsRandU(type, sz, dtype, false);
@ -86,8 +91,9 @@ PERF_TEST_P_(AddCPerfTest, TestPerformance)
}
// Comparison ////////////////////////////////////////////////////////////
// FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
EXPECT_EQ(out_mat_gapi.size(), sz);
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
}
SANITY_CHECK_NOTHING();
}
@ -132,10 +138,13 @@ PERF_TEST_P_(SubPerfTest, TestPerformance)
PERF_TEST_P_(SubCPerfTest, TestPerformance)
{
Size sz = get<0>(GetParam());
MatType type = get<1>(GetParam());
int dtype = get<2>(GetParam());
cv::GCompileArgs compile_args = get<3>(GetParam());
compare_f cmpF;
cv::Size sz;
MatType type = -1;
int dtype = -1;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, type, dtype, compile_args) = GetParam();
initMatsRandU(type, sz, dtype, false);
@ -159,8 +168,9 @@ PERF_TEST_P_(SubCPerfTest, TestPerformance)
}
// Comparison ////////////////////////////////////////////////////////////
// FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
EXPECT_EQ(out_mat_gapi.size(), sz);
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
}
SANITY_CHECK_NOTHING();
}
@ -206,19 +216,23 @@ PERF_TEST_P_(SubRCPerfTest, TestPerformance)
PERF_TEST_P_(MulPerfTest, TestPerformance)
{
Size sz = get<0>(GetParam());
MatType type = get<1>(GetParam());
int dtype = get<2>(GetParam());
cv::GCompileArgs compile_args = get<3>(GetParam());
compare_f cmpF;
cv::Size sz;
MatType type = -1;
int dtype = -1;
double scale = 1.0;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, type, dtype, scale, compile_args) = GetParam();
initMatsRandU(type, sz, dtype, false);
// OpenCV code ///////////////////////////////////////////////////////////
cv::multiply(in_mat1, in_mat2, out_mat_ocv, 1.0, dtype);
cv::multiply(in_mat1, in_mat2, out_mat_ocv, scale, dtype);
// G-API code ////////////////////////////////////////////////////////////
cv::GMat in1, in2, out;
out = cv::gapi::mul(in1, in2, 1.0, dtype);
out = cv::gapi::mul(in1, in2, scale, dtype);
cv::GComputation c(GIn(in1, in2), GOut(out));
// Warm-up graph engine:
@ -232,8 +246,9 @@ PERF_TEST_P_(MulPerfTest, TestPerformance)
}
// Comparison ////////////////////////////////////////////////////////////
// FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
EXPECT_EQ(out_mat_gapi.size(), sz);
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
}
SANITY_CHECK_NOTHING();
}
@ -242,17 +257,21 @@ PERF_TEST_P_(MulPerfTest, TestPerformance)
PERF_TEST_P_(MulDoublePerfTest, TestPerformance)
{
Size sz = get<0>(GetParam());
MatType type = get<1>(GetParam());
int dtype = get<2>(GetParam());
cv::GCompileArgs compile_args = get<3>(GetParam());
compare_f cmpF;
cv::Size sz;
MatType type = -1;
int dtype = -1;
double scale = 1.0;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, type, dtype, compile_args) = GetParam();
auto& rng = cv::theRNG();
double d = rng.uniform(0.0, 10.0);
initMatrixRandU(type, sz, dtype, false);
// OpenCV code ///////////////////////////////////////////////////////////
cv::multiply(in_mat1, d, out_mat_ocv, 1, dtype);
cv::multiply(in_mat1, d, out_mat_ocv, scale, dtype);
// G-API code ////////////////////////////////////////////////////////////
cv::GMat in1, out;
@ -270,8 +289,9 @@ PERF_TEST_P_(MulDoublePerfTest, TestPerformance)
}
// Comparison ////////////////////////////////////////////////////////////
// FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
EXPECT_EQ(out_mat_gapi.size(), sz);
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
}
SANITY_CHECK_NOTHING();
}
@ -280,15 +300,19 @@ PERF_TEST_P_(MulDoublePerfTest, TestPerformance)
PERF_TEST_P_(MulCPerfTest, TestPerformance)
{
Size sz = get<0>(GetParam());
MatType type = get<1>(GetParam());
int dtype = get<2>(GetParam());
cv::GCompileArgs compile_args = get<3>(GetParam());
compare_f cmpF;
cv::Size sz;
MatType type = -1;
int dtype = -1;
double scale = 1.0;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, type, dtype, compile_args) = GetParam();
initMatsRandU(type, sz, dtype, false);
// OpenCV code ///////////////////////////////////////////////////////////
cv::multiply(in_mat1, sc, out_mat_ocv, 1, dtype);
cv::multiply(in_mat1, sc, out_mat_ocv, scale, dtype);
// G-API code ////////////////////////////////////////////////////////////
cv::GMat in1, out;
@ -307,8 +331,9 @@ PERF_TEST_P_(MulCPerfTest, TestPerformance)
}
// Comparison ////////////////////////////////////////////////////////////
// FIXIT unrealiable check: EXPECT_EQ(0, cv::countNonZero(out_mat_gapi != out_mat_ocv));
EXPECT_EQ(out_mat_gapi.size(), sz);
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
}
SANITY_CHECK_NOTHING();
}
@ -321,17 +346,23 @@ PERF_TEST_P_(DivPerfTest, TestPerformance)
Size sz = get<1>(GetParam());
MatType type = get<2>(GetParam());
int dtype = get<3>(GetParam());
cv::GCompileArgs compile_args = get<4>(GetParam());
double scale = get<4>(GetParam());
cv::GCompileArgs compile_args = get<5>(GetParam());
// FIXIT Unstable input data for divide
initMatsRandU(type, sz, dtype, false);
//This condition need to workaround bug in OpenCV.
//It reinitializes divider matrix without zero values.
if (dtype == CV_16S && dtype != type)
cv::randu(in_mat2, cv::Scalar::all(1), cv::Scalar::all(255));
// OpenCV code ///////////////////////////////////////////////////////////
cv::divide(in_mat1, in_mat2, out_mat_ocv, dtype);
cv::divide(in_mat1, in_mat2, out_mat_ocv, scale, dtype);
// G-API code ////////////////////////////////////////////////////////////
cv::GMat in1, in2, out;
out = cv::gapi::div(in1, in2, dtype);
out = cv::gapi::div(in1, in2, scale, dtype);
cv::GComputation c(GIn(in1, in2), GOut(out));
// Warm-up graph engine:
@ -345,8 +376,9 @@ PERF_TEST_P_(DivPerfTest, TestPerformance)
}
// Comparison ////////////////////////////////////////////////////////////
// FIXIT unrealiable check: EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
EXPECT_EQ(out_mat_gapi.size(), sz);
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
}
SANITY_CHECK_NOTHING();
}
@ -1905,6 +1937,171 @@ PERF_TEST_P_(ConvertToPerfTest, TestPerformance)
//------------------------------------------------------------------------------
PERF_TEST_P_(KMeansNDPerfTest, TestPerformance)
{
cv::Size sz;
CompareMats cmpF;
int K = -1;
cv::KmeansFlags flags = cv::KMEANS_RANDOM_CENTERS;
cv::GCompileArgs compile_args;
std::tie(sz, cmpF, K, flags, compile_args) = GetParam();
MatType2 type = CV_32FC1;
initMatrixRandU(type, sz, -1, false);
double compact_gapi = -1.;
cv::Mat labels_gapi, centers_gapi;
if (flags & cv::KMEANS_USE_INITIAL_LABELS)
{
const int amount = sz.height;
cv::Mat bestLabels(cv::Size{1, amount}, CV_32SC1);
cv::randu(bestLabels, 0, K);
cv::GComputation c(kmeansTestGAPI(in_mat1, bestLabels, K, flags, std::move(compile_args),
compact_gapi, labels_gapi, centers_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_mat1, bestLabels),
cv::gout(compact_gapi, labels_gapi, centers_gapi));
}
kmeansTestOpenCVCompare(in_mat1, bestLabels, K, flags, compact_gapi, labels_gapi,
centers_gapi, cmpF);
}
else
{
cv::GComputation c(kmeansTestGAPI(in_mat1, K, flags, std::move(compile_args), compact_gapi,
labels_gapi, centers_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_mat1), cv::gout(compact_gapi, labels_gapi, centers_gapi));
}
kmeansTestValidate(sz, type, K, compact_gapi, labels_gapi, centers_gapi);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(KMeans2DPerfTest, TestPerformance)
{
int amount = -1;
int K = -1;
cv::KmeansFlags flags = cv::KMEANS_RANDOM_CENTERS;
cv::GCompileArgs compile_args;
std::tie(amount, K, flags, compile_args) = GetParam();
std::vector<cv::Point2f> in_vector{};
initPointsVectorRandU(amount, in_vector);
double compact_gapi = -1.;
std::vector<int> labels_gapi{};
std::vector<cv::Point2f> centers_gapi{};
if (flags & cv::KMEANS_USE_INITIAL_LABELS)
{
std::vector<int> bestLabels(amount);
cv::randu(bestLabels, 0, K);
cv::GComputation c(kmeansTestGAPI(in_vector, bestLabels, K, flags, std::move(compile_args),
compact_gapi, labels_gapi, centers_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector, bestLabels),
cv::gout(compact_gapi, labels_gapi, centers_gapi));
}
kmeansTestOpenCVCompare(in_vector, bestLabels, K, flags, compact_gapi, labels_gapi,
centers_gapi);
}
else
{
cv::GComputation c(kmeansTestGAPI(in_vector, K, flags, std::move(compile_args),
compact_gapi, labels_gapi, centers_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector), cv::gout(compact_gapi, labels_gapi, centers_gapi));
}
kmeansTestValidate({-1, amount}, -1, K, compact_gapi, labels_gapi, centers_gapi);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(KMeans3DPerfTest, TestPerformance)
{
int amount = -1;
int K = -1;
cv::KmeansFlags flags = cv::KMEANS_RANDOM_CENTERS;
cv::GCompileArgs compile_args;
std::tie(amount, K, flags, compile_args) = GetParam();
std::vector<cv::Point3f> in_vector{};
initPointsVectorRandU(amount, in_vector);
double compact_gapi = -1.;
std::vector<int> labels_gapi;
std::vector<cv::Point3f> centers_gapi;
if (flags & cv::KMEANS_USE_INITIAL_LABELS)
{
std::vector<int> bestLabels(amount);
cv::randu(bestLabels, 0, K);
cv::GComputation c(kmeansTestGAPI(in_vector, bestLabels, K, flags, std::move(compile_args),
compact_gapi, labels_gapi, centers_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector, bestLabels),
cv::gout(compact_gapi, labels_gapi, centers_gapi));
}
kmeansTestOpenCVCompare(in_vector, bestLabels, K, flags, compact_gapi, labels_gapi,
centers_gapi);
}
else
{
cv::GComputation c(kmeansTestGAPI(in_vector, K, flags, std::move(compile_args),
compact_gapi, labels_gapi, centers_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector), cv::gout(compact_gapi, labels_gapi, centers_gapi));
}
kmeansTestValidate({-1, amount}, -1, K, compact_gapi, labels_gapi, centers_gapi);
}
SANITY_CHECK_NOTHING();
}
//------------------------------------------------------------------------------
PERF_TEST_P_(TransposePerfTest, TestPerformance)
{
compare_f cmpF;
cv::Size sz_in;
MatType type = -1;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz_in, type, compile_args) = GetParam();
initMatrixRandU(type, sz_in, type, false);
// OpenCV code ///////////////////////////////////////////////////////////
cv::transpose(in_mat1, out_mat_ocv);
// G-API code ////////////////////////////////////////////////////////////
cv::GMat in;
auto out = cv::gapi::transpose(in);
cv::GComputation c(cv::GIn(in), cv::GOut(out));
// Warm-up graph engine:
c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi), std::move(compile_args));
TEST_CYCLE()
{
c.apply(cv::gin(in_mat1), cv::gout(out_mat_gapi));
}
// Comparison ////////////////////////////////////////////////////////////
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
}
SANITY_CHECK_NOTHING();
}
//------------------------------------------------------------------------------
PERF_TEST_P_(ResizePerfTest, TestPerformance)
{
compare_f cmpF = get<0>(GetParam());
@ -1984,6 +2181,89 @@ PERF_TEST_P_(ResizeFxFyPerfTest, TestPerformance)
{
cc(gin(in_mat1), gout(out_mat_gapi));
}
// Comparison ////////////////////////////////////////////////////////////
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
}
SANITY_CHECK_NOTHING();
}
//------------------------------------------------------------------------------
// This test cases were created to control performance result of test scenario mentioned here:
// https://stackoverflow.com/questions/60629331/opencv-gapi-performance-not-good-as-expected
PERF_TEST_P_(BottleneckKernelsConstInputPerfTest, TestPerformance)
{
compare_f cmpF = get<0>(GetParam());
std::string fileName = get<1>(GetParam());
cv::GCompileArgs compile_args = get<2>(GetParam());
in_mat1 = cv::imread(findDataFile(fileName));
cv::Mat cvvga;
cv::Mat cvgray;
cv::Mat cvblurred;
cv::resize(in_mat1, cvvga, cv::Size(), 0.5, 0.5);
cv::cvtColor(cvvga, cvgray, cv::COLOR_BGR2GRAY);
cv::blur(cvgray, cvblurred, cv::Size(3, 3));
cv::Canny(cvblurred, out_mat_ocv, 32, 128, 3);
cv::GMat in;
cv::GMat vga = cv::gapi::resize(in, cv::Size(), 0.5, 0.5, INTER_LINEAR);
cv::GMat gray = cv::gapi::BGR2Gray(vga);
cv::GMat blurred = cv::gapi::blur(gray, cv::Size(3, 3));
cv::GMat out = cv::gapi::Canny(blurred, 32, 128, 3);
cv::GComputation ac(in, out);
auto cc = ac.compile(descr_of(gin(in_mat1)),
std::move(compile_args));
cc(gin(in_mat1), gout(out_mat_gapi));
TEST_CYCLE()
{
cc(gin(in_mat1), gout(out_mat_gapi));
}
// Comparison ////////////////////////////////////////////////////////////
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
}
SANITY_CHECK_NOTHING();
}
//------------------------------------------------------------------------------
PERF_TEST_P_(ResizeInSimpleGraphPerfTest, TestPerformance)
{
compare_f cmpF = get<0>(GetParam());
MatType type = get<1>(GetParam());
cv::Size sz_in = get<2>(GetParam());
cv::GCompileArgs compile_args = get<3>(GetParam());
initMatsRandU(type, sz_in, type, false);
cv::Mat add_res_ocv;
cv::add(in_mat1, in_mat2, add_res_ocv);
cv::resize(add_res_ocv, out_mat_ocv, cv::Size(), 0.5, 0.5);
cv::GMat in1, in2;
cv::GMat add_res_gapi = cv::gapi::add(in1, in2);
cv::GMat out = cv::gapi::resize(add_res_gapi, cv::Size(), 0.5, 0.5, INTER_LINEAR);
cv::GComputation ac(GIn(in1, in2), GOut(out));
auto cc = ac.compile(descr_of(gin(in_mat1, in_mat2)),
std::move(compile_args));
cc(gin(in_mat1, in_mat2), gout(out_mat_gapi));
TEST_CYCLE()
{
cc(gin(in_mat1, in_mat2), gout(out_mat_gapi));
}
// Comparison ////////////////////////////////////////////////////////////
{

View File

@ -30,6 +30,8 @@ class ErodePerfTest : public TestPerfParams<tuple<compare_f, MatType,i
class Erode3x3PerfTest : public TestPerfParams<tuple<compare_f, MatType,cv::Size,int, cv::GCompileArgs>> {};
class DilatePerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int, cv::GCompileArgs>> {};
class Dilate3x3PerfTest : public TestPerfParams<tuple<compare_f, MatType,cv::Size,int, cv::GCompileArgs>> {};
class MorphologyExPerfTest : public TestPerfParams<tuple<compare_f,MatType,cv::Size,
cv::MorphTypes,cv::GCompileArgs>> {};
class SobelPerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int,int,int, cv::GCompileArgs>> {};
class SobelXYPerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int,int, cv::GCompileArgs>> {};
class LaplacianPerfTest : public TestPerfParams<tuple<compare_f, MatType,int,cv::Size,int,
@ -41,6 +43,44 @@ class CannyPerfTest : public TestPerfParams<tuple<compare_f, MatType,c
class GoodFeaturesPerfTest : public TestPerfParams<tuple<compare_vector_f<cv::Point2f>, std::string,
int,int,double,double,int,bool,
cv::GCompileArgs>> {};
class FindContoursPerfTest : public TestPerfParams<tuple<CompareMats, MatType,cv::Size,
cv::RetrievalModes,
cv::ContourApproximationModes,
cv::GCompileArgs>> {};
class FindContoursHPerfTest : public TestPerfParams<tuple<CompareMats, MatType,cv::Size,
cv::RetrievalModes,
cv::ContourApproximationModes,
cv::GCompileArgs>> {};
class BoundingRectMatPerfTest :
public TestPerfParams<tuple<CompareRects, MatType,cv::Size,bool, cv::GCompileArgs>> {};
class BoundingRectVector32SPerfTest :
public TestPerfParams<tuple<CompareRects, cv::Size, cv::GCompileArgs>> {};
class BoundingRectVector32FPerfTest :
public TestPerfParams<tuple<CompareRects, cv::Size, cv::GCompileArgs>> {};
class FitLine2DMatVectorPerfTest : public TestPerfParams<tuple<CompareVecs<float, 4>,
MatType,cv::Size,cv::DistanceTypes,
cv::GCompileArgs>> {};
class FitLine2DVector32SPerfTest : public TestPerfParams<tuple<CompareVecs<float, 4>,
cv::Size,cv::DistanceTypes,
cv::GCompileArgs>> {};
class FitLine2DVector32FPerfTest : public TestPerfParams<tuple<CompareVecs<float, 4>,
cv::Size,cv::DistanceTypes,
cv::GCompileArgs>> {};
class FitLine2DVector64FPerfTest : public TestPerfParams<tuple<CompareVecs<float, 4>,
cv::Size,cv::DistanceTypes,
cv::GCompileArgs>> {};
class FitLine3DMatVectorPerfTest : public TestPerfParams<tuple<CompareVecs<float, 6>,
MatType,cv::Size,cv::DistanceTypes,
cv::GCompileArgs>> {};
class FitLine3DVector32SPerfTest : public TestPerfParams<tuple<CompareVecs<float, 6>,
cv::Size,cv::DistanceTypes,
cv::GCompileArgs>> {};
class FitLine3DVector32FPerfTest : public TestPerfParams<tuple<CompareVecs<float, 6>,
cv::Size,cv::DistanceTypes,
cv::GCompileArgs>> {};
class FitLine3DVector64FPerfTest : public TestPerfParams<tuple<CompareVecs<float, 6>,
cv::Size,cv::DistanceTypes,
cv::GCompileArgs>> {};
class EqHistPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {};
class BGR2RGBPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {};
class RGB2GrayPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {};

View File

@ -11,6 +11,8 @@
#include "gapi_imgproc_perf_tests.hpp"
#include "../../test/common/gapi_imgproc_tests_common.hpp"
namespace opencv_test
{
@ -491,6 +493,49 @@ PERF_TEST_P_(Dilate3x3PerfTest, TestPerformance)
//------------------------------------------------------------------------------
PERF_TEST_P_(MorphologyExPerfTest, TestPerformance)
{
compare_f cmpF;
MatType type = 0;
cv::MorphTypes op = cv::MORPH_ERODE;
cv::Size sz;
cv::GCompileArgs compile_args;
std::tie(cmpF, type, sz, op, compile_args) = GetParam();
initMatrixRandN(type, sz, type, false);
cv::MorphShapes defShape = cv::MORPH_RECT;
int defKernSize = 3;
cv::Mat kernel = cv::getStructuringElement(defShape, cv::Size(defKernSize, defKernSize));
// OpenCV code /////////////////////////////////////////////////////////////
{
cv::morphologyEx(in_mat1, out_mat_ocv, op, kernel);
}
// G-API code //////////////////////////////////////////////////////////////
cv::GMat in;
auto out = cv::gapi::morphologyEx(in, op, kernel);
cv::GComputation c(in, out);
// Warm-up graph engine:
c.apply(in_mat1, out_mat_gapi, std::move(compile_args));
TEST_CYCLE()
{
c.apply(in_mat1, out_mat_gapi);
}
// Comparison //////////////////////////////////////////////////////////////
{
EXPECT_TRUE(cmpF(out_mat_gapi, out_mat_ocv));
EXPECT_EQ(out_mat_gapi.size(), sz);
}
SANITY_CHECK_NOTHING();
}
//------------------------------------------------------------------------------
PERF_TEST_P_(SobelPerfTest, TestPerformance)
{
compare_f cmpF;
@ -750,6 +795,332 @@ PERF_TEST_P_(GoodFeaturesPerfTest, TestPerformance)
//------------------------------------------------------------------------------
PERF_TEST_P_(FindContoursPerfTest, TestPerformance)
{
CompareMats cmpF;
MatType type;
cv::Size sz;
cv::RetrievalModes mode;
cv::ContourApproximationModes method;
cv::GCompileArgs compile_args;
std::tie(cmpF, type, sz, mode, method, compile_args) = GetParam();
cv::Mat in;
initMatForFindingContours(in, sz, type);
cv::Point offset = cv::Point();
std::vector<cv::Vec4i> out_hier_gapi = std::vector<cv::Vec4i>();
std::vector<std::vector<cv::Point>> out_cnts_gapi;
cv::GComputation c(findContoursTestGAPI(in, mode, method, std::move(compile_args),
out_cnts_gapi, out_hier_gapi, offset));
TEST_CYCLE()
{
c.apply(gin(in, offset), gout(out_cnts_gapi));
}
findContoursTestOpenCVCompare(in, mode, method, out_cnts_gapi, out_hier_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(FindContoursHPerfTest, TestPerformance)
{
CompareMats cmpF;
MatType type;
cv::Size sz;
cv::RetrievalModes mode;
cv::ContourApproximationModes method;
cv::GCompileArgs compile_args;
std::tie(cmpF, type, sz, mode, method, compile_args) = GetParam();
cv::Mat in;
initMatForFindingContours(in, sz, type);
cv::Point offset = cv::Point();
std::vector<std::vector<cv::Point>> out_cnts_gapi;
std::vector<cv::Vec4i> out_hier_gapi;
cv::GComputation c(findContoursTestGAPI<HIERARCHY>(in, mode, method, std::move(compile_args),
out_cnts_gapi, out_hier_gapi, offset));
TEST_CYCLE()
{
c.apply(gin(in, offset), gout(out_cnts_gapi, out_hier_gapi));
}
findContoursTestOpenCVCompare<HIERARCHY>(in, mode, method, out_cnts_gapi, out_hier_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
//------------------------------------------------------------------------------
PERF_TEST_P_(BoundingRectMatPerfTest, TestPerformance)
{
CompareRects cmpF;
cv::Size sz;
MatType type;
bool initByVector = false;
cv::GCompileArgs compile_args;
std::tie(cmpF, type, sz, initByVector, compile_args) = GetParam();
if (initByVector)
{
initMatByPointsVectorRandU<cv::Point_>(type, sz, -1);
}
else
{
initMatrixRandU(type, sz, -1, false);
}
cv::Rect out_rect_gapi;
cv::GComputation c(boundingRectTestGAPI(in_mat1, std::move(compile_args), out_rect_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_mat1), cv::gout(out_rect_gapi));
}
boundingRectTestOpenCVCompare(in_mat1, out_rect_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(BoundingRectVector32SPerfTest, TestPerformance)
{
CompareRects cmpF;
cv::Size sz;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, compile_args) = GetParam();
std::vector<cv::Point2i> in_vector;
initPointsVectorRandU(sz.width, in_vector);
cv::Rect out_rect_gapi;
cv::GComputation c(boundingRectTestGAPI(in_vector, std::move(compile_args), out_rect_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector), cv::gout(out_rect_gapi));
}
boundingRectTestOpenCVCompare(in_vector, out_rect_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(BoundingRectVector32FPerfTest, TestPerformance)
{
CompareRects cmpF;
cv::Size sz;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, compile_args) = GetParam();
std::vector<cv::Point2f> in_vector;
initPointsVectorRandU(sz.width, in_vector);
cv::Rect out_rect_gapi;
cv::GComputation c(boundingRectTestGAPI(in_vector, std::move(compile_args), out_rect_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector), cv::gout(out_rect_gapi));
}
boundingRectTestOpenCVCompare(in_vector, out_rect_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
//------------------------------------------------------------------------------
PERF_TEST_P_(FitLine2DMatVectorPerfTest, TestPerformance)
{
CompareVecs<float, 4> cmpF;
cv::Size sz;
MatType type;
cv::DistanceTypes distType;
cv::GCompileArgs compile_args;
std::tie(cmpF, type, sz, distType, compile_args) = GetParam();
initMatByPointsVectorRandU<cv::Point_>(type, sz, -1);
cv::Vec4f out_vec_gapi;
cv::GComputation c(fitLineTestGAPI(in_mat1, distType, std::move(compile_args), out_vec_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_mat1), cv::gout(out_vec_gapi));
}
fitLineTestOpenCVCompare(in_mat1, distType, out_vec_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(FitLine2DVector32SPerfTest, TestPerformance)
{
CompareVecs<float, 4> cmpF;
cv::Size sz;
cv::DistanceTypes distType;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, distType, compile_args) = GetParam();
std::vector<cv::Point2i> in_vector;
initPointsVectorRandU(sz.width, in_vector);
cv::Vec4f out_vec_gapi;
cv::GComputation c(fitLineTestGAPI(in_vector, distType, std::move(compile_args),
out_vec_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector), cv::gout(out_vec_gapi));
}
fitLineTestOpenCVCompare(in_vector, distType, out_vec_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(FitLine2DVector32FPerfTest, TestPerformance)
{
CompareVecs<float, 4> cmpF;
cv::Size sz;
cv::DistanceTypes distType;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, distType, compile_args) = GetParam();
std::vector<cv::Point2f> in_vector;
initPointsVectorRandU(sz.width, in_vector);
cv::Vec4f out_vec_gapi;
cv::GComputation c(fitLineTestGAPI(in_vector, distType, std::move(compile_args),
out_vec_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector), cv::gout(out_vec_gapi));
}
fitLineTestOpenCVCompare(in_vector, distType, out_vec_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(FitLine2DVector64FPerfTest, TestPerformance)
{
CompareVecs<float, 4> cmpF;
cv::Size sz;
cv::DistanceTypes distType;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, distType, compile_args) = GetParam();
std::vector<cv::Point2d> in_vector;
initPointsVectorRandU(sz.width, in_vector);
cv::Vec4f out_vec_gapi;
cv::GComputation c(fitLineTestGAPI(in_vector, distType, std::move(compile_args),
out_vec_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector), cv::gout(out_vec_gapi));
}
fitLineTestOpenCVCompare(in_vector, distType, out_vec_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(FitLine3DMatVectorPerfTest, TestPerformance)
{
CompareVecs<float, 6> cmpF;
cv::Size sz;
MatType type;
cv::DistanceTypes distType;
cv::GCompileArgs compile_args;
std::tie(cmpF, type, sz, distType, compile_args) = GetParam();
initMatByPointsVectorRandU<cv::Point3_>(type, sz, -1);
cv::Vec6f out_vec_gapi;
cv::GComputation c(fitLineTestGAPI(in_mat1, distType, std::move(compile_args), out_vec_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_mat1), cv::gout(out_vec_gapi));
}
fitLineTestOpenCVCompare(in_mat1, distType, out_vec_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(FitLine3DVector32SPerfTest, TestPerformance)
{
CompareVecs<float, 6> cmpF;
cv::Size sz;
cv::DistanceTypes distType;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, distType, compile_args) = GetParam();
std::vector<cv::Point3i> in_vector;
initPointsVectorRandU(sz.width, in_vector);
cv::Vec6f out_vec_gapi;
cv::GComputation c(fitLineTestGAPI(in_vector, distType, std::move(compile_args),
out_vec_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector), cv::gout(out_vec_gapi));
}
fitLineTestOpenCVCompare(in_vector, distType, out_vec_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(FitLine3DVector32FPerfTest, TestPerformance)
{
CompareVecs<float, 6> cmpF;
cv::Size sz;
cv::DistanceTypes distType;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, distType, compile_args) = GetParam();
std::vector<cv::Point3f> in_vector;
initPointsVectorRandU(sz.width, in_vector);
cv::Vec6f out_vec_gapi;
cv::GComputation c(fitLineTestGAPI(in_vector, distType, std::move(compile_args),
out_vec_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector), cv::gout(out_vec_gapi));
}
fitLineTestOpenCVCompare(in_vector, distType, out_vec_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(FitLine3DVector64FPerfTest, TestPerformance)
{
CompareVecs<float, 6> cmpF;
cv::Size sz;
cv::DistanceTypes distType;
cv::GCompileArgs compile_args;
std::tie(cmpF, sz, distType, compile_args) = GetParam();
std::vector<cv::Point3d> in_vector;
initPointsVectorRandU(sz.width, in_vector);
cv::Vec6f out_vec_gapi;
cv::GComputation c(fitLineTestGAPI(in_vector, distType, std::move(compile_args),
out_vec_gapi));
TEST_CYCLE()
{
c.apply(cv::gin(in_vector), cv::gout(out_vec_gapi));
}
fitLineTestOpenCVCompare(in_vector, distType, out_vec_gapi, cmpF);
SANITY_CHECK_NOTHING();
}
//------------------------------------------------------------------------------
PERF_TEST_P_(EqHistPerfTest, TestPerformance)
{
compare_f cmpF = get<0>(GetParam());

View File

@ -26,6 +26,15 @@ class OptFlowLKForPyrPerfTest : public TestPerfParams<tuple<std::string,int,tupl
class BuildPyr_CalcOptFlow_PipelinePerfTest : public TestPerfParams<tuple<std::string,int,int,bool,
cv::GCompileArgs>> {};
class BackgroundSubtractorPerfTest:
public TestPerfParams<tuple<cv::gapi::video::BackgroundSubtractorType, std::string,
bool, double, std::size_t, cv::GCompileArgs, CompareMats>> {};
class KalmanFilterControlPerfTest :
public TestPerfParams<tuple<MatType2, int, int, size_t, bool, cv::GCompileArgs>> {};
class KalmanFilterNoControlPerfTest :
public TestPerfParams<tuple<MatType2, int, int, size_t, bool, cv::GCompileArgs>> {};
} // opencv_test
#endif // OPENCV_GAPI_VIDEO_PERF_TESTS_HPP

View File

@ -154,6 +154,244 @@ PERF_TEST_P_(BuildPyr_CalcOptFlow_PipelinePerfTest, TestPerformance)
//------------------------------------------------------------------------------
#ifdef HAVE_OPENCV_VIDEO
PERF_TEST_P_(BackgroundSubtractorPerfTest, TestPerformance)
{
namespace gvideo = cv::gapi::video;
gvideo::BackgroundSubtractorType opType;
std::string filePath = "";
bool detectShadows = false;
double learningRate = -1.;
std::size_t testNumFrames = 0;
cv::GCompileArgs compileArgs;
CompareMats cmpF;
std::tie(opType, filePath, detectShadows, learningRate, testNumFrames,
compileArgs, cmpF) = GetParam();
const int histLength = 500;
double thr = -1;
switch (opType)
{
case gvideo::TYPE_BS_MOG2:
{
thr = 16.;
break;
}
case gvideo::TYPE_BS_KNN:
{
thr = 400.;
break;
}
default:
FAIL() << "unsupported type of BackgroundSubtractor";
}
const gvideo::BackgroundSubtractorParams bsp(opType, histLength, thr, detectShadows,
learningRate);
// Retrieving frames
std::vector<cv::Mat> frames;
frames.reserve(testNumFrames);
{
cv::Mat frame;
cv::VideoCapture cap;
if (!cap.open(findDataFile(filePath)))
throw SkipTestException("Video file can not be opened");
for (std::size_t i = 0; i < testNumFrames && cap.read(frame); i++)
{
frames.push_back(frame);
}
}
GAPI_Assert(testNumFrames == frames.size() && "Can't read required number of frames");
// G-API graph declaration
cv::GMat in;
cv::GMat out = cv::gapi::BackgroundSubtractor(in, bsp);
cv::GComputation c(cv::GIn(in), cv::GOut(out));
auto cc = c.compile(cv::descr_of(frames[0]), std::move(compileArgs));
cv::Mat gapiForeground;
TEST_CYCLE()
{
cc.prepareForNewStream();
for (size_t i = 0; i < testNumFrames; i++)
{
cc(cv::gin(frames[i]), cv::gout(gapiForeground));
}
}
// OpenCV Background Subtractor declaration
cv::Ptr<cv::BackgroundSubtractor> pOCVBackSub;
if (opType == gvideo::TYPE_BS_MOG2)
pOCVBackSub = cv::createBackgroundSubtractorMOG2(histLength, thr, detectShadows);
else if (opType == gvideo::TYPE_BS_KNN)
pOCVBackSub = cv::createBackgroundSubtractorKNN(histLength, thr, detectShadows);
cv::Mat ocvForeground;
for (size_t i = 0; i < testNumFrames; i++)
{
pOCVBackSub->apply(frames[i], ocvForeground, learningRate);
}
// Validation
EXPECT_TRUE(cmpF(gapiForeground, ocvForeground));
SANITY_CHECK_NOTHING();
}
//------------------------------------------------------------------------------
inline void generateInputKalman(const int mDim, const MatType2& type,
const size_t testNumMeasurements, const bool receiveRandMeas,
std::vector<bool>& haveMeasurements,
std::vector<cv::Mat>& measurements)
{
cv::RNG& rng = cv::theRNG();
measurements.clear();
haveMeasurements = std::vector<bool>(testNumMeasurements, true);
for (size_t i = 0; i < testNumMeasurements; i++)
{
if (receiveRandMeas)
{
haveMeasurements[i] = rng(2u) == 1; // returns 0 or 1 - whether we have measurement
// at this iteration or not
} // if not - testing the slowest case in which we have measurements at every iteration
cv::Mat measurement = cv::Mat::zeros(mDim, 1, type);
if (haveMeasurements[i])
{
cv::randu(measurement, cv::Scalar::all(-1), cv::Scalar::all(1));
}
measurements.push_back(measurement.clone());
}
}
inline void generateInputKalman(const int mDim, const int cDim, const MatType2& type,
const size_t testNumMeasurements, const bool receiveRandMeas,
std::vector<bool>& haveMeasurements,
std::vector<cv::Mat>& measurements,
std::vector<cv::Mat>& ctrls)
{
generateInputKalman(mDim, type, testNumMeasurements, receiveRandMeas,
haveMeasurements, measurements);
ctrls.clear();
cv::Mat ctrl(cDim, 1, type);
for (size_t i = 0; i < testNumMeasurements; i++)
{
cv::randu(ctrl, cv::Scalar::all(-1), cv::Scalar::all(1));
ctrls.push_back(ctrl.clone());
}
}
PERF_TEST_P_(KalmanFilterControlPerfTest, TestPerformance)
{
MatType2 type = -1;
int dDim = -1, mDim = -1;
size_t testNumMeasurements = 0;
bool receiveRandMeas = true;
cv::GCompileArgs compileArgs;
std::tie(type, dDim, mDim, testNumMeasurements, receiveRandMeas, compileArgs) = GetParam();
const int cDim = 2;
cv::gapi::KalmanParams kp;
initKalmanParams(type, dDim, mDim, cDim, kp);
// Generating input
std::vector<bool> haveMeasurements;
std::vector<cv::Mat> measurements, ctrls;
generateInputKalman(mDim, cDim, type, testNumMeasurements, receiveRandMeas,
haveMeasurements, measurements, ctrls);
// G-API graph declaration
cv::GMat m, ctrl;
cv::GOpaque<bool> have_m;
cv::GMat out = cv::gapi::KalmanFilter(m, have_m, ctrl, kp);
cv::GComputation c(cv::GIn(m, have_m, ctrl), cv::GOut(out));
auto cc = c.compile(
cv::descr_of(cv::gin(cv::Mat(mDim, 1, type), true, cv::Mat(cDim, 1, type))),
std::move(compileArgs));
cv::Mat gapiKState(dDim, 1, type);
TEST_CYCLE()
{
cc.prepareForNewStream();
for (size_t i = 0; i < testNumMeasurements; i++)
{
bool hvMeas = haveMeasurements[i];
cc(cv::gin(measurements[i], hvMeas, ctrls[i]), cv::gout(gapiKState));
}
}
// OpenCV reference KalmanFilter initialization
cv::KalmanFilter ocvKalman(dDim, mDim, cDim, type);
initKalmanFilter(kp, true, ocvKalman);
cv::Mat ocvKState(dDim, 1, type);
for (size_t i = 0; i < testNumMeasurements; i++)
{
ocvKState = ocvKalman.predict(ctrls[i]);
if (haveMeasurements[i])
ocvKState = ocvKalman.correct(measurements[i]);
}
// Validation
EXPECT_TRUE(AbsExact().to_compare_f()(gapiKState, ocvKState));
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(KalmanFilterNoControlPerfTest, TestPerformance)
{
MatType2 type = -1;
int dDim = -1, mDim = -1;
size_t testNumMeasurements = 0;
bool receiveRandMeas = true;
cv::GCompileArgs compileArgs;
std::tie(type, dDim, mDim, testNumMeasurements, receiveRandMeas, compileArgs) = GetParam();
const int cDim = 0;
cv::gapi::KalmanParams kp;
initKalmanParams(type, dDim, mDim, cDim, kp);
// Generating input
std::vector<bool> haveMeasurements;
std::vector<cv::Mat> measurements;
generateInputKalman(mDim, type, testNumMeasurements, receiveRandMeas,
haveMeasurements, measurements);
// G-API graph declaration
cv::GMat m;
cv::GOpaque<bool> have_m;
cv::GMat out = cv::gapi::KalmanFilter(m, have_m, kp);
cv::GComputation c(cv::GIn(m, have_m), cv::GOut(out));
auto cc = c.compile(cv::descr_of(cv::gin(cv::Mat(mDim, 1, type), true)),
std::move(compileArgs));
cv::Mat gapiKState(dDim, 1, type);
TEST_CYCLE()
{
cc.prepareForNewStream();
for (size_t i = 0; i < testNumMeasurements; i++)
{
bool hvMeas = haveMeasurements[i];
cc(cv::gin(measurements[i], hvMeas), cv::gout(gapiKState));
}
}
// OpenCV reference KalmanFilter declaration
cv::KalmanFilter ocvKalman(dDim, mDim, cDim, type);
initKalmanFilter(kp, false, ocvKalman);
cv::Mat ocvKState(dDim, 1, type);
for (size_t i = 0; i < testNumMeasurements; i++)
{
ocvKState = ocvKalman.predict();
if (haveMeasurements[i])
ocvKState = ocvKalman.correct(measurements[i]);
}
// Validation
EXPECT_TRUE(AbsExact().to_compare_f()(gapiKState, ocvKState));
SANITY_CHECK_NOTHING();
}
#endif // HAVE_OPENCV_VIDEO
} // opencv_test
#endif // OPENCV_GAPI_VIDEO_PERF_TESTS_INL_HPP

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2020 Intel Corporation
// Copyright (C) 2018-2021 Intel Corporation
#include "../perf_precomp.hpp"
@ -22,7 +22,8 @@ INSTANTIATE_TEST_CASE_P(AddPerfTestCPU, AddPerfTest,
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(AddCPerfTestCPU, AddCPerfTest,
Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
Combine(Values(AbsExact().to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_32F),
Values(cv::compile_args(CORE_CPU))));
@ -34,7 +35,8 @@ INSTANTIATE_TEST_CASE_P(SubPerfTestCPU, SubPerfTest,
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(SubCPerfTestCPU, SubCPerfTest,
Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
Combine(Values(AbsExact().to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_32F),
Values(cv::compile_args(CORE_CPU))));
@ -46,19 +48,23 @@ INSTANTIATE_TEST_CASE_P(SubRCPerfTestCPU, SubRCPerfTest,
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(MulPerfTestCPU, MulPerfTest,
Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
Combine(Values(AbsExact().to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_32F),
Values(2.0),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(MulDoublePerfTestCPU, MulDoublePerfTest,
Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_32F),
Values(cv::compile_args(CORE_CPU))));
Combine(Values(AbsExact().to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_32F),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(MulCPerfTestCPU, MulCPerfTest,
Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
Combine(Values(AbsExact().to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_32F),
Values(cv::compile_args(CORE_CPU))));
@ -67,7 +73,8 @@ INSTANTIATE_TEST_CASE_P(DivPerfTestCPU, DivPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_32F),
Values(-1, CV_8U, CV_16U, CV_16S, CV_32F),
Values(2.3),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(DivCPerfTestCPU, DivCPerfTest,
@ -282,18 +289,67 @@ INSTANTIATE_TEST_CASE_P(ConvertToPerfTestCPU, ConvertToPerfTest,
Values(0.0),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(KMeansNDPerfTestCPU, KMeansNDPerfTest,
Combine(Values(cv::Size(1, 20),
cv::Size(16, 4096)),
Values(AbsTolerance(0.01).to_compare_obj()),
Values(5, 15),
Values(cv::KMEANS_RANDOM_CENTERS,
cv::KMEANS_PP_CENTERS,
cv::KMEANS_RANDOM_CENTERS | cv::KMEANS_USE_INITIAL_LABELS,
cv::KMEANS_PP_CENTERS | cv::KMEANS_USE_INITIAL_LABELS),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(KMeans2DPerfTestCPU, KMeans2DPerfTest,
Combine(Values(20, 4096),
Values(5, 15),
Values(cv::KMEANS_RANDOM_CENTERS,
cv::KMEANS_PP_CENTERS,
cv::KMEANS_RANDOM_CENTERS | cv::KMEANS_USE_INITIAL_LABELS,
cv::KMEANS_PP_CENTERS | cv::KMEANS_USE_INITIAL_LABELS),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(KMeans3DPerfTestCPU, KMeans3DPerfTest,
Combine(Values(20, 4096),
Values(5, 15),
Values(cv::KMEANS_RANDOM_CENTERS,
cv::KMEANS_PP_CENTERS,
cv::KMEANS_RANDOM_CENTERS | cv::KMEANS_USE_INITIAL_LABELS,
cv::KMEANS_PP_CENTERS | cv::KMEANS_USE_INITIAL_LABELS),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(TransposePerfTestCPU, TransposePerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1,
CV_8UC2, CV_16UC2, CV_16SC2, CV_32FC2,
CV_8UC3, CV_16UC3, CV_16SC3, CV_32FC3),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(ResizePerfTestCPU, ResizePerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_16UC1, CV_16SC1),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1),
Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(cv::Size(64, 64),
cv::Size(30, 30)),
cv::Size(32, 32)),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(BottleneckKernelsPerfTestCPU, BottleneckKernelsConstInputPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values("cv/optflow/frames/1080p_00.png", "cv/optflow/frames/720p_00.png",
"cv/optflow/frames/VGA_00.png", "cv/dnn_face/recognition/Aaron_Tippin_0001.jpg"),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(ResizeInSimpleGraphPerfTestCPU, ResizeInSimpleGraphPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC3),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(cv::compile_args(CORE_CPU))));
INSTANTIATE_TEST_CASE_P(ResizeFxFyPerfTestCPU, ResizeFxFyPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_16UC1, CV_16SC1),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1),
Values(cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_AREA),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(0.5, 0.1),

View File

@ -18,11 +18,12 @@ INSTANTIATE_TEST_CASE_P(AddPerfTestFluid, AddPerfTest,
Values(-1, CV_8U, CV_32F),
Values(cv::compile_args(CORE_FLUID))));
// INSTANTIATE_TEST_CASE_P(AddCPerfTestFluid, AddCPerfTest,
// Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
// Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
// Values(-1, CV_8U, CV_16U, CV_32F),
// Values(cv::compile_args(CORE_FLUID))));
INSTANTIATE_TEST_CASE_P(AddCPerfTestFluid, AddCPerfTest,
Combine(Values(Tolerance_FloatRel_IntAbs(1e-6, 1).to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_16S, CV_32F),
Values(cv::compile_args(CORE_FLUID))));
INSTANTIATE_TEST_CASE_P(SubPerfTestFluid, SubPerfTest,
Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
@ -30,11 +31,12 @@ INSTANTIATE_TEST_CASE_P(SubPerfTestFluid, SubPerfTest,
Values(-1, CV_8U, CV_32F),
Values(cv::compile_args(CORE_FLUID))));
// INSTANTIATE_TEST_CASE_P(SubCPerfTestFluid, SubCPerfTest,
// Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
// Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
// Values(-1, CV_8U, CV_16U, CV_32F),
// Values(cv::compile_args(CORE_FLUID))));
INSTANTIATE_TEST_CASE_P(SubCPerfTestFluid, SubCPerfTest,
Combine(Values(Tolerance_FloatRel_IntAbs(1e-6, 1).to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_16S, CV_32F),
Values(cv::compile_args(CORE_FLUID))));
// INSTANTIATE_TEST_CASE_P(SubRCPerfTestFluid, SubRCPerfTest,
// Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
@ -42,30 +44,35 @@ INSTANTIATE_TEST_CASE_P(SubPerfTestFluid, SubPerfTest,
// Values(-1, CV_8U, CV_16U, CV_32F),
// Values(cv::compile_args(CORE_FLUID))));
// INSTANTIATE_TEST_CASE_P(MulPerfTestFluid, MulPerfTest,
// Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
// Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
// Values(-1, CV_8U, CV_16U, CV_32F),
// Values(cv::compile_args(CORE_FLUID))));
INSTANTIATE_TEST_CASE_P(MulPerfTestFluid, MulPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_16S, CV_32F),
Values(2.0),
Values(cv::compile_args(CORE_FLUID))));
// INSTANTIATE_TEST_CASE_P(MulDoublePerfTestFluid, MulDoublePerfTest,
// Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
// Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
// Values(-1, CV_8U, CV_16U, CV_32F),
// Values(cv::compile_args(CORE_FLUID))));
INSTANTIATE_TEST_CASE_P(MulDoublePerfTestFluid, MulDoublePerfTest,
Combine(Values(Tolerance_FloatRel_IntAbs(1e-6, 1).to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_32F),
Values(cv::compile_args(CORE_FLUID))));
// INSTANTIATE_TEST_CASE_P(MulCPerfTestFluid, MulCPerfTest,
// Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
// Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
// Values(-1, CV_8U, CV_16U, CV_32F),
// Values(cv::compile_args(CORE_FLUID))));
INSTANTIATE_TEST_CASE_P(MulCPerfTestFluid, MulCPerfTest,
Combine(Values(Tolerance_FloatRel_IntAbs(1e-6, 1).to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_16S, CV_32F),
Values(cv::compile_args(CORE_FLUID))));
// INSTANTIATE_TEST_CASE_P(DivPerfTestFluid, DivPerfTest,
// Combine(Values(AbsExact().to_compare_f()),
// Values(szSmall128, szVGA, sz720p, sz1080p),
// Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
// Values(-1, CV_8U, CV_16U, CV_32F),
// Values(cv::compile_args(CORE_FLUID))));
INSTANTIATE_TEST_CASE_P(DivPerfTestFluid, DivPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(-1, CV_8U, CV_16U, CV_16S, CV_32F),
Values(2.3),
Values(cv::compile_args(CORE_FLUID))));
// INSTANTIATE_TEST_CASE_P(DivCPerfTestFluid, DivCPerfTest,
// Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
@ -147,7 +154,9 @@ INSTANTIATE_TEST_CASE_P(AbsDiffPerfTestFluid, AbsDiffPerfTest,
INSTANTIATE_TEST_CASE_P(AbsDiffCPerfTestFluid, AbsDiffCPerfTest,
Combine(Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1),
Values(CV_8UC1, CV_16UC1, CV_16SC1, CV_8UC2,
CV_16UC2, CV_16SC2, CV_8UC3, CV_16UC3,
CV_16SC3, CV_8UC4, CV_16UC4, CV_16SC4),
Values(cv::compile_args(CORE_FLUID))));
// INSTANTIATE_TEST_CASE_P(SumPerfTestFluid, SumPerfTest,
@ -275,18 +284,31 @@ INSTANTIATE_TEST_CASE_P(ConvertToPerfTestFluid, ConvertToPerfTest,
Values(cv::compile_args(CORE_FLUID))));
INSTANTIATE_TEST_CASE_P(ResizePerfTestFluid, ResizePerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC3/*CV_8UC1, CV_16UC1, CV_16SC1*/),
Values(/*cv::INTER_NEAREST,*/ cv::INTER_LINEAR/*, cv::INTER_AREA*/),
Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 1).to_compare_f()),
Values(CV_8UC3),
Values(cv::INTER_LINEAR),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(cv::Size(64, 64),
cv::Size(30, 30)),
Values(cv::compile_args(CORE_FLUID))));
#define IMGPROC_FLUID cv::gapi::imgproc::fluid::kernels()
INSTANTIATE_TEST_CASE_P(BottleneckKernelsPerfTestFluid, BottleneckKernelsConstInputPerfTest,
Combine(Values(AbsSimilarPoints(0, 1).to_compare_f()),
Values("cv/optflow/frames/1080p_00.png", "cv/optflow/frames/720p_00.png",
"cv/optflow/frames/VGA_00.png", "cv/dnn_face/recognition/Aaron_Tippin_0001.jpg"),
Values(cv::compile_args(CORE_FLUID, IMGPROC_FLUID))));
INSTANTIATE_TEST_CASE_P(ResizeInSimpleGraphPerfTestFluid, ResizeInSimpleGraphPerfTest,
Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 1).to_compare_f()),
Values(CV_8UC3),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(cv::compile_args(CORE_FLUID, IMGPROC_FLUID))));
INSTANTIATE_TEST_CASE_P(ResizeFxFyPerfTestFluid, ResizeFxFyPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC3/*CV_8UC1, CV_16UC1, CV_16SC1*/),
Values(/*cv::INTER_NEAREST,*/ cv::INTER_LINEAR/*, cv::INTER_AREA*/),
Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 1).to_compare_f()),
Values(CV_8UC3),
Values(cv::INTER_LINEAR),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(0.5, 0.1),
Values(0.5, 0.1),

View File

@ -104,6 +104,26 @@ INSTANTIATE_TEST_CASE_P(Dilate3x3PerfTestCPU, Dilate3x3PerfTest,
Values(1, 2, 4),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(MorphologyExPerfTestCPU, MorphologyExPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1),
Values(szVGA, sz720p, sz1080p),
Values(cv::MorphTypes::MORPH_ERODE,
cv::MorphTypes::MORPH_DILATE,
cv::MorphTypes::MORPH_OPEN,
cv::MorphTypes::MORPH_CLOSE,
cv::MorphTypes::MORPH_GRADIENT,
cv::MorphTypes::MORPH_TOPHAT,
cv::MorphTypes::MORPH_BLACKHAT),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(MorphologyExHitMissPerfTestCPU, MorphologyExPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1),
Values(szVGA, sz720p, sz1080p),
Values(cv::MorphTypes::MORPH_HITMISS),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(SobelPerfTestCPU, SobelPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1),
@ -174,6 +194,126 @@ INSTANTIATE_TEST_CASE_P(GoodFeaturesInternalPerfTestCPU, GoodFeaturesPerfTest,
Values(true),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FindContoursPerfTestCPU, FindContoursPerfTest,
Combine(Values(AbsExact().to_compare_obj()),
Values(CV_8UC1),
Values(szVGA, sz720p, sz1080p),
Values(RETR_EXTERNAL, RETR_LIST, RETR_CCOMP, RETR_TREE),
Values(CHAIN_APPROX_NONE, CHAIN_APPROX_SIMPLE,
CHAIN_APPROX_TC89_L1, CHAIN_APPROX_TC89_KCOS),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FindContours32SPerfTestCPU, FindContoursPerfTest,
Combine(Values(AbsExact().to_compare_obj()),
Values(CV_32SC1),
Values(szVGA, sz720p, sz1080p),
Values(RETR_CCOMP, RETR_FLOODFILL),
Values(CHAIN_APPROX_NONE, CHAIN_APPROX_SIMPLE,
CHAIN_APPROX_TC89_L1, CHAIN_APPROX_TC89_KCOS),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FindContoursHPerfTestCPU, FindContoursHPerfTest,
Combine(Values(AbsExact().to_compare_obj()),
Values(CV_8UC1),
Values(szVGA, sz720p, sz1080p),
Values(RETR_EXTERNAL, RETR_LIST, RETR_CCOMP, RETR_TREE),
Values(CHAIN_APPROX_NONE, CHAIN_APPROX_SIMPLE,
CHAIN_APPROX_TC89_L1, CHAIN_APPROX_TC89_KCOS),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FindContoursH32SPerfTestCPU, FindContoursHPerfTest,
Combine(Values(AbsExact().to_compare_obj()),
Values(CV_32SC1),
Values(szVGA, sz720p, sz1080p),
Values(RETR_CCOMP, RETR_FLOODFILL),
Values(CHAIN_APPROX_NONE, CHAIN_APPROX_SIMPLE,
CHAIN_APPROX_TC89_L1, CHAIN_APPROX_TC89_KCOS),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(BoundingRectMatPerfTestCPU, BoundingRectMatPerfTest,
Combine(Values(IoUToleranceRect(0).to_compare_obj()),
Values(CV_8UC1),
Values(szVGA, sz720p, sz1080p),
Values(false),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(BoundingRectMatVectorPerfTestCPU, BoundingRectMatPerfTest,
Combine(Values(IoUToleranceRect(1e-5).to_compare_obj()),
Values(CV_32S, CV_32F),
Values(szVGA, sz720p, sz1080p),
Values(true),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(BoundingRectVector32SPerfTestCPU, BoundingRectVector32SPerfTest,
Combine(Values(IoUToleranceRect(0).to_compare_obj()),
Values(szVGA, sz720p, sz1080p),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(BoundingRectVector32FPerfTestCPU, BoundingRectVector32FPerfTest,
Combine(Values(IoUToleranceRect(1e-5).to_compare_obj()),
Values(szVGA, sz720p, sz1080p),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FitLine2DMatVectorPerfTestCPU, FitLine2DMatVectorPerfTest,
Combine(Values(RelDiffToleranceVec<float, 4>(0.01).to_compare_obj()),
Values(CV_8U, CV_8S, CV_16U, CV_16S,
CV_32S, CV_32F, CV_64F),
Values(cv::Size(8, 0), cv::Size(1024, 0)),
Values(DIST_L1, DIST_L2, DIST_L12, DIST_FAIR,
DIST_WELSCH, DIST_HUBER),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FitLine2DVector32SPerfTestCPU, FitLine2DVector32SPerfTest,
Combine(Values(RelDiffToleranceVec<float, 4>(0.01).to_compare_obj()),
Values(cv::Size(8, 0), cv::Size(1024, 0)),
Values(DIST_L1, DIST_L2, DIST_L12, DIST_FAIR,
DIST_WELSCH, DIST_HUBER),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FitLine2DVector32FPerfTestCPU, FitLine2DVector32FPerfTest,
Combine(Values(RelDiffToleranceVec<float, 4>(0.01).to_compare_obj()),
Values(cv::Size(8, 0), cv::Size(1024, 0)),
Values(DIST_L1, DIST_L2, DIST_L12, DIST_FAIR,
DIST_WELSCH, DIST_HUBER),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FitLine2DVector64FPerfTestCPU, FitLine2DVector64FPerfTest,
Combine(Values(RelDiffToleranceVec<float, 4>(0.01).to_compare_obj()),
Values(cv::Size(8, 0), cv::Size(1024, 0)),
Values(DIST_L1, DIST_L2, DIST_L12, DIST_FAIR,
DIST_WELSCH, DIST_HUBER),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FitLine3DMatVectorPerfTestCPU, FitLine3DMatVectorPerfTest,
Combine(Values(RelDiffToleranceVec<float, 6>(0.01).to_compare_obj()),
Values(CV_8U, CV_8S, CV_16U, CV_16S,
CV_32S, CV_32F, CV_64F),
Values(cv::Size(8, 0), cv::Size(1024, 0)),
Values(DIST_L1, DIST_L2, DIST_L12, DIST_FAIR,
DIST_WELSCH, DIST_HUBER),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FitLine3DVector32SPerfTestCPU, FitLine3DVector32SPerfTest,
Combine(Values(RelDiffToleranceVec<float, 6>(0.01).to_compare_obj()),
Values(cv::Size(8, 0), cv::Size(1024, 0)),
Values(DIST_L1, DIST_L2, DIST_L12, DIST_FAIR,
DIST_WELSCH, DIST_HUBER),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FitLine3DVector32FPerfTestCPU, FitLine3DVector32FPerfTest,
Combine(Values(RelDiffToleranceVec<float, 6>(0.01).to_compare_obj()),
Values(cv::Size(8, 0), cv::Size(1024, 0)),
Values(DIST_L1, DIST_L2, DIST_L12, DIST_FAIR,
DIST_WELSCH, DIST_HUBER),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(FitLine3DVector64FPerfTestCPU, FitLine3DVector64FPerfTest,
Combine(Values(RelDiffToleranceVec<float, 6>(0.01).to_compare_obj()),
Values(cv::Size(8, 0), cv::Size(1024, 0)),
Values(DIST_L1, DIST_L2, DIST_L12, DIST_FAIR,
DIST_WELSCH, DIST_HUBER),
Values(cv::compile_args(IMGPROC_CPU))));
INSTANTIATE_TEST_CASE_P(EqHistPerfTestCPU, EqHistPerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(szVGA, sz720p, sz1080p),

View File

@ -70,7 +70,7 @@ INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKForPyrPerfTestCPU), OptFlowLKF
Values(cv::TermCriteria(cv::TermCriteria::COUNT |
cv::TermCriteria::EPS,
30, 0.01)),
Values(true, false),
testing::Bool(),
Values(cv::compile_args(VIDEO_CPU))));
INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKInternalPerfTestCPU),
@ -90,7 +90,7 @@ INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildPyr_CalcOptFlow_PipelinePerfTestCP
Combine(Values("cv/optflow/frames/1080p_%02d.png"),
Values(7, 11),
Values(1000),
Values(true, false),
testing::Bool(),
Values(cv::compile_args(VIDEO_CPU))));
INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildPyr_CalcOptFlow_PipelineInternalTestPerfCPU),
@ -100,4 +100,33 @@ INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildPyr_CalcOptFlow_PipelineInternalTe
Values(3),
Values(true),
Values(cv::compile_args(VIDEO_CPU))));
INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BackgroundSubtractorPerfTestCPU),
BackgroundSubtractorPerfTest,
Combine(Values(cv::gapi::video::TYPE_BS_MOG2,
cv::gapi::video::TYPE_BS_KNN),
Values("cv/video/768x576.avi", "cv/video/1920x1080.avi"),
testing::Bool(),
Values(0., 0.5, 1.),
Values(5),
Values(cv::compile_args(VIDEO_CPU)),
Values(AbsExact().to_compare_obj())));
INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(KalmanFilterControlPerfTestCPU),
KalmanFilterControlPerfTest,
Combine(Values(CV_32FC1, CV_64FC1),
Values(2, 5),
Values(2, 5),
Values(5),
testing::Bool(),
Values(cv::compile_args(VIDEO_CPU))));
INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(KalmanFilterNoControlPerfTestCPU),
KalmanFilterNoControlPerfTest,
Combine(Values(CV_32FC1, CV_64FC1),
Values(2, 5),
Values(2, 5),
Values(5),
testing::Bool(),
Values(cv::compile_args(VIDEO_CPU))));
} // opencv_test

View File

@ -20,7 +20,8 @@ INSTANTIATE_TEST_CASE_P(AddPerfTestGPU, AddPerfTest,
Values(cv::compile_args(CORE_GPU))));
INSTANTIATE_TEST_CASE_P(AddCPerfTestGPU, AddCPerfTest,
Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
Combine(Values(AbsExact().to_compare_f()),
Values( szSmall128, szVGA, sz720p, sz1080p ),
Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
Values( -1, CV_8U, CV_16U, CV_32F ),
Values(cv::compile_args(CORE_GPU))));
@ -32,7 +33,8 @@ INSTANTIATE_TEST_CASE_P(SubPerfTestGPU, SubPerfTest,
Values(cv::compile_args(CORE_GPU))));
INSTANTIATE_TEST_CASE_P(SubCPerfTestGPU, SubCPerfTest,
Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
Combine(Values(AbsExact().to_compare_f()),
Values( szSmall128, szVGA, sz720p, sz1080p ),
Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
Values( -1, CV_8U, CV_16U, CV_32F ),
Values(cv::compile_args(CORE_GPU))));
@ -44,28 +46,33 @@ INSTANTIATE_TEST_CASE_P(SubRCPerfTestGPU, SubRCPerfTest,
Values(cv::compile_args(CORE_GPU))));
INSTANTIATE_TEST_CASE_P(MulPerfTestGPU, MulPerfTest,
Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
Combine(Values(AbsExact().to_compare_f()),
Values( szSmall128, szVGA, sz720p, sz1080p ),
Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
Values( -1, CV_8U, CV_16U, CV_32F ),
Values(2.0),
Values(cv::compile_args(CORE_GPU))));
INSTANTIATE_TEST_CASE_P(MulDoublePerfTestGPU, MulDoublePerfTest,
Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 1).to_compare_f()),
Values( szSmall128, szVGA, sz720p, sz1080p ),
Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
Values( -1, CV_8U, CV_16U, CV_32F ),
Values(cv::compile_args(CORE_GPU))));
INSTANTIATE_TEST_CASE_P(MulCPerfTestGPU, MulCPerfTest,
Combine(Values( szSmall128, szVGA, sz720p, sz1080p ),
Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 1).to_compare_f()),
Values( szSmall128, szVGA, sz720p, sz1080p ),
Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
Values( -1, CV_8U, CV_16U, CV_32F ),
Values(cv::compile_args(CORE_GPU))));
INSTANTIATE_TEST_CASE_P(DivPerfTestGPU, DivPerfTest,
Combine(Values(Tolerance_FloatRel_IntAbs(1e-5, 2).to_compare_f()),
Combine(Values(AbsTolerance(2).to_compare_f()),
Values( szSmall128, szVGA, sz720p, sz1080p ),
Values( CV_8UC1, CV_8UC3, CV_16UC1, CV_16SC1, CV_32FC1 ),
Values( -1, CV_8U, CV_16U, CV_32F ),
Values(2.3),
Values(cv::compile_args(CORE_GPU))));
INSTANTIATE_TEST_CASE_P(DivCPerfTestGPU, DivCPerfTest,
@ -276,6 +283,14 @@ INSTANTIATE_TEST_CASE_P(ConvertToPerfTestGPU, ConvertToPerfTest,
Values(0.0),
Values(cv::compile_args(CORE_GPU))));
INSTANTIATE_TEST_CASE_P(TransposePerfTestGPU, TransposePerfTest,
Combine(Values(AbsExact().to_compare_f()),
Values(szSmall128, szVGA, sz720p, sz1080p),
Values(CV_8UC1, CV_16UC1, CV_16SC1, CV_32FC1,
CV_8UC2, CV_16UC2, CV_16SC2, CV_32FC2,
CV_8UC3, CV_16UC3, CV_16SC3, CV_32FC3),
Values(cv::compile_args(CORE_GPU))));
INSTANTIATE_TEST_CASE_P(ResizePerfTestGPU, ResizePerfTest,
Combine(Values(AbsSimilarPoints(2, 0.05).to_compare_f()),
Values(CV_8UC1, CV_16UC1, CV_16SC1),

View File

@ -0,0 +1,83 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifdef HAVE_ONEVPL
#include "../perf_precomp.hpp"
#include "../../test/common/gapi_tests_common.hpp"
#include <opencv2/gapi/streaming/onevpl/source.hpp>
#include <opencv2/gapi/streaming/cap.hpp>
namespace opencv_test
{
using namespace perf;
const std::string files[] = {
"highgui/video/big_buck_bunny.h265",
"highgui/video/big_buck_bunny.h264",
};
const std::string codec[] = {
"MFX_CODEC_HEVC",
"MFX_CODEC_AVC"
};
using source_t = std::string;
using codec_t = std::string;
using source_description_t = std::tuple<source_t, codec_t>;
class OneVPLSourcePerfTest : public TestPerfParams<source_description_t> {};
class VideoCapSourcePerfTest : public TestPerfParams<source_t> {};
PERF_TEST_P_(OneVPLSourcePerfTest, TestPerformance)
{
using namespace cv::gapi::wip::onevpl;
const auto params = GetParam();
source_t src = findDataFile(get<0>(params));
codec_t type = get<1>(params);
std::vector<CfgParam> cfg_params {
CfgParam::create<std::string>("mfxImplDescription.Impl", "MFX_IMPL_TYPE_HARDWARE"),
CfgParam::create("mfxImplDescription.mfxDecoderDescription.decoder.CodecID", type),
};
auto source_ptr = cv::gapi::wip::make_onevpl_src(src, cfg_params);
cv::gapi::wip::Data out;
TEST_CYCLE()
{
source_ptr->pull(out);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P_(VideoCapSourcePerfTest, TestPerformance)
{
using namespace cv::gapi::wip;
source_t src = findDataFile(GetParam());
auto source_ptr = make_src<GCaptureSource>(src);
Data out;
TEST_CYCLE()
{
source_ptr->pull(out);
}
SANITY_CHECK_NOTHING();
}
INSTANTIATE_TEST_CASE_P(Streaming, OneVPLSourcePerfTest,
Values(source_description_t(files[0], codec[0]),
source_description_t(files[1], codec[1])));
INSTANTIATE_TEST_CASE_P(Streaming, VideoCapSourcePerfTest,
Values(files[0],
files[1]));
} // namespace opencv_test
#endif // HAVE_ONEVPL

View File

@ -0,0 +1,733 @@
#include <algorithm>
#include <cctype>
#include <cmath>
#include <iostream>
#include <limits>
#include <numeric>
#include <stdexcept>
#include <string>
#include <vector>
#include <opencv2/gapi.hpp>
#include <opencv2/gapi/core.hpp>
#include <opencv2/gapi/imgproc.hpp>
#include <opencv2/gapi/cpu/gcpukernel.hpp>
#include <opencv2/gapi/infer.hpp>
#include <opencv2/gapi/infer/ie.hpp>
#include <opencv2/gapi/streaming/cap.hpp>
#include <opencv2/gapi/gopaque.hpp>
#include <opencv2/highgui.hpp>
const std::string about =
"This is an OpenCV-based version of OMZ MTCNN Face Detection example";
const std::string keys =
"{ h help | | Print this help message }"
"{ input | | Path to the input video file }"
"{ mtcnnpm | mtcnn-p.xml | Path to OpenVINO MTCNN P (Proposal) detection model (.xml)}"
"{ mtcnnpd | CPU | Target device for the MTCNN P (e.g. CPU, GPU, VPU, ...) }"
"{ mtcnnrm | mtcnn-r.xml | Path to OpenVINO MTCNN R (Refinement) detection model (.xml)}"
"{ mtcnnrd | CPU | Target device for the MTCNN R (e.g. CPU, GPU, VPU, ...) }"
"{ mtcnnom | mtcnn-o.xml | Path to OpenVINO MTCNN O (Output) detection model (.xml)}"
"{ mtcnnod | CPU | Target device for the MTCNN O (e.g. CPU, GPU, VPU, ...) }"
"{ thrp | 0.6 | MTCNN P confidence threshold}"
"{ thrr | 0.7 | MTCNN R confidence threshold}"
"{ thro | 0.7 | MTCNN O confidence threshold}"
"{ half_scale | false | MTCNN P use half scale pyramid}"
"{ queue_capacity | 1 | Streaming executor queue capacity. Calculated automaticaly if 0}"
;
namespace {
std::string weights_path(const std::string& model_path) {
const auto EXT_LEN = 4u;
const auto sz = model_path.size();
CV_Assert(sz > EXT_LEN);
const auto ext = model_path.substr(sz - EXT_LEN);
CV_Assert(cv::toLowerCase(ext) == ".xml");
return model_path.substr(0u, sz - EXT_LEN) + ".bin";
}
//////////////////////////////////////////////////////////////////////
} // anonymous namespace
namespace custom {
namespace {
// Define custom structures and operations
#define NUM_REGRESSIONS 4
#define NUM_PTS 5
struct BBox {
int x1;
int y1;
int x2;
int y2;
cv::Rect getRect() const { return cv::Rect(x1,
y1,
x2 - x1,
y2 - y1); }
BBox getSquare() const {
BBox bbox;
float bboxWidth = static_cast<float>(x2 - x1);
float bboxHeight = static_cast<float>(y2 - y1);
float side = std::max(bboxWidth, bboxHeight);
bbox.x1 = static_cast<int>(static_cast<float>(x1) + (bboxWidth - side) * 0.5f);
bbox.y1 = static_cast<int>(static_cast<float>(y1) + (bboxHeight - side) * 0.5f);
bbox.x2 = static_cast<int>(static_cast<float>(bbox.x1) + side);
bbox.y2 = static_cast<int>(static_cast<float>(bbox.y1) + side);
return bbox;
}
};
struct Face {
BBox bbox;
float score;
std::array<float, NUM_REGRESSIONS> regression;
std::array<float, 2 * NUM_PTS> ptsCoords;
static void applyRegression(std::vector<Face>& faces, bool addOne = false) {
for (auto& face : faces) {
float bboxWidth =
face.bbox.x2 - face.bbox.x1 + static_cast<float>(addOne);
float bboxHeight =
face.bbox.y2 - face.bbox.y1 + static_cast<float>(addOne);
face.bbox.x1 = static_cast<int>(static_cast<float>(face.bbox.x1) + (face.regression[1] * bboxWidth));
face.bbox.y1 = static_cast<int>(static_cast<float>(face.bbox.y1) + (face.regression[0] * bboxHeight));
face.bbox.x2 = static_cast<int>(static_cast<float>(face.bbox.x2) + (face.regression[3] * bboxWidth));
face.bbox.y2 = static_cast<int>(static_cast<float>(face.bbox.y2) + (face.regression[2] * bboxHeight));
}
}
static void bboxes2Squares(std::vector<Face>& faces) {
for (auto& face : faces) {
face.bbox = face.bbox.getSquare();
}
}
static std::vector<Face> runNMS(std::vector<Face>& faces, const float threshold,
const bool useMin = false) {
std::vector<Face> facesNMS;
if (faces.empty()) {
return facesNMS;
}
std::sort(faces.begin(), faces.end(), [](const Face& f1, const Face& f2) {
return f1.score > f2.score;
});
std::vector<int> indices(faces.size());
std::iota(indices.begin(), indices.end(), 0);
while (indices.size() > 0) {
const int idx = indices[0];
facesNMS.push_back(faces[idx]);
std::vector<int> tmpIndices = indices;
indices.clear();
const float area1 = static_cast<float>(faces[idx].bbox.x2 - faces[idx].bbox.x1 + 1) *
static_cast<float>(faces[idx].bbox.y2 - faces[idx].bbox.y1 + 1);
for (size_t i = 1; i < tmpIndices.size(); ++i) {
int tmpIdx = tmpIndices[i];
const float interX1 = static_cast<float>(std::max(faces[idx].bbox.x1, faces[tmpIdx].bbox.x1));
const float interY1 = static_cast<float>(std::max(faces[idx].bbox.y1, faces[tmpIdx].bbox.y1));
const float interX2 = static_cast<float>(std::min(faces[idx].bbox.x2, faces[tmpIdx].bbox.x2));
const float interY2 = static_cast<float>(std::min(faces[idx].bbox.y2, faces[tmpIdx].bbox.y2));
const float bboxWidth = std::max(0.0f, (interX2 - interX1 + 1));
const float bboxHeight = std::max(0.0f, (interY2 - interY1 + 1));
const float interArea = bboxWidth * bboxHeight;
const float area2 = static_cast<float>(faces[tmpIdx].bbox.x2 - faces[tmpIdx].bbox.x1 + 1) *
static_cast<float>(faces[tmpIdx].bbox.y2 - faces[tmpIdx].bbox.y1 + 1);
float overlap = 0.0;
if (useMin) {
overlap = interArea / std::min(area1, area2);
} else {
overlap = interArea / (area1 + area2 - interArea);
}
if (overlap <= threshold) {
indices.push_back(tmpIdx);
}
}
}
return facesNMS;
}
};
const float P_NET_WINDOW_SIZE = 12.0f;
std::vector<Face> buildFaces(const cv::Mat& scores,
const cv::Mat& regressions,
const float scaleFactor,
const float threshold) {
auto w = scores.size[3];
auto h = scores.size[2];
auto size = w * h;
const float* scores_data = scores.ptr<float>();
scores_data += size;
const float* reg_data = regressions.ptr<float>();
auto out_side = std::max(h, w);
auto in_side = 2 * out_side + 11;
float stride = 0.0f;
if (out_side != 1)
{
stride = static_cast<float>(in_side - P_NET_WINDOW_SIZE) / static_cast<float>(out_side - 1);
}
std::vector<Face> boxes;
for (int i = 0; i < size; i++) {
if (scores_data[i] >= (threshold)) {
float y = static_cast<float>(i / w);
float x = static_cast<float>(i - w * y);
Face faceInfo;
BBox& faceBox = faceInfo.bbox;
faceBox.x1 = std::max(0, static_cast<int>((x * stride) / scaleFactor));
faceBox.y1 = std::max(0, static_cast<int>((y * stride) / scaleFactor));
faceBox.x2 = static_cast<int>((x * stride + P_NET_WINDOW_SIZE - 1.0f) / scaleFactor);
faceBox.y2 = static_cast<int>((y * stride + P_NET_WINDOW_SIZE - 1.0f) / scaleFactor);
faceInfo.regression[0] = reg_data[i];
faceInfo.regression[1] = reg_data[i + size];
faceInfo.regression[2] = reg_data[i + 2 * size];
faceInfo.regression[3] = reg_data[i + 3 * size];
faceInfo.score = scores_data[i];
boxes.push_back(faceInfo);
}
}
return boxes;
}
// Define networks for this sample
using GMat2 = std::tuple<cv::GMat, cv::GMat>;
using GMat3 = std::tuple<cv::GMat, cv::GMat, cv::GMat>;
using GMats = cv::GArray<cv::GMat>;
using GRects = cv::GArray<cv::Rect>;
using GSize = cv::GOpaque<cv::Size>;
G_API_NET(MTCNNRefinement,
<GMat2(cv::GMat)>,
"sample.custom.mtcnn_refinement");
G_API_NET(MTCNNOutput,
<GMat3(cv::GMat)>,
"sample.custom.mtcnn_output");
using GFaces = cv::GArray<Face>;
G_API_OP(BuildFaces,
<GFaces(cv::GMat, cv::GMat, float, float)>,
"sample.custom.mtcnn.build_faces") {
static cv::GArrayDesc outMeta(const cv::GMatDesc&,
const cv::GMatDesc&,
const float,
const float) {
return cv::empty_array_desc();
}
};
G_API_OP(RunNMS,
<GFaces(GFaces, float, bool)>,
"sample.custom.mtcnn.run_nms") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc&,
const float, const bool) {
return cv::empty_array_desc();
}
};
G_API_OP(AccumulatePyramidOutputs,
<GFaces(GFaces, GFaces)>,
"sample.custom.mtcnn.accumulate_pyramid_outputs") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc&,
const cv::GArrayDesc&) {
return cv::empty_array_desc();
}
};
G_API_OP(ApplyRegression,
<GFaces(GFaces, bool)>,
"sample.custom.mtcnn.apply_regression") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc&, const bool) {
return cv::empty_array_desc();
}
};
G_API_OP(BBoxesToSquares,
<GFaces(GFaces)>,
"sample.custom.mtcnn.bboxes_to_squares") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc&) {
return cv::empty_array_desc();
}
};
G_API_OP(R_O_NetPreProcGetROIs,
<GRects(GFaces, GSize)>,
"sample.custom.mtcnn.bboxes_r_o_net_preproc_get_rois") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc&, const cv::GOpaqueDesc&) {
return cv::empty_array_desc();
}
};
G_API_OP(RNetPostProc,
<GFaces(GFaces, GMats, GMats, float)>,
"sample.custom.mtcnn.rnet_postproc") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc&,
const cv::GArrayDesc&,
const cv::GArrayDesc&,
const float) {
return cv::empty_array_desc();
}
};
G_API_OP(ONetPostProc,
<GFaces(GFaces, GMats, GMats, GMats, float)>,
"sample.custom.mtcnn.onet_postproc") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc&,
const cv::GArrayDesc&,
const cv::GArrayDesc&,
const cv::GArrayDesc&,
const float) {
return cv::empty_array_desc();
}
};
G_API_OP(SwapFaces,
<GFaces(GFaces)>,
"sample.custom.mtcnn.swap_faces") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc&) {
return cv::empty_array_desc();
}
};
//Custom kernels implementation
GAPI_OCV_KERNEL(OCVBuildFaces, BuildFaces) {
static void run(const cv::Mat & in_scores,
const cv::Mat & in_regresssions,
const float scaleFactor,
const float threshold,
std::vector<Face> &out_faces) {
out_faces = buildFaces(in_scores, in_regresssions, scaleFactor, threshold);
}
};// GAPI_OCV_KERNEL(BuildFaces)
GAPI_OCV_KERNEL(OCVRunNMS, RunNMS) {
static void run(const std::vector<Face> &in_faces,
const float threshold,
const bool useMin,
std::vector<Face> &out_faces) {
std::vector<Face> in_faces_copy = in_faces;
out_faces = Face::runNMS(in_faces_copy, threshold, useMin);
}
};// GAPI_OCV_KERNEL(RunNMS)
GAPI_OCV_KERNEL(OCVAccumulatePyramidOutputs, AccumulatePyramidOutputs) {
static void run(const std::vector<Face> &total_faces,
const std::vector<Face> &in_faces,
std::vector<Face> &out_faces) {
out_faces = total_faces;
out_faces.insert(out_faces.end(), in_faces.begin(), in_faces.end());
}
};// GAPI_OCV_KERNEL(AccumulatePyramidOutputs)
GAPI_OCV_KERNEL(OCVApplyRegression, ApplyRegression) {
static void run(const std::vector<Face> &in_faces,
const bool addOne,
std::vector<Face> &out_faces) {
std::vector<Face> in_faces_copy = in_faces;
Face::applyRegression(in_faces_copy, addOne);
out_faces.clear();
out_faces.insert(out_faces.end(), in_faces_copy.begin(), in_faces_copy.end());
}
};// GAPI_OCV_KERNEL(ApplyRegression)
GAPI_OCV_KERNEL(OCVBBoxesToSquares, BBoxesToSquares) {
static void run(const std::vector<Face> &in_faces,
std::vector<Face> &out_faces) {
std::vector<Face> in_faces_copy = in_faces;
Face::bboxes2Squares(in_faces_copy);
out_faces.clear();
out_faces.insert(out_faces.end(), in_faces_copy.begin(), in_faces_copy.end());
}
};// GAPI_OCV_KERNEL(BBoxesToSquares)
GAPI_OCV_KERNEL(OCVR_O_NetPreProcGetROIs, R_O_NetPreProcGetROIs) {
static void run(const std::vector<Face> &in_faces,
const cv::Size & in_image_size,
std::vector<cv::Rect> &outs) {
outs.clear();
for (const auto& face : in_faces) {
cv::Rect tmp_rect = face.bbox.getRect();
//Compare to transposed sizes width<->height
tmp_rect &= cv::Rect(tmp_rect.x, tmp_rect.y, in_image_size.height - tmp_rect.x, in_image_size.width - tmp_rect.y) &
cv::Rect(0, 0, in_image_size.height, in_image_size.width);
outs.push_back(tmp_rect);
}
}
};// GAPI_OCV_KERNEL(R_O_NetPreProcGetROIs)
GAPI_OCV_KERNEL(OCVRNetPostProc, RNetPostProc) {
static void run(const std::vector<Face> &in_faces,
const std::vector<cv::Mat> &in_scores,
const std::vector<cv::Mat> &in_regresssions,
const float threshold,
std::vector<Face> &out_faces) {
out_faces.clear();
for (unsigned int k = 0; k < in_faces.size(); ++k) {
const float* scores_data = in_scores[k].ptr<float>();
const float* reg_data = in_regresssions[k].ptr<float>();
if (scores_data[1] >= threshold) {
Face info = in_faces[k];
info.score = scores_data[1];
std::copy_n(reg_data, NUM_REGRESSIONS, info.regression.begin());
out_faces.push_back(info);
}
}
}
};// GAPI_OCV_KERNEL(RNetPostProc)
GAPI_OCV_KERNEL(OCVONetPostProc, ONetPostProc) {
static void run(const std::vector<Face> &in_faces,
const std::vector<cv::Mat> &in_scores,
const std::vector<cv::Mat> &in_regresssions,
const std::vector<cv::Mat> &in_landmarks,
const float threshold,
std::vector<Face> &out_faces) {
out_faces.clear();
for (unsigned int k = 0; k < in_faces.size(); ++k) {
const float* scores_data = in_scores[k].ptr<float>();
const float* reg_data = in_regresssions[k].ptr<float>();
const float* landmark_data = in_landmarks[k].ptr<float>();
if (scores_data[1] >= threshold) {
Face info = in_faces[k];
info.score = scores_data[1];
for (size_t i = 0; i < 4; ++i) {
info.regression[i] = reg_data[i];
}
float w = info.bbox.x2 - info.bbox.x1 + 1.0f;
float h = info.bbox.y2 - info.bbox.y1 + 1.0f;
for (size_t p = 0; p < NUM_PTS; ++p) {
info.ptsCoords[2 * p] =
info.bbox.x1 + static_cast<float>(landmark_data[NUM_PTS + p]) * w - 1;
info.ptsCoords[2 * p + 1] = info.bbox.y1 + static_cast<float>(landmark_data[p]) * h - 1;
}
out_faces.push_back(info);
}
}
}
};// GAPI_OCV_KERNEL(ONetPostProc)
GAPI_OCV_KERNEL(OCVSwapFaces, SwapFaces) {
static void run(const std::vector<Face> &in_faces,
std::vector<Face> &out_faces) {
std::vector<Face> in_faces_copy = in_faces;
out_faces.clear();
if (!in_faces_copy.empty()) {
for (size_t i = 0; i < in_faces_copy.size(); ++i) {
std::swap(in_faces_copy[i].bbox.x1, in_faces_copy[i].bbox.y1);
std::swap(in_faces_copy[i].bbox.x2, in_faces_copy[i].bbox.y2);
for (size_t p = 0; p < NUM_PTS; ++p) {
std::swap(in_faces_copy[i].ptsCoords[2 * p], in_faces_copy[i].ptsCoords[2 * p + 1]);
}
}
out_faces = in_faces_copy;
}
}
};// GAPI_OCV_KERNEL(SwapFaces)
} // anonymous namespace
} // namespace custom
namespace vis {
namespace {
void bbox(const cv::Mat& m, const cv::Rect& rc) {
cv::rectangle(m, rc, cv::Scalar{ 0,255,0 }, 2, cv::LINE_8, 0);
};
using rectPoints = std::pair<cv::Rect, std::vector<cv::Point>>;
static cv::Mat drawRectsAndPoints(const cv::Mat& img,
const std::vector<rectPoints> data) {
cv::Mat outImg;
img.copyTo(outImg);
for (const auto& el : data) {
vis::bbox(outImg, el.first);
auto pts = el.second;
for (size_t i = 0; i < pts.size(); ++i) {
cv::circle(outImg, pts[i], 3, cv::Scalar(0, 255, 255), 1);
}
}
return outImg;
}
} // anonymous namespace
} // namespace vis
//Infer helper function
namespace {
static inline std::tuple<cv::GMat, cv::GMat> run_mtcnn_p(cv::GMat &in, const std::string &id) {
cv::GInferInputs inputs;
inputs["data"] = in;
auto outputs = cv::gapi::infer<cv::gapi::Generic>(id, inputs);
auto regressions = outputs.at("conv4-2");
auto scores = outputs.at("prob1");
return std::make_tuple(regressions, scores);
}
static inline std::string get_pnet_level_name(const cv::Size &in_size) {
return "MTCNNProposal_" + std::to_string(in_size.width) + "x" + std::to_string(in_size.height);
}
int calculate_scales(const cv::Size &input_size, std::vector<double> &out_scales, std::vector<cv::Size> &out_sizes ) {
//calculate multi - scale and limit the maxinum side to 1000
//pr_scale: limit the maxinum side to 1000, < 1.0
double pr_scale = 1.0;
double h = static_cast<double>(input_size.height);
double w = static_cast<double>(input_size.width);
if (std::min(w, h) > 1000)
{
pr_scale = 1000.0 / std::min(h, w);
w = w * pr_scale;
h = h * pr_scale;
}
else if (std::max(w, h) < 1000)
{
w = w * pr_scale;
h = h * pr_scale;
}
//multi - scale
out_scales.clear();
out_sizes.clear();
const double factor = 0.709;
int factor_count = 0;
double minl = std::min(h, w);
while (minl >= 12)
{
const double current_scale = pr_scale * std::pow(factor, factor_count);
cv::Size current_size(static_cast<int>(static_cast<double>(input_size.width) * current_scale),
static_cast<int>(static_cast<double>(input_size.height) * current_scale));
out_scales.push_back(current_scale);
out_sizes.push_back(current_size);
minl *= factor;
factor_count += 1;
}
return factor_count;
}
int calculate_half_scales(const cv::Size &input_size, std::vector<double>& out_scales, std::vector<cv::Size>& out_sizes) {
double pr_scale = 0.5;
const double h = static_cast<double>(input_size.height);
const double w = static_cast<double>(input_size.width);
//multi - scale
out_scales.clear();
out_sizes.clear();
const double factor = 0.5;
int factor_count = 0;
double minl = std::min(h, w);
while (minl >= 12.0*2.0)
{
const double current_scale = pr_scale;
cv::Size current_size(static_cast<int>(static_cast<double>(input_size.width) * current_scale),
static_cast<int>(static_cast<double>(input_size.height) * current_scale));
out_scales.push_back(current_scale);
out_sizes.push_back(current_size);
minl *= factor;
factor_count += 1;
pr_scale *= 0.5;
}
return factor_count;
}
const int MAX_PYRAMID_LEVELS = 13;
//////////////////////////////////////////////////////////////////////
} // anonymous namespace
int main(int argc, char* argv[]) {
cv::CommandLineParser cmd(argc, argv, keys);
cmd.about(about);
if (cmd.has("help")) {
cmd.printMessage();
return 0;
}
const auto input_file_name = cmd.get<std::string>("input");
const auto model_path_p = cmd.get<std::string>("mtcnnpm");
const auto target_dev_p = cmd.get<std::string>("mtcnnpd");
const auto conf_thresh_p = cmd.get<float>("thrp");
const auto model_path_r = cmd.get<std::string>("mtcnnrm");
const auto target_dev_r = cmd.get<std::string>("mtcnnrd");
const auto conf_thresh_r = cmd.get<float>("thrr");
const auto model_path_o = cmd.get<std::string>("mtcnnom");
const auto target_dev_o = cmd.get<std::string>("mtcnnod");
const auto conf_thresh_o = cmd.get<float>("thro");
const auto use_half_scale = cmd.get<bool>("half_scale");
const auto streaming_queue_capacity = cmd.get<unsigned int>("queue_capacity");
std::vector<cv::Size> level_size;
std::vector<double> scales;
//MTCNN input size
cv::VideoCapture cap;
cap.open(input_file_name);
if (!cap.isOpened())
CV_Assert(false);
auto in_rsz = cv::Size{ static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH)),
static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT)) };
//Calculate scales, number of pyramid levels and sizes for PNet pyramid
auto pyramid_levels = use_half_scale ? calculate_half_scales(in_rsz, scales, level_size) :
calculate_scales(in_rsz, scales, level_size);
CV_Assert(pyramid_levels <= MAX_PYRAMID_LEVELS);
//Proposal part of MTCNN graph
//Preprocessing BGR2RGB + transpose (NCWH is expected instead of NCHW)
cv::GMat in_original;
cv::GMat in_originalRGB = cv::gapi::BGR2RGB(in_original);
cv::GMat in_transposedRGB = cv::gapi::transpose(in_originalRGB);
cv::GOpaque<cv::Size> in_sz = cv::gapi::streaming::size(in_original);
cv::GMat regressions[MAX_PYRAMID_LEVELS];
cv::GMat scores[MAX_PYRAMID_LEVELS];
cv::GArray<custom::Face> nms_p_faces[MAX_PYRAMID_LEVELS];
cv::GArray<custom::Face> total_faces[MAX_PYRAMID_LEVELS];
//The very first PNet pyramid layer to init total_faces[0]
std::tie(regressions[0], scores[0]) = run_mtcnn_p(in_transposedRGB, get_pnet_level_name(level_size[0]));
cv::GArray<custom::Face> faces0 = custom::BuildFaces::on(scores[0], regressions[0], static_cast<float>(scales[0]), conf_thresh_p);
cv::GArray<custom::Face> final_p_faces_for_bb2squares = custom::ApplyRegression::on(faces0, true);
cv::GArray<custom::Face> final_faces_pnet0 = custom::BBoxesToSquares::on(final_p_faces_for_bb2squares);
total_faces[0] = custom::RunNMS::on(final_faces_pnet0, 0.5f, false);
//The rest PNet pyramid layers to accumlate all layers result in total_faces[PYRAMID_LEVELS - 1]]
for (int i = 1; i < pyramid_levels; ++i)
{
std::tie(regressions[i], scores[i]) = run_mtcnn_p(in_transposedRGB, get_pnet_level_name(level_size[i]));
cv::GArray<custom::Face> faces = custom::BuildFaces::on(scores[i], regressions[i], static_cast<float>(scales[i]), conf_thresh_p);
cv::GArray<custom::Face> final_p_faces_for_bb2squares_i = custom::ApplyRegression::on(faces, true);
cv::GArray<custom::Face> final_faces_pnet_i = custom::BBoxesToSquares::on(final_p_faces_for_bb2squares_i);
nms_p_faces[i] = custom::RunNMS::on(final_faces_pnet_i, 0.5f, false);
total_faces[i] = custom::AccumulatePyramidOutputs::on(total_faces[i - 1], nms_p_faces[i]);
}
//Proposal post-processing
cv::GArray<custom::Face> final_faces_pnet = custom::RunNMS::on(total_faces[pyramid_levels - 1], 0.7f, true);
//Refinement part of MTCNN graph
cv::GArray<cv::Rect> faces_roi_pnet = custom::R_O_NetPreProcGetROIs::on(final_faces_pnet, in_sz);
cv::GArray<cv::GMat> regressionsRNet, scoresRNet;
std::tie(regressionsRNet, scoresRNet) = cv::gapi::infer<custom::MTCNNRefinement>(faces_roi_pnet, in_transposedRGB);
//Refinement post-processing
cv::GArray<custom::Face> rnet_post_proc_faces = custom::RNetPostProc::on(final_faces_pnet, scoresRNet, regressionsRNet, conf_thresh_r);
cv::GArray<custom::Face> nms07_r_faces_total = custom::RunNMS::on(rnet_post_proc_faces, 0.7f, false);
cv::GArray<custom::Face> final_r_faces_for_bb2squares = custom::ApplyRegression::on(nms07_r_faces_total, true);
cv::GArray<custom::Face> final_faces_rnet = custom::BBoxesToSquares::on(final_r_faces_for_bb2squares);
//Output part of MTCNN graph
cv::GArray<cv::Rect> faces_roi_rnet = custom::R_O_NetPreProcGetROIs::on(final_faces_rnet, in_sz);
cv::GArray<cv::GMat> regressionsONet, scoresONet, landmarksONet;
std::tie(regressionsONet, landmarksONet, scoresONet) = cv::gapi::infer<custom::MTCNNOutput>(faces_roi_rnet, in_transposedRGB);
//Output post-processing
cv::GArray<custom::Face> onet_post_proc_faces = custom::ONetPostProc::on(final_faces_rnet, scoresONet, regressionsONet, landmarksONet, conf_thresh_o);
cv::GArray<custom::Face> final_o_faces_for_nms07 = custom::ApplyRegression::on(onet_post_proc_faces, true);
cv::GArray<custom::Face> nms07_o_faces_total = custom::RunNMS::on(final_o_faces_for_nms07, 0.7f, true);
cv::GArray<custom::Face> final_faces_onet = custom::SwapFaces::on(nms07_o_faces_total);
cv::GComputation graph_mtcnn(cv::GIn(in_original), cv::GOut(cv::gapi::copy(in_original), final_faces_onet));
// MTCNN Refinement detection network
auto mtcnnr_net = cv::gapi::ie::Params<custom::MTCNNRefinement>{
model_path_r, // path to topology IR
weights_path(model_path_r), // path to weights
target_dev_r, // device specifier
}.cfgOutputLayers({ "conv5-2", "prob1" }).cfgInputLayers({ "data" });
// MTCNN Output detection network
auto mtcnno_net = cv::gapi::ie::Params<custom::MTCNNOutput>{
model_path_o, // path to topology IR
weights_path(model_path_o), // path to weights
target_dev_o, // device specifier
}.cfgOutputLayers({ "conv6-2", "conv6-3", "prob1" }).cfgInputLayers({ "data" });
auto networks_mtcnn = cv::gapi::networks(mtcnnr_net, mtcnno_net);
// MTCNN Proposal detection network
for (int i = 0; i < pyramid_levels; ++i)
{
std::string net_id = get_pnet_level_name(level_size[i]);
std::vector<size_t> reshape_dims = { 1, 3, (size_t)level_size[i].width, (size_t)level_size[i].height };
cv::gapi::ie::Params<cv::gapi::Generic> mtcnnp_net{
net_id, // tag
model_path_p, // path to topology IR
weights_path(model_path_p), // path to weights
target_dev_p, // device specifier
};
mtcnnp_net.cfgInputReshape({ {"data", reshape_dims} });
networks_mtcnn += cv::gapi::networks(mtcnnp_net);
}
auto kernels_mtcnn = cv::gapi::kernels< custom::OCVBuildFaces
, custom::OCVRunNMS
, custom::OCVAccumulatePyramidOutputs
, custom::OCVApplyRegression
, custom::OCVBBoxesToSquares
, custom::OCVR_O_NetPreProcGetROIs
, custom::OCVRNetPostProc
, custom::OCVONetPostProc
, custom::OCVSwapFaces
>();
auto mtcnn_args = cv::compile_args(networks_mtcnn, kernels_mtcnn);
if (streaming_queue_capacity != 0)
mtcnn_args += cv::compile_args(cv::gapi::streaming::queue_capacity{ streaming_queue_capacity });
auto pipeline_mtcnn = graph_mtcnn.compileStreaming(std::move(mtcnn_args));
std::cout << "Reading " << input_file_name << std::endl;
// Input stream
auto in_src = cv::gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(input_file_name);
// Set the pipeline source & start the pipeline
pipeline_mtcnn.setSource(cv::gin(in_src));
pipeline_mtcnn.start();
// Declare the output data & run the processing loop
cv::TickMeter tm;
cv::Mat image;
std::vector<custom::Face> out_faces;
tm.start();
int frames = 0;
while (pipeline_mtcnn.pull(cv::gout(image, out_faces))) {
frames++;
std::cout << "Final Faces Size " << out_faces.size() << std::endl;
std::vector<vis::rectPoints> data;
// show the image with faces in it
for (const auto& out_face : out_faces) {
std::vector<cv::Point> pts;
for (size_t p = 0; p < NUM_PTS; ++p) {
pts.push_back(
cv::Point(static_cast<int>(out_face.ptsCoords[2 * p]), static_cast<int>(out_face.ptsCoords[2 * p + 1])));
}
auto rect = out_face.bbox.getRect();
auto d = std::make_pair(rect, pts);
data.push_back(d);
}
// Visualize results on the frame
auto resultImg = vis::drawRectsAndPoints(image, data);
tm.stop();
const auto fps_str = std::to_string(frames / tm.getTimeSec()) + " FPS";
cv::putText(resultImg, fps_str, { 0,32 }, cv::FONT_HERSHEY_SIMPLEX, 1.0, { 0,255,0 }, 2);
cv::imshow("Out", resultImg);
cv::waitKey(1);
out_faces.clear();
tm.start();
}
tm.stop();
std::cout << "Processed " << frames << " frames"
<< " (" << frames / tm.getTimeSec() << " FPS)" << std::endl;
return 0;
}

View File

@ -9,6 +9,7 @@
#include <opencv2/gapi/streaming/cap.hpp>
#include <opencv2/gapi/cpu/gcpukernel.hpp>
#include <opencv2/highgui.hpp> // CommandLineParser
#include <opencv2/gapi/infer/parsers.hpp>
const std::string about =
"This is an OpenCV-based version of Gaze Estimation example";
@ -16,13 +17,13 @@ const std::string keys =
"{ h help | | Print this help message }"
"{ input | | Path to the input video file }"
"{ facem | face-detection-retail-0005.xml | Path to OpenVINO face detection model (.xml) }"
"{ faced | CPU | Target device for the face detection (e.g. CPU, GPU, VPU, ...) }"
"{ faced | CPU | Target device for the face detection (e.g. CPU, GPU, ...) }"
"{ landm | facial-landmarks-35-adas-0002.xml | Path to OpenVINO landmarks detector model (.xml) }"
"{ landd | CPU | Target device for the landmarks detector (e.g. CPU, GPU, VPU, ...) }"
"{ landd | CPU | Target device for the landmarks detector (e.g. CPU, GPU, ...) }"
"{ headm | head-pose-estimation-adas-0001.xml | Path to OpenVINO head pose estimation model (.xml) }"
"{ headd | CPU | Target device for the head pose estimation inference (e.g. CPU, GPU, VPU, ...) }"
"{ headd | CPU | Target device for the head pose estimation inference (e.g. CPU, GPU, ...) }"
"{ gazem | gaze-estimation-adas-0002.xml | Path to OpenVINO gaze vector estimaiton model (.xml) }"
"{ gazed | CPU | Target device for the gaze vector estimation inference (e.g. CPU, GPU, VPU, ...) }"
"{ gazed | CPU | Target device for the gaze vector estimation inference (e.g. CPU, GPU, ...) }"
;
namespace {
@ -58,16 +59,6 @@ G_API_OP(Size, <GSize(cv::GMat)>, "custom.gapi.size") {
}
};
G_API_OP(ParseSSD,
<GRects(cv::GMat, GSize, bool)>,
"custom.gaze_estimation.parseSSD") {
static cv::GArrayDesc outMeta( const cv::GMatDesc &
, const cv::GOpaqueDesc &
, bool) {
return cv::empty_array_desc();
}
};
// Left/Right eye per every face
G_API_OP(ParseEyes,
<std::tuple<GRects, GRects>(GMats, GRects, GSize)>,
@ -91,27 +82,6 @@ G_API_OP(ProcessPoses,
}
};
void adjustBoundingBox(cv::Rect& boundingBox) {
auto w = boundingBox.width;
auto h = boundingBox.height;
boundingBox.x -= static_cast<int>(0.067 * w);
boundingBox.y -= static_cast<int>(0.028 * h);
boundingBox.width += static_cast<int>(0.15 * w);
boundingBox.height += static_cast<int>(0.13 * h);
if (boundingBox.width < boundingBox.height) {
auto dx = (boundingBox.height - boundingBox.width);
boundingBox.x -= dx / 2;
boundingBox.width += dx;
} else {
auto dy = (boundingBox.width - boundingBox.height);
boundingBox.y -= dy / 2;
boundingBox.height += dy;
}
}
void gazeVectorToGazeAngles(const cv::Point3f& gazeVector,
cv::Point2f& gazeAngles) {
auto r = cv::norm(gazeVector);
@ -130,55 +100,6 @@ GAPI_OCV_KERNEL(OCVSize, Size) {
}
};
GAPI_OCV_KERNEL(OCVParseSSD, ParseSSD) {
static void run(const cv::Mat &in_ssd_result,
const cv::Size &upscale,
const bool filter_out_of_bounds,
std::vector<cv::Rect> &out_objects) {
const auto &in_ssd_dims = in_ssd_result.size;
CV_Assert(in_ssd_dims.dims() == 4u);
const int MAX_PROPOSALS = in_ssd_dims[2];
const int OBJECT_SIZE = in_ssd_dims[3];
CV_Assert(OBJECT_SIZE == 7); // fixed SSD object size
const cv::Rect surface({0,0}, upscale);
out_objects.clear();
const float *data = in_ssd_result.ptr<float>();
for (int i = 0; i < MAX_PROPOSALS; i++) {
const float image_id = data[i * OBJECT_SIZE + 0];
const float label = data[i * OBJECT_SIZE + 1];
const float confidence = data[i * OBJECT_SIZE + 2];
const float rc_left = data[i * OBJECT_SIZE + 3];
const float rc_top = data[i * OBJECT_SIZE + 4];
const float rc_right = data[i * OBJECT_SIZE + 5];
const float rc_bottom = data[i * OBJECT_SIZE + 6];
(void) label;
if (image_id < 0.f) {
break; // marks end-of-detections
}
if (confidence < 0.5f) {
continue; // skip objects with low confidence
}
cv::Rect rc; // map relative coordinates to the original image scale
rc.x = static_cast<int>(rc_left * upscale.width);
rc.y = static_cast<int>(rc_top * upscale.height);
rc.width = static_cast<int>(rc_right * upscale.width) - rc.x;
rc.height = static_cast<int>(rc_bottom * upscale.height) - rc.y;
adjustBoundingBox(rc); // TODO: new option?
const auto clipped_rc = rc & surface; // TODO: new option?
if (filter_out_of_bounds) {
if (clipped_rc.area() != rc.area()) {
continue;
}
}
out_objects.emplace_back(clipped_rc);
}
}
};
cv::Rect eyeBox(const cv::Rect &face_rc,
float p1_x, float p1_y, float p2_x, float p2_y,
float scale = 1.8f) {
@ -335,11 +256,10 @@ int main(int argc, char *argv[])
cmd.printMessage();
return 0;
}
cv::GMat in;
cv::GMat faces = cv::gapi::infer<custom::Faces>(in);
cv::GOpaque<cv::Size> sz = custom::Size::on(in); // FIXME
cv::GArray<cv::Rect> faces_rc = custom::ParseSSD::on(faces, sz, true);
cv::GOpaque<cv::Size> sz = cv::gapi::streaming::size(in);
cv::GArray<cv::Rect> faces_rc = cv::gapi::parseSSD(faces, sz, 0.5f, true, true);
cv::GArray<cv::GMat> angles_y, angles_p, angles_r;
std::tie(angles_y, angles_p, angles_r) = cv::gapi::infer<custom::HeadPose>(faces_rc, in);
cv::GArray<cv::GMat> heads_pos = custom::ProcessPoses::on(angles_y, angles_p, angles_r);
@ -386,7 +306,6 @@ int main(int argc, char *argv[])
}.cfgInputLayers({"left_eye_image", "right_eye_image", "head_pose_angles"});
auto kernels = cv::gapi::kernels< custom::OCVSize
, custom::OCVParseSSD
, custom::OCVParseEyes
, custom::OCVProcessPoses>();
auto networks = cv::gapi::networks(face_net, head_net, landmarks_net, gaze_net);

View File

@ -156,7 +156,6 @@ int main(int argc, char *argv[])
auto in_src = cv::gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(input);
pipeline.setSource(cv::gin(in_src));
pipeline.start();
cv::util::optional<cv::Mat> out_frame;
cv::util::optional<std::vector<cv::Rect>> out_faces;
@ -167,8 +166,13 @@ int main(int argc, char *argv[])
std::vector<cv::Mat> last_emotions;
cv::VideoWriter writer;
cv::TickMeter tm;
std::size_t frames = 0u;
tm.start();
pipeline.start();
while (pipeline.pull(cv::gout(out_frame, out_faces, out_emotions))) {
++frames;
if (out_faces && out_emotions) {
last_faces = *out_faces;
last_emotions = *out_emotions;
@ -191,5 +195,7 @@ int main(int argc, char *argv[])
cv::waitKey(1);
}
}
tm.stop();
std::cout << "Processed " << frames << " frames" << " (" << frames / tm.getTimeSec() << " FPS)" << std::endl;
return 0;
}

View File

@ -13,6 +13,7 @@
#include <opencv2/gapi/cpu/gcpukernel.hpp>
#include <opencv2/gapi/streaming/cap.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/gapi/infer/parsers.hpp>
const std::string keys =
"{ h help | | Print this help message }"
@ -69,36 +70,18 @@ using GRect = cv::GOpaque<cv::Rect>;
using GSize = cv::GOpaque<cv::Size>;
using GPrims = cv::GArray<cv::gapi::wip::draw::Prim>;
G_API_OP(GetSize, <GSize(cv::GMat)>, "sample.custom.get-size") {
static cv::GOpaqueDesc outMeta(const cv::GMatDesc &) {
return cv::empty_gopaque_desc();
}
};
G_API_OP(LocateROI, <GRect(cv::GMat)>, "sample.custom.locate-roi") {
static cv::GOpaqueDesc outMeta(const cv::GMatDesc &) {
return cv::empty_gopaque_desc();
}
};
G_API_OP(ParseSSD, <GDetections(cv::GMat, GRect, GSize)>, "sample.custom.parse-ssd") {
static cv::GArrayDesc outMeta(const cv::GMatDesc &, const cv::GOpaqueDesc &, const cv::GOpaqueDesc &) {
return cv::empty_array_desc();
}
};
G_API_OP(BBoxes, <GPrims(GDetections, GRect)>, "sample.custom.b-boxes") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc &, const cv::GOpaqueDesc &) {
return cv::empty_array_desc();
}
};
GAPI_OCV_KERNEL(OCVGetSize, GetSize) {
static void run(const cv::Mat &in, cv::Size &out) {
out = {in.cols, in.rows};
}
};
GAPI_OCV_KERNEL(OCVLocateROI, LocateROI) {
// This is the place where we can run extra analytics
// on the input image frame and select the ROI (region
@ -124,55 +107,6 @@ GAPI_OCV_KERNEL(OCVLocateROI, LocateROI) {
}
};
GAPI_OCV_KERNEL(OCVParseSSD, ParseSSD) {
static void run(const cv::Mat &in_ssd_result,
const cv::Rect &in_roi,
const cv::Size &in_parent_size,
std::vector<cv::Rect> &out_objects) {
const auto &in_ssd_dims = in_ssd_result.size;
CV_Assert(in_ssd_dims.dims() == 4u);
const int MAX_PROPOSALS = in_ssd_dims[2];
const int OBJECT_SIZE = in_ssd_dims[3];
CV_Assert(OBJECT_SIZE == 7); // fixed SSD object size
const cv::Size up_roi = in_roi.size();
const cv::Rect surface({0,0}, in_parent_size);
out_objects.clear();
const float *data = in_ssd_result.ptr<float>();
for (int i = 0; i < MAX_PROPOSALS; i++) {
const float image_id = data[i * OBJECT_SIZE + 0];
const float label = data[i * OBJECT_SIZE + 1];
const float confidence = data[i * OBJECT_SIZE + 2];
const float rc_left = data[i * OBJECT_SIZE + 3];
const float rc_top = data[i * OBJECT_SIZE + 4];
const float rc_right = data[i * OBJECT_SIZE + 5];
const float rc_bottom = data[i * OBJECT_SIZE + 6];
(void) label; // unused
if (image_id < 0.f) {
break; // marks end-of-detections
}
if (confidence < 0.5f) {
continue; // skip objects with low confidence
}
// map relative coordinates to the original image scale
// taking the ROI into account
cv::Rect rc;
rc.x = static_cast<int>(rc_left * up_roi.width);
rc.y = static_cast<int>(rc_top * up_roi.height);
rc.width = static_cast<int>(rc_right * up_roi.width) - rc.x;
rc.height = static_cast<int>(rc_bottom * up_roi.height) - rc.y;
rc.x += in_roi.x;
rc.y += in_roi.y;
out_objects.emplace_back(rc & surface);
}
}
};
GAPI_OCV_KERNEL(OCVBBoxes, BBoxes) {
// This kernel converts the rectangles into G-API's
// rendering primitives
@ -211,9 +145,7 @@ int main(int argc, char *argv[])
cmd.get<std::string>("faced"), // device specifier
};
auto kernels = cv::gapi::kernels
< custom::OCVGetSize
, custom::OCVLocateROI
, custom::OCVParseSSD
<custom::OCVLocateROI
, custom::OCVBBoxes>();
auto networks = cv::gapi::networks(face_net);
@ -222,16 +154,17 @@ int main(int argc, char *argv[])
cv::GStreamingCompiled pipeline;
auto inputs = cv::gin(cv::gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(input));
cv::GMat in;
cv::GOpaque<cv::Size> sz = cv::gapi::streaming::size(in);
if (opt_roi.has_value()) {
// Use the value provided by user
std::cout << "Will run inference for static region "
<< opt_roi.value()
<< " only"
<< std::endl;
cv::GMat in;
cv::GOpaque<cv::Rect> in_roi;
auto blob = cv::gapi::infer<custom::FaceDetector>(in_roi, in);
auto rcs = custom::ParseSSD::on(blob, in_roi, custom::GetSize::on(in));
cv::GArray<cv::Rect> rcs = cv::gapi::parseSSD(blob, sz, 0.5f, true, true);
auto out = cv::gapi::wip::draw::render3ch(in, custom::BBoxes::on(rcs, in_roi));
pipeline = cv::GComputation(cv::GIn(in, in_roi), cv::GOut(out))
.compileStreaming(cv::compile_args(kernels, networks));
@ -242,10 +175,9 @@ int main(int argc, char *argv[])
// Automatically detect ROI to infer. Make it output parameter
std::cout << "ROI is not set or invalid. Locating it automatically"
<< std::endl;
cv::GMat in;
cv::GOpaque<cv::Rect> roi = custom::LocateROI::on(in);
auto blob = cv::gapi::infer<custom::FaceDetector>(roi, in);
auto rcs = custom::ParseSSD::on(blob, roi, custom::GetSize::on(in));
cv::GArray<cv::Rect> rcs = cv::gapi::parseSSD(blob, sz, 0.5f, true, true);
auto out = cv::gapi::wip::draw::render3ch(in, custom::BBoxes::on(rcs, roi));
pipeline = cv::GComputation(cv::GIn(in), cv::GOut(out))
.compileStreaming(cv::compile_args(kernels, networks));
@ -256,9 +188,15 @@ int main(int argc, char *argv[])
pipeline.start();
cv::Mat out;
size_t frames = 0u;
cv::TickMeter tm;
tm.start();
while (pipeline.pull(cv::gout(out))) {
cv::imshow("Out", out);
cv::waitKey(1);
++frames;
}
tm.stop();
std::cout << "Processed " << frames << " frames" << " (" << frames / tm.getTimeSec() << " FPS)" << std::endl;
return 0;
}

View File

@ -14,6 +14,7 @@
#include <opencv2/gapi/cpu/gcpukernel.hpp>
#include <opencv2/gapi/streaming/cap.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/gapi/infer/parsers.hpp>
namespace custom {
@ -23,71 +24,12 @@ using GDetections = cv::GArray<cv::Rect>;
using GSize = cv::GOpaque<cv::Size>;
using GPrims = cv::GArray<cv::gapi::wip::draw::Prim>;
G_API_OP(GetSize, <GSize(cv::GMat)>, "sample.custom.get-size") {
static cv::GOpaqueDesc outMeta(const cv::GMatDesc &) {
return cv::empty_gopaque_desc();
}
};
G_API_OP(ParseSSD, <GDetections(cv::GMat, GSize)>, "sample.custom.parse-ssd") {
static cv::GArrayDesc outMeta(const cv::GMatDesc &, const cv::GOpaqueDesc &) {
return cv::empty_array_desc();
}
};
G_API_OP(BBoxes, <GPrims(GDetections)>, "sample.custom.b-boxes") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc &) {
return cv::empty_array_desc();
}
};
GAPI_OCV_KERNEL(OCVGetSize, GetSize) {
static void run(const cv::Mat &in, cv::Size &out) {
out = {in.cols, in.rows};
}
};
GAPI_OCV_KERNEL(OCVParseSSD, ParseSSD) {
static void run(const cv::Mat &in_ssd_result,
const cv::Size &in_parent_size,
std::vector<cv::Rect> &out_objects) {
const auto &in_ssd_dims = in_ssd_result.size;
CV_Assert(in_ssd_dims.dims() == 4u);
const int MAX_PROPOSALS = in_ssd_dims[2];
const int OBJECT_SIZE = in_ssd_dims[3];
CV_Assert(OBJECT_SIZE == 7); // fixed SSD object size
const cv::Rect surface({0,0}, in_parent_size);
out_objects.clear();
const float *data = in_ssd_result.ptr<float>();
for (int i = 0; i < MAX_PROPOSALS; i++) {
const float image_id = data[i * OBJECT_SIZE + 0];
const float label = data[i * OBJECT_SIZE + 1];
const float confidence = data[i * OBJECT_SIZE + 2];
const float rc_left = data[i * OBJECT_SIZE + 3];
const float rc_top = data[i * OBJECT_SIZE + 4];
const float rc_right = data[i * OBJECT_SIZE + 5];
const float rc_bottom = data[i * OBJECT_SIZE + 6];
(void) label; // unused
if (image_id < 0.f) {
break; // marks end-of-detections
}
if (confidence < 0.5f) {
continue; // skip objects with low confidence
}
// map relative coordinates to the original image scale
cv::Rect rc;
rc.x = static_cast<int>(rc_left * in_parent_size.width);
rc.y = static_cast<int>(rc_top * in_parent_size.height);
rc.width = static_cast<int>(rc_right * in_parent_size.width) - rc.x;
rc.height = static_cast<int>(rc_bottom * in_parent_size.height) - rc.y;
out_objects.emplace_back(rc & surface);
}
}
};
GAPI_OCV_KERNEL(OCVBBoxes, BBoxes) {
// This kernel converts the rectangles into G-API's
// rendering primitives
@ -151,7 +93,6 @@ void remap_ssd_ports(const std::unordered_map<std::string, cv::Mat> &onnx,
}
} // anonymous namespace
const std::string keys =
"{ h help | | Print this help message }"
"{ input | | Path to the input video file }"
@ -175,15 +116,14 @@ int main(int argc, char *argv[])
auto obj_net = cv::gapi::onnx::Params<custom::ObjDetector>{obj_model_path}
.cfgOutputLayers({"detection_output"})
.cfgPostProc({cv::GMatDesc{CV_32F, {1,1,200,7}}}, remap_ssd_ports);
auto kernels = cv::gapi::kernels< custom::OCVGetSize
, custom::OCVParseSSD
, custom::OCVBBoxes>();
auto kernels = cv::gapi::kernels<custom::OCVBBoxes>();
auto networks = cv::gapi::networks(obj_net);
// Now build the graph
cv::GMat in;
auto blob = cv::gapi::infer<custom::ObjDetector>(in);
auto rcs = custom::ParseSSD::on(blob, custom::GetSize::on(in));
cv::GArray<cv::Rect> rcs =
cv::gapi::parseSSD(blob, cv::gapi::streaming::size(in), 0.5f, true, true);
auto out = cv::gapi::wip::draw::render3ch(in, custom::BBoxes::on(rcs));
cv::GStreamingCompiled pipeline = cv::GComputation(cv::GIn(in), cv::GOut(out))
.compileStreaming(cv::compile_args(kernels, networks));
@ -192,12 +132,16 @@ int main(int argc, char *argv[])
// The execution part
pipeline.setSource(std::move(inputs));
pipeline.start();
cv::TickMeter tm;
cv::VideoWriter writer;
size_t frames = 0u;
cv::Mat outMat;
tm.start();
pipeline.start();
while (pipeline.pull(cv::gout(outMat))) {
++frames;
cv::imshow("Out", outMat);
cv::waitKey(1);
if (!output.empty()) {
@ -209,5 +153,7 @@ int main(int argc, char *argv[])
writer << outMat;
}
}
tm.stop();
std::cout << "Processed " << frames << " frames" << " (" << frames / tm.getTimeSec() << " FPS)" << std::endl;
return 0;
}

View File

@ -0,0 +1,390 @@
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cctype>
#include <tuple>
#include <opencv2/imgproc.hpp>
#include <opencv2/gapi.hpp>
#include <opencv2/gapi/core.hpp>
#include <opencv2/gapi/cpu/gcpukernel.hpp>
#include <opencv2/gapi/infer/ie.hpp>
#include <opencv2/gapi/render.hpp>
#include <opencv2/gapi/streaming/onevpl/source.hpp>
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp>
#include <opencv2/highgui.hpp> // CommandLineParser
#include <opencv2/gapi/infer/parsers.hpp>
#ifdef HAVE_INF_ENGINE
#include <inference_engine.hpp> // ParamMap
#ifdef HAVE_DIRECTX
#ifdef HAVE_D3D11
#pragma comment(lib,"d3d11.lib")
// get rid of generate macro max/min/etc from DX side
#define D3D11_NO_HELPERS
#define NOMINMAX
#include <cldnn/cldnn_config.hpp>
#include <d3d11.h>
#pragma comment(lib, "dxgi")
#undef NOMINMAX
#undef D3D11_NO_HELPERS
#endif // HAVE_D3D11
#endif // HAVE_DIRECTX
#endif // HAVE_INF_ENGINE
const std::string about =
"This is an OpenCV-based version of oneVPLSource decoder example";
const std::string keys =
"{ h help | | Print this help message }"
"{ input | | Path to the input demultiplexed video file }"
"{ output | | Path to the output RAW video file. Use .avi extension }"
"{ facem | face-detection-adas-0001.xml | Path to OpenVINO IE face detection model (.xml) }"
"{ faced | CPU | Target device for face detection model (e.g. CPU, GPU, VPU, ...) }"
"{ cfg_params | <prop name>:<value>;<prop name>:<value> | Semicolon separated list of oneVPL mfxVariants which is used for configuring source (see `MFXSetConfigFilterProperty` by https://spec.oneapi.io/versions/latest/elements/oneVPL/source/index.html) }";
namespace {
std::string get_weights_path(const std::string &model_path) {
const auto EXT_LEN = 4u;
const auto sz = model_path.size();
CV_Assert(sz > EXT_LEN);
auto ext = model_path.substr(sz - EXT_LEN);
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){
return static_cast<unsigned char>(std::tolower(c));
});
CV_Assert(ext == ".xml");
return model_path.substr(0u, sz - EXT_LEN) + ".bin";
}
#ifdef HAVE_INF_ENGINE
#ifdef HAVE_DIRECTX
#ifdef HAVE_D3D11
// Since ATL headers might not be available on specific MSVS Build Tools
// we use simple `CComPtr` implementation like as `ComPtrGuard`
// which is not supposed to be the full functional replacement of `CComPtr`
// and it uses as RAII to make sure utilization is correct
template <typename COMNonManageableType>
void release(COMNonManageableType *ptr) {
if (ptr) {
ptr->Release();
}
}
template <typename COMNonManageableType>
using ComPtrGuard = std::unique_ptr<COMNonManageableType, decltype(&release<COMNonManageableType>)>;
template <typename COMNonManageableType>
ComPtrGuard<COMNonManageableType> createCOMPtrGuard(COMNonManageableType *ptr = nullptr) {
return ComPtrGuard<COMNonManageableType> {ptr, &release<COMNonManageableType>};
}
using AccelParamsType = std::tuple<ComPtrGuard<ID3D11Device>, ComPtrGuard<ID3D11DeviceContext>>;
AccelParamsType create_device_with_ctx(IDXGIAdapter* adapter) {
UINT flags = 0;
D3D_FEATURE_LEVEL feature_levels[] = { D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
};
D3D_FEATURE_LEVEL featureLevel;
ID3D11Device* ret_device_ptr = nullptr;
ID3D11DeviceContext* ret_ctx_ptr = nullptr;
HRESULT err = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN,
nullptr, flags,
feature_levels,
ARRAYSIZE(feature_levels),
D3D11_SDK_VERSION, &ret_device_ptr,
&featureLevel, &ret_ctx_ptr);
if (FAILED(err)) {
throw std::runtime_error("Cannot create D3D11CreateDevice, error: " +
std::to_string(HRESULT_CODE(err)));
}
return std::make_tuple(createCOMPtrGuard(ret_device_ptr),
createCOMPtrGuard(ret_ctx_ptr));
}
#endif // HAVE_D3D11
#endif // HAVE_DIRECTX
#endif // HAVE_INF_ENGINE
} // anonymous namespace
namespace custom {
G_API_NET(FaceDetector, <cv::GMat(cv::GMat)>, "face-detector");
using GDetections = cv::GArray<cv::Rect>;
using GRect = cv::GOpaque<cv::Rect>;
using GSize = cv::GOpaque<cv::Size>;
using GPrims = cv::GArray<cv::gapi::wip::draw::Prim>;
G_API_OP(LocateROI, <GRect(GSize)>, "sample.custom.locate-roi") {
static cv::GOpaqueDesc outMeta(const cv::GOpaqueDesc &) {
return cv::empty_gopaque_desc();
}
};
G_API_OP(BBoxes, <GPrims(GDetections, GRect)>, "sample.custom.b-boxes") {
static cv::GArrayDesc outMeta(const cv::GArrayDesc &, const cv::GOpaqueDesc &) {
return cv::empty_array_desc();
}
};
GAPI_OCV_KERNEL(OCVLocateROI, LocateROI) {
// This is the place where we can run extra analytics
// on the input image frame and select the ROI (region
// of interest) where we want to detect our objects (or
// run any other inference).
//
// Currently it doesn't do anything intelligent,
// but only crops the input image to square (this is
// the most convenient aspect ratio for detectors to use)
static void run(const cv::Size& in_size, cv::Rect &out_rect) {
// Identify the central point & square size (- some padding)
const auto center = cv::Point{in_size.width/2, in_size.height/2};
auto sqside = std::min(in_size.width, in_size.height);
// Now build the central square ROI
out_rect = cv::Rect{ center.x - sqside/2
, center.y - sqside/2
, sqside
, sqside
};
}
};
GAPI_OCV_KERNEL(OCVBBoxes, BBoxes) {
// This kernel converts the rectangles into G-API's
// rendering primitives
static void run(const std::vector<cv::Rect> &in_face_rcs,
const cv::Rect &in_roi,
std::vector<cv::gapi::wip::draw::Prim> &out_prims) {
out_prims.clear();
const auto cvt = [](const cv::Rect &rc, const cv::Scalar &clr) {
return cv::gapi::wip::draw::Rect(rc, clr, 2);
};
out_prims.emplace_back(cvt(in_roi, CV_RGB(0,255,255))); // cyan
for (auto &&rc : in_face_rcs) {
out_prims.emplace_back(cvt(rc, CV_RGB(0,255,0))); // green
}
}
};
} // namespace custom
namespace cfg {
typename cv::gapi::wip::onevpl::CfgParam create_from_string(const std::string &line);
}
int main(int argc, char *argv[]) {
cv::CommandLineParser cmd(argc, argv, keys);
cmd.about(about);
if (cmd.has("help")) {
cmd.printMessage();
return 0;
}
// get file name
std::string file_path = cmd.get<std::string>("input");
const std::string output = cmd.get<std::string>("output");
const auto face_model_path = cmd.get<std::string>("facem");
// check ouput file extension
if (!output.empty()) {
auto ext = output.find_last_of(".");
if (ext == std::string::npos || (output.substr(ext + 1) != "avi")) {
std::cerr << "Output file should have *.avi extension for output video" << std::endl;
return -1;
}
}
// get oneVPL cfg params from cmd
std::stringstream params_list(cmd.get<std::string>("cfg_params"));
std::vector<cv::gapi::wip::onevpl::CfgParam> source_cfgs;
try {
std::string line;
while (std::getline(params_list, line, ';')) {
source_cfgs.push_back(cfg::create_from_string(line));
}
} catch (const std::exception& ex) {
std::cerr << "Invalid cfg parameter: " << ex.what() << std::endl;
return -1;
}
const std::string& device_id = cmd.get<std::string>("faced");
auto face_net = cv::gapi::ie::Params<custom::FaceDetector> {
face_model_path, // path to topology IR
get_weights_path(face_model_path), // path to weights
device_id
};
// Create device_ptr & context_ptr using graphic API
// InferenceEngine requires such device & context to create its own
// remote shared context through InferenceEngine::ParamMap in
// GAPI InferenceEngine backend to provide interoperability with onevpl::GSource
// So GAPI InferenceEngine backend and onevpl::GSource MUST share the same
// device and context
void* accel_device_ptr = nullptr;
void* accel_ctx_ptr = nullptr;
#ifdef HAVE_INF_ENGINE
#ifdef HAVE_DIRECTX
#ifdef HAVE_D3D11
auto dx11_dev = createCOMPtrGuard<ID3D11Device>();
auto dx11_ctx = createCOMPtrGuard<ID3D11DeviceContext>();
if (device_id.find("GPU") != std::string::npos) {
auto adapter_factory = createCOMPtrGuard<IDXGIFactory>();
{
IDXGIFactory* out_factory = nullptr;
HRESULT err = CreateDXGIFactory(__uuidof(IDXGIFactory),
reinterpret_cast<void**>(&out_factory));
if (FAILED(err)) {
std::cerr << "Cannot create CreateDXGIFactory, error: " << HRESULT_CODE(err) << std::endl;
return -1;
}
adapter_factory = createCOMPtrGuard(out_factory);
}
auto intel_adapter = createCOMPtrGuard<IDXGIAdapter>();
UINT adapter_index = 0;
const unsigned int refIntelVendorID = 0x8086;
IDXGIAdapter* out_adapter = nullptr;
while (adapter_factory->EnumAdapters(adapter_index, &out_adapter) != DXGI_ERROR_NOT_FOUND) {
DXGI_ADAPTER_DESC desc{};
out_adapter->GetDesc(&desc);
if (desc.VendorId == refIntelVendorID) {
intel_adapter = createCOMPtrGuard(out_adapter);
break;
}
++adapter_index;
}
if (!intel_adapter) {
std::cerr << "No Intel GPU adapter on aboard. Exit" << std::endl;
return -1;
}
std::tie(dx11_dev, dx11_ctx) = create_device_with_ctx(intel_adapter.get());
accel_device_ptr = reinterpret_cast<void*>(dx11_dev.get());
accel_ctx_ptr = reinterpret_cast<void*>(dx11_ctx.get());
// put accel type description for VPL source
source_cfgs.push_back(cfg::create_from_string(
"mfxImplDescription.AccelerationMode"
":"
"MFX_ACCEL_MODE_VIA_D3D11"));
}
#endif // HAVE_D3D11
#endif // HAVE_DIRECTX
// set ctx_config for GPU device only - no need in case of CPU device type
if (device_id.find("GPU") != std::string::npos) {
InferenceEngine::ParamMap ctx_config({{"CONTEXT_TYPE", "VA_SHARED"},
{"VA_DEVICE", accel_device_ptr} });
face_net.cfgContextParams(ctx_config);
}
#endif // HAVE_INF_ENGINE
auto kernels = cv::gapi::kernels
< custom::OCVLocateROI
, custom::OCVBBoxes>();
auto networks = cv::gapi::networks(face_net);
// Create source
cv::Ptr<cv::gapi::wip::IStreamSource> cap;
try {
if (device_id.find("GPU") != std::string::npos) {
cap = cv::gapi::wip::make_onevpl_src(file_path, source_cfgs,
device_id,
accel_device_ptr,
accel_ctx_ptr);
} else {
cap = cv::gapi::wip::make_onevpl_src(file_path, source_cfgs);
}
std::cout << "oneVPL source desription: " << cap->descr_of() << std::endl;
} catch (const std::exception& ex) {
std::cerr << "Cannot create source: " << ex.what() << std::endl;
return -1;
}
cv::GMetaArg descr = cap->descr_of();
auto frame_descr = cv::util::get<cv::GFrameDesc>(descr);
// Now build the graph
cv::GFrame in;
auto size = cv::gapi::streaming::size(in);
auto roi = custom::LocateROI::on(size);
auto blob = cv::gapi::infer<custom::FaceDetector>(roi, in);
cv::GArray<cv::Rect> rcs = cv::gapi::parseSSD(blob, size, 0.5f, true, true);
auto out_frame = cv::gapi::wip::draw::renderFrame(in, custom::BBoxes::on(rcs, roi));
auto out = cv::gapi::streaming::BGR(out_frame);
cv::GStreamingCompiled pipeline;
try {
pipeline = cv::GComputation(cv::GIn(in), cv::GOut(out))
.compileStreaming(cv::compile_args(kernels, networks));
} catch (const std::exception& ex) {
std::cerr << "Exception occured during pipeline construction: " << ex.what() << std::endl;
return -1;
}
// The execution part
// TODO USE may set pool size from outside and set queue_capacity size,
// compile arg: cv::gapi::streaming::queue_capacity
pipeline.setSource(std::move(cap));
pipeline.start();
size_t frames = 0u;
cv::TickMeter tm;
cv::VideoWriter writer;
if (!output.empty() && !writer.isOpened()) {
const auto sz = cv::Size{frame_descr.size.width, frame_descr.size.height};
writer.open(output, cv::VideoWriter::fourcc('M','J','P','G'), 25.0, sz);
CV_Assert(writer.isOpened());
}
cv::Mat outMat;
tm.start();
while (pipeline.pull(cv::gout(outMat))) {
cv::imshow("Out", outMat);
cv::waitKey(1);
if (!output.empty()) {
writer << outMat;
}
++frames;
}
tm.stop();
std::cout << "Processed " << frames << " frames" << " (" << frames / tm.getTimeSec() << " FPS)" << std::endl;
return 0;
}
namespace cfg {
typename cv::gapi::wip::onevpl::CfgParam create_from_string(const std::string &line) {
using namespace cv::gapi::wip;
if (line.empty()) {
throw std::runtime_error("Cannot parse CfgParam from emply line");
}
std::string::size_type name_endline_pos = line.find(':');
if (name_endline_pos == std::string::npos) {
throw std::runtime_error("Cannot parse CfgParam from: " + line +
"\nExpected separator \":\"");
}
std::string name = line.substr(0, name_endline_pos);
std::string value = line.substr(name_endline_pos + 1);
return cv::gapi::wip::onevpl::CfgParam::create(name, value);
}
}

View File

@ -13,6 +13,7 @@
#include <opencv2/gapi/cpu/gcpukernel.hpp>
#include <opencv2/gapi/streaming/cap.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/gapi/infer/parsers.hpp>
const std::string about =
"This is an OpenCV-based version of Privacy Masking Camera example";
@ -49,12 +50,6 @@ G_API_NET(FaceDetector, <cv::GMat(cv::GMat)>, "face-detector"
using GDetections = cv::GArray<cv::Rect>;
G_API_OP(ParseSSD, <GDetections(cv::GMat, cv::GMat, int)>, "custom.privacy_masking.postproc") {
static cv::GArrayDesc outMeta(const cv::GMatDesc &, const cv::GMatDesc &, int) {
return cv::empty_array_desc();
}
};
using GPrims = cv::GArray<cv::gapi::wip::draw::Prim>;
G_API_OP(ToMosaic, <GPrims(GDetections, GDetections)>, "custom.privacy_masking.to_mosaic") {
@ -63,53 +58,6 @@ G_API_OP(ToMosaic, <GPrims(GDetections, GDetections)>, "custom.privacy_masking.t
}
};
GAPI_OCV_KERNEL(OCVParseSSD, ParseSSD) {
static void run(const cv::Mat &in_ssd_result,
const cv::Mat &in_frame,
const int filter_label,
std::vector<cv::Rect> &out_objects) {
const auto &in_ssd_dims = in_ssd_result.size;
CV_Assert(in_ssd_dims.dims() == 4u);
const int MAX_PROPOSALS = in_ssd_dims[2];
const int OBJECT_SIZE = in_ssd_dims[3];
CV_Assert(OBJECT_SIZE == 7); // fixed SSD object size
const cv::Size upscale = in_frame.size();
const cv::Rect surface({0,0}, upscale);
out_objects.clear();
const float *data = in_ssd_result.ptr<float>();
for (int i = 0; i < MAX_PROPOSALS; i++) {
const float image_id = data[i * OBJECT_SIZE + 0];
const float label = data[i * OBJECT_SIZE + 1];
const float confidence = data[i * OBJECT_SIZE + 2];
const float rc_left = data[i * OBJECT_SIZE + 3];
const float rc_top = data[i * OBJECT_SIZE + 4];
const float rc_right = data[i * OBJECT_SIZE + 5];
const float rc_bottom = data[i * OBJECT_SIZE + 6];
if (image_id < 0.f) {
break; // marks end-of-detections
}
if (confidence < 0.5f) {
continue; // skip objects with low confidence
}
if (filter_label != -1 && static_cast<int>(label) != filter_label) {
continue; // filter out object classes if filter is specified
}
cv::Rect rc; // map relative coordinates to the original image scale
rc.x = static_cast<int>(rc_left * upscale.width);
rc.y = static_cast<int>(rc_top * upscale.height);
rc.width = static_cast<int>(rc_right * upscale.width) - rc.x;
rc.height = static_cast<int>(rc_bottom * upscale.height) - rc.y;
out_objects.emplace_back(rc & surface);
}
}
};
GAPI_OCV_KERNEL(OCVToMosaic, ToMosaic) {
static void run(const std::vector<cv::Rect> &in_plate_rcs,
const std::vector<cv::Rect> &in_face_rcs,
@ -150,10 +98,13 @@ int main(int argc, char *argv[])
cv::GMat blob_faces = cv::gapi::infer<custom::FaceDetector>(in);
// VehLicDetector from Open Model Zoo marks vehicles with label "1" and
// license plates with label "2", filter out license plates only.
cv::GArray<cv::Rect> rc_plates = custom::ParseSSD::on(blob_plates, in, 2);
cv::GOpaque<cv::Size> sz = cv::gapi::streaming::size(in);
cv::GArray<cv::Rect> rc_plates, rc_faces;
cv::GArray<int> labels;
std::tie(rc_plates, labels) = cv::gapi::parseSSD(blob_plates, sz, 0.5f, 2);
// Face detector produces faces only so there's no need to filter by label,
// pass "-1".
cv::GArray<cv::Rect> rc_faces = custom::ParseSSD::on(blob_faces, in, -1);
std::tie(rc_faces, labels) = cv::gapi::parseSSD(blob_faces, sz, 0.5f, -1);
cv::GMat out = cv::gapi::wip::draw::render3ch(in, custom::ToMosaic::on(rc_plates, rc_faces));
cv::GComputation graph(in, out);
@ -169,7 +120,7 @@ int main(int argc, char *argv[])
weights_path(face_model_path), // path to weights
cmd.get<std::string>("faced"), // device specifier
};
auto kernels = cv::gapi::kernels<custom::OCVParseSSD, custom::OCVToMosaic>();
auto kernels = cv::gapi::kernels<custom::OCVToMosaic>();
auto networks = cv::gapi::networks(plate_net, face_net);
cv::TickMeter tm;

View File

@ -0,0 +1,184 @@
#include <opencv2/imgproc.hpp>
#include <opencv2/gapi/infer/ie.hpp>
#include <opencv2/gapi/cpu/gcpukernel.hpp>
#include <opencv2/gapi/streaming/cap.hpp>
#include <opencv2/gapi/operators.hpp>
#include <opencv2/highgui.hpp>
const std::string keys =
"{ h help | | Print this help message }"
"{ input | | Path to the input video file }"
"{ output | | Path to the output video file }"
"{ ssm | semantic-segmentation-adas-0001.xml | Path to OpenVINO IE semantic segmentation model (.xml) }";
// 20 colors for 20 classes of semantic-segmentation-adas-0001
const std::vector<cv::Vec3b> colors = {
{ 128, 64, 128 },
{ 232, 35, 244 },
{ 70, 70, 70 },
{ 156, 102, 102 },
{ 153, 153, 190 },
{ 153, 153, 153 },
{ 30, 170, 250 },
{ 0, 220, 220 },
{ 35, 142, 107 },
{ 152, 251, 152 },
{ 180, 130, 70 },
{ 60, 20, 220 },
{ 0, 0, 255 },
{ 142, 0, 0 },
{ 70, 0, 0 },
{ 100, 60, 0 },
{ 90, 0, 0 },
{ 230, 0, 0 },
{ 32, 11, 119 },
{ 0, 74, 111 },
};
namespace {
std::string get_weights_path(const std::string &model_path) {
const auto EXT_LEN = 4u;
const auto sz = model_path.size();
CV_Assert(sz > EXT_LEN);
auto ext = model_path.substr(sz - EXT_LEN);
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){
return static_cast<unsigned char>(std::tolower(c));
});
CV_Assert(ext == ".xml");
return model_path.substr(0u, sz - EXT_LEN) + ".bin";
}
void classesToColors(const cv::Mat &out_blob,
cv::Mat &mask_img) {
const int H = out_blob.size[0];
const int W = out_blob.size[1];
mask_img.create(H, W, CV_8UC3);
GAPI_Assert(out_blob.type() == CV_8UC1);
const uint8_t* const classes = out_blob.ptr<uint8_t>();
for (int rowId = 0; rowId < H; ++rowId) {
for (int colId = 0; colId < W; ++colId) {
uint8_t class_id = classes[rowId * W + colId];
mask_img.at<cv::Vec3b>(rowId, colId) =
class_id < colors.size()
? colors[class_id]
: cv::Vec3b{0, 0, 0}; // NB: sample supports 20 classes
}
}
}
void probsToClasses(const cv::Mat& probs, cv::Mat& classes) {
const int C = probs.size[1];
const int H = probs.size[2];
const int W = probs.size[3];
classes.create(H, W, CV_8UC1);
GAPI_Assert(probs.depth() == CV_32F);
float* out_p = reinterpret_cast<float*>(probs.data);
uint8_t* classes_p = reinterpret_cast<uint8_t*>(classes.data);
for (int h = 0; h < H; ++h) {
for (int w = 0; w < W; ++w) {
double max = 0;
int class_id = 0;
for (int c = 0; c < C; ++c) {
int idx = c * H * W + h * W + w;
if (out_p[idx] > max) {
max = out_p[idx];
class_id = c;
}
}
classes_p[h * W + w] = static_cast<uint8_t>(class_id);
}
}
}
} // anonymous namespace
namespace custom {
G_API_OP(PostProcessing, <cv::GMat(cv::GMat, cv::GMat)>, "sample.custom.post_processing") {
static cv::GMatDesc outMeta(const cv::GMatDesc &in, const cv::GMatDesc &) {
return in;
}
};
GAPI_OCV_KERNEL(OCVPostProcessing, PostProcessing) {
static void run(const cv::Mat &in, const cv::Mat &out_blob, cv::Mat &out) {
cv::Mat classes;
// NB: If output has more than single plane, it contains probabilities
// otherwise class id.
if (out_blob.size[1] > 1) {
probsToClasses(out_blob, classes);
} else {
out_blob.convertTo(classes, CV_8UC1);
classes = classes.reshape(1, out_blob.size[2]);
}
cv::Mat mask_img;
classesToColors(classes, mask_img);
cv::resize(mask_img, out, in.size());
}
};
} // namespace custom
int main(int argc, char *argv[]) {
cv::CommandLineParser cmd(argc, argv, keys);
if (cmd.has("help")) {
cmd.printMessage();
return 0;
}
// Prepare parameters first
const std::string input = cmd.get<std::string>("input");
const std::string output = cmd.get<std::string>("output");
const auto model_path = cmd.get<std::string>("ssm");
const auto weights_path = get_weights_path(model_path);
const auto device = "CPU";
G_API_NET(SemSegmNet, <cv::GMat(cv::GMat)>, "semantic-segmentation");
const auto net = cv::gapi::ie::Params<SemSegmNet> {
model_path, weights_path, device
};
const auto kernels = cv::gapi::kernels<custom::OCVPostProcessing>();
const auto networks = cv::gapi::networks(net);
// Now build the graph
cv::GMat in;
cv::GMat out_blob = cv::gapi::infer<SemSegmNet>(in);
cv::GMat post_proc_out = custom::PostProcessing::on(in, out_blob);
cv::GMat blending_in = in * 0.3f;
cv::GMat blending_out = post_proc_out * 0.7f;
cv::GMat out = blending_in + blending_out;
cv::GStreamingCompiled pipeline = cv::GComputation(cv::GIn(in), cv::GOut(out))
.compileStreaming(cv::compile_args(kernels, networks));
auto inputs = cv::gin(cv::gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(input));
// The execution part
pipeline.setSource(std::move(inputs));
cv::VideoWriter writer;
cv::TickMeter tm;
cv::Mat outMat;
std::size_t frames = 0u;
tm.start();
pipeline.start();
while (pipeline.pull(cv::gout(outMat))) {
++frames;
cv::imshow("Out", outMat);
cv::waitKey(1);
if (!output.empty()) {
if (!writer.isOpened()) {
const auto sz = cv::Size{outMat.cols, outMat.rows};
writer.open(output, cv::VideoWriter::fourcc('M','J','P','G'), 25.0, sz);
CV_Assert(writer.isOpened());
}
writer << outMat;
}
}
tm.stop();
std::cout << "Processed " << frames << " frames" << " (" << frames / tm.getTimeSec() << " FPS)" << std::endl;
return 0;
}

View File

@ -23,6 +23,31 @@
#include "compiler/gmodelbuilder.hpp"
#include "compiler/gcompiler.hpp"
#include "compiler/gcompiled_priv.hpp"
#include "compiler/gstreaming_priv.hpp"
static cv::GTypesInfo collectInfo(const cv::gimpl::GModel::ConstGraph& g,
const std::vector<ade::NodeHandle>& nhs) {
cv::GTypesInfo info;
info.reserve(nhs.size());
ade::util::transform(nhs, std::back_inserter(info), [&g](const ade::NodeHandle& nh) {
const auto& data = g.metadata(nh).get<cv::gimpl::Data>();
return cv::GTypeInfo{data.shape, data.kind, data.ctor};
});
return info;
}
// NB: This function is used to collect graph input/output info.
// Needed for python bridge to unpack inputs and constructs outputs properly.
static cv::GraphInfo::Ptr collectGraphInfo(const cv::GComputation::Priv& priv)
{
auto g = cv::gimpl::GCompiler::makeGraph(priv);
cv::gimpl::GModel::ConstGraph cgr(*g);
auto in_info = collectInfo(cgr, cgr.metadata().get<cv::gimpl::Protocol>().in_nhs);
auto out_info = collectInfo(cgr, cgr.metadata().get<cv::gimpl::Protocol>().out_nhs);
return cv::GraphInfo::Ptr(new cv::GraphInfo{std::move(in_info), std::move(out_info)});
}
// cv::GComputation private implementation /////////////////////////////////////
// <none>
@ -105,8 +130,37 @@ cv::GStreamingCompiled cv::GComputation::compileStreaming(GMetaArgs &&metas, GCo
cv::GStreamingCompiled cv::GComputation::compileStreaming(GCompileArgs &&args)
{
// NB: Used by python bridge
if (!m_priv->m_info)
{
m_priv->m_info = collectGraphInfo(*m_priv);
}
cv::gimpl::GCompiler comp(*this, {}, std::move(args));
return comp.compileStreaming();
auto compiled = comp.compileStreaming();
compiled.priv().setInInfo(m_priv->m_info->inputs);
compiled.priv().setOutInfo(m_priv->m_info->outputs);
return compiled;
}
cv::GStreamingCompiled cv::GComputation::compileStreaming(const cv::detail::ExtractMetaCallback &callback,
GCompileArgs &&args)
{
// NB: Used by python bridge
if (!m_priv->m_info)
{
m_priv->m_info = collectGraphInfo(*m_priv);
}
auto ins = callback(m_priv->m_info->inputs);
cv::gimpl::GCompiler comp(*this, std::move(ins), std::move(args));
auto compiled = comp.compileStreaming();
compiled.priv().setInInfo(m_priv->m_info->inputs);
compiled.priv().setOutInfo(m_priv->m_info->outputs);
return compiled;
}
// FIXME: Introduce similar query/test method for GMetaArgs as a building block
@ -172,50 +226,25 @@ void cv::GComputation::apply(const std::vector<cv::Mat> &ins,
}
// NB: This overload is called from python code
cv::GRunArgs cv::GComputation::apply(GRunArgs &&ins, GCompileArgs &&args)
cv::GRunArgs cv::GComputation::apply(const cv::detail::ExtractArgsCallback &callback,
GCompileArgs &&args)
{
recompile(descr_of(ins), std::move(args));
// NB: Used by python bridge
if (!m_priv->m_info)
{
m_priv->m_info = collectGraphInfo(*m_priv);
}
const auto& out_info = m_priv->m_lastCompiled.priv().outInfo();
auto ins = callback(m_priv->m_info->inputs);
recompile(descr_of(ins), std::move(args));
GRunArgs run_args;
GRunArgsP outs;
run_args.reserve(out_info.size());
outs.reserve(out_info.size());
run_args.reserve(m_priv->m_info->outputs.size());
outs.reserve(m_priv->m_info->outputs.size());
cv::detail::constructGraphOutputs(m_priv->m_info->outputs, run_args, outs);
for (auto&& info : out_info)
{
switch (info.shape)
{
case cv::GShape::GMAT:
{
run_args.emplace_back(cv::Mat{});
outs.emplace_back(&cv::util::get<cv::Mat>(run_args.back()));
break;
}
case cv::GShape::GSCALAR:
{
run_args.emplace_back(cv::Scalar{});
outs.emplace_back(&cv::util::get<cv::Scalar>(run_args.back()));
break;
}
case cv::GShape::GARRAY:
{
switch (info.kind)
{
case cv::detail::OpaqueKind::CV_POINT2F:
run_args.emplace_back(cv::detail::VectorRef{std::vector<cv::Point2f>{}});
outs.emplace_back(cv::util::get<cv::detail::VectorRef>(run_args.back()));
break;
default:
util::throw_error(std::logic_error("Unsupported kind for GArray"));
}
break;
}
default:
util::throw_error(std::logic_error("Only cv::GMat and cv::GScalar are supported for python output"));
}
}
m_priv->m_lastCompiled(std::move(ins), std::move(outs));
return run_args;
}

View File

@ -21,6 +21,13 @@
namespace cv {
struct GraphInfo
{
using Ptr = std::shared_ptr<GraphInfo>;
cv::GTypesInfo inputs;
cv::GTypesInfo outputs;
};
class GComputation::Priv
{
public:
@ -36,9 +43,10 @@ public:
, Dump // A deserialized graph
>;
GCompiled m_lastCompiled;
GMetaArgs m_lastMetas; // TODO: make GCompiled remember its metas?
Shape m_shape;
GCompiled m_lastCompiled;
GMetaArgs m_lastMetas; // TODO: make GCompiled remember its metas?
Shape m_shape;
GraphInfo::Ptr m_info; // NB: Used by python bridge
};
}

View File

@ -7,77 +7,20 @@
#include "precomp.hpp"
#include <functional> // hash
#include <numeric> // accumulate
#include <unordered_set>
#include <iterator>
#include <ade/util/algorithm.hpp>
#include <opencv2/gapi/infer.hpp>
#include <unordered_set>
cv::gapi::GNetPackage::GNetPackage(std::initializer_list<GNetParam> ii)
: networks(ii) {
}
cv::gapi::GNetPackage::GNetPackage(std::vector<GNetParam> nets)
: networks(nets) {
}
std::vector<cv::gapi::GBackend> cv::gapi::GNetPackage::backends() const {
std::unordered_set<cv::gapi::GBackend> unique_set;
for (const auto &nn : networks) unique_set.insert(nn.backend);
return std::vector<cv::gapi::GBackend>(unique_set.begin(), unique_set.end());
}
// FIXME: Inference API is currently only available in full mode
#if !defined(GAPI_STANDALONE)
cv::GInferInputs::GInferInputs()
: in_blobs(std::make_shared<Map>())
{
}
cv::GMat& cv::GInferInputs::operator[](const std::string& name) {
return (*in_blobs)[name];
}
const cv::GInferInputs::Map& cv::GInferInputs::getBlobs() const {
return *in_blobs;
}
void cv::GInferInputs::setInput(const std::string& name, const cv::GMat& value) {
in_blobs->emplace(name, value);
}
struct cv::GInferOutputs::Priv
{
Priv(std::shared_ptr<cv::GCall>);
std::shared_ptr<cv::GCall> call;
InOutInfo* info = nullptr;
std::unordered_map<std::string, cv::GMat> out_blobs;
};
cv::GInferOutputs::Priv::Priv(std::shared_ptr<cv::GCall> c)
: call(std::move(c)), info(cv::util::any_cast<InOutInfo>(&call->params()))
{
}
cv::GInferOutputs::GInferOutputs(std::shared_ptr<cv::GCall> call)
: m_priv(std::make_shared<cv::GInferOutputs::Priv>(std::move(call)))
{
}
cv::GMat cv::GInferOutputs::at(const std::string& name)
{
auto it = m_priv->out_blobs.find(name);
if (it == m_priv->out_blobs.end()) {
// FIXME: Avoid modifying GKernel
// Expect output to be always GMat
m_priv->call->kernel().outShapes.push_back(cv::GShape::GMAT);
// ...so _empty_ constructor is passed here.
m_priv->call->kernel().outCtors.emplace_back(cv::util::monostate{});
int out_idx = static_cast<int>(m_priv->out_blobs.size());
it = m_priv->out_blobs.emplace(name, m_priv->call->yield(out_idx)).first;
m_priv->info->out_names.push_back(name);
}
return it->second;
}
#endif // GAPI_STANDALONE

View File

@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2019 Intel Corporation
// Copyright (C) 2018-2021 Intel Corporation
#include "precomp.hpp"
@ -55,6 +55,16 @@ const std::vector<cv::GTransform> &cv::gapi::GKernelPackage::get_transformations
return m_transformations;
}
std::vector<std::string> cv::gapi::GKernelPackage::get_kernel_ids() const
{
std::vector<std::string> ids;
for (auto &&id : m_id_kernels)
{
ids.emplace_back(id.first);
}
return ids;
}
cv::gapi::GKernelPackage cv::gapi::combine(const GKernelPackage &lhs,
const GKernelPackage &rhs)
{

View File

@ -201,6 +201,52 @@ bool cv::can_describe(const GMetaArgs &metas, const GRunArgs &args)
});
}
void cv::gimpl::proto::validate_input_meta_arg(const cv::GMetaArg& meta)
{
switch (meta.index())
{
case cv::GMetaArg::index_of<cv::GMatDesc>():
{
cv::gimpl::proto::validate_input_meta(cv::util::get<GMatDesc>(meta)); //may throw
break;
}
default:
break;
}
}
void cv::gimpl::proto::validate_input_meta(const cv::GMatDesc& meta)
{
if (meta.dims.empty())
{
if (!(meta.size.height > 0 && meta.size.width > 0))
{
cv::util::throw_error
(std::logic_error(
"Image format is invalid. Size must contain positive values"
", got width: " + std::to_string(meta.size.width ) +
(", height: ") + std::to_string(meta.size.height)));
}
if (!(meta.chan > 0))
{
cv::util::throw_error
(std::logic_error(
"Image format is invalid. Channel mustn't be negative value, got channel: " +
std::to_string(meta.chan)));
}
}
if (!(meta.depth >= 0))
{
cv::util::throw_error
(std::logic_error(
"Image format is invalid. Depth must be positive value, got depth: " +
std::to_string(meta.depth)));
}
// All checks are ok
}
// FIXME: Is it tested for all types?
// FIXME: Where does this validation happen??
void cv::validate_input_arg(const GRunArg& arg)
@ -212,13 +258,15 @@ void cv::validate_input_arg(const GRunArg& arg)
case GRunArg::index_of<cv::UMat>():
{
const auto desc = cv::descr_of(util::get<cv::UMat>(arg));
GAPI_Assert(desc.size.height != 0 && desc.size.width != 0 && "incorrect dimensions of cv::UMat!"); break;
cv::gimpl::proto::validate_input_meta(desc); //may throw
break;
}
#endif // !defined(GAPI_STANDALONE)
case GRunArg::index_of<cv::Mat>():
{
const auto desc = cv::descr_of(util::get<cv::Mat>(arg));
GAPI_Assert(desc.size.height != 0 && desc.size.width != 0 && "incorrect dimensions of Mat!"); break;
cv::gimpl::proto::validate_input_meta(desc); //may throw
break;
}
default:
// No extra handling

Some files were not shown because too many files have changed in this diff Show More