diff --git a/modules/gapi/include/opencv2/gapi/streaming/format.hpp b/modules/gapi/include/opencv2/gapi/streaming/format.hpp index 2837f301d4..c9d2fa3e0a 100644 --- a/modules/gapi/include/opencv2/gapi/streaming/format.hpp +++ b/modules/gapi/include/opencv2/gapi/streaming/format.hpp @@ -20,6 +20,19 @@ G_API_OP(GBGR, , "org.opencv.streaming.BGR") static GMatDesc outMeta(const GFrameDesc& in) { return GMatDesc{CV_8U, 3, in.size}; } }; +G_API_OP(GY, , "org.opencv.streaming.Y") { + static GMatDesc outMeta(const GFrameDesc& frameDesc) { + return GMatDesc { CV_8U, 1, frameDesc.size , false }; + } +}; + +G_API_OP(GUV, , "org.opencv.streaming.UV") { + static GMatDesc outMeta(const GFrameDesc& frameDesc) { + return GMatDesc { CV_8U, 2, cv::Size(frameDesc.size.width / 2, frameDesc.size.height / 2), + false }; + } +}; + /** @brief Gets bgr plane from input frame @note Function textual ID is "org.opencv.streaming.BGR" @@ -29,6 +42,25 @@ G_API_OP(GBGR, , "org.opencv.streaming.BGR") */ GAPI_EXPORTS cv::GMat BGR(const cv::GFrame& in); +/** @brief Extracts Y plane from media frame. + +Output image is 8-bit 1-channel image of @ref CV_8UC1. + +@note Function textual ID is "org.opencv.streaming.Y" + +@param frame input media frame. +*/ +GAPI_EXPORTS GMat Y(const cv::GFrame& frame); + +/** @brief Extracts UV plane from media frame. + +Output image is 8-bit 2-channel image of @ref CV_8UC2. + +@note Function textual ID is "org.opencv.streaming.UV" + +@param frame input media frame. +*/ +GAPI_EXPORTS GMat UV(const cv::GFrame& frame); } // namespace streaming //! @addtogroup gapi_transform diff --git a/modules/gapi/src/api/kernels_streaming.cpp b/modules/gapi/src/api/kernels_streaming.cpp index 25045eafbd..c88fbede5b 100644 --- a/modules/gapi/src/api/kernels_streaming.cpp +++ b/modules/gapi/src/api/kernels_streaming.cpp @@ -78,3 +78,11 @@ cv::GMat cv::gapi::streaming::desync(const cv::GMat &g) { cv::GMat cv::gapi::streaming::BGR(const cv::GFrame& in) { return cv::gapi::streaming::GBGR::on(in); } + +cv::GMat cv::gapi::streaming::Y(const cv::GFrame& in){ + return cv::gapi::streaming::GY::on(in); +} + +cv::GMat cv::gapi::streaming::UV(const cv::GFrame& in){ + return cv::gapi::streaming::GUV::on(in); +} diff --git a/modules/gapi/src/backends/common/gbackend.hpp b/modules/gapi/src/backends/common/gbackend.hpp index 6b2cabccc7..790d7650d9 100644 --- a/modules/gapi/src/backends/common/gbackend.hpp +++ b/modules/gapi/src/backends/common/gbackend.hpp @@ -58,32 +58,51 @@ namespace gimpl { struct Data; struct RcDesc; - struct GAPI_EXPORTS RMatMediaAdapterBGR final: public cv::RMat::Adapter + struct GAPI_EXPORTS RMatMediaFrameAdapter final: public cv::RMat::Adapter { - explicit RMatMediaAdapterBGR(const cv::MediaFrame& frame) : m_frame(frame) { }; + using MapDescF = std::function; + using MapDataF = std::function; + + RMatMediaFrameAdapter(const cv::MediaFrame& frame, + const MapDescF& frameDescToMatDesc, + const MapDataF& frameViewToMat) : + m_frame(frame), + m_frameDesc(frame.desc()), + m_frameDescToMatDesc(frameDescToMatDesc), + m_frameViewToMat(frameViewToMat) + { } virtual cv::RMat::View access(cv::RMat::Access a) override { - auto view = m_frame.access(a == cv::RMat::Access::W ? cv::MediaFrame::Access::W - : cv::MediaFrame::Access::R); - auto ptr = reinterpret_cast(view.ptr[0]); - auto stride = view.stride[0]; + auto rmatToFrameAccess = [](cv::RMat::Access rmatAccess) { + switch(rmatAccess) { + case cv::RMat::Access::R: + return cv::MediaFrame::Access::R; + case cv::RMat::Access::W: + return cv::MediaFrame::Access::W; + default: + cv::util::throw_error(std::logic_error("cv::RMat::Access::R or " + "cv::RMat::Access::W can only be mapped to cv::MediaFrame::Access!")); + } + }; - std::shared_ptr view_ptr = - std::make_shared(std::move(view)); - auto callback = [view_ptr]() mutable { view_ptr.reset(); }; + auto fv = m_frame.access(rmatToFrameAccess(a)); - return cv::RMat::View(desc(), ptr, stride, callback); + auto fvHolder = std::make_shared(std::move(fv)); + auto callback = [fvHolder]() mutable { fvHolder.reset(); }; + + return asView(m_frameViewToMat(m_frame.desc(), *fvHolder), callback); } virtual cv::GMatDesc desc() const override { - const auto& desc = m_frame.desc(); - GAPI_Assert(desc.fmt == cv::MediaFormat::BGR); - return cv::GMatDesc{CV_8U, 3, desc.size}; + return m_frameDescToMatDesc(m_frameDesc); } cv::MediaFrame m_frame; + cv::GFrameDesc m_frameDesc; + MapDescF m_frameDescToMatDesc; + MapDataF m_frameViewToMat; }; diff --git a/modules/gapi/src/backends/streaming/gstreamingbackend.cpp b/modules/gapi/src/backends/streaming/gstreamingbackend.cpp index a497b68ad4..09abd32c05 100644 --- a/modules/gapi/src/backends/streaming/gstreamingbackend.cpp +++ b/modules/gapi/src/backends/streaming/gstreamingbackend.cpp @@ -4,6 +4,8 @@ // // Copyright (C) 2020 Intel Corporation +#include + #if !defined(GAPI_STANDALONE) #include #endif // !defined(GAPI_STANDALONE) @@ -11,6 +13,7 @@ #include // throw_error #include // kernels +#include "logger.hpp" #include "api/gbackend_priv.hpp" #include "backends/common/gbackend.hpp" @@ -197,16 +200,47 @@ cv::gapi::GKernelPackage cv::gimpl::streaming::kernels() #if !defined(GAPI_STANDALONE) +class GAccessorActorBase : public cv::gapi::streaming::IActor { +public: + explicit GAccessorActorBase(const cv::GCompileArgs&) {} + virtual void run(cv::gimpl::GIslandExecutable::IInput &in, + cv::gimpl::GIslandExecutable::IOutput &out) override { + const auto in_msg = in.get(); + if (cv::util::holds_alternative(in_msg)) + { + out.post(cv::gimpl::EndOfStream{}); + return; + } + + const cv::GRunArgs &in_args = cv::util::get(in_msg); + GAPI_Assert(in_args.size() == 1u); + auto frame = cv::util::get(in_args[0]); + + cv::GRunArgP out_arg = out.get(0); + auto& rmat = *cv::util::get(out_arg); + + extractRMat(frame, rmat); + + out.meta(out_arg, in_args[0].meta); + out.post(std::move(out_arg)); + } + + virtual void extractRMat(const cv::MediaFrame& frame, cv::RMat& rmat) = 0; + +protected: + std::once_flag m_warnFlag; +}; + struct GOCVBGR: public cv::detail::KernelTag { using API = cv::gapi::streaming::GBGR; static cv::gapi::GBackend backend() { return cv::gapi::streaming::backend(); } - class Actor final: public cv::gapi::streaming::IActor { - public: - explicit Actor(const cv::GCompileArgs&) {} - virtual void run(cv::gimpl::GIslandExecutable::IInput &in, - cv::gimpl::GIslandExecutable::IOutput&out) override; + class Actor final: public GAccessorActorBase + { + public: + using GAccessorActorBase::GAccessorActorBase; + virtual void extractRMat(const cv::MediaFrame& frame, cv::RMat& rmat) override; }; static cv::gapi::streaming::IActor::Ptr create(const cv::GCompileArgs& args) @@ -216,49 +250,173 @@ struct GOCVBGR: public cv::detail::KernelTag static cv::gapi::streaming::GStreamingKernel kernel() { return {&create}; }; }; -void GOCVBGR::Actor::run(cv::gimpl::GIslandExecutable::IInput &in, - cv::gimpl::GIslandExecutable::IOutput &out) +void GOCVBGR::Actor::extractRMat(const cv::MediaFrame& frame, cv::RMat& rmat) { - const auto in_msg = in.get(); - if (cv::util::holds_alternative(in_msg)) - { - out.post(cv::gimpl::EndOfStream{}); - return; - } - - const cv::GRunArgs &in_args = cv::util::get(in_msg); - GAPI_Assert(in_args.size() == 1u); - - cv::GRunArgP out_arg = out.get(0); - auto frame = cv::util::get(in_args[0]); const auto& desc = frame.desc(); - - auto& rmat = *cv::util::get(out_arg); switch (desc.fmt) { case cv::MediaFormat::BGR: - rmat = cv::make_rmat(frame); + { + rmat = cv::make_rmat(frame, + [](const cv::GFrameDesc& d){ return cv::GMatDesc(CV_8U, 3, d.size); }, + [](const cv::GFrameDesc& d, const cv::MediaFrame::View& v){ + return cv::Mat(d.size, CV_8UC3, v.ptr[0], v.stride[0]); + }); break; + } case cv::MediaFormat::NV12: - { - cv::Mat bgr; - auto view = frame.access(cv::MediaFrame::Access::R); - cv::Mat y_plane (desc.size, CV_8UC1, view.ptr[0], view.stride[0]); - cv::Mat uv_plane(desc.size / 2, CV_8UC2, view.ptr[1], view.stride[1]); - cv::cvtColorTwoPlane(y_plane, uv_plane, bgr, cv::COLOR_YUV2BGR_NV12); - rmat = cv::make_rmat(bgr); - break; - } + { + std::call_once(m_warnFlag, + [](){ + GAPI_LOG_WARNING(NULL, "\nOn-the-fly conversion from NV12 to BGR will happen.\n" + "Conversion may cost a lot for images with high resolution.\n" + "To retrieve cv::Mat-s from NV12 cv::MediaFrame for free, you may use " + "cv::gapi::streaming::Y and cv::gapi::streaming::UV accessors.\n"); + }); + + cv::Mat bgr; + auto view = frame.access(cv::MediaFrame::Access::R); + cv::Mat y_plane (desc.size, CV_8UC1, view.ptr[0], view.stride[0]); + cv::Mat uv_plane(desc.size / 2, CV_8UC2, view.ptr[1], view.stride[1]); + cv::cvtColorTwoPlane(y_plane, uv_plane, bgr, cv::COLOR_YUV2BGR_NV12); + rmat = cv::make_rmat(bgr); + break; + } default: cv::util::throw_error( std::logic_error("Unsupported MediaFormat for cv::gapi::streaming::BGR")); } - out.post(std::move(out_arg)); +} + +struct GOCVY: public cv::detail::KernelTag +{ + using API = cv::gapi::streaming::GY; + static cv::gapi::GBackend backend() { return cv::gapi::streaming::backend(); } + + class Actor final: public GAccessorActorBase + { + public: + using GAccessorActorBase::GAccessorActorBase; + virtual void extractRMat(const cv::MediaFrame& frame, cv::RMat& rmat) override; + }; + + static cv::gapi::streaming::IActor::Ptr create(const cv::GCompileArgs& args) + { + return cv::gapi::streaming::IActor::Ptr(new Actor(args)); + } + static cv::gapi::streaming::GStreamingKernel kernel() { return {&create}; }; +}; + +void GOCVY::Actor::extractRMat(const cv::MediaFrame& frame, cv::RMat& rmat) +{ + const auto& desc = frame.desc(); + switch (desc.fmt) + { + case cv::MediaFormat::BGR: + { + std::call_once(m_warnFlag, + [](){ + GAPI_LOG_WARNING(NULL, "\nOn-the-fly conversion from BGR to NV12 Y plane will " + "happen.\n" + "Conversion may cost a lot for images with high resolution.\n" + "To retrieve cv::Mat from BGR cv::MediaFrame for free, you may use " + "cv::gapi::streaming::BGR accessor.\n"); + }); + + auto view = frame.access(cv::MediaFrame::Access::R); + cv::Mat tmp_bgr(desc.size, CV_8UC3, view.ptr[0], view.stride[0]); + cv::Mat yuv; + cvtColor(tmp_bgr, yuv, cv::COLOR_BGR2YUV_I420); + rmat = cv::make_rmat(yuv.rowRange(0, desc.size.height)); + break; + } + case cv::MediaFormat::NV12: + { + rmat = cv::make_rmat(frame, + [](const cv::GFrameDesc& d){ return cv::GMatDesc(CV_8U, 1, d.size); }, + [](const cv::GFrameDesc& d, const cv::MediaFrame::View& v){ + return cv::Mat(d.size, CV_8UC1, v.ptr[0], v.stride[0]); + }); + break; + } + default: + cv::util::throw_error( + std::logic_error("Unsupported MediaFormat for cv::gapi::streaming::Y")); + } +} + +struct GOCVUV: public cv::detail::KernelTag +{ + using API = cv::gapi::streaming::GUV; + static cv::gapi::GBackend backend() { return cv::gapi::streaming::backend(); } + + class Actor final: public GAccessorActorBase + { + public: + using GAccessorActorBase::GAccessorActorBase; + virtual void extractRMat(const cv::MediaFrame& frame, cv::RMat& rmat) override; + }; + + static cv::gapi::streaming::IActor::Ptr create(const cv::GCompileArgs& args) + { + return cv::gapi::streaming::IActor::Ptr(new Actor(args)); + } + static cv::gapi::streaming::GStreamingKernel kernel() { return {&create}; }; +}; + +void GOCVUV::Actor::extractRMat(const cv::MediaFrame& frame, cv::RMat& rmat) +{ + const auto& desc = frame.desc(); + switch (desc.fmt) + { + case cv::MediaFormat::BGR: + { + std::call_once(m_warnFlag, + [](){ + GAPI_LOG_WARNING(NULL, "\nOn-the-fly conversion from BGR to NV12 UV plane will " + "happen.\n" + "Conversion may cost a lot for images with high resolution.\n" + "To retrieve cv::Mat from BGR cv::MediaFrame for free, you may use " + "cv::gapi::streaming::BGR accessor.\n"); + }); + + auto view = frame.access(cv::MediaFrame::Access::R); + + cv::Mat tmp_bgr(desc.size, CV_8UC3, view.ptr[0], view.stride[0]); + cv::Mat yuv; + cvtColor(tmp_bgr, yuv, cv::COLOR_BGR2YUV_I420); + + cv::Mat uv; + std::vector dims = { desc.size.height / 2, + desc.size.width / 2 }; + auto start = desc.size.height; + auto range_h = desc.size.height / 4; + std::vector uv_planes = { + yuv.rowRange(start, start + range_h).reshape(0, dims), + yuv.rowRange(start + range_h, start + range_h * 2).reshape(0, dims) + }; + cv::merge(uv_planes, uv); + rmat = cv::make_rmat(uv); + break; + } + case cv::MediaFormat::NV12: + { + rmat = cv::make_rmat(frame, + [](const cv::GFrameDesc& d){ return cv::GMatDesc(CV_8U, 2, d.size / 2); }, + [](const cv::GFrameDesc& d, const cv::MediaFrame::View& v){ + return cv::Mat(d.size / 2, CV_8UC2, v.ptr[1], v.stride[1]); + }); + break; + } + default: + cv::util::throw_error( + std::logic_error("Unsupported MediaFormat for cv::gapi::streaming::UV")); + } } cv::gapi::GKernelPackage cv::gapi::streaming::kernels() { - return cv::gapi::kernels(); + return cv::gapi::kernels(); } #else diff --git a/modules/gapi/test/common/gapi_tests_common.hpp b/modules/gapi/test/common/gapi_tests_common.hpp index 151bf9ccc7..777574964a 100644 --- a/modules/gapi/test/common/gapi_tests_common.hpp +++ b/modules/gapi/test/common/gapi_tests_common.hpp @@ -58,7 +58,7 @@ namespace return o; } - inline void initTestDataPath() + inline bool initTestDataPathSilent() { #ifndef WINRT static bool initialized = false; @@ -66,15 +66,32 @@ namespace { // Since G-API has no own test data (yet), it is taken from the common space const char* testDataPath = getenv("OPENCV_TEST_DATA_PATH"); - GAPI_Assert(testDataPath != nullptr && - "OPENCV_TEST_DATA_PATH environment variable is either not set or set incorrectly."); - - cvtest::addDataSearchPath(testDataPath); - initialized = true; + if (testDataPath != nullptr) { + cvtest::addDataSearchPath(testDataPath); + initialized = true; + } } + + return initialized; #endif // WINRT } + inline void initTestDataPath() + { + bool initialized = initTestDataPathSilent(); + GAPI_Assert(initialized && + "OPENCV_TEST_DATA_PATH environment variable is either not set or set incorrectly."); + } + + inline void initTestDataPathOrSkip() + { + bool initialized = initTestDataPathSilent(); + if (!initialized) + { + throw cvtest::SkipTestException("Can't find test data"); + } + } + template inline void initPointRandU(cv::RNG &rng, cv::Point_& pt) { GAPI_Assert(std::is_integral::value); diff --git a/modules/gapi/test/streaming/gapi_streaming_tests.cpp b/modules/gapi/test/streaming/gapi_streaming_tests.cpp index f1aa37b518..627c1438b3 100644 --- a/modules/gapi/test/streaming/gapi_streaming_tests.cpp +++ b/modules/gapi/test/streaming/gapi_streaming_tests.cpp @@ -7,6 +7,8 @@ #include "../test_precomp.hpp" +#include "../common/gapi_tests_common.hpp" + #include // sleep_for (Delay) #include @@ -140,7 +142,7 @@ public: TestMediaNV12(cv::Mat y, cv::Mat uv) : m_y(y), m_uv(uv) { } cv::GFrameDesc meta() const override { - return cv::GFrameDesc{cv::MediaFormat::NV12, cv::Size(m_y.cols, m_y.rows)}; + return cv::GFrameDesc{cv::MediaFormat::NV12, m_y.size()}; } cv::MediaFrame::View access(cv::MediaFrame::Access) override { cv::MediaFrame::View::Ptrs pp = { @@ -199,24 +201,24 @@ class NV12Source : public cv::gapi::wip::GCaptureSource { public: explicit NV12Source(const std::string& pipeline) : cv::gapi::wip::GCaptureSource(pipeline) { -} - -bool pull(cv::gapi::wip::Data& data) { - if (cv::gapi::wip::GCaptureSource::pull(data)) { - cv::Mat bgr = cv::util::get(data); - cv::Mat y, uv; - cvtBGR2NV12(bgr, y, uv); - data = cv::MediaFrame::Create(y, uv); - return true; } - return false; -} -GMetaArg descr_of() const override { - return cv::GMetaArg{cv::GFrameDesc{cv::MediaFormat::NV12, - cv::util::get( - cv::gapi::wip::GCaptureSource::descr_of()).size}}; -} + bool pull(cv::gapi::wip::Data& data) { + if (cv::gapi::wip::GCaptureSource::pull(data)) { + cv::Mat bgr = cv::util::get(data); + cv::Mat y, uv; + cvtBGR2NV12(bgr, y, uv); + data = cv::MediaFrame::Create(y, uv); + return true; + } + return false; + } + + GMetaArg descr_of() const override { + return cv::GMetaArg{cv::GFrameDesc{cv::MediaFormat::NV12, + cv::util::get( + cv::gapi::wip::GCaptureSource::descr_of()).size}}; + } }; } // anonymous namespace @@ -1819,82 +1821,277 @@ TEST(GAPI_Streaming, Reshape) } } -TEST(GAPI_Streaming, AccessBGRFromBGRFrame) +namespace { + enum class TestSourceType { + BGR, + NV12 + }; + + cv::gapi::wip::IStreamSource::Ptr createTestSource(TestSourceType sourceType, + const std::string& pipeline) { + assert(sourceType == TestSourceType::BGR || sourceType == TestSourceType::NV12); + + cv::gapi::wip::IStreamSource::Ptr ptr { }; + + switch (sourceType) { + case TestSourceType::BGR: { + try { + ptr = cv::gapi::wip::make_src(pipeline); + } + catch(...) { + throw SkipTestException(std::string("BGRSource for '") + pipeline + + "' couldn't be created!"); + } + break; + } + case TestSourceType::NV12: { + try { + ptr = cv::gapi::wip::make_src(pipeline); + } + catch(...) { + throw SkipTestException(std::string("NV12Source for '") + pipeline + + "' couldn't be created!"); + } + break; + } + default: { + throw SkipTestException("Incorrect type of source! " + "Something went wrong in the test!"); + } + } + + return ptr; + } +} // anonymous namespace + +struct GAPI_Accessors_In_Streaming : public TestWithParam< + std::tuple, + TestSourceType, + std::function> + > +{ }; + +TEST_P(GAPI_Accessors_In_Streaming, AccuracyTest) { - initTestDataPath(); - std::string filepath = findDataFile("cv/video/768x576.avi"); + std::string filepath; + std::function accessor; + TestSourceType sourceType = TestSourceType::BGR; + std::function fromBGR; + + std::tie(filepath, accessor, sourceType, fromBGR) = GetParam(); + + initTestDataPathOrSkip(); + const std::string& absFilePath = findDataFile(filepath, false); cv::GFrame in; - auto out = cv::gapi::streaming::BGR(in); - + cv::GMat out = accessor(in); cv::GComputation comp(cv::GIn(in), cv::GOut(out)); auto cc = comp.compileStreaming(); - try { - cc.setSource(filepath); - } catch(...) { - throw SkipTestException("Video file can not be opened"); - } + auto src = createTestSource(sourceType, absFilePath); + cc.setSource(src); cv::VideoCapture cap; - cap.open(filepath); + cap.open(absFilePath); if (!cap.isOpened()) throw SkipTestException("Video file can not be opened"); - cv::Mat ocv_mat, gapi_mat; + cv::Mat cap_mat, ocv_mat, gapi_mat; std::size_t num_frames = 0u; std::size_t max_frames = 10u; cc.start(); - while (cc.pull(cv::gout(gapi_mat)) && num_frames < max_frames) + while (num_frames < max_frames && cc.pull(cv::gout(gapi_mat))) { num_frames++; - cap >> ocv_mat; + cap >> cap_mat; + ocv_mat = fromBGR(cap_mat); EXPECT_EQ(0, cvtest::norm(ocv_mat, gapi_mat, NORM_INF)); } + + cc.stop(); } -TEST(GAPI_Streaming, AccessBGRFromNV12Frame) +INSTANTIATE_TEST_CASE_P(AccessBGRFromBGRFrame, GAPI_Accessors_In_Streaming, + Combine(Values("cv/video/768x576.avi"), + Values(cv::gapi::streaming::BGR), + Values(TestSourceType::BGR), + Values([](const cv::Mat& bgr){ return bgr; }) + ) + ); + +INSTANTIATE_TEST_CASE_P(AccessBGRFromNV12Frame, GAPI_Accessors_In_Streaming, + Combine(Values("cv/video/768x576.avi"), + Values(cv::gapi::streaming::BGR), + Values(TestSourceType::NV12), + Values([](const cv::Mat& bgr) + { + cv::Mat y, uv, out_bgr; + cvtBGR2NV12(bgr, y, uv); + cv::cvtColorTwoPlane(y, uv, out_bgr, + cv::COLOR_YUV2BGR_NV12); + return out_bgr; + }) + ) + ); + +INSTANTIATE_TEST_CASE_P(AccessYFromNV12Frame, GAPI_Accessors_In_Streaming, + Combine(Values("cv/video/768x576.avi"), + Values(cv::gapi::streaming::Y), + Values(TestSourceType::NV12), + Values([](const cv::Mat& bgr) + { + cv::Mat y, uv; + cvtBGR2NV12(bgr, y, uv); + return y; + }) + ) + ); + +INSTANTIATE_TEST_CASE_P(AccessYFromBGRFrame, GAPI_Accessors_In_Streaming, + Combine(Values("cv/video/768x576.avi"), + Values(cv::gapi::streaming::Y), + Values(TestSourceType::BGR), + Values([](const cv::Mat& bgr) + { + cv::Mat y, uv; + cvtBGR2NV12(bgr, y, uv); + return y; + }) + ) + ); + +INSTANTIATE_TEST_CASE_P(AccessUVFromNV12Frame, GAPI_Accessors_In_Streaming, + Combine(Values("cv/video/768x576.avi"), + Values(cv::gapi::streaming::UV), + Values(TestSourceType::NV12), + Values([](const cv::Mat& bgr) + { + cv::Mat y, uv; + cvtBGR2NV12(bgr, y, uv); + return uv; + }) + ) + ); + +INSTANTIATE_TEST_CASE_P(AccessUVFromBGRFrame, GAPI_Accessors_In_Streaming, + Combine(Values("cv/video/768x576.avi"), + Values(cv::gapi::streaming::UV), + Values(TestSourceType::BGR), + Values([](const cv::Mat& bgr) + { + cv::Mat y, uv; + cvtBGR2NV12(bgr, y, uv); + return uv; + }) + ) + ); + +struct GAPI_Accessors_Meta_In_Streaming : public TestWithParam< + std::tuple, + TestSourceType, + std::function> + > +{ }; + +TEST_P(GAPI_Accessors_Meta_In_Streaming, AccuracyTest) { - initTestDataPath(); - std::string filepath = findDataFile("cv/video/768x576.avi"); + std::string filepath; + std::function accessor; + TestSourceType sourceType = TestSourceType::BGR; + std::function fromBGR; + + std::tie(filepath, accessor, sourceType, fromBGR) = GetParam(); + + initTestDataPathOrSkip(); + const std::string& absFilePath = findDataFile(filepath, false); cv::GFrame in; - auto out = cv::gapi::streaming::BGR(in); - - cv::GComputation comp(cv::GIn(in), cv::GOut(out)); + cv::GMat gmat = accessor(in); + cv::GMat resized = cv::gapi::resize(gmat, cv::Size(1920, 1080)); + cv::GOpaque outId = cv::gapi::streaming::seq_id(resized); + cv::GOpaque outTs = cv::gapi::streaming::timestamp(resized); + cv::GComputation comp(cv::GIn(in), cv::GOut(resized, outId, outTs)); auto cc = comp.compileStreaming(); - try { - cc.setSource(filepath); - } catch(...) { - throw SkipTestException("Video file can not be opened"); - } + auto src = createTestSource(sourceType, absFilePath); + cc.setSource(src); cv::VideoCapture cap; - cap.open(filepath); + cap.open(absFilePath); if (!cap.isOpened()) throw SkipTestException("Video file can not be opened"); - cv::Mat ocv_mat, gapi_mat; + cv::Mat cap_mat, req_mat, ocv_mat, gapi_mat; + int64_t seq_id = 0, timestamp = 0; + std::set all_seq_ids; + std::vector all_timestamps; + std::size_t num_frames = 0u; std::size_t max_frames = 10u; cc.start(); - while (cc.pull(cv::gout(gapi_mat)) && num_frames < max_frames) + while (num_frames < max_frames && cc.pull(cv::gout(gapi_mat, seq_id, timestamp))) { num_frames++; - cap >> ocv_mat; - - cv::Mat y, uv; - cvtBGR2NV12(ocv_mat, y, uv); - cv::cvtColorTwoPlane(y, uv, ocv_mat, cv::COLOR_YUV2BGR_NV12); + cap >> cap_mat; + req_mat = fromBGR(cap_mat); + cv::resize(req_mat, ocv_mat, cv::Size(1920, 1080)); EXPECT_EQ(0, cvtest::norm(ocv_mat, gapi_mat, NORM_INF)); + + all_seq_ids.insert(seq_id); + all_timestamps.push_back(timestamp); } + + cc.stop(); + + EXPECT_EQ(all_seq_ids.begin(), all_seq_ids.find(0L)); + auto last_elem_it = --all_seq_ids.end(); + EXPECT_EQ(last_elem_it, all_seq_ids.find(int64_t(max_frames - 1L))); + EXPECT_EQ(max_frames, all_seq_ids.size()); + + EXPECT_EQ(max_frames, all_timestamps.size()); + EXPECT_TRUE(std::is_sorted(all_timestamps.begin(), all_timestamps.end())); } +INSTANTIATE_TEST_CASE_P(BGRAccessorMeta, GAPI_Accessors_Meta_In_Streaming, + Combine(Values("cv/video/768x576.avi"), + Values(cv::gapi::streaming::BGR), + Values(TestSourceType::BGR), + Values([](const cv::Mat& bgr) { return bgr; }) + ) + ); + +INSTANTIATE_TEST_CASE_P(YAccessorMeta, GAPI_Accessors_Meta_In_Streaming, + Combine(Values("cv/video/768x576.avi"), + Values(cv::gapi::streaming::Y), + Values(TestSourceType::NV12), + Values([](const cv::Mat& bgr) + { + cv::Mat y, uv; + cvtBGR2NV12(bgr, y, uv); + return y; + }) + ) + ); + +INSTANTIATE_TEST_CASE_P(UVAccessorMeta, GAPI_Accessors_Meta_In_Streaming, + Combine(Values("cv/video/768x576.avi"), + Values(cv::gapi::streaming::UV), + Values(TestSourceType::NV12), + Values([](const cv::Mat& bgr) + { + cv::Mat y, uv; + cvtBGR2NV12(bgr, y, uv); + return uv; + }) + ) + ); + TEST(GAPI_Streaming, TestPythonAPI) { cv::Size sz(200, 200);