From 050c960dfca68b5fc4209a8c542945f268ad48b7 Mon Sep 17 00:00:00 2001 From: Dmitry Matveev Date: Fri, 2 Oct 2020 21:48:54 +0300 Subject: [PATCH] G-API: Integrated cv::MediaFrame as I/O type + CPU backend --- .../gapi/include/opencv2/gapi/cpu/gcpukernel.hpp | 2 +- modules/gapi/include/opencv2/gapi/garg.hpp | 5 +- modules/gapi/include/opencv2/gapi/gframe.hpp | 4 +- modules/gapi/include/opencv2/gapi/gkernel.hpp | 3 +- modules/gapi/include/opencv2/gapi/gmetaarg.hpp | 2 + modules/gapi/include/opencv2/gapi/gtype_traits.hpp | 4 +- modules/gapi/include/opencv2/gapi/media.hpp | 2 +- modules/gapi/src/api/gbackend.cpp | 12 +++++ modules/gapi/src/api/gframe.cpp | 23 ++++++--- modules/gapi/src/api/gproto.cpp | 14 +++++ modules/gapi/src/api/media.cpp | 2 +- modules/gapi/src/backends/common/gbackend.hpp | 13 +++-- modules/gapi/src/backends/common/serialization.cpp | 17 ++++++ modules/gapi/src/backends/common/serialization.hpp | 6 +++ modules/gapi/src/backends/cpu/gcpubackend.cpp | 8 +-- modules/gapi/src/compiler/gcompiled.cpp | 6 +++ modules/gapi/src/compiler/gcompiler.cpp | 4 +- modules/gapi/src/compiler/passes/dump_dot.cpp | 1 + modules/gapi/test/gapi_frame_tests.cpp | 60 ++++++++++------------ 19 files changed, 132 insertions(+), 56 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp index 86ceace..ef67930 100644 --- a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp +++ b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp @@ -164,7 +164,7 @@ template<> struct get_in }; template<> struct get_in { - static cv::Mat get(GCPUContext &ctx, int idx) { return get_in::get(ctx, idx); } + static cv::MediaFrame get(GCPUContext &ctx, int idx) { return ctx.inArg(idx); } }; template<> struct get_in { diff --git a/modules/gapi/include/opencv2/gapi/garg.hpp b/modules/gapi/include/opencv2/gapi/garg.hpp index f054e87..67ce0d9 100644 --- a/modules/gapi/include/opencv2/gapi/garg.hpp +++ b/modules/gapi/include/opencv2/gapi/garg.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -100,7 +102,8 @@ using GRunArg = util::variant< cv::Mat, cv::Scalar, cv::detail::VectorRef, - cv::detail::OpaqueRef + cv::detail::OpaqueRef, + cv::MediaFrame >; using GRunArgs = std::vector; diff --git a/modules/gapi/include/opencv2/gapi/gframe.hpp b/modules/gapi/include/opencv2/gapi/gframe.hpp index d066873..f555a93 100644 --- a/modules/gapi/include/opencv2/gapi/gframe.hpp +++ b/modules/gapi/include/opencv2/gapi/gframe.hpp @@ -42,7 +42,7 @@ private: }; /** @} */ -enum class MediaFormat +enum class MediaFormat: int { BGR = 0, NV12, @@ -56,6 +56,8 @@ struct GAPI_EXPORTS GFrameDesc { MediaFormat fmt; cv::Size size; + + bool operator== (const GFrameDesc &) const; }; static inline GFrameDesc empty_gframe_desc() { return GFrameDesc{}; } /** @} */ diff --git a/modules/gapi/include/opencv2/gapi/gkernel.hpp b/modules/gapi/include/opencv2/gapi/gkernel.hpp index 7752b10..b04cede 100644 --- a/modules/gapi/include/opencv2/gapi/gkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/gkernel.hpp @@ -95,11 +95,12 @@ namespace detail template struct MetaType; template<> struct MetaType { using type = GMatDesc; }; template<> struct MetaType { using type = GMatDesc; }; - template<> struct MetaType { using type = GMatDesc; }; + template<> struct MetaType { using type = GFrameDesc; }; template<> struct MetaType { using type = GScalarDesc; }; template struct MetaType > { using type = GArrayDesc; }; template struct MetaType > { using type = GOpaqueDesc; }; template struct MetaType { using type = T; }; // opaque args passed as-is + // FIXME: Move it to type traits? // 2. Hacky test based on MetaType to check if we operate on G-* type or not template using is_nongapi_type = std::is_same::type>; diff --git a/modules/gapi/include/opencv2/gapi/gmetaarg.hpp b/modules/gapi/include/opencv2/gapi/gmetaarg.hpp index 499de45..f21182c 100644 --- a/modules/gapi/include/opencv2/gapi/gmetaarg.hpp +++ b/modules/gapi/include/opencv2/gapi/gmetaarg.hpp @@ -18,6 +18,7 @@ #include #include #include +#include namespace cv { @@ -38,6 +39,7 @@ using GMetaArg = util::variant , GScalarDesc , GArrayDesc , GOpaqueDesc + , GFrameDesc >; GAPI_EXPORTS std::ostream& operator<<(std::ostream& os, const GMetaArg &); diff --git a/modules/gapi/include/opencv2/gapi/gtype_traits.hpp b/modules/gapi/include/opencv2/gapi/gtype_traits.hpp index 35a5567..c9800b2 100644 --- a/modules/gapi/include/opencv2/gapi/gtype_traits.hpp +++ b/modules/gapi/include/opencv2/gapi/gtype_traits.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include namespace cv @@ -67,7 +68,7 @@ namespace detail template<> struct GTypeTraits { static constexpr const ArgKind kind = ArgKind::GFRAME; - static constexpr const GShape shape = GShape::GMAT; + static constexpr const GShape shape = GShape::GFRAME; static constexpr const OpaqueKind op_kind = OpaqueKind::CV_UNKNOWN; }; template<> struct GTypeTraits @@ -125,6 +126,7 @@ namespace detail template<> struct GTypeOf { using type = cv::GScalar; }; template struct GTypeOf > { using type = cv::GArray; }; template struct GTypeOf { using type = cv::GOpaque;}; + template<> struct GTypeOf { using type = cv::GFrame; }; // FIXME: This is not quite correct since IStreamSource may produce not only Mat but also Scalar // and vector data. TODO: Extend the type dispatching on these types too. template<> struct GTypeOf { using type = cv::GMat;}; diff --git a/modules/gapi/include/opencv2/gapi/media.hpp b/modules/gapi/include/opencv2/gapi/media.hpp index 394db9b..a7fe258 100644 --- a/modules/gapi/include/opencv2/gapi/media.hpp +++ b/modules/gapi/include/opencv2/gapi/media.hpp @@ -27,7 +27,7 @@ public: explicit MediaFrame(AdapterPtr &&); template static cv::MediaFrame Create(Args&&...); - View access(Access); + View access(Access) const; cv::GFrameDesc desc() const; private: diff --git a/modules/gapi/src/api/gbackend.cpp b/modules/gapi/src/api/gbackend.cpp index 97b91bd..600e5cc 100644 --- a/modules/gapi/src/api/gbackend.cpp +++ b/modules/gapi/src/api/gbackend.cpp @@ -150,6 +150,10 @@ void bindInArg(Mag& mag, const RcDesc &rc, const GRunArg &arg, HandleRMat handle mag.template slot()[rc.id] = util::get(arg); break; + case GShape::GFRAME: + mag.template slot()[rc.id] = util::get(arg); + break; + default: util::throw_error(std::logic_error("Unsupported GShape type")); } @@ -216,6 +220,7 @@ void resetInternalData(Mag& mag, const Data &d) break; case GShape::GMAT: + case GShape::GFRAME: // Do nothing here - FIXME unify with initInternalData? break; @@ -236,6 +241,7 @@ cv::GRunArg getArg(const Mag& mag, const RcDesc &ref) // (and constructed by either bindIn/Out or resetInternal) case GShape::GARRAY: return GRunArg(mag.template slot().at(ref.id)); case GShape::GOPAQUE: return GRunArg(mag.template slot().at(ref.id)); + case GShape::GFRAME: return GRunArg(mag.template slot().at(ref.id)); default: util::throw_error(std::logic_error("Unsupported GShape type")); break; @@ -329,6 +335,12 @@ void unbind(Mag& mag, const RcDesc &rc) mag.slot().erase(rc.id); break; + case GShape::GFRAME: + // MediaFrame can also be associated with external memory, + // so requires a special handling here. + mag.slot().erase(rc.id); + break; + default: GAPI_Assert(false); } diff --git a/modules/gapi/src/api/gframe.cpp b/modules/gapi/src/api/gframe.cpp index 405924b..3f228f1 100644 --- a/modules/gapi/src/api/gframe.cpp +++ b/modules/gapi/src/api/gframe.cpp @@ -13,16 +13,11 @@ // cv::GFrame public implementation ////////////////////////////////////////////// cv::GFrame::GFrame() - : m_priv(new GOrigin(GShape::GMAT, GNode::Param())) { - // N.B.: The shape here is still GMAT as currently cv::Mat is used - // as an underlying host type. Will be changed to GFRAME once - // GExecutor & GStreamingExecutor & selected backends will be extended - // to support cv::MediaFrame. + : m_priv(new GOrigin(GShape::GFRAME, GNode::Param())) { } cv::GFrame::GFrame(const GNode &n, std::size_t out) - : m_priv(new GOrigin(GShape::GMAT, n, out)) { - // N.B.: GMAT is here for the same reason as above ^ + : m_priv(new GOrigin(GShape::GFRAME, n, out)) { } cv::GOrigin& cv::GFrame::priv() { @@ -34,7 +29,19 @@ const cv::GOrigin& cv::GFrame::priv() const { } namespace cv { -std::ostream& operator<<(std::ostream& os, const cv::GFrameDesc &) { + +bool GFrameDesc::operator== (const GFrameDesc &rhs) const { + return fmt == rhs.fmt && size == rhs.size; +} + +std::ostream& operator<<(std::ostream& os, const cv::GFrameDesc &d) { + os << '['; + switch (d.fmt) { + case MediaFormat::BGR: os << "BGR"; break; + case MediaFormat::NV12: os << "NV12"; break; + default: GAPI_Assert(false && "Invalid media format"); + } + os << ' ' << d.size << ']'; return os; } diff --git a/modules/gapi/src/api/gproto.cpp b/modules/gapi/src/api/gproto.cpp index 47cc47bb..ec7674a 100644 --- a/modules/gapi/src/api/gproto.cpp +++ b/modules/gapi/src/api/gproto.cpp @@ -122,6 +122,9 @@ cv::GMetaArg cv::descr_of(const cv::GRunArg &arg) case GRunArg::index_of(): return cv::GMetaArg(cv::util::get(arg).desc()); + case GRunArg::index_of(): + return cv::GMetaArg(cv::util::get(arg).desc()); + default: util::throw_error(std::logic_error("Unsupported GRunArg type")); } } @@ -133,6 +136,7 @@ cv::GMetaArgs cv::descr_of(const cv::GRunArgs &args) return metas; } +// FIXME: Is it tested for all types? cv::GMetaArg cv::descr_of(const cv::GRunArgP &argp) { switch (argp.index()) @@ -148,6 +152,7 @@ cv::GMetaArg cv::descr_of(const cv::GRunArgP &argp) } } +// FIXME: Is it tested for all types?? bool cv::can_describe(const GMetaArg& meta, const GRunArgP& argp) { switch (argp.index()) @@ -164,6 +169,7 @@ bool cv::can_describe(const GMetaArg& meta, const GRunArgP& argp) } } +// FIXME: Is it tested for all types?? bool cv::can_describe(const GMetaArg& meta, const GRunArg& arg) { switch (arg.index()) @@ -179,6 +185,7 @@ bool cv::can_describe(const GMetaArg& meta, const GRunArg& arg) case GRunArg::index_of(): return util::holds_alternative(meta); // FIXME(?) may be not the best option case GRunArg::index_of(): return util::holds_alternative(meta) && util::get(meta).canDescribe(cv::util::get(arg)); + case GRunArg::index_of(): return meta == cv::GMetaArg(util::get(arg).desc()); default: util::throw_error(std::logic_error("Unsupported GRunArg type")); } } @@ -192,6 +199,8 @@ bool cv::can_describe(const GMetaArgs &metas, const GRunArgs &args) }); } +// FIXME: Is it tested for all types? +// FIXME: Where does this validation happen?? void cv::validate_input_arg(const GRunArg& arg) { // FIXME: It checks only Mat argument @@ -248,6 +257,11 @@ std::ostream& operator<<(std::ostream& os, const cv::GMetaArg &arg) case cv::GMetaArg::index_of(): os << util::get(arg); break; + + case cv::GMetaArg::index_of(): + os << util::get(arg); + break; + default: GAPI_Assert(false); } diff --git a/modules/gapi/src/api/media.cpp b/modules/gapi/src/api/media.cpp index 1edfb80..212902e 100644 --- a/modules/gapi/src/api/media.cpp +++ b/modules/gapi/src/api/media.cpp @@ -22,7 +22,7 @@ cv::GFrameDesc cv::MediaFrame::desc() const { return m->adapter->meta(); } -cv::MediaFrame::View cv::MediaFrame::access(Access code) { +cv::MediaFrame::View cv::MediaFrame::access(Access code) const { return m->adapter->access(code); } diff --git a/modules/gapi/src/backends/common/gbackend.hpp b/modules/gapi/src/backends/common/gbackend.hpp index 650ff59..f747a0d 100644 --- a/modules/gapi/src/backends/common/gbackend.hpp +++ b/modules/gapi/src/backends/common/gbackend.hpp @@ -60,11 +60,18 @@ namespace magazine { }; } // namespace magazine + +using Mag = magazine::Class< cv::Mat + , cv::Scalar + , cv::detail::VectorRef + , cv::detail::OpaqueRef + , cv::RMat + , cv::RMat::View + , cv::MediaFrame #if !defined(GAPI_STANDALONE) -using Mag = magazine::Class; -#else -using Mag = magazine::Class; + , cv::UMat #endif + >; namespace magazine { diff --git a/modules/gapi/src/backends/common/serialization.cpp b/modules/gapi/src/backends/common/serialization.cpp index 0ccc78a..c0b3281 100644 --- a/modules/gapi/src/backends/common/serialization.cpp +++ b/modules/gapi/src/backends/common/serialization.cpp @@ -174,6 +174,17 @@ IIStream& operator>> (IIStream& is, cv::RMat&) { return is; } +IOStream& operator<< (IOStream& os, const cv::MediaFrame &) { + // Stub + GAPI_Assert(false && "cv::MediaFrame serialization is not supported!"); + return os; +} +IIStream& operator>> (IIStream& is, cv::MediaFrame &) { + // Stub + GAPI_Assert(false && "cv::MediaFrame serialization is not supported!"); + return is; +} + namespace { @@ -555,6 +566,12 @@ IIStream& operator>> (IIStream& is, cv::GMatDesc &d) { return is >> d.depth >> d.chan >> d.size >> d.planar >> d.dims; } +IOStream& operator<< (IOStream& os, const cv::GFrameDesc &d) { + return put_enum(os, d.fmt) << d.size; +} +IIStream& operator>> (IIStream& is, cv::GFrameDesc &d) { + return get_enum(is, d.fmt) >> d.size; +} IOStream& operator<< (IOStream& os, const cv::gimpl::RcDesc &rc) { // FIXME: HostCtor is not serialized! diff --git a/modules/gapi/src/backends/common/serialization.hpp b/modules/gapi/src/backends/common/serialization.hpp index 74afee5..4c60e71 100644 --- a/modules/gapi/src/backends/common/serialization.hpp +++ b/modules/gapi/src/backends/common/serialization.hpp @@ -83,6 +83,9 @@ GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::GOpaqueDesc &); GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::GArrayDesc &); GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::GArrayDesc &); +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::GFrameDesc &); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::GFrameDesc &); + #if !defined(GAPI_STANDALONE) GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::UMat &); GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::UMat &); @@ -100,6 +103,9 @@ GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::detail::VectorRef &); GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::detail::OpaqueRef &); GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::detail::OpaqueRef &); +GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::MediaFrame &); +GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::MediaFrame &); + GAPI_EXPORTS IOStream& operator<< (IOStream& os, const cv::gimpl::RcDesc &rc); GAPI_EXPORTS IIStream& operator>> (IIStream& is, cv::gimpl::RcDesc &rc); diff --git a/modules/gapi/src/backends/cpu/gcpubackend.cpp b/modules/gapi/src/backends/cpu/gcpubackend.cpp index 032875e..5e25403 100644 --- a/modules/gapi/src/backends/cpu/gcpubackend.cpp +++ b/modules/gapi/src/backends/cpu/gcpubackend.cpp @@ -128,9 +128,10 @@ cv::GArg cv::gimpl::GCPUExecutable::packArg(const GArg &arg) // No API placeholders allowed at this point // FIXME: this check has to be done somewhere in compilation stage. GAPI_Assert( arg.kind != cv::detail::ArgKind::GMAT - && arg.kind != cv::detail::ArgKind::GSCALAR - && arg.kind != cv::detail::ArgKind::GARRAY - && arg.kind != cv::detail::ArgKind::GOPAQUE); + && arg.kind != cv::detail::ArgKind::GSCALAR + && arg.kind != cv::detail::ArgKind::GARRAY + && arg.kind != cv::detail::ArgKind::GOPAQUE + && arg.kind != cv::detail::ArgKind::GFRAME); if (arg.kind != cv::detail::ArgKind::GOBJREF) { @@ -150,6 +151,7 @@ cv::GArg cv::gimpl::GCPUExecutable::packArg(const GArg &arg) // (and constructed by either bindIn/Out or resetInternal) case GShape::GARRAY: return GArg(m_res.slot().at(ref.id)); case GShape::GOPAQUE: return GArg(m_res.slot().at(ref.id)); + case GShape::GFRAME: return GArg(m_res.slot().at(ref.id)); default: util::throw_error(std::logic_error("Unsupported GShape type")); break; diff --git a/modules/gapi/src/compiler/gcompiled.cpp b/modules/gapi/src/compiler/gcompiled.cpp index 00340da..263878c 100644 --- a/modules/gapi/src/compiler/gcompiled.cpp +++ b/modules/gapi/src/compiler/gcompiled.cpp @@ -57,6 +57,11 @@ void cv::GCompiled::Priv::checkArgs(const cv::gimpl::GRuntimeArgs &args) const // FIXME: Add details on what is actually wrong } validate_input_args(args.inObjs); + // FIXME: Actually, the passed parameter vector is never checked + // against its shapes - so if you compile with GScalarDesc passed + // for GMat argument, you will get your compilation right (!!) + // Probably it was there but somehow that olds checks (if they + // exist) are bypassed now. } bool cv::GCompiled::Priv::canReshape() const @@ -97,6 +102,7 @@ cv::GCompiled::operator bool() const void cv::GCompiled::operator() (GRunArgs &&ins, GRunArgsP &&outs) { + // FIXME: Check that matches the protocol!!! // FIXME: Check that matches the protocol m_priv->run(cv::gimpl::GRuntimeArgs{std::move(ins),std::move(outs)}); } diff --git a/modules/gapi/src/compiler/gcompiler.cpp b/modules/gapi/src/compiler/gcompiler.cpp index b82f101..2f46ea8 100644 --- a/modules/gapi/src/compiler/gcompiler.cpp +++ b/modules/gapi/src/compiler/gcompiler.cpp @@ -311,9 +311,11 @@ void cv::gimpl::GCompiler::validateInputMeta() // FIXME: Auto-generate methods like this from traits: case GProtoArg::index_of(): case GProtoArg::index_of(): - case GProtoArg::index_of(): return util::holds_alternative(meta); + case GProtoArg::index_of(): + return util::holds_alternative(meta); + case GProtoArg::index_of(): return util::holds_alternative(meta); diff --git a/modules/gapi/src/compiler/passes/dump_dot.cpp b/modules/gapi/src/compiler/passes/dump_dot.cpp index 15e6a9f..b7f5ea9 100644 --- a/modules/gapi/src/compiler/passes/dump_dot.cpp +++ b/modules/gapi/src/compiler/passes/dump_dot.cpp @@ -33,6 +33,7 @@ void dumpDot(const ade::Graph &g, std::ostream& os) {cv::GShape::GSCALAR, "GScalar"}, {cv::GShape::GARRAY, "GArray"}, {cv::GShape::GOPAQUE, "GOpaque"}, + {cv::GShape::GFRAME, "GFrame"}, }; auto format_op_label = [&gr](ade::NodeHandle nh) -> std::string { diff --git a/modules/gapi/test/gapi_frame_tests.cpp b/modules/gapi/test/gapi_frame_tests.cpp index c0361bd..ab27108 100644 --- a/modules/gapi/test/gapi_frame_tests.cpp +++ b/modules/gapi/test/gapi_frame_tests.cpp @@ -14,47 +14,21 @@ namespace opencv_test { G_API_OP(GBlurFrame, , "test.blur_frame") { - static GMatDesc outMeta(GMatDesc in) { - return in; + static GMatDesc outMeta(GFrameDesc in) { + return cv::GMatDesc(CV_8U,3,in.size); } }; GAPI_OCV_KERNEL(OCVBlurFrame, GBlurFrame) { - static void run(const cv::Mat& in, cv::Mat& out) { - cv::blur(in, out, cv::Size{3,3}); + static void run(const cv::MediaFrame &in, cv::Mat& out) { + GAPI_Assert(in.desc().fmt == cv::MediaFormat::BGR); + cv::MediaFrame::View view = in.access(cv::MediaFrame::Access::R); + cv::blur(cv::Mat(in.desc().size, CV_8UC3, view.ptr[0], view.stride[0]), + out, + cv::Size{3,3}); } }; -struct GFrameTest : public ::testing::Test { - cv::Size sz{32,32}; - cv::Mat in_mat; - cv::Mat out_mat; - cv::Mat out_mat_ocv; - - GFrameTest() - : in_mat(cv::Mat(sz, CV_8UC1)) - , out_mat(cv::Mat::zeros(sz, CV_8UC1)) - , out_mat_ocv(cv::Mat::zeros(sz, CV_8UC1)) { - cv::randn(in_mat, cv::Scalar::all(127.0f), cv::Scalar::all(40.f)); - cv::blur(in_mat, out_mat_ocv, cv::Size{3,3}); - } - - void check() { - EXPECT_EQ(0, cvtest::norm(out_mat, out_mat_ocv, NORM_INF)); - } -}; - -TEST_F(GFrameTest, Input) { - cv::GFrame in; - auto out = GBlurFrame::on(in); - cv::GComputation c(cv::GIn(in), cv::GOut(out)); - - auto pkg = cv::gapi::kernels(); - c.apply(cv::gin(in_mat), cv::gout(out_mat), cv::compile_args(pkg)); - - check(); -} - //////////////////////////////////////////////////////////////////////////////// // cv::MediaFrame tests namespace { @@ -108,6 +82,7 @@ struct MediaFrame_BGR: public MediaFrame_Test { M bgr; MediaFrame_BGR() : bgr(M::eye(240, 320, CV_8UC3)) { + cv::randn(bgr, cv::Scalar::all(127.0f), cv::Scalar::all(40.f)); frame = MF::Create(bgr); } }; @@ -128,6 +103,23 @@ TEST_F(MediaFrame_BGR, Access) { EXPECT_EQ(bgr.step, view2.stride[0]); } +TEST_F(MediaFrame_BGR, Input) { + // Run the OpenCV code + cv::Mat out_mat_ocv, out_mat_gapi; + cv::blur(bgr, out_mat_ocv, cv::Size{3,3}); + + // Run the G-API code + cv::GFrame in; + cv::GMat out = GBlurFrame::on(in); + cv::GComputation(cv::GIn(in), cv::GOut(out)) + .apply(cv::gin(frame), + cv::gout(out_mat_gapi), + cv::compile_args(cv::gapi::kernels())); + + // Compare + EXPECT_EQ(0, cvtest::norm(out_mat_ocv, out_mat_gapi, NORM_INF)); +} + struct MediaFrame_NV12: public MediaFrame_Test { cv::Size sz; cv::Mat buf, y, uv; -- 2.7.4