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

Compare commits

...

14 Commits

Author SHA1 Message Date
Ruslan Garnov 6860e8ed77 Added desync RMats and MediaFrames support 2021-10-12 11:54:22 +03:00
Maxim Pashchenkov 719059f671 G-API: Removing ParseSSD overload.
* Removed specialization.

* Removed united

original commit: 651967b95c
2021-10-12 11:54:22 +03:00
Anatoliy Talamanov 4eb82c2ed0 G-API: Handle reshape for generic case in GExecutor
* Handle reshape for generic case for GExecutor

* Add initResources

* Add tests

* Refactor reshape method

original commit: 499d8adb75
2021-10-12 11:54:22 +03:00
Anatoliy Talamanov 5516c2eda1 Fix GExecutor WriteBackExec 2021-10-12 11:54:22 +03:00
Anatoliy Talamanov 18ea4758e2 Check adapter in executor 2021-10-12 11:54:22 +03:00
Anatoliy Talamanov 1c2792bc60 [G-API] Extend compileStreaming to support different overloads
* Make different overloads

* Order python compileStreaming overloads

* Fix compileStreaming bug

* Replace

gin -> descr_of

* Set error message

* Fix review comments

* Use macros for pyopencv_to GMetaArgs
* Use GAPI_PROP_RW
* Not split Prims python stuff

original commit: 5ad6ff239b
2021-10-12 11:54:22 +03:00
Maxim Pashchenkov eb68476aaf G-API: Python. Desync.
* Desync. GMat.

* Alignment

original commit: 05f1939b02
2021-10-12 11:54:22 +03:00
Anatoliy Talamanov 8d0fc8bfb5 G-API: Wrap render functionality to python
* Wrap render Rect prim

* Add all primitives and tests

* Cover mosaic and image

* Handle error in pyopencv_to(Prim)

* Move Mosaic and Rect ctors wrappers to shadow file

* Use GAPI_PROP_RW

* Fix indent

original commit: 9fe49497bb
2021-10-12 11:54:22 +03:00
Anatoliy Talamanov e2f5671280 G-API: Extend python bindings
* Extend G-API bindings

* Wrap timestamp, seqNo, seq_id
* Wrap copy
* Wrap parseSSD, parseYolo

* Rewrap cv.gapi.networks

* Add test for metabackend in pytnon

* Remove int64 pyopencv_to

original commit: fb7ef76e74
2021-10-12 11:54:11 +03:00
Anatoliy Talamanov ba30403581 G-API: Support vaargs for cv.compile_args
* Support cv.compile_args to work with variadic number of inputs

* Disable python2.x G-API

* Move compile_args to gapi pkg

original commit: 53eca2ff5b
2021-10-12 11:44:19 +03:00
Anatoliy Talamanov 0a31fa19f8 [G-API] Expand PyParams to support constInput
* Wrap constInputs to python

* Wrap cfgNumRequests

* Fix alignment

* Move macro to the line above

original commit: ba539eb9aa
2021-10-12 11:43:53 +03:00
Maksim Shabunin 8cfe9546f3 Option to enable/disable plugin linking with OpenCV 2021-08-16 15:01:29 +03:00
Vincent Rabaud fd2b5411e4 Fix potential NaN in cv::norm.
There can be an int overflow.
cv::norm( InputArray _src, int normType, InputArray _mask ) is fine,
not cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask ).
2021-06-15 21:21:21 +03:00
Alexander Alekhin dd66cccbbd OpenCV version '-openvino' 2021-06-10 16:51:21 +03:00
44 changed files with 2356 additions and 1142 deletions
+11 -4
View File
@@ -78,10 +78,17 @@ function(ocv_create_plugin module default_name dependency_target dependency_targ
set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES PREFIX "${OPENCV_PLUGIN_MODULE_PREFIX}") set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES PREFIX "${OPENCV_PLUGIN_MODULE_PREFIX}")
endif() endif()
if(APPLE) if(WIN32 OR NOT APPLE)
set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") set(OPENCV_PLUGIN_NO_LINK FALSE CACHE BOOL "")
elseif(WIN32) else()
# Hack for Windows only, Linux/MacOS uses global symbol table (without exact .so binding) set(OPENCV_PLUGIN_NO_LINK TRUE CACHE BOOL "")
endif()
if(OPENCV_PLUGIN_NO_LINK)
if(APPLE)
set_target_properties(${OPENCV_PLUGIN_NAME} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
endif()
else()
find_package(OpenCV REQUIRED ${module} ${OPENCV_PLUGIN_DEPS}) find_package(OpenCV REQUIRED ${module} ${OPENCV_PLUGIN_DEPS})
target_link_libraries(${OPENCV_PLUGIN_NAME} PRIVATE ${OpenCV_LIBRARIES}) target_link_libraries(${OPENCV_PLUGIN_NAME} PRIVATE ${OpenCV_LIBRARIES})
endif() endif()
@@ -8,7 +8,7 @@
#define CV_VERSION_MAJOR 4 #define CV_VERSION_MAJOR 4
#define CV_VERSION_MINOR 5 #define CV_VERSION_MINOR 5
#define CV_VERSION_REVISION 3 #define CV_VERSION_REVISION 3
#define CV_VERSION_STATUS "-pre" #define CV_VERSION_STATUS "-openvino"
#define CVAUX_STR_EXP(__A) #__A #define CVAUX_STR_EXP(__A) #__A
#define CVAUX_STR(__A) CVAUX_STR_EXP(__A) #define CVAUX_STR(__A) CVAUX_STR_EXP(__A)
+1 -1
View File
@@ -1194,7 +1194,7 @@ double norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask
// special case to handle "integer" overflow in accumulator // special case to handle "integer" overflow in accumulator
const size_t esz = src1.elemSize(); const size_t esz = src1.elemSize();
const int total = (int)it.size; const int total = (int)it.size;
const int intSumBlockSize = normType == NORM_L1 && depth <= CV_8S ? (1 << 23) : (1 << 15); const int intSumBlockSize = (normType == NORM_L1 && depth <= CV_8S ? (1 << 23) : (1 << 15))/cn;
const int blockSize = std::min(total, intSumBlockSize); const int blockSize = std::min(total, intSumBlockSize);
int isum = 0; int isum = 0;
int count = 0; int count = 0;
+9
View File
@@ -2166,6 +2166,15 @@ TEST(Core_Norm, IPP_regression_NORM_L1_16UC3_small)
EXPECT_EQ((double)20*cn, cv::norm(a, b, NORM_L1, mask)); EXPECT_EQ((double)20*cn, cv::norm(a, b, NORM_L1, mask));
} }
TEST(Core_Norm, NORM_L2_8UC4)
{
// Tests there is no integer overflow in norm computation for multiple channels.
const int kSide = 100;
cv::Mat4b a(kSide, kSide, cv::Scalar(255, 255, 255, 255));
cv::Mat4b b = cv::Mat4b::zeros(kSide, kSide);
const double kNorm = 2.*kSide*255.;
EXPECT_EQ(kNorm, cv::norm(a, b, NORM_L2));
}
TEST(Core_ConvertTo, regression_12121) TEST(Core_ConvertTo, regression_12121)
{ {
@@ -44,6 +44,7 @@ namespace detail
CV_UNKNOWN, // Unknown, generic, opaque-to-GAPI data type unsupported in graph seriallization CV_UNKNOWN, // Unknown, generic, opaque-to-GAPI data type unsupported in graph seriallization
CV_BOOL, // bool user G-API data CV_BOOL, // bool user G-API data
CV_INT, // int 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_DOUBLE, // double user G-API data
CV_FLOAT, // float user G-API data CV_FLOAT, // float user G-API data
CV_UINT64, // uint64_t 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;
template<typename T> struct GOpaqueTraits { static constexpr const OpaqueKind kind = OpaqueKind::CV_UNKNOWN; }; 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<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<double> { static constexpr const OpaqueKind kind = OpaqueKind::CV_DOUBLE; };
template<> struct GOpaqueTraits<float> { static constexpr const OpaqueKind kind = OpaqueKind::CV_FLOAT; }; 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; }; template<> struct GOpaqueTraits<uint64_t> { static constexpr const OpaqueKind kind = OpaqueKind::CV_UINT64; };
@@ -437,11 +437,7 @@ public:
* *
* @sa @ref gapi_compile_args * @sa @ref gapi_compile_args
*/ */
GStreamingCompiled compileStreaming(GMetaArgs &&in_metas, GCompileArgs &&args = {}); GAPI_WRAP 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 = {});
/** /**
* @brief Compile the computation for streaming mode. * @brief Compile the computation for streaming mode.
@@ -464,6 +460,10 @@ public:
*/ */
GAPI_WRAP GStreamingCompiled compileStreaming(GCompileArgs &&args = {}); 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 // 2. Direct metadata version
/** /**
* @overload * @overload
@@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory // 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. // 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 #ifndef OPENCV_GAPI_GSTREAMING_COMPILED_HPP
@@ -65,12 +65,23 @@ using OptionalOpaqueRef = OptRef<cv::detail::OpaqueRef>;
using GOptRunArgP = util::variant< using GOptRunArgP = util::variant<
optional<cv::Mat>*, optional<cv::Mat>*,
optional<cv::RMat>*, optional<cv::RMat>*,
optional<cv::MediaFrame>*,
optional<cv::Scalar>*, optional<cv::Scalar>*,
cv::detail::OptionalVectorRef, cv::detail::OptionalVectorRef,
cv::detail::OptionalOpaqueRef cv::detail::OptionalOpaqueRef
>; >;
using GOptRunArgsP = std::vector<GOptRunArgP>; 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 { namespace detail {
template<typename T> inline GOptRunArgP wrap_opt_arg(optional<T>& arg) { 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}; 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) { template<> inline GOptRunArgP wrap_opt_arg(optional<cv::Scalar> &s) {
return GOptRunArgP{&s}; return GOptRunArgP{&s};
} }
@@ -196,7 +215,7 @@ public:
* @param s a shared pointer to IStreamSource representing the * @param s a shared pointer to IStreamSource representing the
* input video stream. * 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 * @brief Constructs and specifies an input video stream for a
@@ -255,7 +274,7 @@ public:
// NB: Used from python // NB: Used from python
/// @private -- Exclude this function from OpenCV documentation /// @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. * @brief Get some next available data from the pipeline.
@@ -379,9 +398,11 @@ namespace streaming {
* In the streaming mode the pipeline steps are connected with queues * In the streaming mode the pipeline steps are connected with queues
* and this compile argument controls every queue's size. * 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) { }; explicit queue_capacity(size_t cap = 1) : capacity(cap) { };
GAPI_PROP_RW
size_t capacity; size_t capacity;
}; };
/** @} */ /** @} */
+5 -2
View File
@@ -136,11 +136,12 @@ public:
} }
template <typename U> 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, m_priv->blobs.emplace(std::piecewise_construct,
std::forward_as_tuple(name), std::forward_as_tuple(name),
std::forward_as_tuple(in)); std::forward_as_tuple(in));
return *this;
} }
using StorageT = cv::util::variant<Ts...>; using StorageT = cv::util::variant<Ts...>;
@@ -653,7 +654,8 @@ namespace gapi {
// A type-erased form of network parameters. // A type-erased form of network parameters.
// Similar to how a type-erased GKernel is represented and used. // 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? std::string tag; // FIXME: const?
GBackend backend; // Specifies the execution model GBackend backend; // Specifies the execution model
util::any params; // Backend-interpreted parameter structure util::any params; // Backend-interpreted parameter structure
@@ -670,6 +672,7 @@ struct GAPI_EXPORTS GNetParam {
*/ */
struct GAPI_EXPORTS_W_SIMPLE GNetPackage { struct GAPI_EXPORTS_W_SIMPLE GNetPackage {
GAPI_WRAP GNetPackage() = default; GAPI_WRAP GNetPackage() = default;
GAPI_WRAP explicit GNetPackage(std::vector<GNetParam> nets);
explicit GNetPackage(std::initializer_list<GNetParam> ii); explicit GNetPackage(std::initializer_list<GNetParam> ii);
std::vector<GBackend> backends() const; std::vector<GBackend> backends() const;
std::vector<GNetParam> networks; std::vector<GNetParam> networks;
@@ -22,17 +22,28 @@ namespace ie {
// This class can be marked as SIMPLE, because it's implemented as pimpl // This class can be marked as SIMPLE, because it's implemented as pimpl
class GAPI_EXPORTS_W_SIMPLE PyParams { class GAPI_EXPORTS_W_SIMPLE PyParams {
public: public:
GAPI_WRAP
PyParams() = default; PyParams() = default;
GAPI_WRAP
PyParams(const std::string &tag, PyParams(const std::string &tag,
const std::string &model, const std::string &model,
const std::string &weights, const std::string &weights,
const std::string &device); const std::string &device);
GAPI_WRAP
PyParams(const std::string &tag, PyParams(const std::string &tag,
const std::string &model, const std::string &model,
const std::string &device); 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; GBackend backend() const;
std::string tag() const; std::string tag() const;
cv::util::any params() const; cv::util::any params() const;
@@ -64,12 +64,13 @@ detection is smaller than confidence threshold, detection is rejected.
given label will get to the output. given label will get to the output.
@return a tuple with a vector of detected boxes and a vector of appropriate labels. @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, GAPI_EXPORTS_W std::tuple<GArray<Rect>, GArray<int>> parseSSD(const GMat& in,
const GOpaque<Size>& inSz, const GOpaque<Size>& inSz,
const float confidenceThreshold = 0.5f, const float confidenceThreshold = 0.5f,
const int filterLabel = -1); const int filterLabel = -1);
/** @brief Parses output of SSD network.
/** @overload
Extracts detection information (box, confidence) from SSD output and Extracts detection information (box, confidence) from SSD output and
filters it by given confidence and by going out of bounds. 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, GAPI_EXPORTS_W GArray<Rect> parseSSD(const GMat& in,
const GOpaque<Size>& inSz, const GOpaque<Size>& inSz,
const float confidenceThreshold = 0.5f, const float confidenceThreshold,
const bool alignmentToSquare = false, const bool alignmentToSquare,
const bool filterOutOfBounds = false); const bool filterOutOfBounds);
/** @brief Parses output of Yolo network. /** @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>. <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. @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, GAPI_EXPORTS_W std::tuple<GArray<Rect>, GArray<int>> parseYolo(const GMat& in,
const GOpaque<Size>& inSz, const GOpaque<Size>& inSz,
const float confidenceThreshold = 0.5f, const float confidenceThreshold = 0.5f,
const float nmsThreshold = 0.5f, const float nmsThreshold = 0.5f,
const std::vector<float>& anchors const std::vector<float>& anchors
= nn::parsers::GParseYolo::defaultAnchors()); = nn::parsers::GParseYolo::defaultAnchors());
} // namespace gapi } // namespace gapi
} // namespace cv } // namespace cv
@@ -13,11 +13,13 @@
# define GAPI_EXPORTS CV_EXPORTS # define GAPI_EXPORTS CV_EXPORTS
/* special informative macros for wrapper generators */ /* special informative macros for wrapper generators */
# define GAPI_PROP CV_PROP # define GAPI_PROP CV_PROP
# define GAPI_PROP_RW CV_PROP_RW
# define GAPI_WRAP CV_WRAP # define GAPI_WRAP CV_WRAP
# define GAPI_EXPORTS_W_SIMPLE CV_EXPORTS_W_SIMPLE # define GAPI_EXPORTS_W_SIMPLE CV_EXPORTS_W_SIMPLE
# define GAPI_EXPORTS_W CV_EXPORTS_W # define GAPI_EXPORTS_W CV_EXPORTS_W
# else # else
# define GAPI_PROP # define GAPI_PROP
# define GAPI_PROP_RW
# define GAPI_WRAP # define GAPI_WRAP
# define GAPI_EXPORTS # define GAPI_EXPORTS
# define GAPI_EXPORTS_W_SIMPLE # define GAPI_EXPORTS_W_SIMPLE
@@ -81,9 +81,9 @@ using GMatDesc2 = std::tuple<cv::GMatDesc,cv::GMatDesc>;
@param prims vector of drawing primitivies @param prims vector of drawing primitivies
@param args graph compile time parameters @param args graph compile time parameters
*/ */
void GAPI_EXPORTS render(cv::Mat& bgr, void GAPI_EXPORTS_W render(cv::Mat& bgr,
const Prims& prims, const Prims& prims,
cv::GCompileArgs&& args = {}); cv::GCompileArgs&& args = {});
/** @brief The function renders on two NV12 planes passed drawing primitivies /** @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 prims vector of drawing primitivies
@param args graph compile time parameters @param args graph compile time parameters
*/ */
void GAPI_EXPORTS render(cv::Mat& y_plane, void GAPI_EXPORTS_W render(cv::Mat& y_plane,
cv::Mat& uv_plane, cv::Mat& uv_plane,
const Prims& prims, const Prims& prims,
cv::GCompileArgs&& args = {}); cv::GCompileArgs&& args = {});
/** @brief The function renders on the input media frame passed drawing primitivies /** @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 src input image: 8-bit unsigned 3-channel image @ref CV_8UC3
@param prims draw primitives @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 /** @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 uv input image: 8-bit unsigned 2-channel image @ref CV_8UC2
@param prims draw primitives @param prims draw primitives
*/ */
GAPI_EXPORTS GMat2 renderNV12(const GMat& y, GAPI_EXPORTS_W GMat2 renderNV12(const GMat& y,
const GMat& uv, const GMat& uv,
const GArray<Prim>& prims); const GArray<Prim>& prims);
/** @brief Renders Media Frame /** @brief Renders Media Frame
@@ -173,7 +173,7 @@ namespace render
{ {
namespace ocv namespace ocv
{ {
GAPI_EXPORTS cv::gapi::GKernelPackage kernels(); GAPI_EXPORTS_W cv::gapi::GKernelPackage kernels();
} // namespace ocv } // namespace ocv
} // namespace render } // namespace render
@@ -41,7 +41,7 @@ struct freetype_font
* *
* Parameters match cv::putText(). * Parameters match cv::putText().
*/ */
struct Text struct GAPI_EXPORTS_W_SIMPLE Text
{ {
/** /**
* @brief Text constructor * @brief Text constructor
@@ -55,6 +55,7 @@ struct Text
* @param lt_ The line type. See #LineTypes * @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 * @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_, Text(const std::string& text_,
const cv::Point& org_, const cv::Point& org_,
int ff_, int ff_,
@@ -68,17 +69,18 @@ struct Text
{ {
} }
GAPI_WRAP
Text() = default; Text() = default;
/*@{*/ /*@{*/
std::string text; //!< The text string to be drawn GAPI_PROP_RW std::string text; //!< The text string to be drawn
cv::Point org; //!< The bottom-left corner of the text string in the image GAPI_PROP_RW cv::Point org; //!< The bottom-left corner of the text string in the image
int ff; //!< The font type, see #HersheyFonts GAPI_PROP_RW int ff; //!< The font type, see #HersheyFonts
double fs; //!< The font scale factor that is multiplied by the font-specific base size GAPI_PROP_RW double fs; //!< The font scale factor that is multiplied by the font-specific base size
cv::Scalar color; //!< The text color GAPI_PROP_RW cv::Scalar color; //!< The text color
int thick; //!< The thickness of the lines used to draw a text GAPI_PROP_RW int thick; //!< The thickness of the lines used to draw a text
int lt; //!< The line type. See #LineTypes GAPI_PROP_RW 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 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(). * Parameters match cv::rectangle().
*/ */
struct Rect struct GAPI_EXPORTS_W_SIMPLE Rect
{ {
/** /**
* @brief Rect constructor * @brief Rect constructor
@@ -142,14 +144,15 @@ struct Rect
{ {
} }
GAPI_WRAP
Rect() = default; Rect() = default;
/*@{*/ /*@{*/
cv::Rect rect; //!< Coordinates of the rectangle GAPI_PROP_RW cv::Rect rect; //!< Coordinates of the rectangle
cv::Scalar color; //!< The rectangle color or brightness (grayscale image) GAPI_PROP_RW 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 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
int lt; //!< The type of the line. See #LineTypes GAPI_PROP_RW int lt; //!< The type of the line. See #LineTypes
int shift; //!< The number of fractional bits in the point coordinates GAPI_PROP_RW int shift; //!< The number of fractional bits in the point coordinates
/*@{*/ /*@{*/
}; };
@@ -158,7 +161,7 @@ struct Rect
* *
* Parameters match cv::circle(). * Parameters match cv::circle().
*/ */
struct Circle struct GAPI_EXPORTS_W_SIMPLE Circle
{ {
/** /**
* @brief Circle constructor * @brief Circle constructor
@@ -170,6 +173,7 @@ struct Circle
* @param lt_ The Type of the circle boundary. See #LineTypes * @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 * @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_, Circle(const cv::Point& center_,
int radius_, int radius_,
const cv::Scalar& color_, const cv::Scalar& color_,
@@ -180,15 +184,16 @@ struct Circle
{ {
} }
GAPI_WRAP
Circle() = default; Circle() = default;
/*@{*/ /*@{*/
cv::Point center; //!< The center of the circle GAPI_PROP_RW cv::Point center; //!< The center of the circle
int radius; //!< The radius of the circle GAPI_PROP_RW int radius; //!< The radius of the circle
cv::Scalar color; //!< The color of the circle GAPI_PROP_RW 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 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
int lt; //!< The Type of the circle boundary. See #LineTypes GAPI_PROP_RW 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 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(). * Parameters match cv::line().
*/ */
struct Line struct GAPI_EXPORTS_W_SIMPLE Line
{ {
/** /**
* @brief Line constructor * @brief Line constructor
@@ -209,6 +214,7 @@ struct Line
* @param lt_ The Type of the line. See #LineTypes * @param lt_ The Type of the line. See #LineTypes
* @param shift_ The number of fractional bits in the point coordinates * @param shift_ The number of fractional bits in the point coordinates
*/ */
GAPI_WRAP
Line(const cv::Point& pt1_, Line(const cv::Point& pt1_,
const cv::Point& pt2_, const cv::Point& pt2_,
const cv::Scalar& color_, const cv::Scalar& color_,
@@ -219,15 +225,16 @@ struct Line
{ {
} }
GAPI_WRAP
Line() = default; Line() = default;
/*@{*/ /*@{*/
cv::Point pt1; //!< The first point of the line segment GAPI_PROP_RW cv::Point pt1; //!< The first point of the line segment
cv::Point pt2; //!< The second point of the line segment GAPI_PROP_RW cv::Point pt2; //!< The second point of the line segment
cv::Scalar color; //!< The line color GAPI_PROP_RW cv::Scalar color; //!< The line color
int thick; //!< The thickness of line GAPI_PROP_RW int thick; //!< The thickness of line
int lt; //!< The Type of the line. See #LineTypes GAPI_PROP_RW int lt; //!< The Type of the line. See #LineTypes
int shift; //!< The number of fractional bits in the point coordinates 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. * Mosaicing is a very basic method to obfuscate regions in the image.
*/ */
struct Mosaic struct GAPI_EXPORTS_W_SIMPLE Mosaic
{ {
/** /**
* @brief Mosaic constructor * @brief Mosaic constructor
@@ -252,12 +259,13 @@ struct Mosaic
{ {
} }
GAPI_WRAP
Mosaic() : cellSz(0), decim(0) {} Mosaic() : cellSz(0), decim(0) {}
/*@{*/ /*@{*/
cv::Rect mos; //!< Coordinates of the mosaic GAPI_PROP_RW cv::Rect mos; //!< Coordinates of the mosaic
int cellSz; //!< Cell size (same for X, Y) GAPI_PROP_RW int cellSz; //!< Cell size (same for X, Y)
int decim; //!< Decimation (0 stands for no decimation) 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. * Image is blended on a frame using the specified mask.
*/ */
struct Image struct GAPI_EXPORTS_W_SIMPLE Image
{ {
/** /**
* @brief Mosaic constructor * @brief Mosaic constructor
@@ -275,6 +283,7 @@ struct Image
* @param img_ Image to draw * @param img_ Image to draw
* @param alpha_ Alpha channel for image to draw (same size and number of channels) * @param alpha_ Alpha channel for image to draw (same size and number of channels)
*/ */
GAPI_WRAP
Image(const cv::Point& org_, Image(const cv::Point& org_,
const cv::Mat& img_, const cv::Mat& img_,
const cv::Mat& alpha_) : const cv::Mat& alpha_) :
@@ -282,19 +291,20 @@ struct Image
{ {
} }
GAPI_WRAP
Image() = default; Image() = default;
/*@{*/ /*@{*/
cv::Point org; //!< The bottom-left corner of the image GAPI_PROP_RW cv::Point org; //!< The bottom-left corner of the image
cv::Mat img; //!< Image to draw GAPI_PROP_RW 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::Mat alpha; //!< Alpha channel for image to draw (same size and number of channels)
/*@{*/ /*@{*/
}; };
/** /**
* @brief This structure represents a polygon to draw. * @brief This structure represents a polygon to draw.
*/ */
struct Poly struct GAPI_EXPORTS_W_SIMPLE Poly
{ {
/** /**
* @brief Mosaic constructor * @brief Mosaic constructor
@@ -305,6 +315,7 @@ struct Poly
* @param lt_ The Type of the line. See #LineTypes * @param lt_ The Type of the line. See #LineTypes
* @param shift_ The number of fractional bits in the point coordinate * @param shift_ The number of fractional bits in the point coordinate
*/ */
GAPI_WRAP
Poly(const std::vector<cv::Point>& points_, Poly(const std::vector<cv::Point>& points_,
const cv::Scalar& color_, const cv::Scalar& color_,
int thick_ = 1, int thick_ = 1,
@@ -314,14 +325,15 @@ struct Poly
{ {
} }
GAPI_WRAP
Poly() = default; Poly() = default;
/*@{*/ /*@{*/
std::vector<cv::Point> points; //!< Points to connect GAPI_PROP_RW std::vector<cv::Point> points; //!< Points to connect
cv::Scalar color; //!< The line color GAPI_PROP_RW cv::Scalar color; //!< The line color
int thick; //!< The thickness of line GAPI_PROP_RW int thick; //!< The thickness of line
int lt; //!< The Type of the line. See #LineTypes GAPI_PROP_RW int lt; //!< The Type of the line. See #LineTypes
int shift; //!< The number of fractional bits in the point coordinate GAPI_PROP_RW int shift; //!< The number of fractional bits in the point coordinate
/*@{*/ /*@{*/
}; };
@@ -336,7 +348,7 @@ using Prim = util::variant
, Poly , Poly
>; >;
using Prims = std::vector<Prim>; using Prims = std::vector<Prim>;
//! @} gapi_draw_prims //! @} gapi_draw_prims
} // namespace draw } // namespace draw
@@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory // 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. // 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 #ifndef OPENCV_GAPI_GSTREAMING_DESYNC_HPP
@@ -73,9 +73,10 @@ G desync(const G &g) {
* which produces an array of cv::util::optional<> objects. * which produces an array of cv::util::optional<> objects.
* *
* @note This feature is highly experimental now and is currently * @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 GMat desync(const GMat &g);
GAPI_EXPORTS GFrame desync(const GFrame &f);
} // namespace streaming } // namespace streaming
} // namespace gapi } // 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 @param in Input image
@return Copy of the input @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 /** @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, (no actual data copied). Use this function to maintain graph contracts,
@@ -11,6 +11,36 @@ def register(mname):
return parameterized 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') @register('cv2')
class GOpaque(): class GOpaque():
# NB: Inheritance from c++ class cause segfault. # NB: Inheritance from c++ class cause segfault.
@@ -54,6 +84,10 @@ class GOpaque():
def __new__(self): def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_RECT) return cv.GOpaqueT(cv.gapi.CV_RECT)
class Prim():
def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_DRAW_PRIM)
class Any(): class Any():
def __new__(self): def __new__(self):
return cv.GOpaqueT(cv.gapi.CV_ANY) return cv.GOpaqueT(cv.gapi.CV_ANY)
@@ -113,6 +147,10 @@ class GArray():
def __new__(self): def __new__(self):
return cv.GArrayT(cv.gapi.CV_GMAT) return cv.GArrayT(cv.gapi.CV_GMAT)
class Prim():
def __new__(self):
return cv.GArray(cv.gapi.CV_DRAW_PRIM)
class Any(): class Any():
def __new__(self): def __new__(self):
return cv.GArray(cv.gapi.CV_ANY) 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.Scalar: cv.gapi.CV_SCALAR,
cv.GArray.Mat: cv.gapi.CV_MAT, cv.GArray.Mat: cv.gapi.CV_MAT,
cv.GArray.GMat: cv.gapi.CV_GMAT, cv.GArray.GMat: cv.gapi.CV_GMAT,
cv.GArray.Prim: cv.gapi.CV_DRAW_PRIM,
cv.GArray.Any: cv.gapi.CV_ANY 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.Point2f: cv.gapi.CV_POINT2F,
cv.GOpaque.Size: cv.gapi.CV_SIZE, cv.GOpaque.Size: cv.gapi.CV_SIZE,
cv.GOpaque.Rect: cv.gapi.CV_RECT, cv.GOpaque.Rect: cv.gapi.CV_RECT,
cv.GOpaque.Prim: cv.gapi.CV_DRAW_PRIM,
cv.GOpaque.Any: cv.gapi.CV_ANY cv.GOpaque.Any: cv.gapi.CV_ANY
} }
type2str = { type2str = {
cv.gapi.CV_BOOL: 'cv.gapi.CV_BOOL' , cv.gapi.CV_BOOL: 'cv.gapi.CV_BOOL' ,
cv.gapi.CV_INT: 'cv.gapi.CV_INT' , cv.gapi.CV_INT: 'cv.gapi.CV_INT' ,
cv.gapi.CV_DOUBLE: 'cv.gapi.CV_DOUBLE' , cv.gapi.CV_DOUBLE: 'cv.gapi.CV_DOUBLE' ,
cv.gapi.CV_FLOAT: 'cv.gapi.CV_FLOAT' , cv.gapi.CV_FLOAT: 'cv.gapi.CV_FLOAT' ,
cv.gapi.CV_STRING: 'cv.gapi.CV_STRING' , cv.gapi.CV_STRING: 'cv.gapi.CV_STRING' ,
cv.gapi.CV_POINT: 'cv.gapi.CV_POINT' , cv.gapi.CV_POINT: 'cv.gapi.CV_POINT' ,
cv.gapi.CV_POINT2F: 'cv.gapi.CV_POINT2F' , cv.gapi.CV_POINT2F: 'cv.gapi.CV_POINT2F' ,
cv.gapi.CV_SIZE: 'cv.gapi.CV_SIZE', cv.gapi.CV_SIZE: 'cv.gapi.CV_SIZE',
cv.gapi.CV_RECT: 'cv.gapi.CV_RECT', cv.gapi.CV_RECT: 'cv.gapi.CV_RECT',
cv.gapi.CV_SCALAR: 'cv.gapi.CV_SCALAR', cv.gapi.CV_SCALAR: 'cv.gapi.CV_SCALAR',
cv.gapi.CV_MAT: 'cv.gapi.CV_MAT', cv.gapi.CV_MAT: 'cv.gapi.CV_MAT',
cv.gapi.CV_GMAT: 'cv.gapi.CV_GMAT' 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 # NB: Second lvl decorator takes class to decorate
@@ -244,3 +285,15 @@ def kernel(op_cls):
return cls return cls
return kernel_with_params 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> #include <opencv2/gapi/python/python.hpp>
// NB: Python wrapper replaces :: with _ for classes // NB: Python wrapper replaces :: with _ for classes
using gapi_GKernelPackage = cv::gapi::GKernelPackage; using gapi_GKernelPackage = cv::gapi::GKernelPackage;
using gapi_GNetPackage = cv::gapi::GNetPackage; using gapi_GNetPackage = cv::gapi::GNetPackage;
using gapi_ie_PyParams = cv::gapi::ie::PyParams; using gapi_ie_PyParams = cv::gapi::ie::PyParams;
using gapi_wip_IStreamSource_Ptr = cv::Ptr<cv::gapi::wip::IStreamSource>; using gapi_wip_IStreamSource_Ptr = cv::Ptr<cv::gapi::wip::IStreamSource>;
using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback; using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback;
using detail_ExtractMetaCallback = cv::detail::ExtractMetaCallback; 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> // NB: Python wrapper generate T_U for T<U>
// This behavior is only observed for inputs // 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_Scalar = cv::GArray<cv::Scalar>;
using GArray_Mat = cv::GArray<cv::Mat>; using GArray_Mat = cv::GArray<cv::Mat>;
using GArray_GMat = cv::GArray<cv::GMat>; 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, // FIXME: Python wrapper generate code without namespace std,
// so it cause error: "string wasn't declared" // so it cause error: "string wasn't declared"
@@ -124,6 +127,95 @@ PyObject* pyopencv_from(const cv::detail::PyObjectHolder& v)
return o; 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<> template<>
PyObject* pyopencv_from(const cv::GArg& value) 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 #define UNSUPPORTED(T) case cv::detail::OpaqueKind::CV_##T: break
switch (value.opaque_kind) switch (value.opaque_kind)
{ {
HANDLE_CASE(BOOL, bool); HANDLE_CASE(BOOL, bool);
HANDLE_CASE(INT, int); HANDLE_CASE(INT, int);
HANDLE_CASE(DOUBLE, double); HANDLE_CASE(INT64, int64_t);
HANDLE_CASE(FLOAT, float); HANDLE_CASE(DOUBLE, double);
HANDLE_CASE(STRING, std::string); HANDLE_CASE(FLOAT, float);
HANDLE_CASE(POINT, cv::Point); HANDLE_CASE(STRING, std::string);
HANDLE_CASE(POINT2F, cv::Point2f); HANDLE_CASE(POINT, cv::Point);
HANDLE_CASE(SIZE, cv::Size); HANDLE_CASE(POINT2F, cv::Point2f);
HANDLE_CASE(RECT, cv::Rect); HANDLE_CASE(SIZE, cv::Size);
HANDLE_CASE(SCALAR, cv::Scalar); HANDLE_CASE(RECT, cv::Rect);
HANDLE_CASE(MAT, cv::Mat); HANDLE_CASE(SCALAR, cv::Scalar);
HANDLE_CASE(UNKNOWN, cv::detail::PyObjectHolder); HANDLE_CASE(MAT, cv::Mat);
HANDLE_CASE(UNKNOWN, cv::detail::PyObjectHolder);
HANDLE_CASE(DRAW_PRIM, cv::gapi::wip::draw::Prim);
UNSUPPORTED(UINT64); UNSUPPORTED(UINT64);
UNSUPPORTED(DRAW_PRIM);
#undef HANDLE_CASE #undef HANDLE_CASE
#undef UNSUPPORTED #undef UNSUPPORTED
} }
@@ -163,6 +256,18 @@ bool pyopencv_to(PyObject* obj, cv::GArg& value, const ArgInfo& info)
return true; 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 <> template <>
bool pyopencv_to(PyObject* obj, std::vector<GCompileArg>& value, const ArgInfo& info) 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); 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<> template<>
PyObject* pyopencv_from(const cv::detail::OpaqueRef& o) 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_BOOL : return pyopencv_from(o.rref<bool>());
case cv::detail::OpaqueKind::CV_INT : return pyopencv_from(o.rref<int>()); 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_DOUBLE : return pyopencv_from(o.rref<double>());
case cv::detail::OpaqueKind::CV_FLOAT : return pyopencv_from(o.rref<float>()); 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>()); 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_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_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_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_UINT64 : break;
case cv::detail::OpaqueKind::CV_SCALAR : break; case cv::detail::OpaqueKind::CV_SCALAR : break;
case cv::detail::OpaqueKind::CV_MAT : break; case cv::detail::OpaqueKind::CV_MAT : break;
case cv::detail::OpaqueKind::CV_DRAW_PRIM : break;
} }
PyErr_SetString(PyExc_TypeError, "Unsupported GOpaque type"); 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_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_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_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_FLOAT : return pyopencv_from_generic_vec(v.rref<float>());
case cv::detail::OpaqueKind::CV_STRING : return pyopencv_from_generic_vec(v.rref<std::string>()); 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_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_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_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_UINT64 : break;
case cv::detail::OpaqueKind::CV_DRAW_PRIM : break;
} }
PyErr_SetString(PyExc_TypeError, "Unsupported GArray type"); 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)); 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; return NULL;
} }
template<> template<>
PyObject* pyopencv_from(const GRunArgs& value) PyObject* pyopencv_from(const GRunArgs& value)
{ {
size_t i, n = value.size(); return value.size() == 1 ? pyopencv_from(value[0]) : pyopencv_from_generic_vec(value);
// 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;
} }
template<> 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<> // FIXME: cv::variant should be wrapped once for all types.
PyObject* pyopencv_from(const GMetaArgs& value) 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> template <typename T>
@@ -318,16 +436,16 @@ void pyopencv_to_generic_vec_with_check(PyObject* from,
} }
template <typename T> template <typename T>
static PyObject* extract_proto_args(PyObject* py_args, PyObject* kw) static T extract_proto_args(PyObject* py_args)
{ {
using namespace cv; using namespace cv;
GProtoArgs args; GProtoArgs args;
Py_ssize_t size = PyTuple_Size(py_args); Py_ssize_t size = PyList_Size(py_args);
args.reserve(size); args.reserve(size);
for (int i = 0; i < size; ++i) 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))) if (PyObject_TypeCheck(item, reinterpret_cast<PyTypeObject*>(pyopencv_GScalar_TypePtr)))
{ {
args.emplace_back(reinterpret_cast<pyopencv_GScalar_t*>(item)->v); 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 else
{ {
PyErr_SetString(PyExc_TypeError, "Unsupported type for cv.GIn()/cv.GOut()"); util::throw_error(std::logic_error("Unsupported type for GProtoArgs"));
return NULL;
} }
} }
return pyopencv_from<T>(T{std::move(args)}); return 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);
} }
static cv::detail::OpaqueRef extract_opaque_ref(PyObject* from, cv::detail::OpaqueKind kind) 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(RECT, cv::Rect);
HANDLE_CASE(UNKNOWN, cv::GArg); HANDLE_CASE(UNKNOWN, cv::GArg);
UNSUPPORTED(UINT64); UNSUPPORTED(UINT64);
UNSUPPORTED(INT64);
UNSUPPORTED(SCALAR); UNSUPPORTED(SCALAR);
UNSUPPORTED(MAT); UNSUPPORTED(MAT);
UNSUPPORTED(DRAW_PRIM); 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 #define UNSUPPORTED(T) case cv::detail::OpaqueKind::CV_##T: break
switch (kind) switch (kind)
{ {
HANDLE_CASE(BOOL, bool); HANDLE_CASE(BOOL, bool);
HANDLE_CASE(INT, int); HANDLE_CASE(INT, int);
HANDLE_CASE(DOUBLE, double); HANDLE_CASE(DOUBLE, double);
HANDLE_CASE(FLOAT, float); HANDLE_CASE(FLOAT, float);
HANDLE_CASE(STRING, std::string); HANDLE_CASE(STRING, std::string);
HANDLE_CASE(POINT, cv::Point); HANDLE_CASE(POINT, cv::Point);
HANDLE_CASE(POINT2F, cv::Point2f); HANDLE_CASE(POINT2F, cv::Point2f);
HANDLE_CASE(SIZE, cv::Size); HANDLE_CASE(SIZE, cv::Size);
HANDLE_CASE(RECT, cv::Rect); HANDLE_CASE(RECT, cv::Rect);
HANDLE_CASE(SCALAR, cv::Scalar); HANDLE_CASE(SCALAR, cv::Scalar);
HANDLE_CASE(MAT, cv::Mat); HANDLE_CASE(MAT, cv::Mat);
HANDLE_CASE(UNKNOWN, cv::GArg); HANDLE_CASE(UNKNOWN, cv::GArg);
HANDLE_CASE(DRAW_PRIM, cv::gapi::wip::draw::Prim);
UNSUPPORTED(UINT64); UNSUPPORTED(UINT64);
UNSUPPORTED(DRAW_PRIM); UNSUPPORTED(INT64);
#undef HANDLE_CASE #undef HANDLE_CASE
#undef UNSUPPORTED #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) static cv::GRunArgs extract_run_args(const cv::GTypesInfo& info, PyObject* py_args)
{ {
cv::GRunArgs args; GAPI_Assert(PyList_Check(py_args));
Py_ssize_t tuple_size = PyTuple_Size(py_args);
args.reserve(tuple_size);
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; 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) static cv::GMetaArgs extract_meta_args(const cv::GTypesInfo& info, PyObject* py_args)
{ {
cv::GMetaArgs metas; GAPI_Assert(PyList_Check(py_args));
Py_ssize_t tuple_size = PyTuple_Size(py_args);
metas.reserve(tuple_size);
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; return metas;
@@ -581,7 +694,8 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel,
cv::detail::PyObjectHolder result( cv::detail::PyObjectHolder result(
PyObject_CallObject(kernel.get(), args.get()), false); PyObject_CallObject(kernel.get(), args.get()), false);
if (PyErr_Occurred()) { if (PyErr_Occurred())
{
PyErr_PrintEx(0); PyErr_PrintEx(0);
PyErr_Clear(); PyErr_Clear();
throw std::logic_error("Python kernel failed with error!"); 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. // NB: In fact it's impossible situation, becase errors were handled above.
GAPI_Assert(result.get() && "Python kernel returned NULL!"); GAPI_Assert(result.get() && "Python kernel returned NULL!");
outs = out_info.size() == 1 ? cv::GRunArgs{extract_run_arg(out_info[0], result.get())} if (out_info.size() == 1)
: extract_run_args(out_info, result.get()); {
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 (...) catch (...)
{ {
@@ -604,30 +737,12 @@ static cv::GRunArgs run_py_kernel(cv::detail::PyObjectHolder kernel,
static GMetaArg get_meta_arg(PyObject* obj) static GMetaArg get_meta_arg(PyObject* obj)
{ {
if (PyObject_TypeCheck(obj, cv::GMetaArg arg;
reinterpret_cast<PyTypeObject*>(pyopencv_GMatDesc_TypePtr))) if (!pyopencv_to(obj, arg, ArgInfo("arg", false)))
{
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
{ {
util::throw_error(std::logic_error("Unsupported output meta type")); util::throw_error(std::logic_error("Unsupported output meta type"));
} }
return arg;
} }
static cv::GMetaArgs get_meta_args(PyObject* tuple) 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, static GMetaArgs run_py_meta(cv::detail::PyObjectHolder out_meta,
const cv::GMetaArgs &meta, const cv::GMetaArgs &meta,
const cv::GArgs &gargs) { const cv::GArgs &gargs)
{
PyGILState_STATE gstate; PyGILState_STATE gstate;
gstate = PyGILState_Ensure(); gstate = PyGILState_Ensure();
@@ -688,7 +804,8 @@ static GMetaArgs run_py_meta(cv::detail::PyObjectHolder out_meta,
cv::detail::PyObjectHolder result( cv::detail::PyObjectHolder result(
PyObject_CallObject(out_meta.get(), args.get()), false); PyObject_CallObject(out_meta.get(), args.get()), false);
if (PyErr_Occurred()) { if (PyErr_Occurred())
{
PyErr_PrintEx(0); PyErr_PrintEx(0);
PyErr_Clear(); PyErr_Clear();
throw std::logic_error("Python outMeta failed with error!"); 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* user_kernel = PyTuple_GetItem(py_args, i);
PyObject* id_obj = PyObject_GetAttrString(user_kernel, "id"); PyObject* id_obj = PyObject_GetAttrString(user_kernel, "id");
if (!id_obj) { if (!id_obj)
{
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"Python kernel should contain id, please use cv.gapi.kernel to define kernel"); "Python kernel should contain id, please use cv.gapi.kernel to define kernel");
return NULL; return NULL;
} }
PyObject* out_meta = PyObject_GetAttrString(user_kernel, "outMeta"); PyObject* out_meta = PyObject_GetAttrString(user_kernel, "outMeta");
if (!out_meta) { if (!out_meta)
{
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"Python kernel should contain outMeta, please use cv.gapi.kernel to define kernel"); "Python kernel should contain outMeta, please use cv.gapi.kernel to define kernel");
return NULL; return NULL;
} }
PyObject* run = PyObject_GetAttrString(user_kernel, "run"); PyObject* run = PyObject_GetAttrString(user_kernel, "run");
if (!run) { if (!run)
{
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"Python kernel should contain run, please use cv.gapi.kernel to define kernel"); "Python kernel should contain run, please use cv.gapi.kernel to define kernel");
return NULL; return NULL;
@@ -756,23 +876,6 @@ static PyObject* pyopencv_cv_gapi_kernels(PyObject* , PyObject* py_args, PyObjec
return pyopencv_from(pkg); 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*) static PyObject* pyopencv_cv_gapi_op(PyObject* , PyObject* py_args, PyObject*)
{ {
using namespace cv; 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))); 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}; cv::detail::PyObjectHolder holder{obj};
auto callback = cv::detail::ExtractArgsCallback{[=](const cv::GTypesInfo& info) 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); PyGILState_Release(gstate);
return args; throw;
}}; }
PyGILState_Release(gstate);
return pyopencv_from(callback); 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); cv::detail::PyObjectHolder holder{obj};
auto callback = cv::detail::ExtractMetaCallback{[=](const cv::GTypesInfo& info) value = cv::detail::ExtractMetaCallback{[=](const cv::GTypesInfo& info)
{ {
PyGILState_STATE gstate; PyGILState_STATE gstate;
gstate = PyGILState_Ensure(); gstate = PyGILState_Ensure();
cv::GMetaArgs args; cv::GMetaArgs args;
try try
{ {
args = extract_meta_args(info, py_args); args = extract_meta_args(info, holder.get());
} }
catch (...) catch (...)
{ {
PyGILState_Release(gstate);
throw;
}
PyGILState_Release(gstate); PyGILState_Release(gstate);
return args; throw;
}}; }
return pyopencv_from(callback); PyGILState_Release(gstate);
return args;
}};
return true;
} }
template<typename T> template<typename T>
@@ -895,9 +999,12 @@ struct PyOpenCV_Converter<cv::GArray<T>>
if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_GArrayT_TypePtr))) if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_GArrayT_TypePtr)))
{ {
auto& array = reinterpret_cast<pyopencv_GArrayT_t*>(obj)->v; auto& array = reinterpret_cast<pyopencv_GArrayT_t*>(obj)->v;
try { try
{
value = cv::util::get<cv::GArray<T>>(array.arg()); value = cv::util::get<cv::GArray<T>>(array.arg());
} catch (...) { }
catch (...)
{
return false; return false;
} }
return true; return true;
@@ -918,9 +1025,12 @@ struct PyOpenCV_Converter<cv::GOpaque<T>>
if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_GOpaqueT_TypePtr))) if (PyObject_TypeCheck(obj, reinterpret_cast<PyTypeObject*>(pyopencv_GOpaqueT_TypePtr)))
{ {
auto& opaque = reinterpret_cast<pyopencv_GOpaqueT_t*>(obj)->v; auto& opaque = reinterpret_cast<pyopencv_GOpaqueT_t*>(obj)->v;
try { try
{
value = cv::util::get<cv::GOpaque<T>>(opaque.arg()); value = cv::util::get<cv::GOpaque<T>>(opaque.arg());
} catch (...) { }
catch (...)
{
return false; return false;
} }
return true; 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 // extend cv.gapi methods
#define PYOPENCV_EXTRA_METHODS_GAPI \ #define PYOPENCV_EXTRA_METHODS_GAPI \
{"kernels", CV_PY_FN_WITH_KW(pyopencv_cv_gapi_kernels), "kernels(...) -> GKernelPackage"}, \ {"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"}, {"__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.hpp>
#include <opencv2/gapi/garg.hpp> #include <opencv2/gapi/garg.hpp>
#include <opencv2/gapi/gopaque.hpp> #include <opencv2/gapi/gopaque.hpp>
#include <opencv2/gapi/render/render_types.hpp> // Prim
#define ID(T, E) T #define ID(T, E) T
#define ID_(T, E) ID(T, E), #define ID_(T, E) ID(T, E),
@@ -24,24 +25,29 @@
GAPI_Assert(false && "Unsupported type"); \ GAPI_Assert(false && "Unsupported type"); \
} }
using cv::gapi::wip::draw::Prim;
#define GARRAY_TYPE_LIST_G(G, G2) \ #define GARRAY_TYPE_LIST_G(G, G2) \
WRAP_ARGS(bool , cv::gapi::ArgType::CV_BOOL, G) \ WRAP_ARGS(bool , cv::gapi::ArgType::CV_BOOL, G) \
WRAP_ARGS(int , cv::gapi::ArgType::CV_INT, G) \ WRAP_ARGS(int , cv::gapi::ArgType::CV_INT, G) \
WRAP_ARGS(double , cv::gapi::ArgType::CV_DOUBLE, G) \ WRAP_ARGS(int64_t , cv::gapi::ArgType::CV_INT64, G) \
WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \ WRAP_ARGS(double , cv::gapi::ArgType::CV_DOUBLE, G) \
WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \ WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \
WRAP_ARGS(cv::Point , cv::gapi::ArgType::CV_POINT, G) \ WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \
WRAP_ARGS(cv::Point2f , cv::gapi::ArgType::CV_POINT2F, G) \ WRAP_ARGS(cv::Point , cv::gapi::ArgType::CV_POINT, G) \
WRAP_ARGS(cv::Size , cv::gapi::ArgType::CV_SIZE, G) \ WRAP_ARGS(cv::Point2f , cv::gapi::ArgType::CV_POINT2F, G) \
WRAP_ARGS(cv::Rect , cv::gapi::ArgType::CV_RECT, G) \ WRAP_ARGS(cv::Size , cv::gapi::ArgType::CV_SIZE, G) \
WRAP_ARGS(cv::Scalar , cv::gapi::ArgType::CV_SCALAR, G) \ WRAP_ARGS(cv::Rect , cv::gapi::ArgType::CV_RECT, G) \
WRAP_ARGS(cv::Mat , cv::gapi::ArgType::CV_MAT, G) \ WRAP_ARGS(cv::Scalar , cv::gapi::ArgType::CV_SCALAR, G) \
WRAP_ARGS(cv::GArg , cv::gapi::ArgType::CV_ANY, G) \ WRAP_ARGS(cv::Mat , cv::gapi::ArgType::CV_MAT, G) \
WRAP_ARGS(cv::GMat , cv::gapi::ArgType::CV_GMAT, G2) \ 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) \ #define GOPAQUE_TYPE_LIST_G(G, G2) \
WRAP_ARGS(bool , cv::gapi::ArgType::CV_BOOL, G) \ WRAP_ARGS(bool , cv::gapi::ArgType::CV_BOOL, G) \
WRAP_ARGS(int , cv::gapi::ArgType::CV_INT, 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(double , cv::gapi::ArgType::CV_DOUBLE, G) \
WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \ WRAP_ARGS(float , cv::gapi::ArgType::CV_FLOAT, G) \
WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \ WRAP_ARGS(std::string , cv::gapi::ArgType::CV_STRING, G) \
@@ -58,6 +64,7 @@ namespace gapi {
enum ArgType { enum ArgType {
CV_BOOL, CV_BOOL,
CV_INT, CV_INT,
CV_INT64,
CV_DOUBLE, CV_DOUBLE,
CV_FLOAT, CV_FLOAT,
CV_STRING, CV_STRING,
@@ -68,6 +75,7 @@ enum ArgType {
CV_SCALAR, CV_SCALAR,
CV_MAT, CV_MAT,
CV_GMAT, CV_GMAT,
CV_DRAW_PRIM,
CV_ANY, CV_ANY,
}; };
+69 -52
View File
@@ -3,64 +3,81 @@
namespace cv 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); class GAPI_EXPORTS_W_SIMPLE GInferInputs
GAPI_EXPORTS_W GCompileArgs compile_args(gapi::GNetPackage pkg); {
GAPI_EXPORTS_W GCompileArgs compile_args(gapi::GKernelPackage kernels, gapi::GNetPackage nets); 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 class GAPI_EXPORTS_W_SIMPLE GInferListInputs
// HACK: Mark them as a class to force python wrapper generate code for this entities {
class GAPI_EXPORTS_W_SIMPLE GProtoArg { }; public:
class GAPI_EXPORTS_W_SIMPLE GProtoInputArgs { }; GAPI_WRAP GInferListInputs();
class GAPI_EXPORTS_W_SIMPLE GProtoOutputArgs { }; GAPI_WRAP GInferListInputs setInput(const std::string& name, const cv::GArray<cv::GMat>& value);
class GAPI_EXPORTS_W_SIMPLE GRunArg { }; GAPI_WRAP GInferListInputs setInput(const std::string& name, const cv::GArray<cv::Rect>& value);
class GAPI_EXPORTS_W_SIMPLE GMetaArg { GAPI_WRAP GMetaArg(); }; };
using GProtoInputArgs = GIOProtoArgs<In_Tag>; class GAPI_EXPORTS_W_SIMPLE GInferOutputs
using GProtoOutputArgs = GIOProtoArgs<Out_Tag>; {
public:
GAPI_WRAP GInferOutputs();
GAPI_WRAP cv::GMat at(const std::string& name);
};
class GAPI_EXPORTS_W_SIMPLE GInferInputs class GAPI_EXPORTS_W_SIMPLE GInferListOutputs
{ {
public: public:
GAPI_WRAP GInferInputs(); GAPI_WRAP GInferListOutputs();
GAPI_WRAP void setInput(const std::string& name, const cv::GMat& value); GAPI_WRAP cv::GArray<cv::GMat> at(const std::string& name);
GAPI_WRAP void setInput(const std::string& name, const cv::GFrame& value); };
};
class GAPI_EXPORTS_W_SIMPLE GInferListInputs namespace gapi
{ {
public: namespace wip
GAPI_WRAP GInferListInputs(); {
GAPI_WRAP void setInput(const std::string& name, const cv::GArray<cv::GMat>& value); class GAPI_EXPORTS_W IStreamSource { };
GAPI_WRAP void setInput(const std::string& name, const cv::GArray<cv::Rect>& value); 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 struct Mosaic
{ {
public: GAPI_WRAP Mosaic(const cv::Rect2i& mos_, int cellSz_, int decim_);
GAPI_WRAP GInferOutputs(); };
GAPI_WRAP cv::GMat at(const std::string& name); } // 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 GAPI_EXPORTS_W cv::GMat desync(const cv::GMat &g);
{ } // namespace streaming
public: } // namespace gapi
GAPI_WRAP GInferListOutputs();
GAPI_WRAP cv::GArray<cv::GMat> at(const std::string& name);
};
namespace detail namespace detail
{ {
struct GAPI_EXPORTS_W_SIMPLE ExtractArgsCallback { }; gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ie::PyParams params);
struct GAPI_EXPORTS_W_SIMPLE ExtractMetaCallback { }; } // namespace detail
} // namespace detail
namespace gapi
{
namespace wip
{
class GAPI_EXPORTS_W IStreamSource { };
} // namespace wip
} // namespace gapi
} // namespace cv } // namespace cv
+179 -157
View File
@@ -3,187 +3,209 @@
import numpy as np import numpy as np
import cv2 as cv import cv2 as cv
import os import os
import sys
import unittest
from tests_common import NewOpenCVTests from tests_common import NewOpenCVTests
# Plaidml is an optional backend try:
pkgs = [
('ocl' , cv.gapi.core.ocl.kernels()), if sys.version_info[:2] < (3, 0):
('cpu' , cv.gapi.core.cpu.kernels()), raise unittest.SkipTest('Python 2.x is not supported')
('fluid' , cv.gapi.core.fluid.kernels())
# ('plaidml', cv.gapi.core.plaidml.kernels()) # 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): def test_add(self):
# TODO: Extend to use any type and size here # TODO: Extend to use any type and size here
sz = (720, 1280) sz = (720, 1280)
in1 = np.full(sz, 100) in1 = np.full(sz, 100)
in2 = np.full(sz, 50) in2 = np.full(sz, 50)
# OpenCV # OpenCV
expected = cv.add(in1, in2) expected = cv.add(in1, in2)
# G-API # G-API
g_in1 = cv.GMat() g_in1 = cv.GMat()
g_in2 = cv.GMat() g_in2 = cv.GMat()
g_out = cv.gapi.add(g_in1, g_in2) g_out = cv.gapi.add(g_in1, g_in2)
comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out)) comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out))
for pkg_name, pkg in pkgs: for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1, in2), args=cv.compile_args(pkg)) actual = comp.apply(cv.gin(in1, in2), args=cv.gapi.compile_args(pkg))
# Comparison # Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF), 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),
'Failed on ' + pkg_name + ' backend') '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): def test_add_uint8(self):
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')]) sz = (720, 1280)
in_mat = cv.cvtColor(cv.imread(img_path), cv.COLOR_RGB2GRAY) in1 = np.full(sz, 100, dtype=np.uint8)
maxv = (30, 30) in2 = np.full(sz, 50 , dtype=np.uint8)
# OpenCV # OpenCV
expected_thresh, expected_mat = cv.threshold(in_mat, maxv[0], maxv[0], cv.THRESH_TRIANGLE) expected = cv.add(in1, in2)
# G-API # G-API
g_in = cv.GMat() g_in1 = cv.GMat()
g_sc = cv.GScalar() g_in2 = cv.GMat()
mat, threshold = cv.gapi.threshold(g_in, g_sc, cv.THRESH_TRIANGLE) g_out = cv.gapi.add(g_in1, g_in2)
comp = cv.GComputation(cv.GIn(g_in, g_sc), cv.GOut(mat, threshold)) comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out))
for pkg_name, pkg in pkgs: for pkg_name, pkg in pkgs:
actual_mat, actual_thresh = comp.apply(cv.gin(in_mat, maxv), args=cv.compile_args(pkg)) actual = comp.apply(cv.gin(in1, in2), args=cv.gapi.compile_args(pkg))
# Comparison # Comparison
self.assertEqual(0.0, cv.norm(expected_mat, actual_mat, cv.NORM_INF), self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend') 'Failed on ' + pkg_name + ' backend')
self.assertEqual(expected_mat.dtype, actual_mat.dtype, self.assertEqual(expected.dtype, actual.dtype, 'Failed on ' + pkg_name + ' backend')
'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): def test_mean(self):
arr = np.random.random(sz).astype(np.float32).T img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
return list(zip(arr[0], arr[1])) 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): def test_split3(self):
# K-means 2D params img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
count = 100 in_mat = cv.imread(img_path)
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 # OpenCV
data = cv.GArrayT(cv.gapi.CV_POINT2F) expected = cv.split(in_mat)
best_labels = cv.GArrayT(cv.gapi.CV_INT)
compactness, out_labels, centers = cv.gapi.kmeans(data, K, best_labels, criteria, attempts, flags); # G-API
comp = cv.GComputation(cv.GIn(data, best_labels), cv.GOut(compactness, out_labels, centers)); 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) def test_threshold(self):
self.assertEqual(amount, len(labels)) img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
self.assertEqual(K, len(centers)) 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__': if __name__ == '__main__':
@@ -3,103 +3,124 @@
import numpy as np import numpy as np
import cv2 as cv import cv2 as cv
import os import os
import sys
import unittest
from tests_common import NewOpenCVTests from tests_common import NewOpenCVTests
# Plaidml is an optional backend try:
pkgs = [
('ocl' , cv.gapi.core.ocl.kernels()), if sys.version_info[:2] < (3, 0):
('cpu' , cv.gapi.core.cpu.kernels()), raise unittest.SkipTest('Python 2.x is not supported')
('fluid' , cv.gapi.core.fluid.kernels())
# ('plaidml', cv.gapi.core.plaidml.kernels()) # 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): def test_good_features_to_track(self):
# TODO: Extend to use any type and size here # 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')]) 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) in1 = cv.cvtColor(cv.imread(img_path), cv.COLOR_RGB2GRAY)
# NB: goodFeaturesToTrack configuration # NB: goodFeaturesToTrack configuration
max_corners = 50 max_corners = 50
quality_lvl = 0.01 quality_lvl = 0.01
min_distance = 10 min_distance = 10
block_sz = 3 block_sz = 3
use_harris_detector = True use_harris_detector = True
k = 0.04 k = 0.04
mask = None mask = None
# OpenCV # OpenCV
expected = cv.goodFeaturesToTrack(in1, max_corners, quality_lvl, expected = cv.goodFeaturesToTrack(in1, max_corners, quality_lvl,
min_distance, mask=mask, min_distance, mask=mask,
blockSize=block_sz, useHarrisDetector=use_harris_detector, k=k) blockSize=block_sz, useHarrisDetector=use_harris_detector, k=k)
# G-API # G-API
g_in = cv.GMat() g_in = cv.GMat()
g_out = cv.gapi.goodFeaturesToTrack(g_in, max_corners, quality_lvl, g_out = cv.gapi.goodFeaturesToTrack(g_in, max_corners, quality_lvl,
min_distance, mask, block_sz, use_harris_detector, k) 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: for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1), args=cv.compile_args(pkg)) actual = comp.apply(cv.gin(in1), args=cv.gapi.compile_args(pkg))
# NB: OpenCV & G-API have different output shapes: # NB: OpenCV & G-API have different output shapes:
# OpenCV - (num_points, 1, 2) # OpenCV - (num_points, 1, 2)
# G-API - (num_points, 2) # G-API - (num_points, 2)
# Comparison # Comparison
self.assertEqual(0.0, cv.norm(expected.flatten(), self.assertEqual(0.0, cv.norm(expected.flatten(),
np.array(actual, dtype=np.float32).flatten(), np.array(actual, dtype=np.float32).flatten(),
cv.NORM_INF), cv.NORM_INF),
'Failed on ' + pkg_name + ' backend') 'Failed on ' + pkg_name + ' backend')
def test_rgb2gray(self): def test_rgb2gray(self):
# TODO: Extend to use any type and size here # 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')]) img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
in1 = cv.imread(img_path) in1 = cv.imread(img_path)
# OpenCV # OpenCV
expected = cv.cvtColor(in1, cv.COLOR_RGB2GRAY) expected = cv.cvtColor(in1, cv.COLOR_RGB2GRAY)
# G-API # G-API
g_in = cv.GMat() g_in = cv.GMat()
g_out = cv.gapi.RGB2Gray(g_in) 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: for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(in1), args=cv.compile_args(pkg)) actual = comp.apply(cv.gin(in1), args=cv.gapi.compile_args(pkg))
# Comparison # Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF), self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend') 'Failed on ' + pkg_name + ' backend')
def test_bounding_rect(self): def test_bounding_rect(self):
sz = 1280 sz = 1280
fscale = 256 fscale = 256
def sample_value(fscale): def sample_value(fscale):
return np.random.uniform(0, 255 * fscale) / 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 # OpenCV
expected = cv.boundingRect(points) expected = cv.boundingRect(points)
# G-API # G-API
g_in = cv.GMat() g_in = cv.GMat()
g_out = cv.gapi.boundingRect(g_in) 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: for pkg_name, pkg in pkgs:
actual = comp.apply(cv.gin(points), args=cv.compile_args(pkg)) actual = comp.apply(cv.gin(points), args=cv.gapi.compile_args(pkg))
# Comparison # Comparison
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF), self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF),
'Failed on ' + pkg_name + ' backend') '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__': if __name__ == '__main__':
+274 -254
View File
@@ -3,318 +3,338 @@
import numpy as np import numpy as np
import cv2 as cv import cv2 as cv
import os import os
import sys
import unittest
from tests_common import NewOpenCVTests from tests_common import NewOpenCVTests
class test_gapi_infer(NewOpenCVTests): try:
def infer_reference_network(self, model_path, weights_path, img): if sys.version_info[:2] < (3, 0):
net = cv.dnn.readNetFromModelOptimizer(model_path, weights_path) raise unittest.SkipTest('Python 2.x is not supported')
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 make_roi(self, img, roi): class test_gapi_infer(NewOpenCVTests):
return img[roi[1]:roi[1] + roi[3], roi[0]:roi[0] + roi[2], ...]
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): def make_roi(self, img, roi):
# NB: Check IE return img[roi[1]:roi[1] + roi[3], roi[0]:roi[0] + roi[2], ...]
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 test_age_gender_infer_roi(self): def test_age_gender_infer(self):
# NB: Check IE # NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE): if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return return
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013' 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')]) 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')]) weights_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
device_id = 'CPU' device_id = 'CPU'
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')]) img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img = cv.imread(img_path) img = cv.resize(cv.imread(img_path), (62,62))
roi = (10, 10, 62, 62)
# OpenCV DNN # OpenCV DNN
dnn_age, dnn_gender = self.infer_reference_network(model_path, 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, weights_path,
self.make_roi(img, roi)) self.make_roi(img, roi))
dnn_age_list.append(age)
dnn_gender_list.append(gender)
# OpenCV G-API # OpenCV G-API
g_in = cv.GMat() g_in = cv.GMat()
g_roi = cv.GOpaqueT(cv.gapi.CV_RECT) g_rois = cv.GArrayT(cv.gapi.CV_RECT)
inputs = cv.GInferInputs() inputs = cv.GInferInputs()
inputs.setInput('data', g_in) inputs.setInput('data', g_in)
outputs = cv.gapi.infer("net", g_roi, inputs) outputs = cv.gapi.infer("net", g_rois, inputs)
age_g = outputs.at("age_conv3") age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob") gender_g = outputs.at("prob")
comp = cv.GComputation(cv.GIn(g_in, g_roi), cv.GOut(age_g, gender_g)) 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) 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 # Check
self.assertEqual(0.0, cv.norm(dnn_gender, gapi_gender, cv.NORM_INF)) for gapi_age, gapi_gender, dnn_age, dnn_gender in zip(gapi_age_list,
self.assertEqual(0.0, cv.norm(dnn_age, gapi_age, cv.NORM_INF)) 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): def test_age_gender_infer2_roi(self):
# NB: Check IE # NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE): if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return return
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013' 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')]) 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')]) weights_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
device_id = 'CPU' device_id = 'CPU'
rois = [(10, 15, 62, 62), (23, 50, 62, 62), (14, 100, 62, 62), (80, 50, 62, 62)] 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_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
img = cv.imread(img_path) img = cv.imread(img_path)
# OpenCV DNN # OpenCV DNN
dnn_age_list = [] dnn_age_list = []
dnn_gender_list = [] dnn_gender_list = []
for roi in rois: for roi in rois:
age, gender = self.infer_reference_network(model_path, age, gender = self.infer_reference_network(model_path,
weights_path, weights_path,
self.make_roi(img, roi)) self.make_roi(img, roi))
dnn_age_list.append(age) dnn_age_list.append(age)
dnn_gender_list.append(gender) dnn_gender_list.append(gender)
# OpenCV G-API # OpenCV G-API
g_in = cv.GMat() g_in = cv.GMat()
g_rois = cv.GArrayT(cv.gapi.CV_RECT) g_rois = cv.GArrayT(cv.gapi.CV_RECT)
inputs = cv.GInferInputs() inputs = cv.GInferListInputs()
inputs.setInput('data', g_in) inputs.setInput('data', g_rois)
outputs = cv.gapi.infer("net", g_rois, inputs) outputs = cv.gapi.infer2("net", g_in, inputs)
age_g = outputs.at("age_conv3") age_g = outputs.at("age_conv3")
gender_g = outputs.at("prob") gender_g = outputs.at("prob")
comp = cv.GComputation(cv.GIn(g_in, g_rois), cv.GOut(age_g, gender_g)) 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) pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
gapi_age_list, gapi_gender_list = comp.apply(cv.gin(img, rois), gapi_age_list, gapi_gender_list = comp.apply(cv.gin(img, rois),
args=cv.compile_args(cv.gapi.networks(pp))) args=cv.gapi.compile_args(cv.gapi.networks(pp)))
# Check # Check
for gapi_age, gapi_gender, dnn_age, dnn_gender in zip(gapi_age_list, for gapi_age, gapi_gender, dnn_age, dnn_gender in zip(gapi_age_list,
gapi_gender_list, gapi_gender_list,
dnn_age_list, dnn_age_list,
dnn_gender_list): dnn_gender_list):
self.assertEqual(0.0, cv.norm(dnn_gender, gapi_gender, cv.NORM_INF)) 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)) 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))
def test_person_detection_retail_0013(self): def test_person_detection_retail_0013(self):
# NB: Check IE # NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE): if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return return
root_path = '/omz_intel_models/intel/person-detection-retail-0013/FP32/person-detection-retail-0013' 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')]) 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')]) 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')]) img_path = self.find_file('gpu/lbpcascade/er.png', [os.environ.get('OPENCV_TEST_DATA_PATH')])
device_id = 'CPU' device_id = 'CPU'
img = cv.resize(cv.imread(img_path), (544, 320)) img = cv.resize(cv.imread(img_path), (544, 320))
# OpenCV DNN # OpenCV DNN
net = cv.dnn.readNetFromModelOptimizer(model_path, weights_path) net = cv.dnn.readNetFromModelOptimizer(model_path, weights_path)
net.setPreferableBackend(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE) net.setPreferableBackend(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE)
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU) net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)
blob = cv.dnn.blobFromImage(img) blob = cv.dnn.blobFromImage(img)
def parseSSD(detections, size): def parseSSD(detections, size):
h, w = size h, w = size
bboxes = [] bboxes = []
detections = detections.reshape(-1, 7) detections = detections.reshape(-1, 7)
for sample_id, class_id, confidence, xmin, ymin, xmax, ymax in detections: for sample_id, class_id, confidence, xmin, ymin, xmax, ymax in detections:
if confidence >= 0.5: if confidence >= 0.5:
x = int(xmin * w) x = int(xmin * w)
y = int(ymin * h) y = int(ymin * h)
width = int(xmax * w - x) width = int(xmax * w - x)
height = int(ymax * h - y) height = int(ymax * h - y)
bboxes.append((x, y, width, height)) bboxes.append((x, y, width, height))
return bboxes return bboxes
net.setInput(blob) net.setInput(blob)
dnn_detections = net.forward() dnn_detections = net.forward()
dnn_boxes = parseSSD(np.array(dnn_detections), img.shape[:2]) dnn_boxes = parseSSD(np.array(dnn_detections), img.shape[:2])
# OpenCV G-API # OpenCV G-API
g_in = cv.GMat() g_in = cv.GMat()
inputs = cv.GInferInputs() inputs = cv.GInferInputs()
inputs.setInput('data', g_in) inputs.setInput('data', g_in)
g_sz = cv.gapi.streaming.size(g_in) g_sz = cv.gapi.streaming.size(g_in)
outputs = cv.gapi.infer("net", inputs) outputs = cv.gapi.infer("net", inputs)
detections = outputs.at("detection_out") detections = outputs.at("detection_out")
bboxes = cv.gapi.parseSSD(detections, g_sz, 0.5, False, False) bboxes = cv.gapi.parseSSD(detections, g_sz, 0.5, False, False)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(bboxes)) comp = cv.GComputation(cv.GIn(g_in), cv.GOut(bboxes))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id) 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)), # Comparison
args=cv.compile_args(cv.gapi.networks(pp))) self.assertEqual(0.0, cv.norm(np.array(dnn_boxes).flatten(),
np.array(gapi_boxes).flatten(),
# Comparison cv.NORM_INF))
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): def test_person_detection_retail_0013(self):
# NB: Check IE # NB: Check IE
if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE): if not cv.dnn.DNN_TARGET_CPU in cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE):
return return
root_path = '/omz_intel_models/intel/person-detection-retail-0013/FP32/person-detection-retail-0013' 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')]) 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')]) 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')]) img_path = self.find_file('gpu/lbpcascade/er.png', [os.environ.get('OPENCV_TEST_DATA_PATH')])
device_id = 'CPU' device_id = 'CPU'
img = cv.resize(cv.imread(img_path), (544, 320)) img = cv.resize(cv.imread(img_path), (544, 320))
# OpenCV DNN # OpenCV DNN
net = cv.dnn.readNetFromModelOptimizer(model_path, weights_path) net = cv.dnn.readNetFromModelOptimizer(model_path, weights_path)
net.setPreferableBackend(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE) net.setPreferableBackend(cv.dnn.DNN_BACKEND_INFERENCE_ENGINE)
net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU) net.setPreferableTarget(cv.dnn.DNN_TARGET_CPU)
blob = cv.dnn.blobFromImage(img) blob = cv.dnn.blobFromImage(img)
def parseSSD(detections, size): def parseSSD(detections, size):
h, w = size h, w = size
bboxes = [] bboxes = []
detections = detections.reshape(-1, 7) detections = detections.reshape(-1, 7)
for sample_id, class_id, confidence, xmin, ymin, xmax, ymax in detections: for sample_id, class_id, confidence, xmin, ymin, xmax, ymax in detections:
if confidence >= 0.5: if confidence >= 0.5:
x = int(xmin * w) x = int(xmin * w)
y = int(ymin * h) y = int(ymin * h)
width = int(xmax * w - x) width = int(xmax * w - x)
height = int(ymax * h - y) height = int(ymax * h - y)
bboxes.append((x, y, width, height)) bboxes.append((x, y, width, height))
return bboxes return bboxes
net.setInput(blob) net.setInput(blob)
dnn_detections = net.forward() dnn_detections = net.forward()
dnn_boxes = parseSSD(np.array(dnn_detections), img.shape[:2]) dnn_boxes = parseSSD(np.array(dnn_detections), img.shape[:2])
# OpenCV G-API # OpenCV G-API
g_in = cv.GMat() g_in = cv.GMat()
inputs = cv.GInferInputs() inputs = cv.GInferInputs()
inputs.setInput('data', g_in) inputs.setInput('data', g_in)
g_sz = cv.gapi.streaming.size(g_in) g_sz = cv.gapi.streaming.size(g_in)
outputs = cv.gapi.infer("net", inputs) outputs = cv.gapi.infer("net", inputs)
detections = outputs.at("detection_out") detections = outputs.at("detection_out")
bboxes = cv.gapi.parseSSD(detections, g_sz, 0.5, False, False) bboxes = cv.gapi.parseSSD(detections, g_sz, 0.5, False, False)
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(bboxes)) comp = cv.GComputation(cv.GIn(g_in), cv.GOut(bboxes))
pp = cv.gapi.ie.params("net", model_path, weights_path, device_id) pp = cv.gapi.ie.params("net", model_path, weights_path, device_id)
gapi_boxes = comp.apply(cv.gin(img.astype(np.float32)), gapi_boxes = comp.apply(cv.gin(img.astype(np.float32)),
args=cv.compile_args(cv.gapi.networks(pp))) args=cv.gapi.compile_args(cv.gapi.networks(pp)))
# Comparison # Comparison
self.assertEqual(0.0, cv.norm(np.array(dnn_boxes).flatten(), self.assertEqual(0.0, cv.norm(np.array(dnn_boxes).flatten(),
np.array(gapi_boxes).flatten(), np.array(gapi_boxes).flatten(),
cv.NORM_INF)) 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__': 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)) comp = cv.GComputation(cv.GIn(g_in1, g_in2), cv.GOut(g_out))
pkg = cv.gapi.kernels(GAddImpl) 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)) 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)) comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_ch1, g_ch2, g_ch3))
pkg = cv.gapi.kernels(GSplit3Impl) 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_ch1, ch1, cv.NORM_INF))
self.assertEqual(0.0, cv.norm(in_ch2, ch2, 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) comp = cv.GComputation(g_in, g_out)
pkg = cv.gapi.kernels(GMeanImpl) 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 # Comparison
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
@@ -287,7 +287,7 @@ try:
comp = cv.GComputation(cv.GIn(g_in, g_sc), cv.GOut(g_out)) comp = cv.GComputation(cv.GIn(g_in, g_sc), cv.GOut(g_out))
pkg = cv.gapi.kernels(GAddCImpl) 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)) 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)) comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_sz))
pkg = cv.gapi.kernels(GSizeImpl) 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)) 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)) comp = cv.GComputation(cv.GIn(g_r), cv.GOut(g_sz))
pkg = cv.gapi.kernels(GSizeRImpl) 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 ? # cv.norm works with tuples ?
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF)) 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)) comp = cv.GComputation(cv.GIn(g_pts), cv.GOut(g_br))
pkg = cv.gapi.kernels(GBoundingRectImpl) 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 ? # cv.norm works with tuples ?
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF)) 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)) comp = cv.GComputation(cv.GIn(g_in), cv.GOut(g_out))
pkg = cv.gapi.kernels(GGoodFeaturesImpl) 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. # NB: OpenCV & G-API have different output types.
# OpenCV - numpy array with shape (num_points, 1, 2) # OpenCV - numpy array with shape (num_points, 1, 2)
@@ -453,10 +453,10 @@ try:
g_in = cv.GArray.Int() g_in = cv.GArray.Int()
comp = cv.GComputation(cv.GIn(g_in), cv.GOut(GSum.on(g_in))) 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) 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, s)
self.assertEqual(18, GSumImpl.last_result) self.assertEqual(18, GSumImpl.last_result)
@@ -488,13 +488,13 @@ try:
'tuple': (42, 42) '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) 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) 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) self.assertEqual((42, 42), out)
@@ -521,7 +521,7 @@ try:
arr1 = [3, 'str'] arr1 = [3, 'str']
out = comp.apply(cv.gin(arr0, arr1), 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) self.assertEqual(arr0 + arr1, out)
@@ -550,7 +550,7 @@ try:
img1 = np.array([1, 2, 3]) img1 = np.array([1, 2, 3])
with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1), with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1),
args=cv.compile_args( args=cv.gapi.compile_args(
cv.gapi.kernels(GAddImpl))) cv.gapi.kernels(GAddImpl)))
@@ -577,7 +577,7 @@ try:
img1 = np.array([1, 2, 3]) img1 = np.array([1, 2, 3])
with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1), with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1),
args=cv.compile_args( args=cv.gapi.compile_args(
cv.gapi.kernels(GAddImpl))) cv.gapi.kernels(GAddImpl)))
@@ -607,7 +607,7 @@ try:
# FIXME: Cause Bad variant access. # FIXME: Cause Bad variant access.
# Need to provide more descriptive error messsage. # Need to provide more descriptive error messsage.
with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1), with self.assertRaises(Exception): comp.apply(cv.gin(img0, img1),
args=cv.compile_args( args=cv.gapi.compile_args(
cv.gapi.kernels(GAddImpl))) cv.gapi.kernels(GAddImpl)))
def test_pipeline_with_custom_kernels(self): def test_pipeline_with_custom_kernels(self):
@@ -657,7 +657,7 @@ try:
g_mean = cv.gapi.mean(g_transposed) g_mean = cv.gapi.mean(g_transposed)
comp = cv.GComputation(cv.GIn(g_bgr), cv.GOut(g_mean)) 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))) cv.gapi.kernels(GResizeImpl, GTransposeImpl)))
self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF)) self.assertEqual(0.0, cv.norm(expected, actual, cv.NORM_INF))
@@ -3,201 +3,367 @@
import numpy as np import numpy as np
import cv2 as cv import cv2 as cv
import os import os
import sys
import unittest
import time
from tests_common import NewOpenCVTests from tests_common import NewOpenCVTests
class test_gapi_streaming(NewOpenCVTests):
def test_image_input(self): try:
sz = (1280, 720) if sys.version_info[:2] < (3, 0):
in_mat = np.random.randint(0, 100, sz).astype(np.uint8) raise unittest.SkipTest('Python 2.x is not supported')
# 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))
def test_video_input(self): @cv.gapi.op('custom.delay', in_types=[cv.GMat], out_types=[cv.GMat])
ksize = 3 class GDelay:
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']]) """Delay for 10 ms."""
# OpenCV @staticmethod
cap = cv.VideoCapture(path) def outMeta(desc):
return desc
# 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;
def test_video_split3(self): @cv.gapi.kernel(GDelay)
path = self.find_file('cv/video/768x576.avi', [os.environ['OPENCV_TEST_DATA_PATH']]) class GDelayImpl:
"""Implementation for GDelay operation."""
# OpenCV @staticmethod
cap = cv.VideoCapture(path) def run(img):
time.sleep(0.01)
# G-API return img
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;
def test_video_add(self): class test_gapi_streaming(NewOpenCVTests):
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']]) def test_image_input(self):
sz = (1280, 720)
# OpenCV in_mat = np.random.randint(0, 100, sz).astype(np.uint8)
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
# OpenCV # OpenCV
frame = cv.cvtColor(frame, cv.COLOR_RGB2GRAY) expected = cv.medianBlur(in_mat, 3)
expected = cv.goodFeaturesToTrack(frame, max_corners, quality_lvl,
min_distance, mask=mask, # G-API
blockSize=block_sz, useHarrisDetector=use_harris_detector, k=k) g_in = cv.GMat()
for e, a in zip(expected, actual): g_out = cv.gapi.medianBlur(g_in, 3)
# NB: OpenCV & G-API have different output shapes: c = cv.GComputation(g_in, g_out)
# OpenCV - (num_points, 1, 2) ccomp = c.compileStreaming(cv.gapi.descr_of(in_mat))
# G-API - (num_points, 2) ccomp.setSource(cv.gin(in_mat))
self.assertEqual(0.0, cv.norm(e.flatten(), ccomp.start()
np.array(a, np.float32).flatten(),
cv.NORM_INF)) _, 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__': if __name__ == '__main__':
NewOpenCVTests.bootstrap() NewOpenCVTests.bootstrap()
@@ -3,29 +3,51 @@
import numpy as np import numpy as np
import cv2 as cv import cv2 as cv
import os import os
import sys
import unittest
from tests_common import NewOpenCVTests from tests_common import NewOpenCVTests
class gapi_types_test(NewOpenCVTests):
def test_garray_type(self): try:
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: if sys.version_info[:2] < (3, 0):
g_array = cv.GArrayT(t) raise unittest.SkipTest('Python 2.x is not supported')
self.assertEqual(t, g_array.type())
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): def test_gopaque_type(self):
types = [cv.gapi.CV_BOOL , cv.gapi.CV_INT , cv.gapi.CV_DOUBLE , cv.gapi.CV_FLOAT, 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_STRING, cv.gapi.CV_POINT , cv.gapi.CV_POINT2F, cv.gapi.CV_SIZE ,
cv.gapi.CV_RECT] cv.gapi.CV_RECT]
for t in types: for t in types:
g_opaque = cv.GOpaqueT(t) g_opaque = cv.GOpaqueT(t)
self.assertEqual(t, g_opaque.type()) 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__': if __name__ == '__main__':
+4
View File
@@ -15,6 +15,10 @@ cv::gapi::GNetPackage::GNetPackage(std::initializer_list<GNetParam> ii)
: networks(ii) { : networks(ii) {
} }
cv::gapi::GNetPackage::GNetPackage(std::vector<GNetParam> nets)
: networks(nets) {
}
std::vector<cv::gapi::GBackend> cv::gapi::GNetPackage::backends() const { std::vector<cv::gapi::GBackend> cv::gapi::GNetPackage::backends() const {
std::unordered_set<cv::gapi::GBackend> unique_set; std::unordered_set<cv::gapi::GBackend> unique_set;
for (const auto &nn : networks) unique_set.insert(nn.backend); for (const auto &nn : networks) unique_set.insert(nn.backend);
+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 // 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. // of this distribution and at http://opencv.org/license.html.
// //
// Copyright (C) 2020 Intel Corporation // Copyright (C) 2020-2021 Intel Corporation
#include "precomp.hpp" #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. // 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) { cv::GMat cv::gapi::streaming::BGR(const cv::GFrame& in) {
return cv::gapi::streaming::GBGR::on(in); return cv::gapi::streaming::GBGR::on(in);
} }
+3 -3
View File
@@ -159,7 +159,7 @@ void drawPrimitivesOCV(cv::Mat& in,
{ {
const auto& rp = cv::util::get<Rect>(p); const auto& rp = cv::util::get<Rect>(p);
const auto color = converter.cvtColor(rp.color); 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; break;
} }
@@ -198,7 +198,7 @@ void drawPrimitivesOCV(cv::Mat& in,
{ {
const auto& cp = cv::util::get<Circle>(p); const auto& cp = cv::util::get<Circle>(p);
const auto color = converter.cvtColor(cp.color); 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; break;
} }
@@ -206,7 +206,7 @@ void drawPrimitivesOCV(cv::Mat& in,
{ {
const auto& lp = cv::util::get<Line>(p); const auto& lp = cv::util::get<Line>(p);
const auto color = converter.cvtColor(lp.color); 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; break;
} }
@@ -85,6 +85,19 @@ class GGraphMetaBackendImpl final: public cv::gapi::GBackend::Priv {
const std::vector<cv::gimpl::Data>&) const override { const std::vector<cv::gimpl::Data>&) const override {
return EPtr{new GraphMetaExecutable(graph, nodes)}; 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() { cv::gapi::GBackend graph_meta_backend() {
+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<cv::Rect>& out_boxes,
std::vector<int>& out_labels) 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, const bool filter_out_of_bounds,
std::vector<cv::Rect>& out_boxes) 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 nn
} // namespace gapi } // namespace gapi
void parseSSDBL(const cv::Mat& in_ssd_result, void ParseSSD(const cv::Mat& in_ssd_result,
const cv::Size& in_size, const cv::Size& in_size,
const float confidence_threshold, const float confidence_threshold,
const int filter_label, const int filter_label,
std::vector<cv::Rect>& out_boxes, const bool alignment_to_square,
std::vector<int>& out_labels) 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>()); cv::gapi::nn::SSDParser parser(in_ssd_result.size, in_size, in_ssd_result.ptr<float>());
out_boxes.clear(); 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); 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) if (image_id < 0.f)
{ {
break; // marks end-of-detections break; // marks end-of-detections
@@ -228,12 +198,14 @@ void parseSSD(const cv::Mat& in_ssd_result,
{ {
continue; // skip objects with low confidence 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) if (alignment_to_square)
{ {
parser.adjustBoundingBox(rc); parser.adjustBoundingBox(rc);
} }
const auto clipped_rc = rc & parser.getSurface(); const auto clipped_rc = rc & parser.getSurface();
if (filter_out_of_bounds) if (filter_out_of_bounds)
{ {
@@ -243,6 +215,7 @@ void parseSSD(const cv::Mat& in_ssd_result,
} }
} }
out_boxes.emplace_back(clipped_rc); out_boxes.emplace_back(clipped_rc);
out_labels.emplace_back(label);
} }
} }
+4 -9
View File
@@ -11,19 +11,14 @@
namespace cv namespace cv
{ {
void parseSSDBL(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,
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 cv::Size& in_size,
const float confidence_threshold, const float confidence_threshold,
const int filter_label,
const bool alignment_to_square, const bool alignment_to_square,
const bool filter_out_of_bounds, 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, void parseYolo(const cv::Mat& in_yolo_result,
const cv::Size& in_size, 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) { const std::string &device) {
return {tag, model, 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;
}
+7 -11
View File
@@ -75,6 +75,11 @@ bool cv::GStreamingCompiled::Priv::pull(cv::GOptRunArgsP &&outs)
return m_exec->pull(std::move(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) bool cv::GStreamingCompiled::Priv::try_pull(cv::GRunArgsP &&outs)
{ {
return m_exec->try_pull(std::move(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)); 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; return m_priv->pull();
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);
} }
bool cv::GStreamingCompiled::pull(cv::GOptRunArgsP &&outs) bool cv::GStreamingCompiled::pull(cv::GOptRunArgsP &&outs)
@@ -46,6 +46,7 @@ public:
void start(); void start();
bool pull(cv::GRunArgsP &&outs); bool pull(cv::GRunArgsP &&outs);
bool pull(cv::GOptRunArgsP &&outs); bool pull(cv::GOptRunArgsP &&outs);
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> pull();
bool try_pull(cv::GRunArgsP &&outs); bool try_pull(cv::GRunArgsP &&outs);
void stop(); void stop();
+19 -6
View File
@@ -7,8 +7,6 @@
#include "precomp.hpp" #include "precomp.hpp"
#include <iostream>
#include <ade/util/zip_range.hpp> #include <ade/util/zip_range.hpp>
#include <opencv2/gapi/opencv_includes.hpp> #include <opencv2/gapi/opencv_includes.hpp>
@@ -157,10 +155,14 @@ void writeBackExec(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg)
// FIXME: // FIXME:
// Rework, find a better way to check if there should be // Rework, find a better way to check if there should be
// a real copy (add a pass to StreamingBackend?) // 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); auto& out_mat = *util::get<cv::Mat*>(g_arg);
const auto& rmat = mag.template slot<cv::RMat>().at(rc.id); const auto& rmat = mag.template slot<cv::RMat>().at(rc.id);
auto mag_data = rmat.get<RMatAdapter>()->data(); auto* adapter = rmat.get<RMatAdapter>();
if (out_mat.data != mag_data) { if ((adapter != nullptr && out_mat.data != adapter->data()) ||
(adapter == nullptr)) {
auto view = rmat.access(RMat::Access::R); auto view = rmat.access(RMat::Access::R);
asMat(view).copyTo(out_mat); asMat(view).copyTo(out_mat);
} }
@@ -407,7 +409,8 @@ bool cv::gimpl::GExecutor::canReshape() const
{ {
// FIXME: Introduce proper reshaping support on GExecutor level // FIXME: Introduce proper reshaping support on GExecutor level
// for all cases! // 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) 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}; ade::passes::PassContext ctx{g};
passes::initMeta(ctx, inMetas); passes::initMeta(ctx, inMetas);
passes::inferMeta(ctx, true); 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() 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 // FIXME: this conversion should be unified
switch (out_obj.index()) switch (out_obj.index())
{ {
HANDLE_CASE(cv::Scalar); break; HANDLE_CASE(cv::Scalar); break;
HANDLE_CASE(cv::RMat); break; HANDLE_CASE(cv::RMat); break;
HANDLE_CASE(cv::MediaFrame); break;
case T::index_of<O<cv::Mat>*>(): { case T::index_of<O<cv::Mat>*>(): {
// Mat: special handling. // Mat: special handling.
@@ -1017,6 +1018,49 @@ void check_DesyncObjectConsumedByMultipleIslands(const cv::gimpl::GIslandModel::
} // for(nodes) } // 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 } // anonymous namespace
class cv::gimpl::GStreamingExecutor::Synchronizer final { class cv::gimpl::GStreamingExecutor::Synchronizer final {
@@ -1320,6 +1364,16 @@ cv::gimpl::GStreamingExecutor::GStreamingExecutor(std::unique_ptr<ade::Graph> &&
// per the same input frame, so the output traffic multiplies) // per the same input frame, so the output traffic multiplies)
GAPI_Assert(m_collector_map.size() > 0u); GAPI_Assert(m_collector_map.size() > 0u);
m_out_queue.set_capacity(queue_capacity * m_collector_map.size()); 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() cv::gimpl::GStreamingExecutor::~GStreamingExecutor()
@@ -1653,6 +1707,31 @@ bool cv::gimpl::GStreamingExecutor::pull(cv::GOptRunArgsP &&outs)
return true; 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) bool cv::gimpl::GStreamingExecutor::try_pull(cv::GRunArgsP &&outs)
{ {
@@ -195,6 +195,8 @@ protected:
void wait_shutdown(); void wait_shutdown();
cv::GTypesInfo out_info;
public: public:
explicit GStreamingExecutor(std::unique_ptr<ade::Graph> &&g_model, explicit GStreamingExecutor(std::unique_ptr<ade::Graph> &&g_model,
const cv::GCompileArgs &comp_args); const cv::GCompileArgs &comp_args);
@@ -203,6 +205,7 @@ public:
void start(); void start();
bool pull(cv::GRunArgsP &&outs); bool pull(cv::GRunArgsP &&outs);
bool pull(cv::GOptRunArgsP &&outs); bool pull(cv::GOptRunArgsP &&outs);
std::tuple<bool, cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>> pull();
bool try_pull(cv::GRunArgsP &&outs); bool try_pull(cv::GRunArgsP &&outs);
void stop(); void stop();
bool running() const; bool running() const;
@@ -6,10 +6,158 @@
#include "../test_precomp.hpp" #include "../test_precomp.hpp"
#include "../gapi_mock_kernels.hpp"
namespace opencv_test namespace opencv_test
{ {
namespace
{
class GMockExecutable final: public cv::gimpl::GIslandExecutable
{
virtual inline bool canReshape() const override {
return m_priv->m_can_reshape;
}
virtual void reshape(ade::Graph&, const GCompileArgs&) override
{
m_priv->m_reshape_counter++;
}
virtual void handleNewStream() override { }
virtual void run(std::vector<InObj>&&, std::vector<OutObj>&&) { }
virtual bool allocatesOutputs() const override
{
return true;
}
virtual cv::RMat allocate(const cv::GMatDesc&) const override
{
m_priv->m_allocate_counter++;
return cv::RMat();
}
// NB: GMockBackendImpl creates new unique_ptr<GMockExecutable>
// on every compile call. Need to share counters between instances in order
// to validate it in tests.
struct Priv
{
bool m_can_reshape;
int m_reshape_counter;
int m_allocate_counter;
};
std::shared_ptr<Priv> m_priv;
public:
GMockExecutable(bool can_reshape = true)
: m_priv(new Priv{can_reshape, 0, 0})
{
};
void setReshape(bool can_reshape) { m_priv->m_can_reshape = can_reshape; }
int getReshapeCounter() const { return m_priv->m_reshape_counter; }
int getAllocateCounter() const { return m_priv->m_allocate_counter; }
};
class GMockBackendImpl final: public cv::gapi::GBackend::Priv
{
virtual void unpackKernel(ade::Graph &,
const ade::NodeHandle &,
const cv::GKernelImpl &) override { }
virtual EPtr compile(const ade::Graph &,
const cv::GCompileArgs &,
const std::vector<ade::NodeHandle> &) const override
{
++m_compile_counter;
return EPtr{new GMockExecutable(m_exec)};
}
mutable int m_compile_counter = 0;
GMockExecutable m_exec;
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;
}
public:
GMockBackendImpl(const GMockExecutable& exec) : m_exec(exec) { };
int getCompileCounter() const { return m_compile_counter; }
};
class GMockFunctor : public gapi::cpu::GOCVFunctor
{
public:
GMockFunctor(cv::gapi::GBackend backend,
const char* id,
const Meta &meta,
const Impl& impl)
: gapi::cpu::GOCVFunctor(id, meta, impl), m_backend(backend)
{
}
cv::gapi::GBackend backend() const override { return m_backend; }
private:
cv::gapi::GBackend m_backend;
};
template<typename K, typename Callable>
GMockFunctor mock_kernel(const cv::gapi::GBackend& backend, Callable c)
{
using P = cv::detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
return GMockFunctor{ backend
, K::id()
, &K::getOutMeta
, std::bind(&P::callFunctor, std::placeholders::_1, c)
};
}
void dummyFooImpl(const cv::Mat&, cv::Mat&) { };
void dummyBarImpl(const cv::Mat&, const cv::Mat&, cv::Mat&) { };
struct GExecutorReshapeTest: public ::testing::Test
{
GExecutorReshapeTest()
: comp([](){
cv::GMat in;
cv::GMat out = I::Bar::on(I::Foo::on(in), in);
return cv::GComputation(in, out);
})
{
backend_impl1 = std::make_shared<GMockBackendImpl>(island1);
backend1 = cv::gapi::GBackend{backend_impl1};
backend_impl2 = std::make_shared<GMockBackendImpl>(island2);
backend2 = cv::gapi::GBackend{backend_impl2};
auto kernel1 = mock_kernel<I::Foo>(backend1, dummyFooImpl);
auto kernel2 = mock_kernel<I::Bar>(backend2, dummyBarImpl);
pkg = cv::gapi::kernels(kernel1, kernel2);
in_mat1 = cv::Mat::eye(32, 32, CV_8UC1);
in_mat2 = cv::Mat::eye(64, 64, CV_8UC1);
}
cv::GComputation comp;
GMockExecutable island1;
std::shared_ptr<GMockBackendImpl> backend_impl1;
cv::gapi::GBackend backend1;
GMockExecutable island2;
std::shared_ptr<GMockBackendImpl> backend_impl2;
cv::gapi::GBackend backend2;
cv::gapi::GKernelPackage pkg;
cv::Mat in_mat1, in_mat2, out_mat;;
};
} // anonymous namespace
// FIXME: avoid code duplication // FIXME: avoid code duplication
// The below graph and config is taken from ComplexIslands test suite // The below graph and config is taken from ComplexIslands test suite
TEST(GExecutor, SmokeTest) TEST(GExecutor, SmokeTest)
@@ -77,6 +225,75 @@ TEST(GExecutor, SmokeTest)
// with breakdown worked) // with breakdown worked)
} }
TEST_F(GExecutorReshapeTest, ReshapeInsteadOfRecompile)
{
// NB: Initial state
EXPECT_EQ(0, backend_impl1->getCompileCounter());
EXPECT_EQ(0, backend_impl2->getCompileCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
EXPECT_EQ(0, island2.getReshapeCounter());
// NB: First compilation.
comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(1, backend_impl1->getCompileCounter());
EXPECT_EQ(1, backend_impl2->getCompileCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
EXPECT_EQ(0, island2.getReshapeCounter());
// NB: GMockBackendImpl implements "reshape" method,
// so it won't be recompiled if the meta is changed.
comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(1, backend_impl1->getCompileCounter());
EXPECT_EQ(1, backend_impl2->getCompileCounter());
EXPECT_EQ(1, island1.getReshapeCounter());
EXPECT_EQ(1, island2.getReshapeCounter());
}
TEST_F(GExecutorReshapeTest, OneBackendNotReshapable)
{
// NB: Make first island not reshapable
island1.setReshape(false);
// NB: Initial state
EXPECT_EQ(0, backend_impl1->getCompileCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
EXPECT_EQ(0, backend_impl2->getCompileCounter());
EXPECT_EQ(0, island2.getReshapeCounter());
// NB: First compilation.
comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(1, backend_impl1->getCompileCounter());
EXPECT_EQ(1, backend_impl2->getCompileCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
EXPECT_EQ(0, island2.getReshapeCounter());
// NB: Since one of islands isn't reshapable
// the entire graph isn't reshapable as well.
comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(2, backend_impl1->getCompileCounter());
EXPECT_EQ(2, backend_impl2->getCompileCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
EXPECT_EQ(0, island2.getReshapeCounter());
}
TEST_F(GExecutorReshapeTest, ReshapeCallAllocate)
{
// NB: Initial state
EXPECT_EQ(0, island1.getAllocateCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
// NB: First compilation.
comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(1, island1.getAllocateCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
// NB: The entire graph is reshapable, so it won't be recompiled, but reshaped.
// Check that reshape call "allocate" to reallocate buffers.
comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(2, island1.getAllocateCounter());
EXPECT_EQ(1, island1.getReshapeCounter());
}
// FIXME: Add explicit tests on GMat/GScalar/GArray<T> being connectors // FIXME: Add explicit tests on GMat/GScalar/GArray<T> being connectors
// between executed islands // between executed islands
@@ -639,8 +639,8 @@ INSTANTIATE_TEST_CASE_P(RenderBGROCVTestRectsImpl, RenderBGROCVTestRects,
Values(cv::Rect(100, 100, 200, 200)), Values(cv::Rect(100, 100, 200, 200)),
Values(cv::Scalar(100, 50, 150)), Values(cv::Scalar(100, 50, 150)),
Values(2), Values(2),
Values(LINE_8), Values(LINE_8, LINE_4),
Values(0))); Values(0, 1)));
INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestRectsImpl, RenderNV12OCVTestRects, INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestRectsImpl, RenderNV12OCVTestRects,
Combine(Values(cv::Size(1280, 720)), Combine(Values(cv::Size(1280, 720)),
@@ -673,8 +673,8 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestCirclesImpl, RenderNV12OCVTestCircles,
Values(10), Values(10),
Values(cv::Scalar(100, 50, 150)), Values(cv::Scalar(100, 50, 150)),
Values(2), Values(2),
Values(LINE_8), Values(LINE_8, LINE_4),
Values(0))); Values(0, 1)));
INSTANTIATE_TEST_CASE_P(RenderMFrameOCVTestCirclesImpl, RenderMFrameOCVTestCircles, INSTANTIATE_TEST_CASE_P(RenderMFrameOCVTestCirclesImpl, RenderMFrameOCVTestCircles,
Combine(Values(cv::Size(1280, 720)), Combine(Values(cv::Size(1280, 720)),
@@ -2,7 +2,7 @@
// It is subject to the license terms in the LICENSE file found in the top-level directory // 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. // of this distribution and at http://opencv.org/license.html.
// //
// Copyright (C) 2019-2020 Intel Corporation // Copyright (C) 2019-2021 Intel Corporation
#include "../test_precomp.hpp" #include "../test_precomp.hpp"
@@ -244,6 +244,35 @@ public:
} }
}; };
void checkPullOverload(const cv::Mat& ref,
const bool has_output,
cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>& args) {
EXPECT_TRUE(has_output);
using runArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
cv::Mat out_mat;
switch (args.index()) {
case runArgs::index_of<cv::GRunArgs>():
{
auto outputs = util::get<cv::GRunArgs>(args);
EXPECT_EQ(1u, outputs.size());
out_mat = cv::util::get<cv::Mat>(outputs[0]);
break;
}
case runArgs::index_of<cv::GOptRunArgs>():
{
auto outputs = util::get<cv::GOptRunArgs>(args);
EXPECT_EQ(1u, outputs.size());
auto opt_mat = cv::util::get<cv::optional<cv::Mat>>(outputs[0]);
ASSERT_TRUE(opt_mat.has_value());
out_mat = *opt_mat;
break;
}
default: GAPI_Assert(false && "Incorrect type of Args");
}
EXPECT_EQ(0., cv::norm(ref, out_mat, cv::NORM_INF));
}
} // anonymous namespace } // anonymous namespace
TEST_P(GAPI_Streaming, SmokeTest_ConstInput_GMat) TEST_P(GAPI_Streaming, SmokeTest_ConstInput_GMat)
@@ -1336,13 +1365,45 @@ TEST(Streaming, Python_Pull_Overload)
bool has_output; bool has_output;
cv::GRunArgs outputs; cv::GRunArgs outputs;
std::tie(has_output, outputs) = ccomp.pull(); using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
RunArgs args;
EXPECT_TRUE(has_output); std::tie(has_output, args) = ccomp.pull();
EXPECT_EQ(1u, outputs.size());
auto out_mat = cv::util::get<cv::Mat>(outputs[0]); checkPullOverload(in_mat, has_output, args);
EXPECT_EQ(0., cv::norm(in_mat, out_mat, cv::NORM_INF));
ccomp.stop();
EXPECT_FALSE(ccomp.running());
}
TEST(GAPI_Streaming_Desync, Python_Pull_Overload)
{
cv::GMat in;
cv::GMat out = cv::gapi::streaming::desync(in);
cv::GComputation c(in, out);
cv::Size sz(3,3);
cv::Mat in_mat(sz, CV_8UC3);
cv::randu(in_mat, cv::Scalar::all(0), cv::Scalar(255));
auto ccomp = c.compileStreaming();
EXPECT_TRUE(ccomp);
EXPECT_FALSE(ccomp.running());
ccomp.setSource(cv::gin(in_mat));
ccomp.start();
EXPECT_TRUE(ccomp.running());
bool has_output;
cv::GRunArgs outputs;
using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
RunArgs args;
std::tie(has_output, args) = ccomp.pull();
checkPullOverload(in_mat, has_output, args);
ccomp.stop(); ccomp.stop();
EXPECT_FALSE(ccomp.running()); EXPECT_FALSE(ccomp.running());
@@ -2132,9 +2193,17 @@ TEST(GAPI_Streaming, TestPythonAPI)
bool is_over = false; bool is_over = false;
cv::GRunArgs out_args; cv::GRunArgs out_args;
using RunArgs = cv::util::variant<cv::GRunArgs, cv::GOptRunArgs>;
RunArgs args;
// NB: Used by python bridge // NB: Used by python bridge
std::tie(is_over, out_args) = cc.pull(); std::tie(is_over, args) = cc.pull();
switch (args.index()) {
case RunArgs::index_of<cv::GRunArgs>():
out_args = util::get<cv::GRunArgs>(args); break;
default: GAPI_Assert(false && "Incorrect type of return value");
}
ASSERT_EQ(1u, out_args.size()); ASSERT_EQ(1u, out_args.size());
ASSERT_TRUE(cv::util::holds_alternative<cv::Mat>(out_args[0])); ASSERT_TRUE(cv::util::holds_alternative<cv::Mat>(out_args[0]));
@@ -2145,4 +2214,69 @@ TEST(GAPI_Streaming, TestPythonAPI)
cc.stop(); cc.stop();
} }
TEST(GAPI_Streaming, TestDesyncRMat) {
cv::GMat in;
auto blurred = cv::gapi::blur(in, cv::Size{3,3});
auto desynced = cv::gapi::streaming::desync(blurred);
auto out = in - blurred;
auto pipe = cv::GComputation(cv::GIn(in), cv::GOut(desynced, out)).compileStreaming();
cv::Size sz(32,32);
cv::Mat in_mat(sz, CV_8UC3);
cv::randu(in_mat, cv::Scalar::all(0), cv::Scalar(255));
pipe.setSource(cv::gin(in_mat));
pipe.start();
cv::optional<cv::RMat> out_desync;
cv::optional<cv::RMat> out_rmat;
while (true) {
// Initially it throwed "bad variant access" since there was
// no RMat handling in wrap_opt_arg
EXPECT_NO_THROW(pipe.pull(cv::gout(out_desync, out_rmat)));
if (out_rmat) break;
}
}
G_API_OP(GTestBlur, <GFrame(GFrame)>, "test.blur") {
static GFrameDesc outMeta(GFrameDesc d) { return d; }
};
GAPI_OCV_KERNEL(GOcvTestBlur, GTestBlur) {
static void run(const cv::MediaFrame& in, cv::MediaFrame& out) {
auto d = in.desc();
GAPI_Assert(d.fmt == cv::MediaFormat::BGR);
auto view = in.access(cv::MediaFrame::Access::R);
cv::Mat mat(d.size, CV_8UC3, view.ptr[0]);
cv::Mat blurred;
cv::blur(mat, blurred, cv::Size{3,3});
out = cv::MediaFrame::Create<TestMediaBGR>(blurred);
}
};
TEST(GAPI_Streaming, TestDesyncMediaFrame) {
initTestDataPath();
cv::GFrame in;
auto blurred = GTestBlur::on(in);
auto desynced = cv::gapi::streaming::desync(blurred);
auto out = GTestBlur::on(blurred);
auto pipe = cv::GComputation(cv::GIn(in), cv::GOut(desynced, out))
.compileStreaming(cv::compile_args(cv::gapi::kernels<GOcvTestBlur>()));
std::string filepath = findDataFile("cv/video/768x576.avi");
try {
pipe.setSource<BGRSource>(filepath);
} catch(...) {
throw SkipTestException("Video file can not be opened");
}
pipe.start();
cv::optional<cv::MediaFrame> out_desync;
cv::optional<cv::MediaFrame> out_frame;
while (true) {
// Initially it throwed "bad variant access" since there was
// no MediaFrame handling in wrap_opt_arg
EXPECT_NO_THROW(pipe.pull(cv::gout(out_desync, out_frame)));
if (out_frame) break;
}
}
} // namespace opencv_test } // namespace opencv_test
-6
View File
@@ -2219,12 +2219,6 @@ static PyMethodDef special_methods[] = {
#ifdef HAVE_OPENCV_DNN #ifdef HAVE_OPENCV_DNN
{"dnn_registerLayer", CV_PY_FN_WITH_KW(pyopencv_cv_dnn_registerLayer), "registerLayer(type, class) -> None"}, {"dnn_registerLayer", CV_PY_FN_WITH_KW(pyopencv_cv_dnn_registerLayer), "registerLayer(type, class) -> None"},
{"dnn_unregisterLayer", CV_PY_FN_WITH_KW(pyopencv_cv_dnn_unregisterLayer), "unregisterLayer(type) -> None"}, {"dnn_unregisterLayer", CV_PY_FN_WITH_KW(pyopencv_cv_dnn_unregisterLayer), "unregisterLayer(type) -> None"},
#endif
#ifdef HAVE_OPENCV_GAPI
{"GIn", CV_PY_FN_WITH_KW(pyopencv_cv_GIn), "GIn(...) -> GInputProtoArgs"},
{"GOut", CV_PY_FN_WITH_KW(pyopencv_cv_GOut), "GOut(...) -> GOutputProtoArgs"},
{"gin", CV_PY_FN_WITH_KW(pyopencv_cv_gin), "gin(...) -> ExtractArgsCallback"},
{"descr_of", CV_PY_FN_WITH_KW(pyopencv_cv_descr_of), "descr_of(...) -> ExtractMetaCallback"},
#endif #endif
{NULL, NULL}, {NULL, NULL},
}; };
+1
View File
@@ -832,6 +832,7 @@ class CppHeaderParser(object):
("GAPI_EXPORTS_W_SIMPLE","CV_EXPORTS_W_SIMPLE"), ("GAPI_EXPORTS_W_SIMPLE","CV_EXPORTS_W_SIMPLE"),
("GAPI_WRAP", "CV_WRAP"), ("GAPI_WRAP", "CV_WRAP"),
("GAPI_PROP", "CV_PROP"), ("GAPI_PROP", "CV_PROP"),
("GAPI_PROP_RW", "CV_PROP_RW"),
('defined(GAPI_STANDALONE)', '0'), ('defined(GAPI_STANDALONE)', '0'),
]) ])