1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-31 00:03:03 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Alekhin
2021-10-15 15:59:36 +00:00
537 changed files with 39768 additions and 10712 deletions
+30
View File
@@ -163,6 +163,22 @@ set(gapi_srcs
src/backends/ie/bindings_ie.cpp
src/backends/python/gpythonbackend.cpp
# 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/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
# Utils (ITT tracing)
src/utils/itt.cpp
)
@@ -234,6 +250,20 @@ 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(WIN32)
# Required for htonl/ntohl on Windows
+7
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()
+7
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}/../")
+2 -1
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
@}
*/
@@ -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?
@@ -40,6 +40,10 @@ namespace gimpl
namespace gapi
{
/**
* @brief This namespace contains G-API CPU backend functions,
* structures, and symbols.
*/
namespace cpu
{
/**
@@ -492,7 +496,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:
@@ -25,6 +25,9 @@ namespace cv {
namespace gapi
{
/**
* @brief This namespace contains G-API Fluid backend functions, structures, and symbols.
*/
namespace fluid
{
/**
+36 -5
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 modules/gapi/samples/dynamic_graph.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 modules/gapi/samples/dynamic_graph.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 modules/gapi/samples/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 modules/gapi/samples/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)
+62 -4
View File
@@ -340,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 {
@@ -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 {
/**
@@ -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; };
@@ -437,11 +437,7 @@ public:
*
* @sa @ref gapi_compile_args
*/
GStreamingCompiled compileStreaming(GMetaArgs &&in_metas, GCompileArgs &&args = {});
/// @private -- Exclude this function from OpenCV documentation
GAPI_WRAP GStreamingCompiled compileStreaming(const cv::detail::ExtractMetaCallback &callback,
GCompileArgs &&args = {});
GAPI_WRAP GStreamingCompiled compileStreaming(GMetaArgs &&in_metas, GCompileArgs &&args = {});
/**
* @brief Compile the computation for streaming mode.
@@ -464,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
+44 -4
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;
@@ -372,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:
@@ -412,6 +413,7 @@ namespace std
namespace cv {
namespace gapi {
/// @private
class GFunctor
{
public:
+33 -5
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:
+26 -1
View File
@@ -307,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)
+1 -1
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 modules/gapi/samples/dynamic_graph.cpp GIOProtoArgs usage
*
*/
template<typename Tg>
+69 -4
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 modules/gapi/samples/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:
@@ -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};
}
@@ -196,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
@@ -255,7 +274,7 @@ public:
// NB: Used from python
/// @private -- Exclude this function from OpenCV documentation
GAPI_WRAP std::tuple<bool, cv::GRunArgs> pull();
GAPI_WRAP std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> pull();
/**
* @brief Get some next available data from the pipeline.
@@ -372,6 +391,14 @@ 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.
@@ -379,9 +406,11 @@ namespace streaming {
* In the streaming mode the pipeline steps are connected with queues
* and this compile argument controls every queue's size.
*/
struct GAPI_EXPORTS queue_capacity
struct GAPI_EXPORTS_W_SIMPLE queue_capacity
{
GAPI_WRAP
explicit queue_capacity(size_t cap = 1) : capacity(cap) { };
GAPI_PROP_RW
size_t capacity;
};
/** @} */
@@ -43,19 +43,6 @@ namespace detail
GOPAQUE, // a cv::GOpaqueU (note - exactly GOpaqueU, not GOpaque<T>!)
};
template<typename T>
constexpr const char* meta_to_string() noexcept;
template<>
constexpr const char* meta_to_string<cv::GMatDesc>() noexcept { return "GMatDesc"; }
template<>
constexpr const char* meta_to_string<cv::GScalarDesc>() noexcept { return "GScalarDesc"; }
template<>
constexpr const char* meta_to_string<cv::GArrayDesc>() noexcept { return "GArrayDesc"; }
template<>
constexpr const char* meta_to_string<cv::GOpaqueDesc>() noexcept { return "GOpaqueDesc"; }
template<>
constexpr const char* meta_to_string<cv::GFrameDesc>() noexcept { return "GFrameDesc";}
// Describe G-API types (G-types) with traits. Mostly used by
// cv::GArg to store meta information about types passed into
// operation arguments. Please note that cv::GComputation is
@@ -35,7 +35,6 @@ namespace detail
template<> struct ProtoToMeta<cv::GScalar> { using type = cv::GScalarDesc; };
template<typename U> struct ProtoToMeta<cv::GArray<U> > { using type = cv::GArrayDesc; };
template<typename U> struct ProtoToMeta<cv::GOpaque<U> > { using type = cv::GOpaqueDesc; };
template<> struct ProtoToMeta<cv::GFrame> { using type = cv::GFrameDesc; };
template<typename T> using ProtoToMetaT = typename ProtoToMeta<T>::type;
//workaround for MSVC 19.0 bug
@@ -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?
+6 -3
View File
@@ -136,11 +136,12 @@ public:
}
template <typename U>
void setInput(const std::string& name, U in)
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...>;
@@ -653,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
@@ -664,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;
@@ -22,17 +22,28 @@ 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);
GBackend backend() const;
std::string tag() const;
cv::util::any params() const;
+45 -4
View File
@@ -24,6 +24,11 @@
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();
@@ -69,7 +74,11 @@ struct ParamDesc {
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;
};
} // namespace detail
@@ -110,7 +119,8 @@ public:
, {}
, {}
, {}
, 1u} {
, 1u
, {}} {
};
/** @overload
@@ -130,7 +140,8 @@ public:
, {}
, {}
, {}
, 1u} {
, 1u
, {}} {
};
/** @brief Specifies sequence of network input layers names for inference.
@@ -212,6 +223,30 @@ public:
return *this;
}
/** @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.
@@ -313,7 +348,10 @@ public:
const std::string &model,
const std::string &weights,
const std::string &device)
: desc{ model, weights, device, {}, {}, {}, 0u, 0u, detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u}, m_tag(tag) {
: desc{ model, weights, device, {}, {}, {}, 0u, 0u,
detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u,
{}},
m_tag(tag) {
};
/** @overload
@@ -328,7 +366,10 @@ public:
Params(const std::string &tag,
const std::string &model,
const std::string &device)
: desc{ model, {}, device, {}, {}, {}, 0u, 0u, detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u}, m_tag(tag) {
: desc{ model, {}, device, {}, {}, {}, 0u, 0u,
detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u,
{}},
m_tag(tag) {
};
/** @see ie::Params::pluginConfig. */
@@ -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();
@@ -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.
@@ -87,9 +88,9 @@ the larger side of the rectangle.
*/
GAPI_EXPORTS_W GArray<Rect> parseSSD(const GMat& in,
const GOpaque<Size>& inSz,
const float confidenceThreshold = 0.5f,
const bool alignmentToSquare = false,
const bool filterOutOfBounds = false);
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
+174 -10
View File
@@ -15,30 +15,119 @@
#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;
// Cast underlying MediaFrame adapter to the particular adapter type,
// return nullptr if underlying type is different
template<typename T> T* get() 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();
@@ -46,6 +135,16 @@ public:
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;
@@ -58,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;
@@ -65,19 +201,38 @@ 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;
@@ -86,7 +241,16 @@ public:
// 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
@@ -29,6 +29,9 @@ namespace gimpl
namespace gapi
{
/**
* @brief This namespace contains G-API OpenCL backend functions, structures, and symbols.
*/
namespace ocl
{
/**
@@ -13,11 +13,13 @@
# 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
@@ -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;
@@ -15,6 +15,11 @@ namespace cv
{
namespace gapi
{
/**
* @brief This namespace contains G-API PlaidML backend functions,
* structures, and symbols.
*/
namespace plaidml
{
@@ -13,6 +13,15 @@
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();
@@ -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,10 +92,10 @@ 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,
const Prims& prims,
cv::GCompileArgs&& args = {});
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
@@ -139,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
@@ -150,9 +150,9 @@ 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
@@ -169,11 +169,15 @@ GAPI_EXPORTS GFrame renderFrame(const GFrame& m_frame,
} // 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
@@ -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
+10 -4
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
@@ -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:
@@ -108,10 +111,12 @@ public:
// 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::Adapter 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::Adapter does nothing by default. "
"Please, implement it in derived class to properly deserialize the object.");
}
};
using AdapterP = std::shared_ptr<Adapter>;
@@ -146,6 +151,7 @@ private:
template<typename T, typename... Ts>
RMat make_rmat(Ts&&... args) { return { std::make_shared<T>(std::forward<Ts>(args)...) }; }
/** @} */
} //namespace cv
+182 -50
View File
@@ -13,69 +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> &p);
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&);
GAPI_EXPORTS std::vector<char> serialize(const std::vector<std::string>&);
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> &p) {
return detail::getVectorOfStrings(p);
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
@@ -83,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:
@@ -99,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;
@@ -116,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
////////////////////////////////////////////////////////////////////////////////
@@ -146,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>
@@ -186,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());
@@ -206,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());
@@ -233,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) {
@@ -254,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;
@@ -268,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);
@@ -298,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::Adapter, 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;
@@ -350,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;
@@ -361,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
@@ -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 modules/gapi/samples/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
@@ -38,6 +38,11 @@ enum class StereoOutputFormat {
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") {
@@ -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
@@ -74,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,
@@ -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
@@ -0,0 +1,74 @@
// 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 <string>
#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
struct GAPI_EXPORTS DataProviderException : public std::exception {
virtual ~DataProviderException() {};
};
struct GAPI_EXPORTS DataProviderSystemErrorException : public DataProviderException {
DataProviderSystemErrorException(int error_code, const std::string& desription = std::string());
virtual ~DataProviderSystemErrorException();
virtual const char* what() const noexcept override;
private:
std::string reason;
};
/**
* @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>;
virtual ~IDataProvider() {};
/**
* 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 0 in EOF which considered as not-fail case.
*
* @param out_data_bytes_size the available capacity of out_data buffer.
* @param out_data the output consumer buffer with capacity out_data_bytes_size.
* @return fetched bytes count.
*/
virtual size_t fetch_data(size_t out_data_bytes_size, void* out_data) = 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
@@ -0,0 +1,64 @@
// 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>
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(std::shared_ptr<IDataProvider> source,
const CfgParams& cfg_params = CfgParams{});
~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
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
@@ -117,6 +117,65 @@ namespace detail
static type get(std::tuple<Objs...>&& objs) { return std::forward<std::tuple<Objs...>>(objs); }
};
} // 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
@@ -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
@@ -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>;
@@ -11,6 +11,36 @@ def register(mname):
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.
@@ -54,6 +84,10 @@ class GOpaque():
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)
@@ -113,6 +147,10 @@ class GArray():
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)
@@ -134,6 +172,7 @@ def op(op_id, in_types, out_types):
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
}
@@ -149,22 +188,24 @@ def op(op_id, in_types, out_types):
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_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
@@ -244,3 +285,15 @@ def kernel(op_cls):
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
+328 -190
View File
@@ -11,12 +11,14 @@
#include <opencv2/gapi/python/python.hpp>
// NB: Python wrapper replaces :: with _ for classes
using gapi_GKernelPackage = cv::gapi::GKernelPackage;
using gapi_GNetPackage = cv::gapi::GNetPackage;
using gapi_ie_PyParams = cv::gapi::ie::PyParams;
using gapi_wip_IStreamSource_Ptr = cv::Ptr<cv::gapi::wip::IStreamSource>;
using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback;
using detail_ExtractMetaCallback = cv::detail::ExtractMetaCallback;
using gapi_GKernelPackage = cv::gapi::GKernelPackage;
using gapi_GNetPackage = cv::gapi::GNetPackage;
using gapi_ie_PyParams = cv::gapi::ie::PyParams;
using gapi_wip_IStreamSource_Ptr = cv::Ptr<cv::gapi::wip::IStreamSource>;
using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback;
using detail_ExtractMetaCallback = cv::detail::ExtractMetaCallback;
using vector_GNetParam = std::vector<cv::gapi::GNetParam>;
using gapi_streaming_queue_capacity = cv::gapi::streaming::queue_capacity;
// NB: Python wrapper generate T_U for T<U>
// This behavior is only observed for inputs
@@ -42,6 +44,7 @@ using GArray_Rect = cv::GArray<cv::Rect>;
using GArray_Scalar = cv::GArray<cv::Scalar>;
using GArray_Mat = cv::GArray<cv::Mat>;
using GArray_GMat = cv::GArray<cv::GMat>;
using GArray_Prim = cv::GArray<cv::gapi::wip::draw::Prim>;
// FIXME: Python wrapper generate code without namespace std,
// so it cause error: "string wasn't declared"
@@ -124,6 +127,95 @@ PyObject* pyopencv_from(const cv::detail::PyObjectHolder& v)
return o;
}
// #FIXME: Is it possible to implement pyopencv_from/pyopencv_to for generic
// cv::variant<Types...> ?
template <>
PyObject* pyopencv_from(const cv::gapi::wip::draw::Prim& prim)
{
switch (prim.index())
{
case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Rect>():
return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Rect>(prim));
case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Text>():
return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Text>(prim));
case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Circle>():
return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Circle>(prim));
case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Line>():
return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Line>(prim));
case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Poly>():
return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Poly>(prim));
case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Mosaic>():
return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Mosaic>(prim));
case cv::gapi::wip::draw::Prim::index_of<cv::gapi::wip::draw::Image>():
return pyopencv_from(cv::util::get<cv::gapi::wip::draw::Image>(prim));
}
util::throw_error(std::logic_error("Unsupported draw primitive type"));
}
template <>
PyObject* pyopencv_from(const cv::gapi::wip::draw::Prims& value)
{
return pyopencv_from_generic_vec(value);
}
template<>
bool pyopencv_to(PyObject* obj, cv::gapi::wip::draw::Prim& value, const ArgInfo&)
{
#define TRY_EXTRACT(Prim) \
if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_gapi_wip_draw_##Prim##_TypePtr))) \
{ \
value = reinterpret_cast<pyopencv_gapi_wip_draw_##Prim##_t*>(obj)->v; \
return true; \
} \
TRY_EXTRACT(Rect)
TRY_EXTRACT(Text)
TRY_EXTRACT(Circle)
TRY_EXTRACT(Line)
TRY_EXTRACT(Mosaic)
TRY_EXTRACT(Image)
TRY_EXTRACT(Poly)
#undef TRY_EXTRACT
failmsg("Unsupported primitive type");
return false;
}
template <>
bool pyopencv_to(PyObject* obj, cv::gapi::wip::draw::Prims& value, const ArgInfo& info)
{
return pyopencv_to_generic_vec(obj, value, info);
}
template <>
bool pyopencv_to(PyObject* obj, cv::GMetaArg& value, const ArgInfo&)
{
#define TRY_EXTRACT(Meta) \
if (PyObject_TypeCheck(obj, \
reinterpret_cast<PyTypeObject*>(pyopencv_##Meta##_TypePtr))) \
{ \
value = reinterpret_cast<pyopencv_##Meta##_t*>(obj)->v; \
return true; \
} \
TRY_EXTRACT(GMatDesc)
TRY_EXTRACT(GScalarDesc)
TRY_EXTRACT(GArrayDesc)
TRY_EXTRACT(GOpaqueDesc)
#undef TRY_EXTRACT
failmsg("Unsupported cv::GMetaArg type");
return false;
}
template <>
bool pyopencv_to(PyObject* obj, cv::GMetaArgs& value, const ArgInfo& info)
{
return pyopencv_to_generic_vec(obj, value, info);
}
template<>
PyObject* pyopencv_from(const cv::GArg& value)
{
@@ -136,20 +228,21 @@ PyObject* pyopencv_from(const cv::GArg& value)
#define UNSUPPORTED(T) case cv::detail::OpaqueKind::CV_##T: break
switch (value.opaque_kind)
{
HANDLE_CASE(BOOL, bool);
HANDLE_CASE(INT, int);
HANDLE_CASE(DOUBLE, double);
HANDLE_CASE(FLOAT, float);
HANDLE_CASE(STRING, std::string);
HANDLE_CASE(POINT, cv::Point);
HANDLE_CASE(POINT2F, cv::Point2f);
HANDLE_CASE(SIZE, cv::Size);
HANDLE_CASE(RECT, cv::Rect);
HANDLE_CASE(SCALAR, cv::Scalar);
HANDLE_CASE(MAT, cv::Mat);
HANDLE_CASE(UNKNOWN, cv::detail::PyObjectHolder);
HANDLE_CASE(BOOL, bool);
HANDLE_CASE(INT, int);
HANDLE_CASE(INT64, int64_t);
HANDLE_CASE(DOUBLE, double);
HANDLE_CASE(FLOAT, float);
HANDLE_CASE(STRING, std::string);
HANDLE_CASE(POINT, cv::Point);
HANDLE_CASE(POINT2F, cv::Point2f);
HANDLE_CASE(SIZE, cv::Size);
HANDLE_CASE(RECT, cv::Rect);
HANDLE_CASE(SCALAR, cv::Scalar);
HANDLE_CASE(MAT, cv::Mat);
HANDLE_CASE(UNKNOWN, cv::detail::PyObjectHolder);
HANDLE_CASE(DRAW_PRIM, cv::gapi::wip::draw::Prim);
UNSUPPORTED(UINT64);
UNSUPPORTED(DRAW_PRIM);
#undef HANDLE_CASE
#undef UNSUPPORTED
}
@@ -163,6 +256,18 @@ bool pyopencv_to(PyObject* obj, cv::GArg& value, const ArgInfo& info)
return true;
}
template <>
bool pyopencv_to(PyObject* obj, std::vector<cv::gapi::GNetParam>& value, const ArgInfo& info)
{
return pyopencv_to_generic_vec(obj, value, info);
}
template <>
PyObject* pyopencv_from(const std::vector<cv::gapi::GNetParam>& value)
{
return pyopencv_from_generic_vec(value);
}
template <>
bool pyopencv_to(PyObject* obj, std::vector<GCompileArg>& value, const ArgInfo& info)
{
@@ -175,12 +280,6 @@ PyObject* pyopencv_from(const std::vector<GCompileArg>& value)
return pyopencv_from_generic_vec(value);
}
template <>
bool pyopencv_to(PyObject* obj, GRunArgs& value, const ArgInfo& info)
{
return pyopencv_to_generic_vec(obj, value, info);
}
template<>
PyObject* pyopencv_from(const cv::detail::OpaqueRef& o)
{
@@ -188,6 +287,7 @@ PyObject* pyopencv_from(const cv::detail::OpaqueRef& o)
{
case cv::detail::OpaqueKind::CV_BOOL : return pyopencv_from(o.rref<bool>());
case cv::detail::OpaqueKind::CV_INT : return pyopencv_from(o.rref<int>());
case cv::detail::OpaqueKind::CV_INT64 : return pyopencv_from(o.rref<int64_t>());
case cv::detail::OpaqueKind::CV_DOUBLE : return pyopencv_from(o.rref<double>());
case cv::detail::OpaqueKind::CV_FLOAT : return pyopencv_from(o.rref<float>());
case cv::detail::OpaqueKind::CV_STRING : return pyopencv_from(o.rref<std::string>());
@@ -196,10 +296,10 @@ PyObject* pyopencv_from(const cv::detail::OpaqueRef& o)
case cv::detail::OpaqueKind::CV_SIZE : return pyopencv_from(o.rref<cv::Size>());
case cv::detail::OpaqueKind::CV_RECT : return pyopencv_from(o.rref<cv::Rect>());
case cv::detail::OpaqueKind::CV_UNKNOWN : return pyopencv_from(o.rref<cv::GArg>());
case cv::detail::OpaqueKind::CV_DRAW_PRIM : return pyopencv_from(o.rref<cv::gapi::wip::draw::Prim>());
case cv::detail::OpaqueKind::CV_UINT64 : break;
case cv::detail::OpaqueKind::CV_SCALAR : break;
case cv::detail::OpaqueKind::CV_MAT : break;
case cv::detail::OpaqueKind::CV_DRAW_PRIM : break;
}
PyErr_SetString(PyExc_TypeError, "Unsupported GOpaque type");
@@ -213,6 +313,7 @@ PyObject* pyopencv_from(const cv::detail::VectorRef& v)
{
case cv::detail::OpaqueKind::CV_BOOL : return pyopencv_from_generic_vec(v.rref<bool>());
case cv::detail::OpaqueKind::CV_INT : return pyopencv_from_generic_vec(v.rref<int>());
case cv::detail::OpaqueKind::CV_INT64 : return pyopencv_from_generic_vec(v.rref<int64_t>());
case cv::detail::OpaqueKind::CV_DOUBLE : return pyopencv_from_generic_vec(v.rref<double>());
case cv::detail::OpaqueKind::CV_FLOAT : return pyopencv_from_generic_vec(v.rref<float>());
case cv::detail::OpaqueKind::CV_STRING : return pyopencv_from_generic_vec(v.rref<std::string>());
@@ -223,8 +324,8 @@ PyObject* pyopencv_from(const cv::detail::VectorRef& v)
case cv::detail::OpaqueKind::CV_SCALAR : return pyopencv_from_generic_vec(v.rref<cv::Scalar>());
case cv::detail::OpaqueKind::CV_MAT : return pyopencv_from_generic_vec(v.rref<cv::Mat>());
case cv::detail::OpaqueKind::CV_UNKNOWN : return pyopencv_from_generic_vec(v.rref<cv::GArg>());
case cv::detail::OpaqueKind::CV_DRAW_PRIM : return pyopencv_from_generic_vec(v.rref<cv::gapi::wip::draw::Prim>());
case cv::detail::OpaqueKind::CV_UINT64 : break;
case cv::detail::OpaqueKind::CV_DRAW_PRIM : break;
}
PyErr_SetString(PyExc_TypeError, "Unsupported GArray type");
@@ -249,52 +350,69 @@ PyObject* pyopencv_from(const GRunArg& v)
return pyopencv_from(util::get<cv::detail::OpaqueRef>(v));
}
PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs");
PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs. Index of variant is unknown");
return NULL;
}
template <typename T>
PyObject* pyopencv_from(const cv::optional<T>& opt)
{
if (!opt.has_value())
{
Py_RETURN_NONE;
}
return pyopencv_from(*opt);
}
template <>
PyObject* pyopencv_from(const GOptRunArg& v)
{
switch (v.index())
{
case GOptRunArg::index_of<cv::optional<cv::Mat>>():
return pyopencv_from(util::get<cv::optional<cv::Mat>>(v));
case GOptRunArg::index_of<cv::optional<cv::Scalar>>():
return pyopencv_from(util::get<cv::optional<cv::Scalar>>(v));
case GOptRunArg::index_of<optional<cv::detail::VectorRef>>():
return pyopencv_from(util::get<optional<cv::detail::VectorRef>>(v));
case GOptRunArg::index_of<optional<cv::detail::OpaqueRef>>():
return pyopencv_from(util::get<optional<cv::detail::OpaqueRef>>(v));
}
PyErr_SetString(PyExc_TypeError, "Failed to unpack GOptRunArg. Index of variant is unknown");
return NULL;
}
template<>
PyObject* pyopencv_from(const GRunArgs& value)
{
size_t i, n = value.size();
// NB: It doesn't make sense to return list with a single element
if (n == 1)
{
PyObject* item = pyopencv_from(value[0]);
if(!item)
{
return NULL;
}
return item;
}
PyObject* list = PyList_New(n);
for(i = 0; i < n; ++i)
{
PyObject* item = pyopencv_from(value[i]);
if(!item)
{
Py_DECREF(list);
PyErr_SetString(PyExc_TypeError, "Failed to unpack GRunArgs");
return NULL;
}
PyList_SetItem(list, i, item);
}
return list;
return value.size() == 1 ? pyopencv_from(value[0]) : pyopencv_from_generic_vec(value);
}
template<>
bool pyopencv_to(PyObject* obj, GMetaArgs& value, const ArgInfo& info)
PyObject* pyopencv_from(const GOptRunArgs& value)
{
return pyopencv_to_generic_vec(obj, value, info);
return value.size() == 1 ? pyopencv_from(value[0]) : pyopencv_from_generic_vec(value);
}
template<>
PyObject* pyopencv_from(const GMetaArgs& value)
// FIXME: cv::variant should be wrapped once for all types.
template <>
PyObject* pyopencv_from(const cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>& v)
{
return pyopencv_from_generic_vec(value);
using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
switch (v.index())
{
case RunArgs::index_of<cv::GRunArgs>():
return pyopencv_from(util::get<cv::GRunArgs>(v));
case RunArgs::index_of<cv::GOptRunArgs>():
return pyopencv_from(util::get<cv::GOptRunArgs>(v));
}
PyErr_SetString(PyExc_TypeError, "Failed to recognize kind of RunArgs. Index of variant is unknown");
return NULL;
}
template <typename T>
@@ -318,16 +436,16 @@ void pyopencv_to_generic_vec_with_check(PyObject* from,
}
template <typename T>
static PyObject* extract_proto_args(PyObject* py_args, PyObject* kw)
static T extract_proto_args(PyObject* py_args)
{
using namespace cv;
GProtoArgs args;
Py_ssize_t size = PyTuple_Size(py_args);
Py_ssize_t size = PyList_Size(py_args);
args.reserve(size);
for (int i = 0; i < size; ++i)
{
PyObject* item = PyTuple_GetItem(py_args, i);
PyObject* item = PyList_GetItem(py_args, i);
if (PyObject_TypeCheck(item, reinterpret_cast<PyTypeObject*>(pyopencv_GScalar_TypePtr)))
{
args.emplace_back(reinterpret_cast<pyopencv_GScalar_t*>(item)->v);
@@ -346,22 +464,11 @@ static PyObject* extract_proto_args(PyObject* py_args, PyObject* kw)
}
else
{
PyErr_SetString(PyExc_TypeError, "Unsupported type for cv.GIn()/cv.GOut()");
return NULL;
util::throw_error(std::logic_error("Unsupported type for GProtoArgs"));
}
}
return pyopencv_from<T>(T{std::move(args)});
}
static PyObject* pyopencv_cv_GIn(PyObject* , PyObject* py_args, PyObject* kw)
{
return extract_proto_args<GProtoInputArgs>(py_args, kw);
}
static PyObject* pyopencv_cv_GOut(PyObject* , PyObject* py_args, PyObject* kw)
{
return extract_proto_args<GProtoOutputArgs>(py_args, kw);
return T(std::move(args));
}
static cv::detail::OpaqueRef extract_opaque_ref(PyObject* from, cv::detail::OpaqueKind kind)
@@ -386,6 +493,7 @@ static cv::detail::OpaqueRef extract_opaque_ref(PyObject* from, cv::detail::Opaq
HANDLE_CASE(RECT, cv::Rect);
HANDLE_CASE(UNKNOWN, cv::GArg);
UNSUPPORTED(UINT64);
UNSUPPORTED(INT64);
UNSUPPORTED(SCALAR);
UNSUPPORTED(MAT);
UNSUPPORTED(DRAW_PRIM);
@@ -406,20 +514,21 @@ static cv::detail::VectorRef extract_vector_ref(PyObject* from, cv::detail::Opaq
#define UNSUPPORTED(T) case cv::detail::OpaqueKind::CV_##T: break
switch (kind)
{
HANDLE_CASE(BOOL, bool);
HANDLE_CASE(INT, int);
HANDLE_CASE(DOUBLE, double);
HANDLE_CASE(FLOAT, float);
HANDLE_CASE(STRING, std::string);
HANDLE_CASE(POINT, cv::Point);
HANDLE_CASE(POINT2F, cv::Point2f);
HANDLE_CASE(SIZE, cv::Size);
HANDLE_CASE(RECT, cv::Rect);
HANDLE_CASE(SCALAR, cv::Scalar);
HANDLE_CASE(MAT, cv::Mat);
HANDLE_CASE(UNKNOWN, cv::GArg);
HANDLE_CASE(BOOL, bool);
HANDLE_CASE(INT, int);
HANDLE_CASE(DOUBLE, double);
HANDLE_CASE(FLOAT, float);
HANDLE_CASE(STRING, std::string);
HANDLE_CASE(POINT, cv::Point);
HANDLE_CASE(POINT2F, cv::Point2f);
HANDLE_CASE(SIZE, cv::Size);
HANDLE_CASE(RECT, cv::Rect);
HANDLE_CASE(SCALAR, cv::Scalar);
HANDLE_CASE(MAT, cv::Mat);
HANDLE_CASE(UNKNOWN, cv::GArg);
HANDLE_CASE(DRAW_PRIM, cv::gapi::wip::draw::Prim);
UNSUPPORTED(UINT64);
UNSUPPORTED(DRAW_PRIM);
UNSUPPORTED(INT64);
#undef HANDLE_CASE
#undef UNSUPPORTED
}
@@ -470,13 +579,15 @@ static cv::GRunArg extract_run_arg(const cv::GTypeInfo& info, PyObject* item)
static cv::GRunArgs extract_run_args(const cv::GTypesInfo& info, PyObject* py_args)
{
cv::GRunArgs args;
Py_ssize_t tuple_size = PyTuple_Size(py_args);
args.reserve(tuple_size);
GAPI_Assert(PyList_Check(py_args));
for (int i = 0; i < tuple_size; ++i)
cv::GRunArgs args;
Py_ssize_t list_size = PyList_Size(py_args);
args.reserve(list_size);
for (int i = 0; i < list_size; ++i)
{
args.push_back(extract_run_arg(info[i], PyTuple_GetItem(py_args, i)));
args.push_back(extract_run_arg(info[i], PyList_GetItem(py_args, i)));
}
return args;
@@ -517,13 +628,15 @@ static cv::GMetaArg extract_meta_arg(const cv::GTypeInfo& info, PyObject* item)
static cv::GMetaArgs extract_meta_args(const cv::GTypesInfo& info, PyObject* py_args)
{
cv::GMetaArgs metas;
Py_ssize_t tuple_size = PyTuple_Size(py_args);
metas.reserve(tuple_size);
GAPI_Assert(PyList_Check(py_args));
for (int i = 0; i < tuple_size; ++i)
cv::GMetaArgs metas;
Py_ssize_t list_size = PyList_Size(py_args);
metas.reserve(list_size);
for (int i = 0; i < list_size; ++i)
{
metas.push_back(extract_meta_arg(info[i], PyTuple_GetItem(py_args, i)));
metas.push_back(extract_meta_arg(info[i], PyList_GetItem(py_args, i)));
}
return metas;
@@ -581,7 +694,8 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel,
cv::detail::PyObjectHolder result(
PyObject_CallObject(kernel.get(), args.get()), false);
if (PyErr_Occurred()) {
if (PyErr_Occurred())
{
PyErr_PrintEx(0);
PyErr_Clear();
throw std::logic_error("Python kernel failed with error!");
@@ -589,8 +703,27 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel,
// NB: In fact it's impossible situation, becase errors were handled above.
GAPI_Assert(result.get() && "Python kernel returned NULL!");
outs = out_info.size() == 1 ? cv::GRunArgs{extract_run_arg(out_info[0], result.get())}
: extract_run_args(out_info, result.get());
if (out_info.size() == 1)
{
outs = cv::GRunArgs{extract_run_arg(out_info[0], result.get())};
}
else if (out_info.size() > 1)
{
GAPI_Assert(PyTuple_Check(result.get()));
Py_ssize_t tuple_size = PyTuple_Size(result.get());
outs.reserve(tuple_size);
for (int i = 0; i < tuple_size; ++i)
{
outs.push_back(extract_run_arg(out_info[i], PyTuple_GetItem(result.get(), i)));
}
}
else
{
// Seems to be impossible case.
GAPI_Assert(false);
}
}
catch (...)
{
@@ -604,30 +737,12 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel,
static GMetaArg get_meta_arg(PyObject* obj)
{
if (PyObject_TypeCheck(obj,
reinterpret_cast<PyTypeObject*>(pyopencv_GMatDesc_TypePtr)))
{
return cv::GMetaArg{reinterpret_cast<pyopencv_GMatDesc_t*>(obj)->v};
}
else if (PyObject_TypeCheck(obj,
reinterpret_cast<PyTypeObject*>(pyopencv_GScalarDesc_TypePtr)))
{
return cv::GMetaArg{reinterpret_cast<pyopencv_GScalarDesc_t*>(obj)->v};
}
else if (PyObject_TypeCheck(obj,
reinterpret_cast<PyTypeObject*>(pyopencv_GArrayDesc_TypePtr)))
{
return cv::GMetaArg{reinterpret_cast<pyopencv_GArrayDesc_t*>(obj)->v};
}
else if (PyObject_TypeCheck(obj,
reinterpret_cast<PyTypeObject*>(pyopencv_GOpaqueDesc_TypePtr)))
{
return cv::GMetaArg{reinterpret_cast<pyopencv_GOpaqueDesc_t*>(obj)->v};
}
else
cv::GMetaArg arg;
if (!pyopencv_to(obj, arg, ArgInfo("arg", false)))
{
util::throw_error(std::logic_error("Unsupported output meta type"));
}
return arg;
}
static cv::GMetaArgs get_meta_args(PyObject* tuple)
@@ -645,8 +760,9 @@ static cv::GMetaArgs get_meta_args(PyObject* tuple)
}
static GMetaArgs run_py_meta(cv::detail::PyObjectHolder out_meta,
const cv::GMetaArgs &meta,
const cv::GArgs &gargs) {
const cv::GMetaArgs &meta,
const cv::GArgs &gargs)
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
@@ -688,7 +804,8 @@ static GMetaArgs run_py_meta(cv::detail::PyObjectHolder out_meta,
cv::detail::PyObjectHolder result(
PyObject_CallObject(out_meta.get(), args.get()), false);
if (PyErr_Occurred()) {
if (PyErr_Occurred())
{
PyErr_PrintEx(0);
PyErr_Clear();
throw std::logic_error("Python outMeta failed with error!");
@@ -720,21 +837,24 @@ static PyObject* pyopencv_cv_gapi_kernels(PyObject* , PyObject* py_args, PyObjec
PyObject* user_kernel = PyTuple_GetItem(py_args, i);
PyObject* id_obj = PyObject_GetAttrString(user_kernel, "id");
if (!id_obj) {
if (!id_obj)
{
PyErr_SetString(PyExc_TypeError,
"Python kernel should contain id, please use cv.gapi.kernel to define kernel");
return NULL;
}
PyObject* out_meta = PyObject_GetAttrString(user_kernel, "outMeta");
if (!out_meta) {
if (!out_meta)
{
PyErr_SetString(PyExc_TypeError,
"Python kernel should contain outMeta, please use cv.gapi.kernel to define kernel");
return NULL;
}
PyObject* run = PyObject_GetAttrString(user_kernel, "run");
if (!run) {
if (!run)
{
PyErr_SetString(PyExc_TypeError,
"Python kernel should contain run, please use cv.gapi.kernel to define kernel");
return NULL;
@@ -756,23 +876,6 @@ static PyObject* pyopencv_cv_gapi_kernels(PyObject* , PyObject* py_args, PyObjec
return pyopencv_from(pkg);
}
static PyObject* pyopencv_cv_gapi_networks(PyObject*, PyObject* py_args, PyObject*)
{
using namespace cv;
gapi::GNetPackage pkg;
Py_ssize_t size = PyTuple_Size(py_args);
for (int i = 0; i < size; ++i)
{
gapi_ie_PyParams params;
PyObject* item = PyTuple_GetItem(py_args, i);
if (pyopencv_to(item, params, ArgInfo("PyParams", false)))
{
pkg += gapi::networks(params);
}
}
return pyopencv_from(pkg);
}
static PyObject* pyopencv_cv_gapi_op(PyObject* , PyObject* py_args, PyObject*)
{
using namespace cv;
@@ -834,53 +937,54 @@ static PyObject* pyopencv_cv_gapi_op(PyObject* , PyObject* py_args, PyObject*)
return pyopencv_from(cv::gapi::wip::op(id, outMetaWrapper, std::move(args)));
}
static PyObject* pyopencv_cv_gin(PyObject*, PyObject* py_args, PyObject*)
template<>
bool pyopencv_to(PyObject* obj, cv::detail::ExtractArgsCallback& value, const ArgInfo&)
{
cv::detail::PyObjectHolder holder{py_args};
auto callback = cv::detail::ExtractArgsCallback{[=](const cv::GTypesInfo& info)
cv::detail::PyObjectHolder holder{obj};
value = cv::detail::ExtractArgsCallback{[=](const cv::GTypesInfo& info)
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
cv::GRunArgs args;
try
{
args = extract_run_args(info, holder.get());
}
catch (...)
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
cv::GRunArgs args;
try
{
args = extract_run_args(info, holder.get());
}
catch (...)
{
PyGILState_Release(gstate);
throw;
}
PyGILState_Release(gstate);
return args;
}};
return pyopencv_from(callback);
throw;
}
PyGILState_Release(gstate);
return args;
}};
return true;
}
static PyObject* pyopencv_cv_descr_of(PyObject*, PyObject* py_args, PyObject*)
template<>
bool pyopencv_to(PyObject* obj, cv::detail::ExtractMetaCallback& value, const ArgInfo&)
{
Py_INCREF(py_args);
auto callback = cv::detail::ExtractMetaCallback{[=](const cv::GTypesInfo& info)
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
cv::detail::PyObjectHolder holder{obj};
value = cv::detail::ExtractMetaCallback{[=](const cv::GTypesInfo& info)
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
cv::GMetaArgs args;
try
{
args = extract_meta_args(info, py_args);
}
catch (...)
{
PyGILState_Release(gstate);
throw;
}
cv::GMetaArgs args;
try
{
args = extract_meta_args(info, holder.get());
}
catch (...)
{
PyGILState_Release(gstate);
return args;
}};
return pyopencv_from(callback);
throw;
}
PyGILState_Release(gstate);
return args;
}};
return true;
}
template<typename T>
@@ -895,9 +999,12 @@ struct PyOpenCV_Converter<cv::GArray<T>>
if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_GArrayT_TypePtr)))
{
auto& array = reinterpret_cast<pyopencv_GArrayT_t*>(obj)->v;
try {
try
{
value = cv::util::get<cv::GArray<T>>(array.arg());
} catch (...) {
}
catch (...)
{
return false;
}
return true;
@@ -918,9 +1025,12 @@ struct PyOpenCV_Converter<cv::GOpaque<T>>
if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_GOpaqueT_TypePtr)))
{
auto& opaque = reinterpret_cast<pyopencv_GOpaqueT_t*>(obj)->v;
try {
try
{
value = cv::util::get<cv::GOpaque<T>>(opaque.arg());
} catch (...) {
}
catch (...)
{
return false;
}
return true;
@@ -929,11 +1039,39 @@ struct PyOpenCV_Converter<cv::GOpaque<T>>
}
};
template<>
bool pyopencv_to(PyObject* obj, cv::GProtoInputArgs& value, const ArgInfo& info)
{
try
{
value = extract_proto_args<cv::GProtoInputArgs>(obj);
return true;
}
catch (...)
{
failmsg("Can't parse cv::GProtoInputArgs");
return false;
}
}
template<>
bool pyopencv_to(PyObject* obj, cv::GProtoOutputArgs& value, const ArgInfo& info)
{
try
{
value = extract_proto_args<cv::GProtoOutputArgs>(obj);
return true;
}
catch (...)
{
failmsg("Can't parse cv::GProtoOutputArgs");
return false;
}
}
// extend cv.gapi methods
#define PYOPENCV_EXTRA_METHODS_GAPI \
{"kernels", CV_PY_FN_WITH_KW(pyopencv_cv_gapi_kernels), "kernels(...) -> GKernelPackage"}, \
{"networks", CV_PY_FN_WITH_KW(pyopencv_cv_gapi_networks), "networks(...) -> GNetPackage"}, \
{"__op", CV_PY_FN_WITH_KW(pyopencv_cv_gapi_op), "__op(...) -> retval\n"},
+21 -13
View File
@@ -10,6 +10,7 @@
#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),
@@ -24,24 +25,29 @@
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(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(cv::GArg , cv::gapi::ArgType::CV_ANY, G) \
WRAP_ARGS(cv::GMat , cv::gapi::ArgType::CV_GMAT, 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) \
@@ -58,6 +64,7 @@ namespace gapi {
enum ArgType {
CV_BOOL,
CV_INT,
CV_INT64,
CV_DOUBLE,
CV_FLOAT,
CV_STRING,
@@ -68,6 +75,7 @@ enum ArgType {
CV_SCALAR,
CV_MAT,
CV_GMAT,
CV_DRAW_PRIM,
CV_ANY,
};
@@ -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))
+69 -52
View File
@@ -3,64 +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);
GAPI_EXPORTS_W GCompileArgs compile_args(gapi::GKernelPackage kernels, gapi::GNetPackage nets);
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 { GAPI_WRAP 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);
};
using GProtoInputArgs = GIOProtoArgs<In_Tag>;
using GProtoOutputArgs = GIOProtoArgs<Out_Tag>;
class GAPI_EXPORTS_W_SIMPLE GInferOutputs
{
public:
GAPI_WRAP GInferOutputs();
GAPI_WRAP cv::GMat at(const std::string& name);
};
class GAPI_EXPORTS_W_SIMPLE GInferInputs
{
public:
GAPI_WRAP GInferInputs();
GAPI_WRAP void setInput(const std::string& name, const cv::GMat& value);
GAPI_WRAP void setInput(const std::string& name, const cv::GFrame& value);
};
class GAPI_EXPORTS_W_SIMPLE GInferListOutputs
{
public:
GAPI_WRAP GInferListOutputs();
GAPI_WRAP cv::GArray<cv::GMat> at(const std::string& name);
};
class GAPI_EXPORTS_W_SIMPLE GInferListInputs
{
public:
GAPI_WRAP GInferListInputs();
GAPI_WRAP void setInput(const std::string& name, const cv::GArray<cv::GMat>& value);
GAPI_WRAP void setInput(const std::string& name, const cv::GArray<cv::Rect>& value);
};
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);
};
class GAPI_EXPORTS_W_SIMPLE GInferOutputs
{
public:
GAPI_WRAP GInferOutputs();
GAPI_WRAP cv::GMat at(const std::string& name);
};
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);
class GAPI_EXPORTS_W_SIMPLE GInferListOutputs
{
public:
GAPI_WRAP GInferListOutputs();
GAPI_WRAP cv::GArray<cv::GMat> at(const std::string& name);
};
GAPI_EXPORTS_W cv::GMat desync(const cv::GMat &g);
} // namespace streaming
} // namespace gapi
namespace detail
{
struct GAPI_EXPORTS_W_SIMPLE ExtractArgsCallback { };
struct GAPI_EXPORTS_W_SIMPLE ExtractMetaCallback { };
} // namespace detail
namespace gapi
{
namespace wip
{
class GAPI_EXPORTS_W IStreamSource { };
} // namespace wip
} // namespace gapi
namespace detail
{
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ie::PyParams params);
} // namespace detail
} // namespace cv
+179 -157
View File
@@ -3,187 +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')
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);
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 generate_random_points(self, sz):
arr = np.random.random(sz).astype(np.float32).T
return list(zip(arr[0], arr[1]))
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_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 = []
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)
# G-API
data = cv.GArrayT(cv.gapi.CV_POINT2F)
best_labels = cv.GArrayT(cv.gapi.CV_INT)
# OpenCV
expected = cv.split(in_mat)
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));
# 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))
compact, labels, centers = comp.apply(cv.gin(in_vector, in_labels));
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')
# Assert
self.assertTrue(compact >= 0)
self.assertEqual(amount, len(labels))
self.assertEqual(K, len(centers))
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__':
@@ -3,103 +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(),
np.array(actual, dtype=np.float32).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 test_bounding_rect(self):
sz = 1280
fscale = 256
def sample_value(fscale):
return np.random.uniform(0, 255 * fscale) / fscale
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)
points = np.array([(sample_value(fscale), sample_value(fscale)) for _ in range(1280)], np.float32)
# OpenCV
expected = cv.boundingRect(points)
# OpenCV
expected = cv.boundingRect(points)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.boundingRect(g_in)
# G-API
g_in = cv.GMat()
g_out = cv.gapi.boundingRect(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(points), 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(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__':
+274 -254
View File
@@ -3,318 +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 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)
blob = cv.dnn.blobFromImage(img)
net.setInput(blob)
return net.forward(net.getUnconnectedOutLayersNames())
if sys.version_info[:2] < (3, 0):
raise unittest.SkipTest('Python 2.x is not supported')
def make_roi(self, img, roi):
return img[roi[1]:roi[1] + roi[3], roi[0]:roi[0] + roi[2], ...]
class test_gapi_infer(NewOpenCVTests):
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)
blob = cv.dnn.blobFromImage(img)
net.setInput(blob)
return net.forward(net.getUnconnectedOutLayersNames())
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
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.resize(cv.imread(img_path), (62,62))
# 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.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 make_roi(self, img, roi):
return img[roi[1]:roi[1] + roi[3], roi[0]:roi[0] + roi[2], ...]
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
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
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'
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)
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))
# OpenCV DNN
dnn_age, dnn_gender = self.infer_reference_network(model_path,
# 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_roi = cv.GOpaqueT(cv.gapi.CV_RECT)
inputs = cv.GInferInputs()
inputs.setInput('data', g_in)
# 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_roi, inputs)
age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob")
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_roi), cv.GOut(age_g, gender_g))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
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, gapi_gender = comp.apply(cv.gin(img, roi), args=cv.compile_args(cv.gapi.networks(pp)))
gapi_age_list, gapi_gender_list = comp.apply(cv.gin(img, rois),
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))
# 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_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
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'
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)
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 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)
# 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.infer("net", g_rois, inputs)
age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob")
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)
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.compile_args(cv.gapi.networks(pp)))
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.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))
# 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
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))
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)
# 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)
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))
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
return bboxes
net.setInput(blob)
dnn_detections = net.forward()
dnn_boxes = parseSSD(np.array(dnn_detections), img.shape[:2])
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)
# 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)
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)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(bboxes))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
gapi_age, gapi_gender = comp.apply(cv.gin(img), args=cv.compile_args(cv.gapi.networks(pp)))
gapi_boxes = comp.apply(cv.gin(img.astype(np.float32)),
args=cv.gapi.compile_args(cv.gapi.networks(pp)))
gapi_boxes = comp.apply(cv.gin(img.astype(np.float32)),
args=cv.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))
# 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
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))
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)
# 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)
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))
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
return bboxes
net.setInput(blob)
dnn_detections = net.forward()
dnn_boxes = parseSSD(np.array(dnn_detections), img.shape[:2])
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)
# 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)
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)
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.compile_args(cv.gapi.networks(pp)))
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))
# 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__':
@@ -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()
@@ -225,7 +225,7 @@ try:
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.compile_args(pkg))
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))
@@ -245,7 +245,7 @@ try:
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.compile_args(pkg))
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))
@@ -266,7 +266,7 @@ try:
comp = cv.GComputation(g_in, g_out)
pkg = cv.gapi.kernels(GMeanImpl)
actual = comp.apply(cv.gin(in_mat), args=cv.compile_args(pkg))
actual = comp.apply(cv.gin(in_mat), args=cv.gapi.compile_args(pkg))
# Comparison
self.assertEqual(expected, actual)
@@ -287,7 +287,7 @@ try:
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.compile_args(pkg))
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))
@@ -305,7 +305,7 @@ try:
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.compile_args(pkg))
actual = comp.apply(cv.gin(in_mat), args=cv.gapi.compile_args(pkg))
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
@@ -322,7 +322,7 @@ try:
comp = cv.GComputation(cv.GIn(g_r), cv.GOut(g_sz))
pkg = cv.gapi.kernels(GSizeRImpl)
actual = comp.apply(cv.gin(roi), args=cv.compile_args(pkg))
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))
@@ -340,7 +340,7 @@ try:
comp = cv.GComputation(cv.GIn(g_pts), cv.GOut(g_br))
pkg = cv.gapi.kernels(GBoundingRectImpl)
actual = comp.apply(cv.gin(points), args=cv.compile_args(pkg))
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))
@@ -371,7 +371,7 @@ try:
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.compile_args(pkg))
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)
@@ -453,10 +453,10 @@ try:
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.compile_args(cv.gapi.kernels(GSumImpl)))
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.compile_args(cv.gapi.kernels(GSumImpl)))
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)
@@ -488,13 +488,13 @@ try:
'tuple': (42, 42)
}
out = comp.apply(cv.gin(table, 'int'), args=cv.compile_args(cv.gapi.kernels(GLookUpImpl)))
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.compile_args(cv.gapi.kernels(GLookUpImpl)))
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.compile_args(cv.gapi.kernels(GLookUpImpl)))
out = comp.apply(cv.gin(table, 'tuple'), args=cv.gapi.compile_args(cv.gapi.kernels(GLookUpImpl)))
self.assertEqual((42, 42), out)
@@ -517,11 +517,11 @@ try:
comp = cv.GComputation(cv.GIn(g_arr0, g_arr1), cv.GOut(g_out))
arr0 = [(2, 2), 2.0]
arr1 = [3, 'str']
arr0 = ((2, 2), 2.0)
arr1 = (3, 'str')
out = comp.apply(cv.gin(arr0, arr1),
args=cv.compile_args(cv.gapi.kernels(GConcatImpl)))
args=cv.gapi.compile_args(cv.gapi.kernels(GConcatImpl)))
self.assertEqual(arr0 + arr1, out)
@@ -550,7 +550,7 @@ try:
img1 = np.array([1, 2, 3])
with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1),
args=cv.compile_args(
args=cv.gapi.compile_args(
cv.gapi.kernels(GAddImpl)))
@@ -577,7 +577,7 @@ try:
img1 = np.array([1, 2, 3])
with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1),
args=cv.compile_args(
args=cv.gapi.compile_args(
cv.gapi.kernels(GAddImpl)))
@@ -607,7 +607,7 @@ try:
# FIXME: Cause Bad variant access.
# Need to provide more descriptive error messsage.
with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1),
args=cv.compile_args(
args=cv.gapi.compile_args(
cv.gapi.kernels(GAddImpl)))
def test_pipeline_with_custom_kernels(self):
@@ -657,7 +657,7 @@ try:
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.compile_args(
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))
@@ -3,201 +3,367 @@
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(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(),
np.array(a, np.float32).flatten(),
cv.NORM_INF))
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.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
proc_num_frames += 1
if proc_num_frames == max_num_frames:
break;
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
@@ -3,29 +3,51 @@
import numpy as np
import cv2 as cv
import os
import sys
import unittest
from tests_common import NewOpenCVTests
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]
try:
for t in types:
g_array = cv.GArrayT(t)
self.assertEqual(t, g_array.type())
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]
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())
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__':
+132 -1
View File
@@ -4,11 +4,23 @@
#include <opencv2/gapi/core.hpp>
#include <opencv2/gapi/imgproc.hpp>
#include <opencv2/gapi/s11n.hpp>
#include <opencv2/gapi/garg.hpp>
#include <opencv2/gapi/gcommon.hpp>
#include <opencv2/gapi/cpu/gcpukernel.hpp>
#include <opencv2/gapi/fluid/core.hpp>
#include <opencv2/gapi/fluid/imgproc.hpp>
static void gscalar_example()
{
//! [gscalar_implicit]
cv::GMat a;
cv::GMat b = a + 1;
//! [gscalar_implicit]
}
static void typed_example()
{
const cv::Size sz(32, 32);
@@ -47,6 +59,120 @@ static void typed_example()
//! [Typed_Example]
}
static void bind_serialization_example()
{
// ! [bind after deserialization]
cv::GCompiled compd;
std::vector<char> bytes;
auto graph = cv::gapi::deserialize<cv::GComputation>(bytes);
auto meta = cv::gapi::deserialize<cv::GMetaArgs>(bytes);
compd = graph.compile(std::move(meta), cv::compile_args());
auto in_args = cv::gapi::deserialize<cv::GRunArgs>(bytes);
auto out_args = cv::gapi::deserialize<cv::GRunArgs>(bytes);
compd(std::move(in_args), cv::gapi::bind(out_args));
// ! [bind after deserialization]
}
static void bind_deserialization_example()
{
// ! [bind before serialization]
std::vector<cv::GRunArgP> graph_outs;
cv::GRunArgs out_args;
for (auto &&out : graph_outs) {
out_args.emplace_back(cv::gapi::bind(out));
}
const auto sargsout = cv::gapi::serialize(out_args);
// ! [bind before serialization]
}
struct SimpleCustomType {
bool val;
bool operator==(const SimpleCustomType& other) const {
return val == other.val;
}
};
struct SimpleCustomType2 {
int val;
std::string name;
std::vector<float> vec;
std::map<int, uint64_t> mmap;
bool operator==(const SimpleCustomType2& other) const {
return val == other.val && name == other.name &&
vec == other.vec && mmap == other.mmap;
}
};
// ! [S11N usage]
namespace cv {
namespace gapi {
namespace s11n {
namespace detail {
template<> struct S11N<SimpleCustomType> {
static void serialize(IOStream &os, const SimpleCustomType &p) {
os << p.val;
}
static SimpleCustomType deserialize(IIStream &is) {
SimpleCustomType p;
is >> p.val;
return p;
}
};
template<> struct S11N<SimpleCustomType2> {
static void serialize(IOStream &os, const SimpleCustomType2 &p) {
os << p.val << p.name << p.vec << p.mmap;
}
static SimpleCustomType2 deserialize(IIStream &is) {
SimpleCustomType2 p;
is >> p.val >> p.name >> p.vec >> p.mmap;
return p;
}
};
} // namespace detail
} // namespace s11n
} // namespace gapi
} // namespace cv
// ! [S11N usage]
namespace cv {
namespace detail {
template<> struct CompileArgTag<SimpleCustomType> {
static const char* tag() {
return "org.opencv.test.simple_custom_type";
}
};
template<> struct CompileArgTag<SimpleCustomType2> {
static const char* tag() {
return "org.opencv.test.simple_custom_type_2";
}
};
} // namespace detail
} // namespace cv
static void s11n_example()
{
SimpleCustomType customVar1 { false };
SimpleCustomType2 customVar2 { 1248, "World", {1280, 720, 640, 480},
{ {5, 32434142342}, {7, 34242432} } };
std::vector<char> sArgs = cv::gapi::serialize(
cv::compile_args(customVar1, customVar2));
cv::GCompileArgs dArgs = cv::gapi::deserialize<cv::GCompileArgs,
SimpleCustomType,
SimpleCustomType2>(sArgs);
SimpleCustomType dCustomVar1 = cv::gapi::getCompileArg<SimpleCustomType>(dArgs).value();
SimpleCustomType2 dCustomVar2 = cv::gapi::getCompileArg<SimpleCustomType2>(dArgs).value();
(void) dCustomVar1;
(void) dCustomVar2;
}
G_TYPED_KERNEL(IAdd, <cv::GMat(cv::GMat)>, "test.custom.add") {
static cv::GMatDesc outMeta(const cv::GMatDesc &in) { return in; }
};
@@ -116,7 +242,12 @@ int main(int argc, char *argv[])
>();
//! [kernels_snippet]
// Just call typed example with no input/output
// Just call typed example with no input/output - avoid warnings about
// unused functions
typed_example();
gscalar_example();
bind_serialization_example();
bind_deserialization_example();
s11n_example();
return 0;
}
+6 -14
View File
@@ -589,30 +589,23 @@ int main(int argc, char* argv[]) {
//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 in_resized[MAX_PYRAMID_LEVELS];
cv::GMat in_transposed[MAX_PYRAMID_LEVELS];
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];
cv::GArray<custom::Face> faces_init(std::vector<custom::Face>{});
//The very first PNet pyramid layer to init total_faces[0]
in_resized[0] = cv::gapi::resize(in_originalRGB, level_size[0]);
in_transposed[0] = cv::gapi::transpose(in_resized[0]);
std::tie(regressions[0], scores[0]) = run_mtcnn_p(in_transposed[0], get_pnet_level_name(level_size[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);
nms_p_faces[0] = custom::RunNMS::on(final_faces_pnet0, 0.5f, false);
total_faces[0] = custom::AccumulatePyramidOutputs::on(faces_init, nms_p_faces[0]);
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)
{
in_resized[i] = cv::gapi::resize(in_originalRGB, level_size[i]);
in_transposed[i] = cv::gapi::transpose(in_resized[i]);
std::tie(regressions[i], scores[i]) = run_mtcnn_p(in_transposed[i], get_pnet_level_name(level_size[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);
@@ -626,8 +619,7 @@ int main(int argc, char* argv[]) {
//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;
cv::GMat in_originalRGB_transposed = cv::gapi::transpose(in_originalRGB);
std::tie(regressionsRNet, scoresRNet) = cv::gapi::infer<custom::MTCNNRefinement>(faces_roi_pnet, in_originalRGB_transposed);
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);
@@ -638,7 +630,7 @@ int main(int argc, char* argv[]) {
//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_originalRGB_transposed);
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);
+5 -5
View File
@@ -16,13 +16,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 {
@@ -338,7 +338,7 @@ int main(int argc, char *argv[])
cv::GMat in;
cv::GMat faces = cv::gapi::infer<custom::Faces>(in);
cv::GOpaque<cv::Size> sz = custom::Size::on(in); // FIXME
cv::GOpaque<cv::Size> sz = cv::gapi::streaming::size(in);
cv::GArray<cv::Rect> faces_rc = custom::ParseSSD::on(faces, sz, 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);
@@ -0,0 +1,293 @@
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cctype>
#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/highgui.hpp> // CommandLineParser
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) }"
"{ 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";
}
} // 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(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(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(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
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;
}
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
};
auto kernels = cv::gapi::kernels
< custom::OCVLocateROI
, custom::OCVParseSSD
, custom::OCVBBoxes>();
auto networks = cv::gapi::networks(face_net);
// Create source
cv::Ptr<cv::gapi::wip::IStreamSource> cap;
try {
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);
auto rcs = custom::ParseSSD::on(blob, roi, size);
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();
int framesCount = 0;
cv::TickMeter t;
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;
t.start();
while (pipeline.pull(cv::gout(outMat))) {
cv::imshow("Out", outMat);
cv::waitKey(1);
if (!output.empty()) {
writer << outMat;
}
framesCount++;
}
t.stop();
std::cout << "Elapsed time: " << t.getTimeSec() << std::endl;
std::cout << "FPS: " << framesCount / t.getTimeSec() << std::endl;
std::cout << "framesCount: " << framesCount << 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);
}
}
+63 -20
View File
@@ -47,6 +47,53 @@ std::string get_weights_path(const std::string &model_path) {
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 {
@@ -57,25 +104,21 @@ G_API_OP(PostProcessing, <cv::GMat(cv::GMat, cv::GMat)>, "sample.custom.post_pro
};
GAPI_OCV_KERNEL(OCVPostProcessing, PostProcessing) {
static void run(const cv::Mat &in, const cv::Mat &detected_classes, cv::Mat &out) {
// This kernel constructs output image by class table and colors vector
// The semantic-segmentation-adas-0001 output a blob with the shape
// [B, C=1, H=1024, W=2048]
const int outHeight = 1024;
const int outWidth = 2048;
cv::Mat maskImg(outHeight, outWidth, CV_8UC3);
const int* const classes = detected_classes.ptr<int>();
for (int rowId = 0; rowId < outHeight; ++rowId) {
for (int colId = 0; colId < outWidth; ++colId) {
size_t classId = static_cast<size_t>(classes[rowId * outWidth + colId]);
maskImg.at<cv::Vec3b>(rowId, colId) =
classId < colors.size()
? colors[classId]
: cv::Vec3b{0, 0, 0}; // sample detects 20 classes
}
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::resize(maskImg, out, in.size());
cv::Mat mask_img;
classesToColors(classes, mask_img);
cv::resize(mask_img, out, in.size());
const float blending = 0.3f;
out = in * blending + out * (1 - blending);
}
@@ -104,8 +147,8 @@ int main(int argc, char *argv[]) {
// Now build the graph
cv::GMat in;
cv::GMat detected_classes = cv::gapi::infer<SemSegmNet>(in);
cv::GMat out = custom::PostProcessing::on(in, detected_classes);
cv::GMat out_blob = cv::gapi::infer<SemSegmNet>(in);
cv::GMat out = custom::PostProcessing::on(in, out_blob);
cv::GStreamingCompiled pipeline = cv::GComputation(cv::GIn(in), cv::GOut(out))
.compileStreaming(cv::compile_args(kernels, networks));
+4
View File
@@ -15,6 +15,10 @@ 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);
-5
View File
@@ -14,7 +14,6 @@
#include "api/gorigin.hpp"
#include "api/gproto_priv.hpp"
#include "logger.hpp"
// FIXME: it should be a visitor!
// FIXME: Reimplement with traits?
@@ -277,13 +276,9 @@ void cv::validate_input_arg(const GRunArg& arg)
void cv::validate_input_args(const GRunArgs& args)
{
GAPI_LOG_DEBUG(nullptr, "Total count: " << args.size());
size_t index = 0;
for (const auto& arg : args)
{
GAPI_LOG_DEBUG(nullptr, "Process index: " << index);
validate_input_arg(arg);
index ++;
}
}
+6 -1
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
#include "precomp.hpp"
@@ -75,6 +75,11 @@ cv::GMat cv::gapi::streaming::desync(const cv::GMat &g) {
// object will feed both branches of the streaming executable.
}
// All notes from the above desync(GMat) are also applicable here
cv::GFrame cv::gapi::streaming::desync(const cv::GFrame &f) {
return cv::gapi::copy(detail::desync(f));
}
cv::GMat cv::gapi::streaming::BGR(const cv::GFrame& in) {
return cv::gapi::streaming::GBGR::on(in);
}
+4
View File
@@ -35,6 +35,10 @@ cv::MediaFrame::IAdapter* cv::MediaFrame::getAdapter() const {
return m->adapter.get();
}
void cv::MediaFrame::serialize(cv::gapi::s11n::IOStream& os) const {
return m->adapter->serialize(os);
}
cv::MediaFrame::View::View(Ptrs&& ptrs, Strides&& strs, Callback &&cb)
: ptr (std::move(ptrs))
, stride(std::move(strs))
+3 -3
View File
@@ -159,7 +159,7 @@ void drawPrimitivesOCV(cv::Mat& in,
{
const auto& rp = cv::util::get<Rect>(p);
const auto color = converter.cvtColor(rp.color);
cv::rectangle(in, rp.rect, color , rp.thick);
cv::rectangle(in, rp.rect, color, rp.thick, rp.lt, rp.shift);
break;
}
@@ -198,7 +198,7 @@ void drawPrimitivesOCV(cv::Mat& in,
{
const auto& cp = cv::util::get<Circle>(p);
const auto color = converter.cvtColor(cp.color);
cv::circle(in, cp.center, cp.radius, color, cp.thick);
cv::circle(in, cp.center, cp.radius, color, cp.thick, cp.lt, cp.shift);
break;
}
@@ -206,7 +206,7 @@ void drawPrimitivesOCV(cv::Mat& in,
{
const auto& lp = cv::util::get<Line>(p);
const auto color = converter.cvtColor(lp.color);
cv::line(in, lp.pt1, lp.pt2, color, lp.thick);
cv::line(in, lp.pt1, lp.pt2, color, lp.thick, lp.lt, lp.shift);
break;
}
+13 -7
View File
@@ -65,25 +65,25 @@ std::vector<char> cv::gapi::serialize(const std::vector<std::string>& vs)
// FIXME: This function should move from S11N to GRunArg-related entities.
// it has nothing to do with the S11N as it is
cv::GRunArgsP cv::gapi::bind(cv::GRunArgs &results)
cv::GRunArgsP cv::gapi::bind(cv::GRunArgs &out_args)
{
cv::GRunArgsP outputs;
outputs.reserve(results.size());
for (cv::GRunArg &res_obj : results)
outputs.reserve(out_args.size());
for (cv::GRunArg &res_obj : out_args)
{
using T = cv::GRunArg;
switch (res_obj.index())
{
#if !defined(GAPI_STANDALONE)
case T::index_of<cv::UMat>() :
outputs.emplace_back((cv::UMat*)(&(cv::util::get<cv::UMat>(res_obj))));
outputs.emplace_back(&(cv::util::get<cv::UMat>(res_obj)));
break;
#endif
case cv::GRunArg::index_of<cv::Mat>() :
outputs.emplace_back((cv::Mat*)(&(cv::util::get<cv::Mat>(res_obj))));
outputs.emplace_back(&(cv::util::get<cv::Mat>(res_obj)));
break;
case cv::GRunArg::index_of<cv::Scalar>() :
outputs.emplace_back((cv::Scalar*)(&(cv::util::get<cv::Scalar>(res_obj))));
outputs.emplace_back(&(cv::util::get<cv::Scalar>(res_obj)));
break;
case T::index_of<cv::detail::VectorRef>() :
outputs.emplace_back(cv::util::get<cv::detail::VectorRef>(res_obj));
@@ -92,7 +92,10 @@ cv::GRunArgsP cv::gapi::bind(cv::GRunArgs &results)
outputs.emplace_back(cv::util::get<cv::detail::OpaqueRef>(res_obj));
break;
case cv::GRunArg::index_of<cv::RMat>() :
outputs.emplace_back((cv::RMat*)(&(cv::util::get<cv::RMat>(res_obj))));
outputs.emplace_back(&(cv::util::get<cv::RMat>(res_obj)));
break;
case cv::GRunArg::index_of<cv::MediaFrame>() :
outputs.emplace_back(&(cv::util::get<cv::MediaFrame>(res_obj)));
break;
default:
GAPI_Assert(false && "This value type is not supported!"); // ...maybe because of STANDALONE mode.
@@ -130,6 +133,9 @@ cv::GRunArg cv::gapi::bind(cv::GRunArgP &out)
case T::index_of<cv::RMat*>() :
return cv::GRunArg(*cv::util::get<cv::RMat*>(out));
case T::index_of<cv::MediaFrame*>() :
return cv::GRunArg(*cv::util::get<cv::MediaFrame*>(out));
default:
// ...maybe our types were extended
GAPI_Assert(false && "This value type is UNKNOWN!");
@@ -85,6 +85,19 @@ class GGraphMetaBackendImpl final: public cv::gapi::GBackend::Priv {
const std::vector<cv::gimpl::Data>&) const override {
return EPtr{new GraphMetaExecutable(graph, nodes)};
}
virtual bool controlsMerge() const override
{
return true;
}
virtual bool allowsMerge(const cv::gimpl::GIslandModel::Graph &,
const ade::NodeHandle &,
const ade::NodeHandle &,
const ade::NodeHandle &) const override
{
return false;
}
};
cv::gapi::GBackend graph_meta_backend() {
@@ -32,6 +32,14 @@ void putData(GSerialized& s, const cv::gimpl::GModel::ConstGraph& cg, const ade:
});
if (s.m_datas.end() == it) {
s.m_datas.push_back(gdata);
if (cg.metadata(nh).contains<gimpl::ConstValue>()) {
size_t datas_num = s.m_datas.size() - 1;
GAPI_DbgAssert(datas_num <= static_cast<size_t>(std::numeric_limits<GSerialized::data_tag_t>::max()));
GSerialized::data_tag_t tag = static_cast<GSerialized::data_tag_t>(datas_num);
s.m_const_datas.emplace(tag,
cg.metadata(nh).get<gimpl::ConstValue>());
}
}
}
@@ -42,11 +50,20 @@ void putOp(GSerialized& s, const cv::gimpl::GModel::ConstGraph& cg, const ade::N
s.m_ops.push_back(op);
}
void mkDataNode(ade::Graph& g, const cv::gimpl::Data& data) {
ade::NodeHandle mkDataNode(ade::Graph& g, const cv::gimpl::Data& data) {
cv::gimpl::GModel::Graph gm(g);
auto nh = gm.createNode();
gm.metadata(nh).set(cv::gimpl::NodeType{cv::gimpl::NodeType::DATA});
gm.metadata(nh).set(data);
return nh;
}
ade::NodeHandle mkConstDataNode(ade::Graph& g, const cv::gimpl::Data& data, const cv::gimpl::ConstValue& const_data) {
auto nh = mkDataNode(g, data);
cv::gimpl::GModel::Graph gm(g);
gm.metadata(nh).set(const_data);
return nh;
}
void mkOpNode(ade::Graph& g, const cv::gimpl::Op& op) {
@@ -184,18 +201,20 @@ IOStream& operator<< (IOStream& os, const cv::RMat& mat) {
return os;
}
IIStream& operator>> (IIStream& is, cv::RMat&) {
util::throw_error(std::logic_error("operator>> for RMat should never be called"));
util::throw_error(std::logic_error("operator>> for RMat should never be called. "
"Instead, cv::gapi::deserialize<cv::GRunArgs, AdapterTypes...>() "
"should be used"));
return is;
}
IOStream& operator<< (IOStream& os, const cv::MediaFrame &) {
// Stub
GAPI_Assert(false && "cv::MediaFrame serialization is not supported!");
IOStream& operator<< (IOStream& os, const cv::MediaFrame &frame) {
frame.serialize(os);
return os;
}
IIStream& operator>> (IIStream& is, cv::MediaFrame &) {
// Stub
GAPI_Assert(false && "cv::MediaFrame serialization is not supported!");
util::throw_error(std::logic_error("operator>> for MediaFrame should never be called. "
"Instead, cv::gapi::deserialize<cv::GRunArgs, AdapterTypes...>() "
"should be used"));
return is;
}
@@ -624,6 +643,10 @@ IOStream& operator<< (IOStream& os, const cv::gimpl::Data &d) {
return os << d.shape << d.rc << d.meta << d.storage << d.kind;
}
IOStream& operator<< (IOStream& os, const cv::gimpl::ConstValue &cd) {
return os << cd.arg;
}
namespace
{
template<typename Ref, typename T, typename... Ts>
@@ -667,6 +690,9 @@ IIStream& operator>> (IIStream& is, cv::gimpl::Data &d) {
return is;
}
IIStream& operator>> (IIStream& is, cv::gimpl::ConstValue &cd) {
return is >> cd.arg;
}
IOStream& operator<< (IOStream& os, const cv::gimpl::DataObjectCounter &c) {
return os << c.m_next_data_id;
@@ -709,18 +735,34 @@ void serialize( IOStream& os
}
s.m_counter = cg.metadata().get<cv::gimpl::DataObjectCounter>();
s.m_proto = p;
os << s.m_ops << s.m_datas << s.m_counter << s.m_proto;
os << s.m_ops << s.m_datas << s.m_counter << s.m_proto << s.m_const_datas;
}
GSerialized deserialize(IIStream &is) {
GSerialized s;
is >> s.m_ops >> s.m_datas >> s.m_counter >> s.m_proto;
is >> s.m_ops >> s.m_datas >> s.m_counter >> s.m_proto >> s.m_const_datas;
return s;
}
void reconstruct(const GSerialized &s, ade::Graph &g) {
GAPI_Assert(g.nodes().empty());
for (const auto& d : s.m_datas) cv::gapi::s11n::mkDataNode(g, d);
GSerialized::data_tag_t tag = 0;
for (const auto& d : s.m_datas) {
if (d.storage == gimpl::Data::Storage::CONST_VAL) {
auto cit = s.m_const_datas.find(tag);
if (cit == s.m_const_datas.end()) {
util::throw_error(std::logic_error("Cannot reconstruct graph: Data::Storage::CONST_VAL by tag: " +
std::to_string(tag) + " requires ConstValue"));
}
mkConstDataNode(g, d, cit->second);
} else {
cv::gapi::s11n::mkDataNode(g, d);
}
tag ++;
}
for (const auto& op : s.m_ops) cv::gapi::s11n::mkOpNode(g, op);
cv::gapi::s11n::linkNodes(g);
@@ -31,6 +31,9 @@ struct GSerialized {
std::vector<cv::gimpl::Data> m_datas;
cv::gimpl::DataObjectCounter m_counter;
cv::gimpl::Protocol m_proto;
using data_tag_t = uint64_t;
std::map<data_tag_t, cv::gimpl::ConstValue> m_const_datas;
};
////////////////////////////////////////////////////////////////////////////////
@@ -97,6 +100,9 @@ GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::gimpl::Op &op);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::gimpl::Data &op);
GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::gimpl::Data &op);
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::gimpl::ConstValue &cd);
GAPI_EXPORTS IIStream& operator>> (IIStream& os, cv::gimpl::ConstValue &cd);
// Render types ////////////////////////////////////////////////////////////////
GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::gapi::wip::draw::Text &t);
+13 -2
View File
@@ -652,7 +652,12 @@ GAPI_OCV_KERNEL(GCPUParseSSDBL, cv::gapi::nn::parsers::GParseSSDBL)
std::vector<cv::Rect>& out_boxes,
std::vector<int>& out_labels)
{
cv::parseSSDBL(in_ssd_result, in_size, confidence_threshold, filter_label, out_boxes, out_labels);
cv::ParseSSD(in_ssd_result, in_size,
confidence_threshold,
filter_label,
false,
false,
out_boxes, out_labels);
}
};
@@ -665,7 +670,13 @@ GAPI_OCV_KERNEL(GOCVParseSSD, cv::gapi::nn::parsers::GParseSSD)
const bool filter_out_of_bounds,
std::vector<cv::Rect>& out_boxes)
{
cv::parseSSD(in_ssd_result, in_size, confidence_threshold, alignment_to_square, filter_out_of_bounds, out_boxes);
std::vector<int> unused_labels;
cv::ParseSSD(in_ssd_result, in_size,
confidence_threshold,
-1,
alignment_to_square,
filter_out_of_bounds,
out_boxes, unused_labels);
}
};
+13 -40
View File
@@ -170,12 +170,14 @@ private:
} // namespace nn
} // namespace gapi
void parseSSDBL(const cv::Mat& in_ssd_result,
const cv::Size& in_size,
const float confidence_threshold,
const int filter_label,
std::vector<cv::Rect>& out_boxes,
std::vector<int>& out_labels)
void ParseSSD(const cv::Mat& in_ssd_result,
const cv::Size& in_size,
const float confidence_threshold,
const int filter_label,
const bool alignment_to_square,
const bool filter_out_of_bounds,
std::vector<cv::Rect>& out_boxes,
std::vector<int>& out_labels)
{
cv::gapi::nn::SSDParser parser(in_ssd_result.size, in_size, in_ssd_result.ptr<float>());
out_boxes.clear();
@@ -188,38 +190,6 @@ void parseSSDBL(const cv::Mat& in_ssd_result,
{
std::tie(rc, image_id, confidence, label) = parser.extract(i);
if (image_id < 0.f)
{
break; // marks end-of-detections
}
if (confidence < confidence_threshold ||
(filter_label != -1 && label != filter_label))
{
continue; // filter out object classes if filter is specified
} // and skip objects with low confidence
out_boxes.emplace_back(rc & parser.getSurface());
out_labels.emplace_back(label);
}
}
void parseSSD(const cv::Mat& in_ssd_result,
const cv::Size& in_size,
const float confidence_threshold,
const bool alignment_to_square,
const bool filter_out_of_bounds,
std::vector<cv::Rect>& out_boxes)
{
cv::gapi::nn::SSDParser parser(in_ssd_result.size, in_size, in_ssd_result.ptr<float>());
out_boxes.clear();
cv::Rect rc;
float image_id, confidence;
int label;
const size_t range = parser.getMaxProposals();
for (size_t i = 0; i < range; ++i)
{
std::tie(rc, image_id, confidence, label) = parser.extract(i);
if (image_id < 0.f)
{
break; // marks end-of-detections
@@ -228,12 +198,14 @@ void parseSSD(const cv::Mat& in_ssd_result,
{
continue; // skip objects with low confidence
}
if((filter_label != -1) && (label != filter_label))
{
continue; // filter out object classes if filter is specified
}
if (alignment_to_square)
{
parser.adjustBoundingBox(rc);
}
const auto clipped_rc = rc & parser.getSurface();
if (filter_out_of_bounds)
{
@@ -243,6 +215,7 @@ void parseSSD(const cv::Mat& in_ssd_result,
}
}
out_boxes.emplace_back(clipped_rc);
out_labels.emplace_back(label);
}
}
+4 -9
View File
@@ -11,19 +11,14 @@
namespace cv
{
void parseSSDBL(const cv::Mat& in_ssd_result,
const cv::Size& in_size,
const float confidence_threshold,
const int filter_label,
std::vector<cv::Rect>& out_boxes,
std::vector<int>& out_labels);
void parseSSD(const cv::Mat& in_ssd_result,
void ParseSSD(const cv::Mat& in_ssd_result,
const cv::Size& in_size,
const float confidence_threshold,
const int filter_label,
const bool alignment_to_square,
const bool filter_out_of_bounds,
std::vector<cv::Rect>& out_boxes);
std::vector<cv::Rect>& out_boxes,
std::vector<int>& out_labels);
void parseYolo(const cv::Mat& in_yolo_result,
const cv::Size& in_size,
@@ -37,3 +37,15 @@ cv::gapi::ie::PyParams cv::gapi::ie::params(const std::string &tag,
const std::string &device) {
return {tag, model, device};
}
cv::gapi::ie::PyParams& cv::gapi::ie::PyParams::constInput(const std::string &layer_name,
const cv::Mat &data,
TraitAs hint) {
m_priv->constInput(layer_name, data, hint);
return *this;
}
cv::gapi::ie::PyParams& cv::gapi::ie::PyParams::cfgNumRequests(size_t nireq) {
m_priv->cfgNumRequests(nireq);
return *this;
}
+165 -75
View File
@@ -108,6 +108,7 @@ inline IE::Precision toIE(int depth) {
case CV_8U: return IE::Precision::U8;
case CV_32S: return IE::Precision::I32;
case CV_32F: return IE::Precision::FP32;
case CV_16F: return IE::Precision::FP16;
default: GAPI_Assert(false && "IE. Unsupported data type");
}
return IE::Precision::UNSPECIFIED;
@@ -161,6 +162,7 @@ inline IE::Blob::Ptr wrapIE(const cv::Mat &mat, cv::gapi::ie::TraitAs hint) {
HANDLE(8U, uint8_t);
HANDLE(32F, float);
HANDLE(32S, int);
HANDLE(16F, int16_t);
#undef HANDLE
default: GAPI_Assert(false && "IE. Unsupported data type");
}
@@ -222,8 +224,17 @@ struct IEUnit {
IE::ExecutableNetwork this_network;
cv::gimpl::ie::wrap::Plugin this_plugin;
InferenceEngine::RemoteContext::Ptr rctx = nullptr;
explicit IEUnit(const cv::gapi::ie::detail::ParamDesc &pp)
: params(pp) {
InferenceEngine::ParamMap* ctx_params =
cv::util::any_cast<InferenceEngine::ParamMap>(&params.context_config);
if (ctx_params != nullptr) {
auto ie_core = cv::gimpl::ie::wrap::getCore();
rctx = ie_core.CreateContext(params.device_id, *ctx_params);
}
if (params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
net = cv::gimpl::ie::wrap::readNetwork(params);
inputs = net.getInputsInfo();
@@ -231,11 +242,7 @@ struct IEUnit {
} else if (params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import) {
this_plugin = cv::gimpl::ie::wrap::getPlugin(params);
this_plugin.SetConfig(params.config);
this_network = cv::gimpl::ie::wrap::importNetwork(this_plugin, params);
// FIXME: ICNNetwork returns InputsDataMap/OutputsDataMap,
// but ExecutableNetwork returns ConstInputsDataMap/ConstOutputsDataMap
inputs = cv::gimpl::ie::wrap::toInputsDataMap(this_network.GetInputsInfo());
outputs = cv::gimpl::ie::wrap::toOutputsDataMap(this_network.GetOutputsInfo());
this_network = cv::gimpl::ie::wrap::importNetwork(this_plugin, params, rctx);
if (!params.reshape_table.empty() || !params.layer_names_to_reshape.empty()) {
GAPI_LOG_WARNING(NULL, "Reshape isn't supported for imported network");
}
@@ -259,10 +266,18 @@ struct IEUnit {
+ params.model_path));
}
if (params.num_in == 1u && params.input_names.empty()) {
params.input_names = { inputs.begin()->first };
if (params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
params.input_names = { inputs.begin()->first };
} else {
params.input_names = { this_network.GetInputsInfo().begin()->first };
}
}
if (params.num_out == 1u && params.output_names.empty()) {
params.output_names = { outputs.begin()->first };
if (params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
params.output_names = { outputs.begin()->first };
} else {
params.output_names = { this_network.GetOutputsInfo().begin()->first };
}
}
if (!params.reshape_table.empty()) {
GAPI_Assert((params.reshape_table.size() + params.layer_names_to_reshape.size()) <=
@@ -279,7 +294,8 @@ struct IEUnit {
// for loadNetwork they can be obtained by using readNetwork
non_const_this->this_plugin = cv::gimpl::ie::wrap::getPlugin(params);
non_const_this->this_plugin.SetConfig(params.config);
non_const_this->this_network = cv::gimpl::ie::wrap::loadNetwork(non_const_this->this_plugin, net, params);
non_const_this->this_network = cv::gimpl::ie::wrap::loadNetwork(non_const_this->this_plugin,
net, params, rctx);
}
return {params, this_plugin, this_network};
@@ -481,7 +497,32 @@ using GConstGIEModel = ade::ConstTypedGraph
, IECallable
>;
inline IE::Blob::Ptr extractRemoteBlob(IECallContext& ctx, std::size_t i) {
GAPI_Assert(ctx.inShape(i) == cv::GShape::GFRAME &&
"Remote blob is supported for MediaFrame only");
cv::util::any any_blob_params = ctx.inFrame(i).blobParams();
auto ie_core = cv::gimpl::ie::wrap::getCore();
using ParamType = std::pair<InferenceEngine::TensorDesc,
InferenceEngine::ParamMap>;
ParamType* blob_params = cv::util::any_cast<ParamType>(&any_blob_params);
if (blob_params == nullptr) {
GAPI_Assert(false && "Incorrect type of blobParams: "
"expected std::pair<InferenceEngine::TensorDesc,"
"InferenceEngine::ParamMap>");
}
return ctx.uu.rctx->CreateBlob(blob_params->first,
blob_params->second);
}
inline IE::Blob::Ptr extractBlob(IECallContext& ctx, std::size_t i) {
if (ctx.uu.rctx != nullptr) {
return extractRemoteBlob(ctx, i);
}
switch (ctx.inShape(i)) {
case cv::GShape::GFRAME: {
const auto& frame = ctx.inFrame(i);
@@ -496,6 +537,24 @@ inline IE::Blob::Ptr extractBlob(IECallContext& ctx, std::size_t i) {
}
GAPI_Assert(false);
}
static void setBlob(InferenceEngine::InferRequest& req,
cv::gapi::ie::detail::ParamDesc::Kind kind,
const std::string& layer_name,
IE::Blob::Ptr blob) {
// NB: In case importNetwork preprocessing must be
// passed as SetBlob argument.
if (kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
req.SetBlob(layer_name, blob);
} else {
GAPI_Assert(kind == cv::gapi::ie::detail::ParamDesc::Kind::Import);
IE::PreProcessInfo info;
info.setResizeAlgorithm(IE::RESIZE_BILINEAR);
req.SetBlob(layer_name, blob, info);
}
}
} // anonymous namespace
std::vector<InferenceEngine::InferRequest> cv::gimpl::ie::IECompiled::createInferRequests() {
@@ -571,6 +630,11 @@ void cv::gimpl::ie::RequestPool::callback(cv::gimpl::ie::RequestPool::Task task,
InferenceEngine::InferRequest& request,
size_t id) {
task.callback(request);
// NB: IE::InferRequest keeps the callback until the new one is set.
// Since user's callback might keep resources that should be released,
// need to destroy its after execution.
// Let's set the empty one to cause the destruction of a callback.
request.SetCompletionCallback([](){});
m_idle_ids.push(id);
}
@@ -772,7 +836,6 @@ static void PostOutputs(InferenceEngine::InferRequest &request,
auto output = ctx->output(i);
ctx->out.meta(output, ctx->input(0).meta);
ctx->out.post(std::move(output));
}
}
@@ -854,25 +917,30 @@ struct Infer: public cv::detail::KernelTag {
// meta order.
GAPI_Assert(uu.params.input_names.size() == in_metas.size()
&& "Known input layers count doesn't match input meta count");
for (auto &&it : ade::util::zip(ade::util::toRange(uu.params.input_names),
ade::util::toRange(in_metas))) {
const auto &input_name = std::get<0>(it);
auto &&ii = uu.inputs.at(input_name);
const auto & mm = std::get<1>(it);
configureInputInfo(ii, mm);
if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm, input_reshape_table);
// NB: Configuring input precision and network reshape must be done
// only in the loadNetwork case.
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
for (auto &&it : ade::util::zip(ade::util::toRange(uu.params.input_names),
ade::util::toRange(in_metas))) {
const auto &input_name = std::get<0>(it);
auto &&ii = uu.inputs.at(input_name);
const auto & mm = std::get<1>(it);
configureInputInfo(ii, mm);
if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm, input_reshape_table);
}
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
}
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
}
// FIXME: This isn't the best place to call reshape function.
// Сorrect solution would be to do this in compile() method of network,
// but now input meta isn't passed to compile() method.
if (!input_reshape_table.empty()) {
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
// FIXME: This isn't the best place to call reshape function.
// Сorrect solution would be to do this in compile() method of network,
// but now input meta isn't passed to compile() method.
if (!input_reshape_table.empty()) {
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
}
}
// FIXME: It would be nice here to have an exact number of network's
@@ -904,7 +972,10 @@ struct Infer: public cv::detail::KernelTag {
// and redirect our data producers to this memory
// (A memory dialog comes to the picture again)
IE::Blob::Ptr this_blob = extractBlob(*ctx, i);
req.SetBlob(ctx->uu.params.input_names[i], this_blob);
setBlob(req,
ctx->uu.params.kind,
ctx->uu.params.input_names[i],
this_blob);
}
// FIXME: Should it be done by kernel ?
// What about to do that in RequestPool ?
@@ -936,22 +1007,26 @@ struct InferROI: public cv::detail::KernelTag {
GAPI_Assert(1u == uu.params.input_names.size());
GAPI_Assert(2u == in_metas.size());
// 0th is ROI, 1st is input image
const auto &input_name = uu.params.input_names.at(0);
auto &&ii = uu.inputs.at(input_name);
auto &&mm = in_metas.at(1u);
configureInputInfo(ii, mm);
if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm, input_reshape_table);
}
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
// NB: Configuring input precision and network reshape must be done
// only in the loadNetwork case.
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
// 0th is ROI, 1st is input image
const auto &input_name = uu.params.input_names.at(0);
auto &&ii = uu.inputs.at(input_name);
auto &&mm = in_metas.at(1u);
configureInputInfo(ii, mm);
if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm, input_reshape_table);
}
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
// FIXME: This isn't the best place to call reshape function.
// Сorrect solution would be to do this in compile() method of network,
// but now input meta isn't passed to compile() method.
if (!input_reshape_table.empty()) {
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
// FIXME: This isn't the best place to call reshape function.
// Сorrect solution would be to do this in compile() method of network,
// but now input meta isn't passed to compile() method.
if (!input_reshape_table.empty()) {
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
}
}
// FIXME: It would be nice here to have an exact number of network's
@@ -980,10 +1055,11 @@ struct InferROI: public cv::detail::KernelTag {
auto&& this_roi = ctx->inArg<cv::detail::OpaqueRef>(0).rref<cv::Rect>();
IE::Blob::Ptr this_blob = extractBlob(*ctx, 1);
req.SetBlob(*(ctx->uu.params.input_names.begin()),
IE::make_shared_blob(this_blob, toIE(this_roi)));
setBlob(req,
ctx->uu.params.kind,
*(ctx->uu.params.input_names.begin()),
IE::make_shared_blob(this_blob,
toIE(this_roi)));
// FIXME: Should it be done by kernel ?
// What about to do that in RequestPool ?
req.StartAsync();
@@ -1018,23 +1094,27 @@ struct InferList: public cv::detail::KernelTag {
GAPI_Assert(uu.params.input_names.size() == (in_metas.size() - 1u)
&& "Known input layers count doesn't match input meta count");
std::size_t idx = 1u;
for (auto &&input_name : uu.params.input_names) {
auto &&ii = uu.inputs.at(input_name);
const auto & mm = in_metas[idx++];
configureInputInfo(ii, mm);
if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm, input_reshape_table);
// NB: Configuring input precision and network reshape must be done
// only in the loadNetwork case.
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
std::size_t idx = 1u;
for (auto &&input_name : uu.params.input_names) {
auto &&ii = uu.inputs.at(input_name);
const auto & mm = in_metas[idx++];
configureInputInfo(ii, mm);
if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm, input_reshape_table);
}
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
}
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
}
// FIXME: This isn't the best place to call reshape function.
// Сorrect solution would be to do this in compile() method of network,
// but now input meta isn't passed to compile() method.
if (!input_reshape_table.empty()) {
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
// FIXME: This isn't the best place to call reshape function.
// Сorrect solution would be to do this in compile() method of network,
// but now input meta isn't passed to compile() method.
if (!input_reshape_table.empty()) {
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
}
}
// roi-list version is much easier at the moment.
@@ -1060,6 +1140,7 @@ struct InferList: public cv::detail::KernelTag {
}
IE::Blob::Ptr this_blob = extractBlob(*ctx, 1);
std::vector<std::vector<int>> cached_dims(ctx->uu.params.num_out);
for (auto i : ade::util::iota(ctx->uu.params.num_out)) {
const IE::DataPtr& ie_out = ctx->uu.outputs.at(ctx->uu.params.output_names[i]);
@@ -1079,7 +1160,10 @@ struct InferList: public cv::detail::KernelTag {
cv::gimpl::ie::RequestPool::Task {
[ctx, rc, this_blob](InferenceEngine::InferRequest &req) {
IE::Blob::Ptr roi_blob = IE::make_shared_blob(this_blob, toIE(rc));
req.SetBlob(ctx->uu.params.input_names[0u], roi_blob);
setBlob(req,
ctx->uu.params.kind,
ctx->uu.params.input_names[0u],
roi_blob);
req.StartAsync();
},
std::bind(callback, std::placeholders::_1, pos)
@@ -1153,19 +1237,23 @@ struct InferList2: public cv::detail::KernelTag {
&& "Non-array inputs are not supported");
if (op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_RECT) {
// This is a cv::Rect -- configure the IE preprocessing
configureInputInfo(ii, mm_0);
if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm_0, input_reshape_table);
}
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
// NB: Configuring input precision and network reshape must be done
// only in the loadNetwork case.
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
// This is a cv::Rect -- configure the IE preprocessing
configureInputInfo(ii, mm_0);
if (uu.params.layer_names_to_reshape.find(input_name) !=
uu.params.layer_names_to_reshape.end()) {
configureInputReshapeByImage(ii, mm_0, input_reshape_table);
}
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
// FIXME: This isn't the best place to call reshape function.
// Сorrect solution would be to do this in compile() method of network,
// but now input meta isn't passed to compile() method.
if (!input_reshape_table.empty()) {
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
// FIXME: This isn't the best place to call reshape function.
// Сorrect solution would be to do this in compile() method of network,
// but now input meta isn't passed to compile() method.
if (!input_reshape_table.empty()) {
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
}
}
} else {
// This is a cv::GMat (equals to: cv::Mat)
@@ -1230,8 +1318,10 @@ struct InferList2: public cv::detail::KernelTag {
GAPI_Assert(false &&
"Only Rect and Mat types are supported for infer list 2!");
}
req.SetBlob(ctx->uu.params.input_names[in_idx], this_blob);
setBlob(req,
ctx->uu.params.kind,
ctx->uu.params.input_names[in_idx],
this_blob);
}
req.StartAsync();
},
@@ -22,24 +22,6 @@ namespace IE = InferenceEngine;
namespace giewrap = cv::gimpl::ie::wrap;
using GIEParam = cv::gapi::ie::detail::ParamDesc;
IE::InputsDataMap giewrap::toInputsDataMap (const IE::ConstInputsDataMap& inputs) {
IE::InputsDataMap transformed;
auto convert = [](const std::pair<std::string, IE::InputInfo::CPtr>& p) {
return std::make_pair(p.first, std::const_pointer_cast<IE::InputInfo>(p.second));
};
std::transform(inputs.begin(), inputs.end(), std::inserter(transformed, transformed.end()), convert);
return transformed;
}
IE::OutputsDataMap giewrap::toOutputsDataMap (const IE::ConstOutputsDataMap& outputs) {
IE::OutputsDataMap transformed;
auto convert = [](const std::pair<std::string, IE::CDataPtr>& p) {
return std::make_pair(p.first, std::const_pointer_cast<IE::Data>(p.second));
};
std::transform(outputs.begin(), outputs.end(), std::inserter(transformed, transformed.end()), convert);
return transformed;
}
#if INF_ENGINE_RELEASE < 2020000000 // < 2020.1
// Load extensions (taken from DNN module)
std::vector<std::string> giewrap::getExtensions(const GIEParam& params) {
@@ -124,7 +106,11 @@ IE::Core giewrap::getPlugin(const GIEParam& params) {
{
try
{
#if INF_ENGINE_RELEASE >= 2021040000
plugin.AddExtension(std::make_shared<IE::Extension>(extlib), params.device_id);
#else
plugin.AddExtension(IE::make_so_pointer<IE::IExtension>(extlib), params.device_id);
#endif
CV_LOG_INFO(NULL, "DNN-IE: Loaded extension plugin: " << extlib);
break;
}
@@ -13,6 +13,7 @@
#include <vector>
#include <string>
#include <fstream>
#include "opencv2/gapi/infer/ie.hpp"
@@ -28,9 +29,6 @@ namespace wrap {
GAPI_EXPORTS std::vector<std::string> getExtensions(const GIEParam& params);
GAPI_EXPORTS IE::CNNNetwork readNetwork(const GIEParam& params);
IE::InputsDataMap toInputsDataMap (const IE::ConstInputsDataMap& inputs);
IE::OutputsDataMap toOutputsDataMap(const IE::ConstOutputsDataMap& outputs);
#if INF_ENGINE_RELEASE < 2019020000 // < 2019.R2
using Plugin = IE::InferencePlugin;
GAPI_EXPORTS IE::InferencePlugin getPlugin(const GIEParam& params);
@@ -50,12 +48,29 @@ GAPI_EXPORTS IE::Core getCore();
GAPI_EXPORTS IE::Core getPlugin(const GIEParam& params);
GAPI_EXPORTS inline IE::ExecutableNetwork loadNetwork( IE::Core& core,
const IE::CNNNetwork& net,
const GIEParam& params) {
return core.LoadNetwork(net, params.device_id);
const GIEParam& params,
IE::RemoteContext::Ptr rctx = nullptr) {
if (rctx != nullptr) {
return core.LoadNetwork(net, rctx);
} else {
return core.LoadNetwork(net, params.device_id);
}
}
GAPI_EXPORTS inline IE::ExecutableNetwork importNetwork( IE::Core& core,
const GIEParam& param) {
return core.ImportNetwork(param.model_path, param.device_id, {});
const GIEParam& params,
IE::RemoteContext::Ptr rctx = nullptr) {
if (rctx != nullptr) {
std::filebuf blobFile;
if (!blobFile.open(params.model_path, std::ios::in | std::ios::binary))
{
blobFile.close();
throw std::runtime_error("Could not open file");
}
std::istream graphBlob(&blobFile);
return core.ImportNetwork(graphBlob, rctx);
} else {
return core.ImportNetwork(params.model_path, params.device_id, {});
}
}
#endif // INF_ENGINE_RELEASE < 2019020000
}}}}
+7 -11
View File
@@ -75,6 +75,11 @@ bool cv::GStreamingCompiled::Priv::pull(cv::GOptRunArgsP &&outs)
return m_exec->pull(std::move(outs));
}
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> cv::GStreamingCompiled::Priv::pull()
{
return m_exec->pull();
}
bool cv::GStreamingCompiled::Priv::try_pull(cv::GRunArgsP &&outs)
{
return m_exec->try_pull(std::move(outs));
@@ -123,18 +128,9 @@ bool cv::GStreamingCompiled::pull(cv::GRunArgsP &&outs)
return m_priv->pull(std::move(outs));
}
std::tuple<bool, cv::GRunArgs> cv::GStreamingCompiled::pull()
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> cv::GStreamingCompiled::pull()
{
GRunArgs run_args;
GRunArgsP outs;
const auto& out_info = m_priv->outInfo();
run_args.reserve(out_info.size());
outs.reserve(out_info.size());
cv::detail::constructGraphOutputs(m_priv->outInfo(), run_args, outs);
bool is_over = m_priv->pull(std::move(outs));
return std::make_tuple(is_over, run_args);
return m_priv->pull();
}
bool cv::GStreamingCompiled::pull(cv::GOptRunArgsP &&outs)
@@ -46,6 +46,7 @@ public:
void start();
bool pull(cv::GRunArgsP &&outs);
bool pull(cv::GOptRunArgsP &&outs);
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> pull();
bool try_pull(cv::GRunArgsP &&outs);
void stop();
+19 -6
View File
@@ -7,8 +7,6 @@
#include "precomp.hpp"
#include <iostream>
#include <ade/util/zip_range.hpp>
#include <opencv2/gapi/opencv_includes.hpp>
@@ -157,10 +155,14 @@ void writeBackExec(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg)
// FIXME:
// Rework, find a better way to check if there should be
// a real copy (add a pass to StreamingBackend?)
// NB: In case RMat adapter not equal to "RMatAdapter" need to
// copy data back to the host as well.
// FIXME: Rename "RMatAdapter" to "OpenCVAdapter".
auto& out_mat = *util::get<cv::Mat*>(g_arg);
const auto& rmat = mag.template slot<cv::RMat>().at(rc.id);
auto mag_data = rmat.get<RMatAdapter>()->data();
if (out_mat.data != mag_data) {
auto* adapter = rmat.get<RMatAdapter>();
if ((adapter != nullptr && out_mat.data != adapter->data()) ||
(adapter == nullptr)) {
auto view = rmat.access(RMat::Access::R);
asMat(view).copyTo(out_mat);
}
@@ -407,7 +409,8 @@ bool cv::gimpl::GExecutor::canReshape() const
{
// FIXME: Introduce proper reshaping support on GExecutor level
// for all cases!
return (m_ops.size() == 1) && m_ops[0].isl_exec->canReshape();
return std::all_of(m_ops.begin(), m_ops.end(),
[](const OpDesc& op) { return op.isl_exec->canReshape(); });
}
void cv::gimpl::GExecutor::reshape(const GMetaArgs& inMetas, const GCompileArgs& args)
@@ -417,7 +420,17 @@ void cv::gimpl::GExecutor::reshape(const GMetaArgs& inMetas, const GCompileArgs&
ade::passes::PassContext ctx{g};
passes::initMeta(ctx, inMetas);
passes::inferMeta(ctx, true);
m_ops[0].isl_exec->reshape(g, args);
// NB: Before reshape islands need to re-init resources for every slot.
for (auto slot : m_slots)
{
initResource(slot.slot_nh, slot.data_nh);
}
for (auto& op : m_ops)
{
op.isl_exec->reshape(g, args);
}
}
void cv::gimpl::GExecutor::prepareForNewStream()
@@ -186,8 +186,9 @@ void sync_data(cv::gimpl::stream::Result &r, cv::GOptRunArgsP &outputs)
// FIXME: this conversion should be unified
switch (out_obj.index())
{
HANDLE_CASE(cv::Scalar); break;
HANDLE_CASE(cv::RMat); break;
HANDLE_CASE(cv::Scalar); break;
HANDLE_CASE(cv::RMat); break;
HANDLE_CASE(cv::MediaFrame); break;
case T::index_of<O<cv::Mat>*>(): {
// Mat: special handling.
@@ -346,6 +347,10 @@ bool QueueReader::getInputVector(std::vector<Q*> &in_queues,
cv::GRunArgs &in_constants,
cv::GRunArgs &isl_inputs)
{
// NB: Need to release resources from the previous step, to fetch new ones.
// On some systems it might be impossible to allocate new memory
// until the old one is released.
m_cmd.clear();
// NOTE: in order to maintain the GRunArg's underlying object
// lifetime, keep the whole cmd vector (of size == # of inputs)
// in memory.
@@ -1017,6 +1022,49 @@ void check_DesyncObjectConsumedByMultipleIslands(const cv::gimpl::GIslandModel::
} // for(nodes)
}
// NB: Construct GRunArgsP based on passed info and store the memory in passed cv::GRunArgs.
// Needed for python bridge, because in case python user doesn't pass output arguments to apply.
void constructOptGraphOutputs(const cv::GTypesInfo &out_info,
cv::GOptRunArgs &args,
cv::GOptRunArgsP &outs)
{
for (auto&& info : out_info)
{
switch (info.shape)
{
case cv::GShape::GMAT:
{
args.emplace_back(cv::optional<cv::Mat>{});
outs.emplace_back(&cv::util::get<cv::optional<cv::Mat>>(args.back()));
break;
}
case cv::GShape::GSCALAR:
{
args.emplace_back(cv::optional<cv::Scalar>{});
outs.emplace_back(&cv::util::get<cv::optional<cv::Scalar>>(args.back()));
break;
}
case cv::GShape::GARRAY:
{
cv::detail::VectorRef ref;
cv::util::get<cv::detail::ConstructVec>(info.ctor)(ref);
args.emplace_back(cv::util::make_optional(std::move(ref)));
outs.emplace_back(wrap_opt_arg(cv::util::get<cv::optional<cv::detail::VectorRef>>(args.back())));
break;
}
case cv::GShape::GOPAQUE:
{
cv::detail::OpaqueRef ref;
cv::util::get<cv::detail::ConstructOpaque>(info.ctor)(ref);
args.emplace_back(cv::util::make_optional(std::move(ref)));
outs.emplace_back(wrap_opt_arg(cv::util::get<cv::optional<cv::detail::OpaqueRef>>(args.back())));
break;
}
default:
cv::util::throw_error(std::logic_error("Unsupported optional output shape for Python"));
}
}
}
} // anonymous namespace
class cv::gimpl::GStreamingExecutor::Synchronizer final {
@@ -1320,6 +1368,16 @@ cv::gimpl::GStreamingExecutor::GStreamingExecutor(std::unique_ptr<ade::Graph> &&
// per the same input frame, so the output traffic multiplies)
GAPI_Assert(m_collector_map.size() > 0u);
m_out_queue.set_capacity(queue_capacity * m_collector_map.size());
// FIXME: The code duplicates logic of collectGraphInfo()
cv::gimpl::GModel::ConstGraph cgr(*m_orig_graph);
auto meta = cgr.metadata().get<cv::gimpl::Protocol>().out_nhs;
out_info.reserve(meta.size());
ade::util::transform(meta, std::back_inserter(out_info), [&cgr](const ade::NodeHandle& nh) {
const auto& data = cgr.metadata(nh).get<cv::gimpl::Data>();
return cv::GTypeInfo{data.shape, data.kind, data.ctor};
});
}
cv::gimpl::GStreamingExecutor::~GStreamingExecutor()
@@ -1653,6 +1711,31 @@ bool cv::gimpl::GStreamingExecutor::pull(cv::GOptRunArgsP &&outs)
return true;
}
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> cv::gimpl::GStreamingExecutor::pull()
{
using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
bool is_over = false;
if (m_desync) {
GOptRunArgs opt_run_args;
GOptRunArgsP opt_outs;
opt_outs.reserve(out_info.size());
opt_run_args.reserve(out_info.size());
constructOptGraphOutputs(out_info, opt_run_args, opt_outs);
is_over = pull(std::move(opt_outs));
return std::make_tuple(is_over, RunArgs(opt_run_args));
}
GRunArgs run_args;
GRunArgsP outs;
run_args.reserve(out_info.size());
outs.reserve(out_info.size());
constructGraphOutputs(out_info, run_args, outs);
is_over = pull(std::move(outs));
return std::make_tuple(is_over, RunArgs(run_args));
}
bool cv::gimpl::GStreamingExecutor::try_pull(cv::GRunArgsP &&outs)
{
@@ -195,6 +195,8 @@ protected:
void wait_shutdown();
cv::GTypesInfo out_info;
public:
explicit GStreamingExecutor(std::unique_ptr<ade::Graph> &&g_model,
const cv::GCompileArgs &comp_args);
@@ -203,6 +205,7 @@ public:
void start();
bool pull(cv::GRunArgsP &&outs);
bool pull(cv::GOptRunArgsP &&outs);
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> pull();
bool try_pull(cv::GRunArgsP &&outs);
void stop();
bool running() const;
@@ -0,0 +1,227 @@
// 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 <cstdlib>
#include <exception>
#include "streaming/onevpl/accelerators/accel_policy_cpu.hpp"
#include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp"
#include "streaming/onevpl/accelerators/surface/surface.hpp"
#include "logger.hpp"
#ifdef _WIN32
#include <windows.h>
#include <sysinfoapi.h>
#endif
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
VPLCPUAccelerationPolicy::VPLCPUAccelerationPolicy() {
GAPI_LOG_INFO(nullptr, "created");
}
VPLCPUAccelerationPolicy::~VPLCPUAccelerationPolicy() {
for (auto& pair : pool_table) {
pair.second.clear();
// do not free key here: last surface will release it
}
GAPI_LOG_INFO(nullptr, "destroyed");
}
void VPLCPUAccelerationPolicy::init(session_t session) {
GAPI_LOG_INFO(nullptr, "initialize session: " << session);
}
void VPLCPUAccelerationPolicy::deinit(session_t session) {
GAPI_LOG_INFO(nullptr, "deinitialize session: " << session);
}
VPLCPUAccelerationPolicy::pool_key_t
VPLCPUAccelerationPolicy::create_surface_pool(size_t pool_size, size_t surface_size_bytes,
surface_ptr_ctr_t creator) {
GAPI_LOG_DEBUG(nullptr, "pool size: " << pool_size << ", surface size bytes: " << surface_size_bytes);
// create empty pool
pool_t pool;
pool.reserve(pool_size);
// allocate workplace memory area
size_t preallocated_raw_bytes = pool_size * surface_size_bytes;
size_t page_size_bytes = 4 * 1024;
void *preallocated_pool_memory_ptr = nullptr;
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
page_size_bytes = sysInfo.dwPageSize;
GAPI_LOG_DEBUG(nullptr, "page size: " << page_size_bytes << ", preallocated_raw_bytes: " << preallocated_raw_bytes);
preallocated_pool_memory_ptr = _aligned_malloc(preallocated_raw_bytes, page_size_bytes);
#else
GAPI_Assert(false && "Compatibility is not tested for systems differ than \"_WIN32\". "
"Please feel free to set it up under OPENCV contribution policy");
#endif
if (!preallocated_pool_memory_ptr) {
throw std::runtime_error("VPLCPUAccelerationPolicy::create_surface_pool - failed: not enough memory."
"Requested surface count: " + std::to_string(pool_size) +
", surface bytes: " + std::to_string(surface_size_bytes));
}
// fill pool with surfaces
std::shared_ptr<void> workspace_mem_owner (preallocated_pool_memory_ptr, [] (void *ptr){
GAPI_LOG_INFO(nullptr, "Free workspace memory: " << ptr);
#ifdef _WIN32
_aligned_free(ptr);
GAPI_LOG_INFO(nullptr, "Released workspace memory: " << ptr);
ptr = nullptr;
#else
GAPI_Assert(false && "Not implemented for systems differ than \"_WIN32\". "
"Please feel free to set it up under OPENCV contribution policy");
#endif
});
size_t i = 0;
try {
for (; i < pool_size; i++) {
size_t preallocated_mem_offset = static_cast<size_t>(i) * surface_size_bytes;
surface_ptr_t surf_ptr = creator(workspace_mem_owner,
preallocated_mem_offset,
preallocated_raw_bytes);
pool.push_back(std::move(surf_ptr));
}
} catch (const std::exception& ex) {
throw std::runtime_error(std::string("VPLCPUAccelerationPolicy::create_surface_pool - ") +
"cannot construct surface index: " + std::to_string(i) + ", error: " +
ex.what() +
"Requested surface count: " + std::to_string(pool_size) +
", surface bytes: " + std::to_string(surface_size_bytes));
}
// remember pool by key
GAPI_LOG_INFO(nullptr, "New pool allocated, key: " << preallocated_pool_memory_ptr <<
", surface count: " << pool.size() <<
", surface size bytes: " << surface_size_bytes);
if (!pool_table.emplace(preallocated_pool_memory_ptr, std::move(pool)).second) {
GAPI_LOG_WARNING(nullptr, "Cannot insert pool, table size: " + std::to_string(pool_table.size()) <<
", key: " << preallocated_pool_memory_ptr << " exists");
GAPI_Assert(false && "Cannot create pool in VPLCPUAccelerationPolicy");
}
return preallocated_pool_memory_ptr;
}
VPLCPUAccelerationPolicy::surface_weak_ptr_t VPLCPUAccelerationPolicy::get_free_surface(pool_key_t key) {
auto pool_it = pool_table.find(key);
if (pool_it == pool_table.end()) {
throw std::runtime_error("VPLCPUAccelerationPolicy::get_free_surface - "
"key is not found, table size: " +
std::to_string(pool_table.size()));
}
pool_t& requested_pool = pool_it->second;
#ifdef TEST_PERF
return requested_pool.find_free();
#else // TEST_PERF
auto it =
std::find_if(requested_pool.begin(), requested_pool.end(),
[](const surface_ptr_t& val) {
GAPI_DbgAssert(val && "Pool contains empty surface");
return !val->get_locks_count();
});
// Limitation realloc pool might be a future extension
if (it == requested_pool.end()) {
std::stringstream ss;
ss << "cannot get free surface from pool, key: " << key << ", size: " << requested_pool.size();
const std::string& str = ss.str();
GAPI_LOG_WARNING(nullptr, str);
throw std::runtime_error(std::string(__FUNCTION__) + " - " + str);
}
return *it;
#endif // TEST_PERF
}
size_t VPLCPUAccelerationPolicy::get_free_surface_count(pool_key_t key) const {
auto pool_it = pool_table.find(key);
if (pool_it == pool_table.end()) {
GAPI_LOG_WARNING(nullptr, "key is not found: " << key <<
", table size: " << pool_table.size());
return 0;
}
#ifdef TEST_PERF
return 0;
#else // TEST_PERF
const pool_t& requested_pool = pool_it->second;
size_t free_surf_count =
std::count_if(requested_pool.begin(), requested_pool.end(),
[](const surface_ptr_t& val) {
GAPI_Assert(val && "Pool contains empty surface");
return !val->get_locks_count();
});
return free_surf_count;
#endif // TEST_PERF
}
size_t VPLCPUAccelerationPolicy::get_surface_count(pool_key_t key) const {
auto pool_it = pool_table.find(key);
if (pool_it == pool_table.end()) {
GAPI_LOG_DEBUG(nullptr, "key is not found: " << key <<
", table size: " << pool_table.size());
return 0;
}
#ifdef TEST_PERF
return 0;
#else // TEST_PERF
return pool_it->second.size();
#endif // TEST_PERF
}
cv::MediaFrame::AdapterPtr VPLCPUAccelerationPolicy::create_frame_adapter(pool_key_t key,
mfxFrameSurface1* surface) {
auto pool_it = pool_table.find(key);
if (pool_it == pool_table.end()) {
std::stringstream ss;
ss << "key is not found: " << key << ", table size: " << pool_table.size();
const std::string& str = ss.str();
GAPI_LOG_WARNING(nullptr, str);
throw std::runtime_error(std::string(__FUNCTION__) + " - " + str);
}
pool_t& requested_pool = pool_it->second;
#ifdef TEST_PERF
return cv::MediaFrame::AdapterPtr{new VPLMediaFrameCPUAdapter(requested_pool.find_by_handle(surface))};
#else // TEST_PERF
auto it =
std::find_if(requested_pool.begin(), requested_pool.end(),
[surface](const surface_ptr_t& val) {
GAPI_DbgAssert(val && "Pool contains empty surface");
return val->get_handle() == surface;
});
// Limitation realloc pool might be a future extension
if (it == requested_pool.end()) {
std::stringstream ss;
ss << "cannot get requested surface from pool, key: " << key << ", surf: "
<< surface << ", pool size: " << requested_pool.size();
const std::string& str = ss.str();
GAPI_LOG_WARNING(nullptr, str);
throw std::runtime_error(std::string(__FUNCTION__) + " - " + str);
}
return cv::MediaFrame::AdapterPtr{new VPLMediaFrameCPUAdapter(*it)};
#endif // TEST_PERF
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
@@ -0,0 +1,57 @@
// 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_ACCELERATORS_ACCEL_POLICY_CPU_HPP
#define GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_CPU_HPP
#include <map>
#include <vector>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/accelerators/accel_policy_interface.hpp"
#ifdef TEST_PERF
#include "streaming/onevpl/accelerators/surface/surface_pool.hpp"
#endif // TEST_PERF
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
// GAPI_EXPORTS for tests
struct GAPI_EXPORTS VPLCPUAccelerationPolicy final : public VPLAccelerationPolicy
{
VPLCPUAccelerationPolicy();
~VPLCPUAccelerationPolicy();
#ifdef TEST_PERF
using pool_t = CachedPool;
#else // TEST_PERF
using pool_t = std::vector<surface_ptr_t>;
#endif // TEST_PERF
void init(session_t session) override;
void deinit(session_t session) override;
pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator) override;
surface_weak_ptr_t get_free_surface(pool_key_t key) override;
size_t get_free_surface_count(pool_key_t key) const override;
size_t get_surface_count(pool_key_t key) const override;
cv::MediaFrame::AdapterPtr create_frame_adapter(pool_key_t key,
mfxFrameSurface1* surface) override;
private:
std::map<pool_key_t, pool_t> pool_table;
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_CPU_HPP
@@ -0,0 +1,119 @@
// 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 "streaming/onevpl/accelerators/accel_policy_dx11.hpp"
#include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp"
#include "streaming/onevpl/accelerators/surface/surface.hpp"
#include "streaming/onevpl/utils.hpp"
#include "logger.hpp"
#ifdef HAVE_DIRECTX
#ifdef HAVE_D3D11
#pragma comment(lib,"d3d11.lib")
#define D3D11_NO_HELPERS
#include <d3d11.h>
#include <d3d11_4.h>
#include <codecvt>
#include "opencv2/core/directx.hpp"
#ifdef HAVE_OPENCL
#include <CL/cl_d3d11.h>
#endif
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
VPLDX11AccelerationPolicy::VPLDX11AccelerationPolicy()
{
#ifdef CPU_ACCEL_ADAPTER
adapter.reset(new VPLCPUAccelerationPolicy);
#endif
}
VPLDX11AccelerationPolicy::~VPLDX11AccelerationPolicy()
{
if (hw_handle)
{
GAPI_LOG_INFO(nullptr, "VPLDX11AccelerationPolicy release ID3D11Device");
hw_handle->Release();
}
}
void VPLDX11AccelerationPolicy::init(session_t session) {
mfxStatus sts = MFXVideoCORE_GetHandle(session, MFX_HANDLE_D3D11_DEVICE, reinterpret_cast<mfxHDL*>(&hw_handle));
if (sts != MFX_ERR_NONE)
{
throw std::logic_error("Cannot create VPLDX11AccelerationPolicy, MFXVideoCORE_GetHandle error: " +
mfxstatus_to_string(sts));
}
GAPI_LOG_INFO(nullptr, "VPLDX11AccelerationPolicy initialized, session: " << session);
}
void VPLDX11AccelerationPolicy::deinit(session_t session) {
GAPI_LOG_INFO(nullptr, "deinitialize session: " << session);
}
VPLDX11AccelerationPolicy::pool_key_t
VPLDX11AccelerationPolicy::create_surface_pool(size_t pool_size, size_t surface_size_bytes,
surface_ptr_ctr_t creator) {
GAPI_LOG_DEBUG(nullptr, "pool size: " << pool_size << ", surface size bytes: " << surface_size_bytes);
#ifdef CPU_ACCEL_ADAPTER
return adapter->create_surface_pool(pool_size, surface_size_bytes, creator);
#endif
(void)pool_size;
(void)surface_size_bytes;
(void)creator;
throw std::runtime_error("VPLDX11AccelerationPolicy::create_surface_pool() is not implemented");
}
VPLDX11AccelerationPolicy::surface_weak_ptr_t VPLDX11AccelerationPolicy::get_free_surface(pool_key_t key)
{
#ifdef CPU_ACCEL_ADAPTER
return adapter->get_free_surface(key);
#endif
(void)key;
throw std::runtime_error("VPLDX11AccelerationPolicy::get_free_surface() is not implemented");
}
size_t VPLDX11AccelerationPolicy::get_free_surface_count(pool_key_t key) const {
#ifdef CPU_ACCEL_ADAPTER
return adapter->get_free_surface_count(key);
#endif
(void)key;
throw std::runtime_error("get_free_surface_count() is not implemented");
}
size_t VPLDX11AccelerationPolicy::get_surface_count(pool_key_t key) const {
#ifdef CPU_ACCEL_ADAPTER
return adapter->get_surface_count(key);
#endif
(void)key;
throw std::runtime_error("VPLDX11AccelerationPolicy::get_surface_count() is not implemented");
}
cv::MediaFrame::AdapterPtr VPLDX11AccelerationPolicy::create_frame_adapter(pool_key_t key,
mfxFrameSurface1* surface) {
#ifdef CPU_ACCEL_ADAPTER
return adapter->create_frame_adapter(key, surface);
#endif
(void)key;
(void)surface;
throw std::runtime_error("VPLDX11AccelerationPolicy::create_frame_adapter() is not implemented");
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_D3D11
#endif // HAVE_DIRECTX
#endif // HAVE_ONEVPL
@@ -0,0 +1,69 @@
// 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_ACCELERATORS_ACCEL_POLICY_DX11_HPP
#define GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_DX11_HPP
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
//TODO Remove the next MACRO right after DX11 implementation
#define CPU_ACCEL_ADAPTER
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
#include "streaming/onevpl/accelerators/accel_policy_interface.hpp"
#ifdef CPU_ACCEL_ADAPTER
#include "streaming/onevpl/accelerators/accel_policy_cpu.hpp"
#endif
#ifdef HAVE_DIRECTX
#ifdef HAVE_D3D11
#define D3D11_NO_HELPERS
#include <d3d11.h>
#include <codecvt>
#include "opencv2/core/directx.hpp"
#ifdef HAVE_OPENCL
#include <CL/cl_d3d11.h>
#endif
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
// GAPI_EXPORTS for tests
struct GAPI_EXPORTS VPLDX11AccelerationPolicy final: public VPLAccelerationPolicy
{
// GAPI_EXPORTS for tests
VPLDX11AccelerationPolicy();
~VPLDX11AccelerationPolicy();
void init(session_t session) override;
void deinit(session_t session) override;
pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator) override;
surface_weak_ptr_t get_free_surface(pool_key_t key) override;
size_t get_free_surface_count(pool_key_t key) const override;
size_t get_surface_count(pool_key_t key) const override;
cv::MediaFrame::AdapterPtr create_frame_adapter(pool_key_t key,
mfxFrameSurface1* surface) override;
private:
ID3D11Device *hw_handle;
#ifdef CPU_ACCEL_ADAPTER
std::unique_ptr<VPLCPUAccelerationPolicy> adapter;
#endif
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_D3D11
#endif // HAVE_DIRECTX
#endif // HAVE_ONEVPL
#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_DX11_HPP
@@ -0,0 +1,61 @@
// 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_ACCELERATORS_ACCEL_POLICY_INTERFACE_HPP
#define GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_INTERFACE_HPP
#include <functional>
#include <memory>
#include <type_traits>
#include <opencv2/gapi/media.hpp>
#ifdef HAVE_ONEVPL
#include <vpl/mfxvideo.h>
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
class Surface;
struct VPLAccelerationPolicy
{
virtual ~VPLAccelerationPolicy() {}
using pool_key_t = void*;
using session_t = mfxSession;
using surface_t = Surface;
using surface_ptr_t = std::shared_ptr<surface_t>;
using surface_weak_ptr_t = std::weak_ptr<surface_t>;
using surface_ptr_ctr_t = std::function<surface_ptr_t(std::shared_ptr<void> out_buf_ptr,
size_t out_buf_ptr_offset,
size_t out_buf_ptr_size)>;
virtual void init(session_t session) = 0;
virtual void deinit(session_t session) = 0;
// Limitation: cannot give guarantee in succesful memory realloccation
// for existing workspace in existing pool (see realloc)
// thus it is not implemented,
// PLEASE provide initial memory area large enough
virtual pool_key_t create_surface_pool(size_t pool_size, size_t surface_size_bytes, surface_ptr_ctr_t creator) = 0;
virtual surface_weak_ptr_t get_free_surface(pool_key_t key) = 0;
virtual size_t get_free_surface_count(pool_key_t key) const = 0;
virtual size_t get_surface_count(pool_key_t key) const = 0;
virtual cv::MediaFrame::AdapterPtr create_frame_adapter(pool_key_t key,
mfxFrameSurface1* surface) = 0;
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_ACCEL_POLICY_INTERFACE_HPP
@@ -0,0 +1,119 @@
// 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
#include "streaming/onevpl/accelerators/surface/cpu_frame_adapter.hpp"
#include "streaming/onevpl/accelerators/surface/surface.hpp"
#include "logger.hpp"
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif
#include <vpl/mfx.h>
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
VPLMediaFrameCPUAdapter::VPLMediaFrameCPUAdapter(std::shared_ptr<Surface> surface):
parent_surface_ptr(surface) {
GAPI_Assert(parent_surface_ptr && "Surface is nullptr");
parent_surface_ptr->obtain_lock();
GAPI_LOG_DEBUG(nullptr, "surface: " << parent_surface_ptr->get_handle() <<
", w: " << parent_surface_ptr->get_info().Width <<
", h: " << parent_surface_ptr->get_info().Height <<
", p: " << parent_surface_ptr->get_data().Pitch);
}
VPLMediaFrameCPUAdapter::~VPLMediaFrameCPUAdapter() {
// Each VPLMediaFrameCPUAdapter releases mfx surface counter
// The last VPLMediaFrameCPUAdapter releases shared Surface pointer
// The last surface pointer releases workspace memory
parent_surface_ptr->release_lock();
}
cv::GFrameDesc VPLMediaFrameCPUAdapter::meta() const {
GFrameDesc desc;
const Surface::info_t& info = parent_surface_ptr->get_info();
switch(info.FourCC)
{
case MFX_FOURCC_I420:
throw std::runtime_error("MediaFrame doesn't support I420 type");
break;
case MFX_FOURCC_NV12:
desc.fmt = MediaFormat::NV12;
break;
default:
throw std::runtime_error("MediaFrame unknown 'fmt' type: " + std::to_string(info.FourCC));
}
desc.size = cv::Size{info.Width, info.Height};
return desc;
}
MediaFrame::View VPLMediaFrameCPUAdapter::access(MediaFrame::Access) {
const Surface::data_t& data = parent_surface_ptr->get_data();
const Surface::info_t& info = parent_surface_ptr->get_info();
using stride_t = typename cv::MediaFrame::View::Strides::value_type;
stride_t pitch = static_cast<stride_t>(data.Pitch);
switch(info.FourCC) {
case MFX_FOURCC_I420:
{
cv::MediaFrame::View::Ptrs pp = {
data.Y,
data.U,
data.V,
nullptr
};
cv::MediaFrame::View::Strides ss = {
pitch,
pitch / 2,
pitch / 2, 0u
};
return cv::MediaFrame::View(std::move(pp), std::move(ss));
}
case MFX_FOURCC_NV12:
{
cv::MediaFrame::View::Ptrs pp = {
data.Y,
data.UV, nullptr, nullptr
};
cv::MediaFrame::View::Strides ss = {
pitch,
pitch, 0u, 0u
};
return cv::MediaFrame::View(std::move(pp), std::move(ss));
}
break;
default:
throw std::runtime_error("MediaFrame unknown 'fmt' type: " + std::to_string(info.FourCC));
}
}
cv::util::any VPLMediaFrameCPUAdapter::blobParams() const {
GAPI_Assert("VPLMediaFrameCPUAdapter::blobParams() is not implemented");
return {};
}
void VPLMediaFrameCPUAdapter::serialize(cv::gapi::s11n::IOStream&) {
GAPI_Assert("VPLMediaFrameCPUAdapter::serialize() is not implemented");
}
void VPLMediaFrameCPUAdapter::deserialize(cv::gapi::s11n::IIStream&) {
GAPI_Assert("VPLMediaFrameCPUAdapter::deserialize() is not implemented");
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
@@ -0,0 +1,43 @@
// 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_ACCELERATORS_SURFACE_CPU_FRAME_ADAPTER_HPP
#define GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_CPU_FRAME_ADAPTER_HPP
#include <memory>
#include <opencv2/gapi/media.hpp>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#ifdef HAVE_ONEVPL
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
class Surface;
class VPLMediaFrameCPUAdapter : public cv::MediaFrame::IAdapter {
public:
// GAPI_EXPORTS for tests
GAPI_EXPORTS explicit VPLMediaFrameCPUAdapter(std::shared_ptr<Surface> assoc_surface);
GAPI_EXPORTS ~VPLMediaFrameCPUAdapter();
cv::GFrameDesc meta() const override;
MediaFrame::View access(MediaFrame::Access) override;
// The default implementation does nothing
cv::util::any blobParams() const override;
void serialize(cv::gapi::s11n::IOStream&) override;
void deserialize(cv::gapi::s11n::IIStream&) override;
private:
std::shared_ptr<Surface> parent_surface_ptr;
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_CPU_FRAME_ADAPTER_HPP
@@ -0,0 +1,78 @@
// 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
#include "streaming/onevpl/accelerators/surface/surface.hpp"
#include "logger.hpp"
#ifdef HAVE_ONEVPL
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
Surface::Surface(std::unique_ptr<handle_t>&& surf, std::shared_ptr<void> associated_memory) :
workspace_memory_ptr(associated_memory),
mfx_surface(std::move(surf)),
mirrored_locked_count() {
GAPI_Assert(mfx_surface && "Surface is nullptr");
mirrored_locked_count.store(mfx_surface->Data.Locked);
GAPI_LOG_DEBUG(nullptr, "create surface: " << mfx_surface <<
", locked count: " << mfx_surface->Data.Locked);
}
Surface::~Surface() {
GAPI_LOG_DEBUG(nullptr, "destroy surface: " << mfx_surface <<
", worspace memory counter: " << workspace_memory_ptr.use_count());
}
std::shared_ptr<Surface> Surface::create_surface(std::unique_ptr<handle_t>&& surf,
std::shared_ptr<void> accociated_memory) {
surface_ptr_t ret {new Surface(std::move(surf), accociated_memory)};
return ret;
}
Surface::handle_t* Surface::get_handle() const {
return mfx_surface.get();
}
const Surface::info_t& Surface::get_info() const {
return mfx_surface->Info;
}
const Surface::data_t& Surface::get_data() const {
return mfx_surface->Data;
}
size_t Surface::get_locks_count() const {
return mirrored_locked_count.load();
}
size_t Surface::obtain_lock() {
size_t locked_count = mirrored_locked_count.fetch_add(1);
GAPI_Assert(locked_count < std::numeric_limits<mfxU16>::max() && "Too many references ");
mfx_surface->Data.Locked = static_cast<mfxU16>(locked_count + 1);
GAPI_LOG_DEBUG(nullptr, "surface: " << mfx_surface.get() <<
", locked times: " << locked_count + 1);
return locked_count; // return preceding value
}
size_t Surface::release_lock() {
size_t locked_count = mirrored_locked_count.fetch_sub(1);
GAPI_Assert(locked_count < std::numeric_limits<mfxU16>::max() && "Too many references ");
GAPI_Assert(locked_count && "Surface lock counter is invalid");
mfx_surface->Data.Locked = static_cast<mfxU16>(locked_count - 1);
GAPI_LOG_DEBUG(nullptr, "surface: " << mfx_surface.get() <<
", locked times: " << locked_count - 1);
return locked_count; // return preceding value
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
@@ -0,0 +1,104 @@
// 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_ACCELERATORS_SURFACE_HPP
#define GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_HPP
#include <atomic>
#include <memory>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif
#include <vpl/mfx.h>
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
/**
* @brief Inner class for managing oneVPL surface through interface `mfxFrameSurface1`.
*
* Surface has no own memory and shares accelerator allocated memory using reference counter semantics.
* So it lives till a last memory consumer (surface/accelerator/media frame) lives.
* This approach allows to support different scenarious in releasing allocated memory
*
* VPL surface `mfxFrameSurface1` support Lock-Free semantics and application MUST NOT operate with a
* surface in locked state. But VPL inner counter is not threadsafe so it would be failed in any concurrent scenario.
* std::atomic counter introduced in a way to overcome this problem.
* But only few scenarious for concurrency are supported here because it is not assumed to implement entire Surface in
* for a fully multithread approach.
* Supported concurrency scenarios deal only with transaction pair: @ref Surface::get_locks_count() against
* @ref Surface::release_lock() - which may be called from different threads. On the other hand @ref Surface::get_locks_count() against
* @ref Surface::obtain_lock() happens in single thread only. Surface doesn't support shared ownership that
* because it doesn't require thread safe guarantee between transactions:
* - @ref Surface::obtain_lock() against @ref Surface::obtain_lock()
* - @ref Surface::obtain_lock() against @ref Surface::release_lock()
* - @ref Surface::release_lock() against @ref Surface::release_lock()
*/
class Surface {
using handle_t = mfxFrameSurface1;
std::shared_ptr<void> workspace_memory_ptr;
std::unique_ptr<handle_t> mfx_surface;
std::atomic<size_t> mirrored_locked_count;
public:
using info_t = mfxFrameInfo;
using data_t = mfxFrameData;
// GAPI_EXPORTS for tests
GAPI_EXPORTS static std::shared_ptr<Surface> create_surface(std::unique_ptr<handle_t>&& surf,
std::shared_ptr<void> accociated_memory);
GAPI_EXPORTS ~Surface();
GAPI_EXPORTS handle_t* get_handle() const;
GAPI_EXPORTS const info_t& get_info() const;
GAPI_EXPORTS const data_t& get_data() const;
/**
* Extract value thread-safe lock counter (see @ref Surface description).
* It's usual situation that counter may be instantly decreased in other thread after this method called.
* We need instantaneous value. This method syncronized in inter-threading way with @ref Surface::release_lock()
*
* @return fetched locks count.
*/
GAPI_EXPORTS size_t get_locks_count() const;
/**
* Atomically increase value of thread-safe lock counter (see @ref Surface description).
* This method is single-threaded happens-after @ref Surface::get_locks_count() and
* multi-threaded happens-before @ref Surface::release_lock()
*
* @return locks count just before its increasing.
*/
GAPI_EXPORTS size_t obtain_lock();
/**
* Atomically decrease value of thread-safe lock counter (see @ref Surface description).
* This method is synchronized with @ref Surface::get_locks_count() and
* multi-threaded happens-after @ref Surface::obtain_lock()
*
* @return locks count just before its decreasing.
*/
GAPI_EXPORTS size_t release_lock();
private:
Surface(std::unique_ptr<handle_t>&& surf, std::shared_ptr<void> accociated_memory);
};
using surface_ptr_t = std::shared_ptr<Surface>;
using surface_weak_ptr_t = std::weak_ptr<Surface>;
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
#endif // GAPI_STREAMING_ONEVPL_ACCELERATORS_SURFACE_HPP
@@ -0,0 +1,71 @@
#include "streaming/onevpl/accelerators/surface/surface_pool.hpp"
#include "streaming/onevpl/accelerators/surface/surface.hpp"
#include "logger.hpp"
#ifdef HAVE_ONEVPL
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
void CachedPool::reserve(size_t size) {
surfaces.reserve(size);
}
size_t CachedPool::size() const {
return surfaces.size();
}
void CachedPool::clear() {
surfaces.clear();
next_free_it = surfaces.begin();
cache.clear();
}
void CachedPool::push_back(surface_ptr_t &&surf) {
cache.insert(std::make_pair(surf->get_handle(), surf));
surfaces.push_back(std::move(surf));
next_free_it = surfaces.begin();
}
CachedPool::surface_ptr_t CachedPool::find_free() {
auto it =
std::find_if(next_free_it, surfaces.end(),
[](const surface_ptr_t& val) {
GAPI_DbgAssert(val && "Pool contains empty surface");
return !val->get_locks_count();
});
// Limitation realloc pool might be a future extension
if (it == surfaces.end()) {
it = std::find_if(surfaces.begin(), next_free_it,
[](const surface_ptr_t& val) {
GAPI_DbgAssert(val && "Pool contains empty surface");
return !val->get_locks_count();
});
if (it == next_free_it) {
std::stringstream ss;
ss << "cannot get free surface from pool, size: " << surfaces.size();
const std::string& str = ss.str();
GAPI_LOG_WARNING(nullptr, str);
throw std::runtime_error(std::string(__FUNCTION__) + " - " + str);
}
}
next_free_it = it;
++next_free_it;
return *it;
}
CachedPool::surface_ptr_t CachedPool::find_by_handle(mfxFrameSurface1* handle) {
auto it = cache.find(handle);
GAPI_Assert(it != cache.end() && "Cannot find cached surface from pool. Data corruption is possible");
return it->second;
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
@@ -0,0 +1,47 @@
#ifndef GAPI_STREAMING_ONEVPL_SURFACE_SURFACE_POOL_HPP
#define GAPI_STREAMING_ONEVPL_SURFACE_SURFACE_POOL_HPP
#include <map>
#include <memory>
#include <vector>
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif
#include <vpl/mfx.h>
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
class Surface;
// GAPI_EXPORTS for tests
class GAPI_EXPORTS CachedPool {
public:
using surface_ptr_t = std::shared_ptr<Surface>;
using surface_container_t = std::vector<surface_ptr_t>;
using free_surface_iterator_t = typename surface_container_t::iterator;
using cached_surface_container_t = std::map<mfxFrameSurface1*, surface_ptr_t>;
void push_back(surface_ptr_t &&surf);
void reserve(size_t size);
size_t size() const;
void clear();
surface_ptr_t find_free();
surface_ptr_t find_by_handle(mfxFrameSurface1* handle);
private:
surface_container_t surfaces;
free_surface_iterator_t next_free_it;
cached_surface_container_t cache;
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
#endif // GAPI_STREAMING_ONEVPL_SURFACE_SURFACE_POOL_HPP
@@ -0,0 +1,137 @@
// 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
#include <opencv2/gapi/util/throw.hpp>
#include <opencv2/gapi/streaming/onevpl/cfg_params.hpp>
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
namespace util {
struct variant_comparator : cv::util::static_visitor<bool, variant_comparator> {
variant_comparator(const CfgParam::value_t& rhs_value) :
rhs(rhs_value) {}
template<typename ValueType>
bool visit(const ValueType& lhs) const {
return lhs < cv::util::get<ValueType>(rhs);
}
private:
const CfgParam::value_t& rhs;
};
} // namespace util
struct CfgParam::Priv {
Priv(const std::string& param_name, CfgParam::value_t&& param_value, bool is_major_param) :
name(param_name), value(std::forward<value_t>(param_value)), major_flag(is_major_param) {
}
const CfgParam::name_t& get_name_impl() const {
return name;
}
const CfgParam::value_t& get_value_impl() const {
return value;
}
bool is_major_impl() const {
return major_flag;
}
// comparison implementation
bool operator< (const Priv& rhs) const {
// implement default pair comparison
if (get_name_impl() < rhs.get_name_impl()) {
return true;
} else if (get_name_impl() > rhs.get_name_impl()) {
return false;
}
//TODO implement operator < for cv::util::variant
const CfgParam::value_t& lvar = get_value_impl();
const CfgParam::value_t& rvar = rhs.get_value_impl();
if (lvar.index() < rvar.index()) {
return true;
} else if (lvar.index() > rvar.index()) {
return false;
}
util::variant_comparator comp(rvar);
return cv::util::visit(comp, lvar);
}
bool operator==(const Priv& rhs) const {
return (get_name_impl() == rhs.get_name_impl())
&& (get_value_impl() == rhs.get_value_impl());
}
bool operator!=(const Priv& rhs) const {
return !(*this == rhs);
}
CfgParam::name_t name;
CfgParam::value_t value;
bool major_flag;
};
CfgParam::CfgParam (const std::string& param_name, value_t&& param_value, bool is_major_param) :
m_priv(new Priv(param_name, std::move(param_value), is_major_param)) {
}
CfgParam::~CfgParam() = default;
CfgParam& CfgParam::operator=(const CfgParam& src) {
if (this != &src) {
m_priv = src.m_priv;
}
return *this;
}
CfgParam& CfgParam::operator=(CfgParam&& src) {
if (this != &src) {
m_priv = std::move(src.m_priv);
}
return *this;
}
CfgParam::CfgParam(const CfgParam& src) :
m_priv(src.m_priv) {
}
CfgParam::CfgParam(CfgParam&& src) :
m_priv(std::move(src.m_priv)) {
}
const CfgParam::name_t& CfgParam::get_name() const {
return m_priv->get_name_impl();
}
const CfgParam::value_t& CfgParam::get_value() const {
return m_priv->get_value_impl();
}
bool CfgParam::is_major() const {
return m_priv->is_major_impl();
}
bool CfgParam::operator< (const CfgParam& rhs) const {
return *m_priv < *rhs.m_priv;
}
bool CfgParam::operator==(const CfgParam& rhs) const {
return *m_priv == *rhs.m_priv;
}
bool CfgParam::operator!=(const CfgParam& rhs) const {
return *m_priv != *rhs.m_priv;
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
@@ -0,0 +1,22 @@
#include <errno.h>
#include <string.h>
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp>
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
DataProviderSystemErrorException::DataProviderSystemErrorException(int error_code, const std::string& desription) {
reason = desription + ", error: " + std::to_string(error_code) + ", desctiption: " + strerror(error_code);
}
DataProviderSystemErrorException::~DataProviderSystemErrorException() = default;
const char* DataProviderSystemErrorException::what() const noexcept {
return reason.c_str();
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
@@ -0,0 +1,310 @@
// 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 <algorithm>
#include <exception>
#include <opencv2/gapi/streaming/onevpl/data_provider_interface.hpp>
#include "streaming/onevpl/engine/decode/decode_engine_legacy.hpp"
#include "streaming/onevpl/engine/decode/decode_session.hpp"
#include "streaming/onevpl/accelerators/accel_policy_interface.hpp"
#include "streaming/onevpl/accelerators/surface/surface.hpp"
#include "streaming/onevpl/utils.hpp"
#include "logger.hpp"
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
/* UTILS */
mfxU32 GetSurfaceSize(mfxU32 FourCC, mfxU32 width, mfxU32 height) {
mfxU32 nbytes = 0;
mfxU32 half_width = width / 2;
mfxU32 half_height = height / 2;
switch (FourCC) {
case MFX_FOURCC_I420:
case MFX_FOURCC_NV12:
nbytes = width * height + 2 * half_width * half_height;
break;
case MFX_FOURCC_I010:
case MFX_FOURCC_P010:
nbytes = width * height + 2 * half_width * half_height;
nbytes *= 2;
break;
case MFX_FOURCC_RGB4:
nbytes = width * height * 4;
break;
default:
GAPI_LOG_WARNING(nullptr, "Unsupported FourCC requested: " << FourCC);
GAPI_Assert(false && "Unsupported FourCC requested");
break;
}
return nbytes;
}
surface_ptr_t create_surface_RGB4(mfxFrameInfo frameInfo,
std::shared_ptr<void> out_buf_ptr,
size_t out_buf_ptr_offset,
size_t out_buf_size)
{
mfxU8* buf = reinterpret_cast<mfxU8*>(out_buf_ptr.get());
mfxU16 surfW = frameInfo.Width * 4;
mfxU16 surfH = frameInfo.Height;
(void)surfH;
// TODO more intelligent check
if (out_buf_size <= out_buf_ptr_offset) {
throw std::runtime_error(std::string("Insufficient buffer size: ") +
std::to_string(out_buf_size) + ", buffer offset: " +
std::to_string(out_buf_ptr_offset) +
", expected surface width: " + std::to_string(surfW) +
", height: " + std::to_string(surfH));
}
std::unique_ptr<mfxFrameSurface1> handle(new mfxFrameSurface1);
memset(handle.get(), 0, sizeof(mfxFrameSurface1));
handle->Info = frameInfo;
handle->Data.B = buf + out_buf_ptr_offset;
handle->Data.G = handle->Data.B + 1;
handle->Data.R = handle->Data.B + 2;
handle->Data.A = handle->Data.B + 3;
handle->Data.Pitch = surfW;
return Surface::create_surface(std::move(handle), out_buf_ptr);
}
surface_ptr_t create_surface_other(mfxFrameInfo frameInfo,
std::shared_ptr<void> out_buf_ptr,
size_t out_buf_ptr_offset,
size_t out_buf_size)
{
mfxU8* buf = reinterpret_cast<mfxU8*>(out_buf_ptr.get());
mfxU16 surfH = frameInfo.Height;
mfxU16 surfW = (frameInfo.FourCC == MFX_FOURCC_P010) ? frameInfo.Width * 2 : frameInfo.Width;
// TODO more intelligent check
if (out_buf_size <=
out_buf_ptr_offset + (surfW * surfH) + ((surfW / 2) * (surfH / 2))) {
throw std::runtime_error(std::string("Insufficient buffer size: ") +
std::to_string(out_buf_size) + ", buffer offset: " +
std::to_string(out_buf_ptr_offset) +
", expected surface width: " + std::to_string(surfW) +
", height: " + std::to_string(surfH));
}
std::unique_ptr<mfxFrameSurface1> handle(new mfxFrameSurface1);
memset(handle.get(), 0, sizeof(mfxFrameSurface1));
handle->Info = frameInfo;
handle->Data.Y = buf + out_buf_ptr_offset;
handle->Data.U = buf + out_buf_ptr_offset + (surfW * surfH);
handle->Data.V = handle->Data.U + ((surfW / 2) * (surfH / 2));
handle->Data.Pitch = surfW;
return Surface::create_surface(std::move(handle), out_buf_ptr);
}
VPLLegacyDecodeEngine::VPLLegacyDecodeEngine(std::unique_ptr<VPLAccelerationPolicy>&& accel)
: ProcessingEngineBase(std::move(accel)) {
GAPI_LOG_INFO(nullptr, "Create Legacy Decode Engine");
create_pipeline(
// 1) Read File
[this] (EngineSession& sess) -> ExecutionStatus
{
LegacyDecodeSession &my_sess = static_cast<LegacyDecodeSession&>(sess);
my_sess.last_status = ReadEncodedStream(my_sess.stream, my_sess.data_provider);
if (my_sess.last_status != MFX_ERR_NONE) {
my_sess.data_provider.reset(); //close source
}
return ExecutionStatus::Continue;
},
// 2) enqueue ASYNC decode
[this] (EngineSession& sess) -> ExecutionStatus
{
LegacyDecodeSession &my_sess = static_cast<LegacyDecodeSession&>(sess);
my_sess.last_status =
MFXVideoDECODE_DecodeFrameAsync(my_sess.session,
my_sess.last_status == MFX_ERR_NONE
? &my_sess.stream
: nullptr, /* No more data to read, start decode draining mode*/
my_sess.procesing_surface_ptr.lock()->get_handle(),
&my_sess.output_surface_ptr,
&my_sess.sync);
return ExecutionStatus::Continue;
},
// 3) Wait for ASYNC decode result
[this] (EngineSession& sess) -> ExecutionStatus
{
if (sess.last_status == MFX_ERR_NONE) // Got 1 decoded frame
{
do {
//TODO try to extract TIMESTAMP
sess.last_status = MFXVideoCORE_SyncOperation(sess.session, sess.sync, 100);
if (MFX_ERR_NONE == sess.last_status) {
LegacyDecodeSession& my_sess = static_cast<LegacyDecodeSession&>(sess);
on_frame_ready(my_sess);
}
} while (sess.last_status == MFX_WRN_IN_EXECUTION);
}
return ExecutionStatus::Continue;
},
// 4) Falls back on generic status procesing
[this] (EngineSession& sess) -> ExecutionStatus
{
return this->process_error(sess.last_status, static_cast<LegacyDecodeSession&>(sess));
}
);
}
void VPLLegacyDecodeEngine::initialize_session(mfxSession mfx_session,
DecoderParams&& decoder_param,
std::shared_ptr<onevpl::IDataProvider> provider)
{
mfxFrameAllocRequest decRequest = {};
// Query number required surfaces for decoder
MFXVideoDECODE_QueryIOSurf(mfx_session, &decoder_param.param, &decRequest);
// External (application) allocation of decode surfaces
GAPI_LOG_DEBUG(nullptr, "Query IOSurf for session: " << mfx_session <<
", mfxFrameAllocRequest.NumFrameSuggested: " << decRequest.NumFrameSuggested <<
", mfxFrameAllocRequest.Type: " << decRequest.Type);
mfxU32 singleSurfaceSize = GetSurfaceSize(decoder_param.param.mfx.FrameInfo.FourCC,
decoder_param.param.mfx.FrameInfo.Width,
decoder_param.param.mfx.FrameInfo.Height);
if (!singleSurfaceSize) {
throw std::runtime_error("Cannot determine surface size for: fourCC" +
std::to_string(decoder_param.param.mfx.FrameInfo.FourCC) +
", width: " + std::to_string(decoder_param.param.mfx.FrameInfo.Width) +
", height: " + std::to_string(decoder_param.param.mfx.FrameInfo.Height));
}
const auto &frameInfo = decoder_param.param.mfx.FrameInfo;
auto surface_creator =
[&frameInfo] (std::shared_ptr<void> out_buf_ptr, size_t out_buf_ptr_offset,
size_t out_buf_size) -> surface_ptr_t {
return (frameInfo.FourCC == MFX_FOURCC_RGB4) ?
create_surface_RGB4(frameInfo, out_buf_ptr, out_buf_ptr_offset,
out_buf_size) :
create_surface_other(frameInfo, out_buf_ptr, out_buf_ptr_offset,
out_buf_size);};
//TODO Configure preallocation size (how many frames we can hold)
const size_t preallocated_frames_count = 30;
VPLAccelerationPolicy::pool_key_t decode_pool_key =
acceleration_policy->create_surface_pool(decRequest.NumFrameSuggested * preallocated_frames_count,
singleSurfaceSize,
surface_creator);
// create session
std::shared_ptr<LegacyDecodeSession> sess_ptr =
register_session<LegacyDecodeSession>(mfx_session,
std::move(decoder_param),
provider);
sess_ptr->init_surface_pool(decode_pool_key);
// prepare working decode surface
sess_ptr->swap_surface(*this);
}
ProcessingEngineBase::ExecutionStatus VPLLegacyDecodeEngine::execute_op(operation_t& op, EngineSession& sess) {
return op(sess);
}
void VPLLegacyDecodeEngine::on_frame_ready(LegacyDecodeSession& sess)
{
GAPI_LOG_DEBUG(nullptr, "[" << sess.session << "], frame ready");
// manage memory ownership rely on acceleration policy
auto frame_adapter = acceleration_policy->create_frame_adapter(sess.decoder_pool_id,
sess.output_surface_ptr);
ready_frames.emplace(cv::MediaFrame(std::move(frame_adapter)), sess.generate_frame_meta());
}
ProcessingEngineBase::ExecutionStatus VPLLegacyDecodeEngine::process_error(mfxStatus status, LegacyDecodeSession& sess)
{
GAPI_LOG_DEBUG(nullptr, "status: " << mfxstatus_to_string(status));
switch (status) {
case MFX_ERR_NONE:
return ExecutionStatus::Continue;
case MFX_ERR_MORE_DATA: // The function requires more bitstream at input before decoding can proceed
if (!sess.data_provider || sess.data_provider->empty()) {
// No more data to drain from decoder, start encode draining mode
return ExecutionStatus::Processed;
}
else
return ExecutionStatus::Continue; // read more data
break;
case MFX_ERR_MORE_SURFACE:
{
// The function requires more frame surface at output before decoding can proceed.
// This applies to external memory allocations and should not be expected for
// a simple internal allocation case like this
try {
sess.swap_surface(*this);
return ExecutionStatus::Continue;
} catch (const std::exception& ex) {
GAPI_LOG_WARNING(nullptr, "[" << sess.session << "] error: " << ex.what());
}
break;
}
case MFX_ERR_DEVICE_LOST:
// For non-CPU implementations,
// Cleanup if device is lost
GAPI_DbgAssert(false && "VPLLegacyDecodeEngine::process_error - "
"MFX_ERR_DEVICE_LOST is not processed");
break;
case MFX_WRN_DEVICE_BUSY:
// For non-CPU implementations,
// Wait a few milliseconds then try again
GAPI_DbgAssert(false && "VPLLegacyDecodeEngine::process_error - "
"MFX_WRN_DEVICE_BUSY is not processed");
break;
case MFX_WRN_VIDEO_PARAM_CHANGED:
// The decoder detected a new sequence header in the bitstream.
// Video parameters may have changed.
// In external memory allocation case, might need to reallocate the output surface
GAPI_DbgAssert(false && "VPLLegacyDecodeEngine::process_error - "
"MFX_WRN_VIDEO_PARAM_CHANGED is not processed");
break;
case MFX_ERR_INCOMPATIBLE_VIDEO_PARAM:
// The function detected that video parameters provided by the application
// are incompatible with initialization parameters.
// The application should close the component and then reinitialize it
GAPI_DbgAssert(false && "VPLLegacyDecodeEngine::process_error - "
"MFX_ERR_INCOMPATIBLE_VIDEO_PARAM is not processed");
break;
case MFX_ERR_REALLOC_SURFACE:
// Bigger surface_work required. May be returned only if
// mfxInfoMFX::EnableReallocRequest was set to ON during initialization.
// This applies to external memory allocations and should not be expected for
// a simple internal allocation case like this
GAPI_DbgAssert(false && "VPLLegacyDecodeEngine::process_error - "
"MFX_ERR_REALLOC_SURFACE is not processed");
break;
default:
GAPI_LOG_WARNING(nullptr, "Unknown status code: " << mfxstatus_to_string(status));
break;
}
return ExecutionStatus::Failed;
}
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
@@ -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 GAPI_STREAMING_ONVPL_DECODE_ENGINE_LEGACY_HPP
#define GAPI_STREAMING_ONVPL_DECODE_ENGINE_LEGACY_HPP
#include <stdio.h>
#include <memory>
#include "streaming/onevpl/engine/processing_engine_base.hpp"
#ifdef HAVE_ONEVPL
#if (MFX_VERSION >= 2000)
#include <vpl/mfxdispatcher.h>
#endif
#include <vpl/mfx.h>
namespace cv {
namespace gapi {
namespace wip {
namespace onevpl {
class LegacyDecodeSession;
struct DecoderParams;
struct IDataProvider;
struct VPLAccelerationPolicy;
class VPLLegacyDecodeEngine : public ProcessingEngineBase {
public:
VPLLegacyDecodeEngine(std::unique_ptr<VPLAccelerationPolicy>&& accel);
void initialize_session(mfxSession mfx_session, DecoderParams&& decoder_param,
std::shared_ptr<IDataProvider> provider) override;
private:
ExecutionStatus execute_op(operation_t& op, EngineSession& sess) override;
ExecutionStatus process_error(mfxStatus status, LegacyDecodeSession& sess);
void on_frame_ready(LegacyDecodeSession& sess);
};
} // namespace onevpl
} // namespace wip
} // namespace gapi
} // namespace cv
#endif // HAVE_ONEVPL
#endif // GAPI_STREAMING_ONVPL_DECODE_ENGINE_LEGACY_HPP

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