diff --git a/modules/gapi/include/opencv2/gapi/gmat.hpp b/modules/gapi/include/opencv2/gapi/gmat.hpp index b6dbf4f575..3d89df7a59 100644 --- a/modules/gapi/include/opencv2/gapi/gmat.hpp +++ b/modules/gapi/include/opencv2/gapi/gmat.hpp @@ -46,6 +46,10 @@ private: std::shared_ptr m_priv; }; +namespace gapi { namespace own { + class Mat; +}}//gapi::own + /** @} */ /** @@ -58,10 +62,16 @@ struct GAPI_EXPORTS GMatDesc int depth; int chan; cv::gapi::own::Size size; // NB.: no multi-dimensional cases covered yet + bool planar; + + GMatDesc(int d, int c, cv::gapi::own::Size s, bool p = false) + : depth(d), chan(c), size(s), planar(p) {} + + GMatDesc() : GMatDesc(-1, -1, {-1,-1}) {} inline bool operator== (const GMatDesc &rhs) const { - return depth == rhs.depth && chan == rhs.chan && size == rhs.size; + return depth == rhs.depth && chan == rhs.chan && size == rhs.size && planar == rhs.planar; } inline bool operator!= (const GMatDesc &rhs) const @@ -69,6 +79,12 @@ struct GAPI_EXPORTS GMatDesc return !(*this == rhs); } + // Checks if the passed mat can be described by this descriptor + // (it handles the case when + // 1-channel mat can be reinterpreted as is (1-channel mat) + // and as a 3-channel planar mat with height divided by 3) + bool canDescribe(const cv::gapi::own::Mat& mat) const; + // Meta combinator: return a new GMatDesc which differs in size by delta // (all other fields are taken unchanged from this GMatDesc) // FIXME: a better name? @@ -88,6 +104,8 @@ struct GAPI_EXPORTS GMatDesc { return withSize(to_own(sz)); } + + bool canDescribe(const cv::Mat& mat) const; #endif // !defined(GAPI_STANDALONE) // Meta combinator: return a new GMatDesc which differs in size by delta // (all other fields are taken unchanged from this GMatDesc) @@ -125,6 +143,43 @@ struct GAPI_EXPORTS GMatDesc desc.chan = dchan; return desc; } + + // Meta combinator: return a new GMatDesc with planar flag set + // (no size changes are performed, only channel interpretation is changed + // (interleaved -> planar) + GMatDesc asPlanar() const + { + GAPI_Assert(planar == false); + GMatDesc desc(*this); + desc.planar = true; + return desc; + } + + // Meta combinator: return a new GMatDesc + // reinterpreting 1-channel input as planar image + // (size height is divided by plane number) + GMatDesc asPlanar(int planes) const + { + GAPI_Assert(planar == false); + GAPI_Assert(chan == 1); + GAPI_Assert(planes > 1); + GAPI_Assert(size.height % planes == 0); + GMatDesc desc(*this); + desc.size.height /= planes; + desc.chan = planes; + return desc.asPlanar(); + } + + // Meta combinator: return a new GMatDesc with planar flag set to false + // (no size changes are performed, only channel interpretation is changed + // (planar -> interleaved) + GMatDesc asInterleaved() const + { + GAPI_Assert(planar == true); + GMatDesc desc(*this); + desc.planar = false; + return desc; + } }; static inline GMatDesc empty_gmat_desc() { return GMatDesc{-1,-1,{-1,-1}}; } @@ -138,7 +193,6 @@ GAPI_EXPORTS GMatDesc descr_of(const cv::UMat &mat); /** @} */ namespace gapi { namespace own { - class Mat; GAPI_EXPORTS GMatDesc descr_of(const Mat &mat); }}//gapi::own diff --git a/modules/gapi/include/opencv2/gapi/gproto.hpp b/modules/gapi/include/opencv2/gapi/gproto.hpp index 7f50623bee..7b11f0de6d 100644 --- a/modules/gapi/include/opencv2/gapi/gproto.hpp +++ b/modules/gapi/include/opencv2/gapi/gproto.hpp @@ -112,8 +112,16 @@ GMetaArgs GAPI_EXPORTS descr_of(const GRunArgs &args); // Transform run-time operation result argument into metadata extracted from that argument // Used to compare the metadata, which generated at compile time with the metadata result operation in run time -GMetaArg GAPI_EXPORTS descr_of(const GRunArgP& argp); +GMetaArg GAPI_EXPORTS descr_of(const GRunArgP& argp); +// Checks if run-time computation argument can be described by metadata +bool GAPI_EXPORTS can_describe(const GMetaArg& meta, const GRunArg& arg); +bool GAPI_EXPORTS can_describe(const GMetaArgs& metas, const GRunArgs& args); + +// Checks if run-time computation result argument can be described by metadata. +// Used to check if the metadata generated at compile time +// coincides with output arguments passed to computation in cpu and ocl backends +bool GAPI_EXPORTS can_describe(const GMetaArg& meta, const GRunArgP& argp); } // namespace cv diff --git a/modules/gapi/include/opencv2/gapi/gtype_traits.hpp b/modules/gapi/include/opencv2/gapi/gtype_traits.hpp index d05e02e0ec..bb47cd3f75 100644 --- a/modules/gapi/include/opencv2/gapi/gtype_traits.hpp +++ b/modules/gapi/include/opencv2/gapi/gtype_traits.hpp @@ -79,6 +79,7 @@ namespace detail template struct GTypeOf; #if !defined(GAPI_STANDALONE) template<> struct GTypeOf { using type = cv::GMat; }; + template<> struct GTypeOf { using type = cv::GMat; }; template<> struct GTypeOf { using type = cv::GScalar; }; #endif // !defined(GAPI_STANDALONE) template<> struct GTypeOf { using type = cv::GMat; }; diff --git a/modules/gapi/src/api/gbackend.cpp b/modules/gapi/src/api/gbackend.cpp index 8144d21d47..3dfd2ef83b 100644 --- a/modules/gapi/src/api/gbackend.cpp +++ b/modules/gapi/src/api/gbackend.cpp @@ -349,5 +349,24 @@ void writeBack(const Mag& mag, const RcDesc &rc, GRunArgP &g_arg, bool is_umat) } } // namespace magazine + +void createMat(const cv::GMatDesc desc, cv::gapi::own::Mat& mat) +{ + const auto type = desc.planar ? desc.depth : CV_MAKETYPE(desc.depth, desc.chan); + const auto size = desc.planar ? cv::gapi::own::Size{desc.size.width, desc.size.height*desc.chan} + : desc.size; + mat.create(size, type); +} + +#if !defined(GAPI_STANDALONE) +void createMat(const cv::GMatDesc desc, cv::Mat& mat) +{ + const auto type = desc.planar ? desc.depth : CV_MAKETYPE(desc.depth, desc.chan); + const auto size = desc.planar ? cv::Size{desc.size.width, desc.size.height*desc.chan} + : cv::gapi::own::to_ocv(desc.size); + mat.create(size, type); +} +#endif + } // namespace gimpl } // namespace cv diff --git a/modules/gapi/src/api/gmat.cpp b/modules/gapi/src/api/gmat.cpp index ac58a9caf1..1466a26540 100644 --- a/modules/gapi/src/api/gmat.cpp +++ b/modules/gapi/src/api/gmat.cpp @@ -99,9 +99,32 @@ std::ostream& operator<<(std::ostream& os, const cv::GMatDesc &desc) break; } - os << "C" << desc.chan << " "; + os << "C" << desc.chan; + if (desc.planar) os << "p"; + os << " "; os << desc.size.width << "x" << desc.size.height; return os; } + +namespace { +template inline bool canDescribeHelper(const GMatDesc& desc, const M& mat) +{ + const auto mat_desc = desc.planar ? descr_of(mat).asPlanar(desc.chan) : descr_of(mat); + return desc == mat_desc; } +} // anonymous namespace + +bool GMatDesc::canDescribe(const cv::gapi::own::Mat& mat) const +{ + return canDescribeHelper(*this, mat); +} + +#if !defined(GAPI_STANDALONE) +bool GMatDesc::canDescribe(const cv::Mat& mat) const +{ + return canDescribeHelper(*this, mat); +} +#endif + +}// namespace cv diff --git a/modules/gapi/src/api/gproto.cpp b/modules/gapi/src/api/gproto.cpp index 70604cbbca..9e3e8e6673 100644 --- a/modules/gapi/src/api/gproto.cpp +++ b/modules/gapi/src/api/gproto.cpp @@ -132,6 +132,51 @@ cv::GMetaArg cv::descr_of(const cv::GRunArgP &argp) } } +bool cv::can_describe(const GMetaArg& meta, const GRunArgP& argp) +{ + switch (argp.index()) + { +#if !defined(GAPI_STANDALONE) + case GRunArgP::index_of(): return util::holds_alternative(meta) && + util::get(meta).canDescribe(*util::get(argp)); + case GRunArgP::index_of(): return meta == GMetaArg(descr_of(*util::get(argp))); + case GRunArgP::index_of(): return meta == GMetaArg(descr_of(*util::get(argp))); +#endif // !defined(GAPI_STANDALONE) + case GRunArgP::index_of(): return util::holds_alternative(meta) && + util::get(meta).canDescribe(*util::get(argp)); + case GRunArgP::index_of(): return meta == GMetaArg(descr_of(*util::get(argp))); + case GRunArgP::index_of(): return meta == GMetaArg(util::get(argp).descr_of()); + default: util::throw_error(std::logic_error("Unsupported GRunArgP type")); + } +} + +bool cv::can_describe(const GMetaArg& meta, const GRunArg& arg) +{ + switch (arg.index()) + { +#if !defined(GAPI_STANDALONE) + case GRunArg::index_of(): return util::holds_alternative(meta) && + util::get(meta).canDescribe(util::get(arg)); + case GRunArg::index_of(): return meta == cv::GMetaArg(descr_of(util::get(arg))); + case GRunArg::index_of(): return meta == cv::GMetaArg(descr_of(util::get(arg))); +#endif // !defined(GAPI_STANDALONE) + case GRunArg::index_of(): return util::holds_alternative(meta) && + util::get(meta).canDescribe(util::get(arg)); + case GRunArg::index_of(): return meta == cv::GMetaArg(descr_of(util::get(arg))); + case GRunArg::index_of(): return meta == cv::GMetaArg(util::get(arg).descr_of()); + default: util::throw_error(std::logic_error("Unsupported GRunArg type")); + } +} + +bool cv::can_describe(const GMetaArgs &metas, const GRunArgs &args) +{ + return metas.size() == args.size() && + std::equal(metas.begin(), metas.end(), args.begin(), + [](const GMetaArg& meta, const GRunArg& arg) { + return can_describe(meta, arg); + }); +} + namespace cv { std::ostream& operator<<(std::ostream& os, const cv::GMetaArg &arg) { diff --git a/modules/gapi/src/backends/common/gbackend.hpp b/modules/gapi/src/backends/common/gbackend.hpp index daa95026f6..0b2a425c24 100644 --- a/modules/gapi/src/backends/common/gbackend.hpp +++ b/modules/gapi/src/backends/common/gbackend.hpp @@ -99,7 +99,10 @@ inline cv::util::optional getCompileArg(const cv::GCompileArgs &args) return cv::util::optional(); } - +void createMat(const cv::GMatDesc desc, cv::gapi::own::Mat& mat); +#if !defined(GAPI_STANDALONE) +void createMat(const cv::GMatDesc desc, cv::Mat& mat); +#endif }} // cv::gimpl diff --git a/modules/gapi/src/backends/cpu/gcpubackend.cpp b/modules/gapi/src/backends/cpu/gcpubackend.cpp index 5cc8bb0b70..8924e3d21d 100644 --- a/modules/gapi/src/backends/cpu/gcpubackend.cpp +++ b/modules/gapi/src/backends/cpu/gcpubackend.cpp @@ -101,8 +101,8 @@ cv::gimpl::GCPUExecutable::GCPUExecutable(const ade::Graph &g, if (desc.storage == Data::Storage::INTERNAL && desc.shape == GShape::GMAT) { const auto mat_desc = util::get(desc.meta); - const auto type = CV_MAKETYPE(mat_desc.depth, mat_desc.chan); - m_res.slot()[desc.rc].create(mat_desc.size, type); + auto& mat = m_res.slot()[desc.rc]; + createMat(mat_desc, mat); } break; } @@ -207,14 +207,16 @@ void cv::gimpl::GCPUExecutable::run(std::vector &&input_objs, //As Kernels are forbidden to allocate memory for (Mat) outputs, //this code seems redundant, at least for Mats //FIXME: unify with cv::detail::ensure_out_mats_not_reallocated + //FIXME: when it's done, remove can_describe(const GMetaArg&, const GRunArgP&) + //and descr_of(const cv::GRunArgP &argp) for (const auto &out_it : ade::util::indexed(op_info.expected_out_metas)) { const auto out_index = ade::util::index(out_it); const auto expected_meta = ade::util::value(out_it); - const auto out_meta = descr_of(context.m_results[out_index]); - if (expected_meta != out_meta) + if (!can_describe(expected_meta, context.m_results[out_index])) { + const auto out_meta = descr_of(context.m_results[out_index]); util::throw_error (std::logic_error ("Output meta doesn't " diff --git a/modules/gapi/src/backends/ocl/goclbackend.cpp b/modules/gapi/src/backends/ocl/goclbackend.cpp index 64dbb07a46..6bfa74e35d 100644 --- a/modules/gapi/src/backends/ocl/goclbackend.cpp +++ b/modules/gapi/src/backends/ocl/goclbackend.cpp @@ -101,8 +101,8 @@ cv::gimpl::GOCLExecutable::GOCLExecutable(const ade::Graph &g, if (desc.storage == Data::Storage::INTERNAL && desc.shape == GShape::GMAT) { const auto mat_desc = util::get(desc.meta); - const auto type = CV_MAKETYPE(mat_desc.depth, mat_desc.chan); - m_res.slot()[desc.rc].create(mat_desc.size.height, mat_desc.size.width, type); + auto& mat = m_res.slot()[desc.rc]; + createMat(mat_desc, mat); } break; } @@ -208,10 +208,10 @@ void cv::gimpl::GOCLExecutable::run(std::vector &&input_objs, { const auto out_index = ade::util::index(out_it); const auto expected_meta = ade::util::value(out_it); - const auto out_meta = descr_of(context.m_results[out_index]); - if (expected_meta != out_meta) + if (!can_describe(expected_meta, context.m_results[out_index])) { + const auto out_meta = descr_of(context.m_results[out_index]); util::throw_error (std::logic_error ("Output meta doesn't " diff --git a/modules/gapi/src/compiler/gcompiled.cpp b/modules/gapi/src/compiler/gcompiled.cpp index 876575d945..00de6997ed 100644 --- a/modules/gapi/src/compiler/gcompiled.cpp +++ b/modules/gapi/src/compiler/gcompiled.cpp @@ -9,7 +9,7 @@ #include -#include "opencv2/gapi/gproto.hpp" // descr_of +#include "opencv2/gapi/gproto.hpp" // can_describe #include "opencv2/gapi/gcompiled.hpp" #include "compiler/gcompiled_priv.hpp" @@ -50,11 +50,10 @@ const cv::GMetaArgs& cv::GCompiled::Priv::outMetas() const void cv::GCompiled::Priv::checkArgs(const cv::gimpl::GRuntimeArgs &args) const { - const auto runtime_metas = descr_of(args.inObjs); - if (runtime_metas != m_metas) + if (!can_describe(m_metas, args.inObjs)) { - util::throw_error(std::logic_error("This object was compiled " - "for different metadata!")); + util::throw_error(std::logic_error("This object was compiled " + "for different metadata!")); // FIXME: Add details on what is actually wrong } } diff --git a/modules/gapi/src/executor/gexecutor.cpp b/modules/gapi/src/executor/gexecutor.cpp index f117c0633a..2594cde444 100644 --- a/modules/gapi/src/executor/gexecutor.cpp +++ b/modules/gapi/src/executor/gexecutor.cpp @@ -99,8 +99,8 @@ void cv::gimpl::GExecutor::initResource(const ade::NodeHandle &orig_nh) case GShape::GMAT: { const auto desc = util::get(d.meta); - const auto type = CV_MAKETYPE(desc.depth, desc.chan); - m_res.slot()[d.rc].create(desc.size, type); + auto& mat = m_res.slot()[d.rc]; + createMat(desc, mat); } break; @@ -152,21 +152,17 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) { using cv::util::get; const auto desc = get(d.meta); - const auto type = CV_MAKETYPE(desc.depth, desc.chan); - #if !defined(GAPI_STANDALONE) // Building as part of OpenCV - follow OpenCV behavior // if output buffer is not enough to hold the result, reallocate it auto& out_mat = *get(args.outObjs.at(index)); - out_mat.create(cv::gapi::own::to_ocv(desc.size), type); + createMat(desc, out_mat); #else // Building standalone - output buffer should always exist, // and _exact_ match our inferred metadata auto& out_mat = *get(args.outObjs.at(index)); - GAPI_Assert( out_mat.type() == type - && out_mat.data != nullptr - && out_mat.rows == desc.size.height - && out_mat.cols == desc.size.width) + GAPI_Assert(out_mat.data != nullptr && + desc.canDescribe(out_mat)) #endif // !defined(GAPI_STANDALONE) } } diff --git a/modules/gapi/test/gapi_desc_tests.cpp b/modules/gapi/test/gapi_desc_tests.cpp index a9d2e573f3..d0c551ad3e 100644 --- a/modules/gapi/test/gapi_desc_tests.cpp +++ b/modules/gapi/test/gapi_desc_tests.cpp @@ -236,4 +236,70 @@ TEST(GAPI_MetaDesc, Typed_Compile_MatchMetaType_Mixed) EXPECT_NO_THROW(cc.compile(desc1, desc2)); } +TEST(GAPI_MetaDesc, Compare_Planar) +{ + const auto desc0 = cv::GMatDesc{CV_8U,3,{32,32},false}; + const auto desc1 = cv::GMatDesc{CV_8U,3,{32,32},false}; + const auto desc2 = cv::GMatDesc{CV_8U,3,{32,32},true}; + const auto desc3 = cv::GMatDesc{CV_8U,3,{64,64},true}; + + EXPECT_TRUE(desc0 == desc1); + EXPECT_TRUE(desc1 != desc2); + EXPECT_TRUE(desc1 != desc3); + EXPECT_TRUE(desc2 != desc3); +} + +TEST(GAPI_MetaDesc, Sanity_asPlanar) +{ + constexpr int w = 32; + constexpr int h = 16; + const auto desc1 = cv::GMatDesc{CV_8U,3,{w,h},false}; + const auto desc2 = cv::GMatDesc{CV_8U,3,{w,h},true}; + + EXPECT_NO_THROW(desc1.asPlanar()); + EXPECT_NO_THROW(desc2.asInterleaved()); + EXPECT_ANY_THROW(desc1.asInterleaved()); + EXPECT_ANY_THROW(desc2.asPlanar()); +} + +TEST(GAPI_MetaDesc, Compare_asPlanar) +{ + constexpr int w = 32; + constexpr int h = 64; + const auto desc0 = cv::GMatDesc{CV_8U,3,{w,h},false}; + const auto desc1 = cv::GMatDesc{CV_8U,3,{w,h},true}; + + EXPECT_TRUE(desc0.asPlanar() == desc1); + EXPECT_TRUE(desc1.asInterleaved() == desc0); +} + +TEST(GAPI_MetaDesc, Compare_asPlanarTransform) +{ + constexpr int w = 64; + constexpr int h = 32; + const auto desc0 = cv::GMatDesc{CV_8U,3,{w,h},true}; + const auto desc1 = cv::GMatDesc{CV_8U,1,{w,h*3},false}; + + EXPECT_ANY_THROW(desc0.asPlanar(3)); + EXPECT_NO_THROW(desc1.asPlanar(3)); + EXPECT_TRUE(desc1.asPlanar(3) == desc0); +} + +TEST(GAPI_MetaDesc, CanDescribe) +{ + constexpr int w = 15; + constexpr int h = 7; + cv::Mat m0(h, w, CV_8UC3); + cv::GMatDesc md0{CV_8U,3,{w,h},false}; + + cv::Mat m1(h*3, w, CV_8UC1); + cv::GMatDesc md10{CV_8U,3,{w,h},true}; + cv::GMatDesc md11{CV_8U,1,{w,h*3},false}; + + EXPECT_TRUE (md0 .canDescribe(m0)); + EXPECT_FALSE(md0 .canDescribe(m1)); + EXPECT_TRUE (md10.canDescribe(m1)); + EXPECT_TRUE (md11.canDescribe(m1)); +} + } // namespace opencv_test diff --git a/modules/gapi/test/gapi_planar_test.cpp b/modules/gapi/test/gapi_planar_test.cpp new file mode 100644 index 0000000000..f763fdcf04 --- /dev/null +++ b/modules/gapi/test/gapi_planar_test.cpp @@ -0,0 +1,211 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2019 Intel Corporation + +#include "test_precomp.hpp" + +#include + +namespace opencv_test +{ + +G_TYPED_KERNEL(GResize3c3p, , "test.resize3c3p") { + static GMatDesc outMeta(GMatDesc in, Size sz, int) { + GAPI_Assert(in.depth == CV_8U); + GAPI_Assert(in.chan == 3); + GAPI_Assert(in.planar == false); + return in.withSize(sz).asPlanar(); + } +}; + +G_TYPED_KERNEL(GResize3p3p, , "test.resize3p3p") { + static GMatDesc outMeta(GMatDesc in, Size sz, int) { + GAPI_Assert(in.depth == CV_8U); + GAPI_Assert(in.chan == 3); + GAPI_Assert(in.planar); + return in.withSize(sz); + } +}; + +static GMatDesc NV12toRGBoutMeta(GMatDesc inY, GMatDesc inUV) +{ + GAPI_Assert(inY.depth == CV_8U); + GAPI_Assert(inUV.depth == CV_8U); + GAPI_Assert(inY.chan == 1); + GAPI_Assert(inY.planar == false); + GAPI_Assert(inUV.chan == 2); + GAPI_Assert(inUV.planar == false); + GAPI_Assert(inY.size.width == 2 * inUV.size.width); + GAPI_Assert(inY.size.height == 2 * inUV.size.height); + return inY.withType(CV_8U, 3); +} + +G_TYPED_KERNEL(GNV12toRGB, , "test.nv12torgb") { + static GMatDesc outMeta(GMatDesc inY, GMatDesc inUV) { + return NV12toRGBoutMeta(inY, inUV); + } +}; + +G_TYPED_KERNEL(GNV12toRGBp, , "test.nv12torgbp") { + static GMatDesc outMeta(GMatDesc inY, GMatDesc inUV) { + return NV12toRGBoutMeta(inY, inUV).asPlanar(); + } +}; + +static void toPlanar(const cv::Mat& in, cv::Mat& out) +{ + GAPI_Assert(out.depth() == in.depth()); + GAPI_Assert(out.channels() == 1); + GAPI_Assert(in.channels() == 3); + GAPI_Assert(out.cols == in.cols); + GAPI_Assert(out.rows == 3*in.rows); + + std::vector outs(3); + for (int i = 0; i < 3; i++) { + outs[i] = out(cv::Rect(0, i*in.rows, in.cols, in.rows)); + } + cv::split(in, outs); +} + +GAPI_OCV_KERNEL(OCVResize3c3p, GResize3c3p) +{ + static void run(const cv::Mat& in, cv::Size out_sz, int interp, cv::Mat& out) + { + cv::Mat resized_mat; + cv::resize(in, resized_mat, out_sz, 0, 0, interp); + + std::vector outs(3); + for (int i = 0; i < 3; i++) { + outs[i] = out(cv::Rect(0, i*out_sz.height, out_sz.width, out_sz.height)); + } + cv::split(resized_mat, outs); + } +}; + +GAPI_OCV_KERNEL(OCVResize3p3p, GResize3p3p) +{ + static void run(const cv::Mat& in, cv::Size out_sz, int interp, cv::Mat& out) + { + std::vector ins(3); + std::vector outs(3); + + int inH = in.rows / 3; + int inW = in.cols; + int outH = out.rows / 3; + int outW = out.cols; + for (int i = 0; i < 3; i++) { + ins [i] = in(cv::Rect(0, i*inH, inW, inH)); + outs[i] = out(cv::Rect(0, i*outH, outW, outH)); + cv::resize(ins[i], outs[i], out_sz, 0, 0, interp); + } + } +}; + +GAPI_OCV_KERNEL(OCVNV12toRGBp, GNV12toRGBp) +{ + static void run(const cv::Mat& inY, const cv::Mat& inUV, cv::Mat& out) + { + cv::Mat rgb; + cv::cvtColorTwoPlane(inY, inUV, rgb, cv::COLOR_YUV2RGB_NV12); + toPlanar(rgb, out); + } +}; + +struct PlanarTest : public TestWithParam > {}; +TEST_P(PlanarTest, Resize3c3p) +{ + cv::Size in_sz, out_sz; + std::tie(in_sz, out_sz) = GetParam(); + int interp = cv::INTER_NEAREST; + + cv::Mat in_mat = cv::Mat(in_sz, CV_8UC3); + cv::randn(in_mat, cv::Scalar::all(127.0f), cv::Scalar::all(40.f)); + + cv::Mat out_mat = cv::Mat::zeros(out_sz.height*3, out_sz.width, CV_8UC1); + cv::Mat out_mat_ocv = cv::Mat::zeros(out_sz.height*3, out_sz.width, CV_8UC1); + + cv::GMat in; + auto out = GResize3c3p::on(in, out_sz, interp); + 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)); + + cv::Mat resized_mat; + cv::resize(in_mat, resized_mat, out_sz, 0, 0, interp); + toPlanar(resized_mat, out_mat_ocv); + + EXPECT_EQ(0, cv::countNonZero(out_mat != out_mat_ocv)); +} + +TEST_P(PlanarTest, Resize3p3p) +{ + cv::Size in_sz, out_sz; + std::tie(in_sz, out_sz) = GetParam(); + int interp = cv::INTER_NEAREST; + + cv::Mat in_mat = cv::Mat(cv::Size{in_sz.width, in_sz.height*3}, CV_8UC1); + cv::randn(in_mat, cv::Scalar::all(127.0f), cv::Scalar::all(40.f)); + + cv::Mat out_mat = cv::Mat::zeros(out_sz.height*3, out_sz.width, CV_8UC1); + cv::Mat out_mat_ocv = cv::Mat::zeros(out_sz.height*3, out_sz.width, CV_8UC1); + + cv::GMat in; + auto out = GResize3p3p::on(in, out_sz, interp); + cv::GComputation c(cv::GIn(in), cv::GOut(out)); + + auto pkg = cv::gapi::kernels(); + + c.compile(cv::descr_of(in_mat).asPlanar(3), cv::compile_args(pkg)) + (cv::gin(in_mat), cv::gout(out_mat)); + + for (int i = 0; i < 3; i++) { + const cv::Mat in_mat_roi = in_mat(cv::Rect(0, i*in_sz.height, in_sz.width, in_sz.height)); + cv::Mat out_mat_roi = out_mat_ocv(cv::Rect(0, i*out_sz.height, out_sz.width, out_sz.height)); + cv::resize(in_mat_roi, out_mat_roi, out_sz, 0, 0, interp); + } + + EXPECT_EQ(0, cv::countNonZero(out_mat != out_mat_ocv)); +} + +TEST_P(PlanarTest, Pipeline) +{ + cv::Size in_sz, out_sz; + std::tie(in_sz, out_sz) = GetParam(); + int interp = cv::INTER_NEAREST; + + cv::Mat in_mat = cv::Mat(cv::Size{in_sz.width, in_sz.height*3/2}, CV_8UC1); + cv::randn(in_mat, cv::Scalar::all(127.0f), cv::Scalar::all(40.f)); + + cv::Size uv_sz(in_sz.width / 2, in_sz.height / 2); + + cv::Mat y_mat = cv::Mat(in_sz, CV_8UC1, in_mat.data); + cv::Mat uv_mat = cv::Mat(uv_sz, CV_8UC2, in_mat.data + in_mat.step1() * in_sz.height); + + cv::Mat out_mat = cv::Mat::zeros(out_sz.height*3, out_sz.width, CV_8UC1); + cv::Mat out_mat_ocv = cv::Mat::zeros(out_sz.height*3, out_sz.width, CV_8UC1); + + cv::GMat inY, inUV; + auto out = GResize3p3p::on(GNV12toRGBp::on(inY, inUV), out_sz, interp); + cv::GComputation c(cv::GIn(inY, inUV), cv::GOut(out)); + + auto pkg = cv::gapi::kernels(); + c.apply(cv::gin(y_mat, uv_mat), cv::gout(out_mat), cv::compile_args(pkg)); + + cv::Mat rgb, resized_mat; + cv::cvtColorTwoPlane(y_mat, uv_mat, rgb, cv::COLOR_YUV2RGB_NV12); + cv::resize(rgb, resized_mat, out_sz, 0, 0, interp); + toPlanar(resized_mat, out_mat_ocv); + + EXPECT_EQ(0, cv::countNonZero(out_mat != out_mat_ocv)); +} + +INSTANTIATE_TEST_CASE_P(Sanity, PlanarTest, + Values(std::make_pair(cv::Size{8, 8}, cv::Size{4, 4}) + ,std::make_pair(cv::Size{960, 540}, cv::Size{224, 224}) + ,std::make_pair(cv::Size{64, 64}, cv::Size{224, 224}) + )); + +} // namespace opencv_test diff --git a/modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp b/modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp index 3f88d6445a..d37df8f7be 100644 --- a/modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp +++ b/modules/gapi/test/internal/gapi_int_gmetaarg_test.cpp @@ -133,4 +133,69 @@ TEST(GMetaArg, Can_Get_Metas_From_Output_Run_Args) EXPECT_EQ(cv::Size(3, 3), m_desc.size); } +TEST(GMetaArg, Can_Describe_RunArg) +{ + cv::Mat m(3, 3, CV_8UC3); + cv::UMat um(3, 3, CV_8UC3); + cv::Scalar s; + constexpr int w = 3, h = 3, c = 3; + uchar data[w*h*c]; + cv::gapi::own::Mat om(h, w, CV_8UC3, data); + cv::gapi::own::Scalar os; + std::vector v; + + GMetaArgs metas = {GMetaArg(descr_of(m)), + GMetaArg(descr_of(um)), + GMetaArg(descr_of(s)), + GMetaArg(descr_of(om)), + GMetaArg(descr_of(os)), + GMetaArg(descr_of(v))}; + + auto in_run_args = cv::gin(m, um, s, om, os, v); + + for (int i = 0; i < 3; i++) { + EXPECT_TRUE(can_describe(metas[i], in_run_args[i])); + } +} + +TEST(GMetaArg, Can_Describe_RunArgs) +{ + cv::Mat m(3, 3, CV_8UC3); + cv::Scalar s; + std::vector v; + + GMetaArgs metas0 = {GMetaArg(descr_of(m)), GMetaArg(descr_of(s)), GMetaArg(descr_of(v))}; + auto in_run_args0 = cv::gin(m, s, v); + + EXPECT_TRUE(can_describe(metas0, in_run_args0)); + + auto in_run_args01 = cv::gin(m, s); + EXPECT_FALSE(can_describe(metas0, in_run_args01)); +} + +TEST(GMetaArg, Can_Describe_RunArgP) +{ + cv::Mat m(3, 3, CV_8UC3); + cv::UMat um(3, 3, CV_8UC3); + cv::Scalar s; + constexpr int w = 3, h = 3, c = 3; + uchar data[w*h*c]; + cv::gapi::own::Mat om(h, w, CV_8UC3, data); + cv::gapi::own::Scalar os; + std::vector v; + + GMetaArgs metas = {GMetaArg(descr_of(m)), + GMetaArg(descr_of(um)), + GMetaArg(descr_of(s)), + GMetaArg(descr_of(om)), + GMetaArg(descr_of(os)), + GMetaArg(descr_of(v))}; + + auto out_run_args = cv::gout(m, um, s, om, os, v); + + for (int i = 0; i < 3; i++) { + EXPECT_TRUE(can_describe(metas[i], out_run_args[i])); + } +} + } // namespace opencv_test