From c0076b58cd7b046484cc280548a6cc6c05975245 Mon Sep 17 00:00:00 2001 From: Ruslan Garnov Date: Mon, 18 Feb 2019 19:27:48 +0300 Subject: [PATCH] Added NV12 support to fluid backend --- .../opencv2/gapi/fluid/gfluidbuffer.hpp | 4 + .../opencv2/gapi/fluid/gfluidkernel.hpp | 3 +- .../gapi/src/backends/fluid/gfluidbackend.cpp | 145 +++++++++++++----- .../gapi/src/backends/fluid/gfluidbackend.hpp | 10 +- .../gapi/src/backends/fluid/gfluidbuffer.cpp | 3 + modules/gapi/test/gapi_fluid_resize_test.cpp | 73 +++++++++ modules/gapi/test/gapi_fluid_test.cpp | 42 +++++ modules/gapi/test/gapi_fluid_test_kernels.cpp | 71 +++++++++ 8 files changed, 306 insertions(+), 45 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp b/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp index 8965ec75b6..8a723128bf 100644 --- a/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp +++ b/modules/gapi/include/opencv2/gapi/fluid/gfluidbuffer.hpp @@ -53,6 +53,10 @@ public: inline const uint8_t* linePtr(int index) const { + // "out_of_window" check: + // user must not request the lines which are outside of specified kernel window + GAPI_DbgAssert(index >= -m_border_size + && index < -m_border_size + static_cast(m_linePtrs.size())); return m_linePtrs[index + m_border_size]; } }; diff --git a/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp index c71c5aa2cc..b6adf9e115 100644 --- a/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp +++ b/modules/gapi/include/opencv2/gapi/fluid/gfluidkernel.hpp @@ -49,7 +49,8 @@ public: enum class Kind { Filter, - Resize + Resize, + NV12toRGB }; // This function is a generic "doWork" callback diff --git a/modules/gapi/src/backends/fluid/gfluidbackend.cpp b/modules/gapi/src/backends/fluid/gfluidbackend.cpp index b05081c506..b45b9b34ae 100644 --- a/modules/gapi/src/backends/fluid/gfluidbackend.cpp +++ b/modules/gapi/src/backends/fluid/gfluidbackend.cpp @@ -67,7 +67,7 @@ namespace { GFluidModel fm(graph); auto fluid_impl = cv::util::any_cast(impl.opaque); - fm.metadata(op_node).set(cv::gimpl::FluidUnit{fluid_impl, {}, 0, 0, 0.0}); + fm.metadata(op_node).set(cv::gimpl::FluidUnit{fluid_impl, {}, 0, {}, 0.0}); } virtual EPtr compile(const ade::Graph &graph, @@ -138,8 +138,8 @@ private: struct FluidFilterAgent : public FluidAgent { private: - virtual int firstWindow() const override; - virtual std::pair linesReadAndnextWindow() const override; + virtual int firstWindow(std::size_t inPort) const override; + virtual std::pair linesReadAndnextWindow(std::size_t inPort) const override; virtual void setRatio(double) override { /* nothing */ } public: using FluidAgent::FluidAgent; @@ -148,14 +148,24 @@ public: struct FluidResizeAgent : public FluidAgent { private: - virtual int firstWindow() const override; - virtual std::pair linesReadAndnextWindow() const override; + virtual int firstWindow(std::size_t inPort) const override; + virtual std::pair linesReadAndnextWindow(std::size_t inPort) const override; virtual void setRatio(double ratio) override; std::unique_ptr m_mapper; public: using FluidAgent::FluidAgent; }; + +struct FluidNV12toRGBAgent : public FluidAgent +{ +private: + virtual int firstWindow(std::size_t inPort) const override; + virtual std::pair linesReadAndnextWindow(std::size_t inPort) const override; + virtual void setRatio(double) override { /* nothing */ } +public: + using FluidAgent::FluidAgent; +}; }} // namespace cv::gimpl cv::gimpl::FluidAgent::FluidAgent(const ade::Graph &g, ade::NodeHandle nh) @@ -182,11 +192,13 @@ void cv::gimpl::FluidAgent::reset() { m_producedLines = 0; - auto lines = firstWindow(); - for (auto &v : in_views) + for (const auto& it : ade::util::indexed(in_views)) { + auto& v = ade::util::value(it); if (v) { + auto idx = ade::util::index(it); + auto lines = firstWindow(idx); v.priv().reset(lines); } } @@ -240,7 +252,7 @@ static int calcResizeWindow(int inH, int outH) } } -static int maxLineConsumption(const cv::GFluidKernel& k, int inH, int outH, int lpi) +static int maxLineConsumption(const cv::GFluidKernel& k, int inH, int outH, int lpi, std::size_t inPort) { switch (k.m_kind) { @@ -260,6 +272,7 @@ static int maxLineConsumption(const cv::GFluidKernel& k, int inH, int outH, int return (inH == 1) ? 1 : 2 + lpi - 1; } } break; + case cv::GFluidKernel::Kind::NV12toRGB: return inPort == 0 ? 2 : 1; break; default: GAPI_Assert(false); return 0; } } @@ -271,6 +284,7 @@ static int borderSize(const cv::GFluidKernel& k) case cv::GFluidKernel::Kind::Filter: return (k.m_window - 1) / 2; break; // Resize never reads from border pixels case cv::GFluidKernel::Kind::Resize: return 0; break; + case cv::GFluidKernel::Kind::NV12toRGB: return 0; break; default: GAPI_Assert(false); return 0; } } @@ -354,31 +368,43 @@ std::pair cv::gimpl::FluidUpscaleMapper::linesReadAndNextWindow(int out return std::make_pair(lines_read, next_window); } -int cv::gimpl::FluidFilterAgent::firstWindow() const +int cv::gimpl::FluidFilterAgent::firstWindow(std::size_t) const { return k.m_window + k.m_lpi - 1; } -std::pair cv::gimpl::FluidFilterAgent::linesReadAndnextWindow() const +std::pair cv::gimpl::FluidFilterAgent::linesReadAndnextWindow(std::size_t) const { int lpi = std::min(k.m_lpi, m_outputLines - m_producedLines - k.m_lpi); return std::make_pair(k.m_lpi, k.m_window - 1 + lpi); } -int cv::gimpl::FluidResizeAgent::firstWindow() const +int cv::gimpl::FluidResizeAgent::firstWindow(std::size_t) const { auto outIdx = out_buffers[0]->priv().y(); auto lpi = std::min(m_outputLines - m_producedLines, k.m_lpi); return m_mapper->firstWindow(outIdx, lpi); } -std::pair cv::gimpl::FluidResizeAgent::linesReadAndnextWindow() const +std::pair cv::gimpl::FluidResizeAgent::linesReadAndnextWindow(std::size_t) const { auto outIdx = out_buffers[0]->priv().y(); auto lpi = std::min(m_outputLines - m_producedLines - k.m_lpi, k.m_lpi); return m_mapper->linesReadAndNextWindow(outIdx, lpi); } +int cv::gimpl::FluidNV12toRGBAgent::firstWindow(std::size_t inPort) const +{ + // 2 lines for Y, 1 for UV + return inPort == 0 ? 2 : 1; +} + +std::pair cv::gimpl::FluidNV12toRGBAgent::linesReadAndnextWindow(std::size_t inPort) const +{ + // 2 lines for Y, 1 for UV + return inPort == 0 ? std::make_pair(2, 2) : std::make_pair(1, 1); +} + void cv::gimpl::FluidResizeAgent::setRatio(double ratio) { if (ratio >= 1.0) @@ -438,11 +464,14 @@ void cv::gimpl::FluidAgent::doWork() k.m_f(in_args, out_buffers); - for (auto& in_view : in_views) + for (const auto& it : ade::util::indexed(in_views)) { + auto& in_view = ade::util::value(it); + if (in_view) { - auto pair = linesReadAndnextWindow(); + auto idx = ade::util::index(it); + auto pair = linesReadAndnextWindow(idx); in_view.priv().readDone(pair.first, pair.second); }; } @@ -554,14 +583,15 @@ void cv::gimpl::GFluidExecutable::initBufferRois(std::vector& readStarts, // only GMats participate in the process so it's valid to obtain GMatDesc const auto& meta = util::get(data.meta); - for (const auto& inNode : oh->inNodes()) + for (const auto& in_edge : oh->inEdges()) { - const auto& in_data = m_gm.metadata(inNode).get(); + const auto& in_node = in_edge->srcNode(); + const auto& in_data = m_gm.metadata(in_node).get(); - if (in_data.shape == GShape::GMAT && fg.metadata(inNode).contains()) + if (in_data.shape == GShape::GMAT && fg.metadata(in_node).contains()) { const auto& in_meta = util::get(in_data.meta); - const auto& fd = fg.metadata(inNode).get(); + const auto& fd = fg.metadata(in_node).get(); auto adjFilterRoi = [](cv::gapi::own::Rect produced, int b, int max_height) { // Extend with border roi which should be produced, crop to logical image size @@ -599,13 +629,29 @@ void cv::gimpl::GFluidExecutable::initBufferRois(std::vector& readStarts, return roi; }; + auto adjNV12Roi = [&](cv::gapi::own::Rect produced, std::size_t port) { + GAPI_Assert(produced.x % 2 == 0); + GAPI_Assert(produced.y % 2 == 0); + GAPI_Assert(produced.width % 2 == 0); + GAPI_Assert(produced.height % 2 == 0); + + cv::gapi::own::Rect roi; + switch (port) { + case 0: roi = produced; break; + case 1: roi = cv::gapi::own::Rect{ produced.x/2, produced.y/2, produced.width/2, produced.height/2 }; break; + default: GAPI_Assert(false); + } + return roi; + }; + cv::gapi::own::Rect produced = rois[m_id_map.at(data.rc)]; cv::gapi::own::Rect resized; switch (fg.metadata(oh).get().k.m_kind) { - case GFluidKernel::Kind::Filter: resized = produced; break; - case GFluidKernel::Kind::Resize: resized = adjResizeRoi(produced, in_meta.size, meta.size); break; + case GFluidKernel::Kind::Filter: resized = produced; break; + case GFluidKernel::Kind::Resize: resized = adjResizeRoi(produced, in_meta.size, meta.size); break; + case GFluidKernel::Kind::NV12toRGB: resized = adjNV12Roi(produced, m_gm.metadata(in_edge).get().port); break; default: GAPI_Assert(false); } @@ -618,7 +664,7 @@ void cv::gimpl::GFluidExecutable::initBufferRois(std::vector& readStarts, readStarts[in_id] = readStart; rois[in_id] = roi; // Continue traverse on internal (w.r.t Island) data nodes only. - if (fd.internal) nodesToVisit.push(inNode); + if (fd.internal) nodesToVisit.push(in_node); } else { @@ -626,7 +672,7 @@ void cv::gimpl::GFluidExecutable::initBufferRois(std::vector& readStarts, GAPI_Assert(rois[in_id] == roi); } } // if (in_data.shape == GShape::GMAT) - } // for (const auto& inNode : oh->inNodes()) + } // for (const auto& in_edge : oh->inEdges()) } // if (!startNode->inNodes().empty()) } // while (!nodesToVisit.empty()) } @@ -666,8 +712,9 @@ cv::gimpl::GFluidExecutable::GFluidExecutable(const ade::Graph &g, const auto& fu = fg.metadata(nh).get(); switch (fu.k.m_kind) { - case GFluidKernel::Kind::Filter: m_agents.emplace_back(new FluidFilterAgent(m_g, nh)); break; - case GFluidKernel::Kind::Resize: m_agents.emplace_back(new FluidResizeAgent(m_g, nh)); break; + case GFluidKernel::Kind::Filter: m_agents.emplace_back(new FluidFilterAgent(m_g, nh)); break; + case GFluidKernel::Kind::Resize: m_agents.emplace_back(new FluidResizeAgent(m_g, nh)); break; + case GFluidKernel::Kind::NV12toRGB: m_agents.emplace_back(new FluidNV12toRGBAgent(m_g, nh)); break; default: GAPI_Assert(false); } // NB.: in_buffer_ids size is equal to Arguments size, not Edges size!!! @@ -849,21 +896,35 @@ namespace } } - GAPI_Assert(in_hs.size() == 1 && out_ws.size() == 1 && out_hs.size() == 1); + auto &fu = fg.metadata(node).get(); + + GAPI_Assert((out_ws.size() == 1 && out_hs.size() == 1) && + ((in_hs.size() == 1) || + ((in_hs.size() == 2) && fu.k.m_kind == cv::GFluidKernel::Kind::NV12toRGB))); + + const auto &op = g.metadata(node).get(); + fu.line_consumption.resize(op.args.size(), 0); auto in_h = *in_hs .cbegin(); auto out_h = *out_hs.cbegin(); - auto &fu = fg.metadata(node).get(); fu.ratio = (double)in_h / out_h; - int line_consumption = maxLineConsumption(fu.k, in_h, out_h, fu.k.m_lpi); - int border_size = borderSize(fu.k); + // Set line consumption for each image (GMat) input + for (const auto& in_edge : node->inEdges()) + { + const auto& d = g.metadata(in_edge->srcNode()).get(); + if (d.shape == cv::GShape::GMAT) + { + auto port = g.metadata(in_edge).get().port; + fu.line_consumption[port] = maxLineConsumption(fu.k, in_h, out_h, fu.k.m_lpi, port); - fu.border_size = border_size; - fu.line_consumption = line_consumption; + GModel::log(g, node, "Line consumption (port " + std::to_string(port) + "): " + + std::to_string(fu.line_consumption[port])); + } + } - GModel::log(g, node, "Line consumption: " + std::to_string(fu.line_consumption)); + fu.border_size = borderSize(fu.k); GModel::log(g, node, "Border size: " + std::to_string(fu.border_size)); } } @@ -884,17 +945,20 @@ namespace { const auto &fu = fg.metadata(node).get(); - for (const auto &in_data_node : node->inNodes()) + for (const auto &in_edge : node->inEdges()) { + const auto &in_data_node = in_edge->srcNode(); + auto port = g.metadata(in_edge).get().port; + auto &fd = fg.metadata(in_data_node).get(); // Update (not Set) fields here since a single data node may be // accessed by multiple consumers - fd.max_consumption = std::max(fu.line_consumption, fd.max_consumption); + fd.max_consumption = std::max(fu.line_consumption[port], fd.max_consumption); fd.border_size = std::max(fu.border_size, fd.border_size); GModel::log(g, in_data_node, "Line consumption: " + std::to_string(fd.max_consumption) - + " (upd by " + std::to_string(fu.line_consumption) + ")", node); + + " (upd by " + std::to_string(fu.line_consumption[port]) + ")", node); GModel::log(g, in_data_node, "Border size: " + std::to_string(fd.border_size), node); } } @@ -914,17 +978,18 @@ namespace { const auto &fu = fg.metadata(node).get(); - const int own_latency = fu.line_consumption - fu.border_size; GModel::log(g, node, "LPI: " + std::to_string(fu.k.m_lpi)); // Output latency is max(input_latency) + own_latency - int in_latency = 0; - for (const auto &in_data_node : node->inNodes()) + int out_latency = 0; + for (const auto &in_edge: node->inEdges()) { // FIXME: ASSERT(DATA), ASSERT(FLUIDDATA) - in_latency = std::max(in_latency, fg.metadata(in_data_node).get().latency); + const auto port = g.metadata(in_edge).get().port; + const auto own_latency = fu.line_consumption[port] - fu.border_size; + const auto in_latency = fg.metadata(in_edge->srcNode()).get().latency; + out_latency = std::max(out_latency, in_latency + own_latency); } - const int out_latency = in_latency + own_latency; for (const auto &out_data_node : node->outNodes()) { @@ -1018,7 +1083,7 @@ void cv::gimpl::GFluidExecutable::makeReshape(const std::vector if (buf_idx >= 0) { - agent->in_views[in_idx].priv().allocate(fu.line_consumption, fu.border); + agent->in_views[in_idx].priv().allocate(fu.line_consumption[in_idx], fu.border); } } diff --git a/modules/gapi/src/backends/fluid/gfluidbackend.hpp b/modules/gapi/src/backends/fluid/gfluidbackend.hpp index ba8b9771f8..d540999783 100644 --- a/modules/gapi/src/backends/fluid/gfluidbackend.hpp +++ b/modules/gapi/src/backends/fluid/gfluidbackend.hpp @@ -4,10 +4,12 @@ // // Copyright (C) 2018 Intel Corporation - #ifndef OPENCV_GAPI_FLUID_BACKEND_HPP #define OPENCV_GAPI_FLUID_BACKEND_HPP +// FIXME? Actually gfluidbackend.hpp is not included anywhere +// and can be placed in gfluidbackend.cpp + #include "opencv2/gapi/garg.hpp" #include "opencv2/gapi/gproto.hpp" #include "opencv2/gapi/fluid/gfluidkernel.hpp" @@ -25,7 +27,7 @@ struct FluidUnit GFluidKernel k; gapi::fluid::BorderOpt border; int border_size; - int line_consumption; + std::vector line_consumption; double ratio; }; @@ -90,8 +92,8 @@ public: private: // FIXME!!! // move to another class - virtual int firstWindow() const = 0; - virtual std::pair linesReadAndnextWindow() const = 0; + virtual int firstWindow(std::size_t inPort) const = 0; + virtual std::pair linesReadAndnextWindow(std::size_t inPort) const = 0; }; class GFluidExecutable final: public GIslandExecutable diff --git a/modules/gapi/src/backends/fluid/gfluidbuffer.cpp b/modules/gapi/src/backends/fluid/gfluidbuffer.cpp index 1d78a37a44..0bfdd66349 100644 --- a/modules/gapi/src/backends/fluid/gfluidbuffer.cpp +++ b/modules/gapi/src/backends/fluid/gfluidbuffer.cpp @@ -519,6 +519,9 @@ void fluid::Buffer::Priv::allocate(BorderOpt border, // Init physical buffer // FIXME? combine line_consumption with skew? + // FIXME? This formula serves general case to avoid possible deadlock, + // in some cases this value can be smaller: + // 2 lines produced, 2 consumed, data_height can be 2, not 3 auto data_height = std::max(line_consumption, skew) + m_writer_lpi - 1; m_storage = createStorage(data_height, diff --git a/modules/gapi/test/gapi_fluid_resize_test.cpp b/modules/gapi/test/gapi_fluid_resize_test.cpp index bc0b991e66..dbffd394bc 100644 --- a/modules/gapi/test/gapi_fluid_resize_test.cpp +++ b/modules/gapi/test/gapi_fluid_resize_test.cpp @@ -717,4 +717,77 @@ INSTANTIATE_TEST_CASE_P(ResizeTestCPU, BlursAfterResizeTest, std::make_tuple(cv::Size{64,64}, cv::Size{49,49}, cv::Rect{0,39,49,10})))); +struct NV12PlusResizeTest : public TestWithParam > {}; +TEST_P(NV12PlusResizeTest, Test) +{ + cv::Size y_sz, out_sz; + cv::Rect roi; + std::tie(y_sz, out_sz, roi) = GetParam(); + int interp = cv::INTER_LINEAR; + + cv::Size uv_sz(y_sz.width / 2, y_sz.height / 2); + cv::Size in_sz(y_sz.width, y_sz.height*3/2); + + cv::Mat in_mat = cv::Mat(in_sz, CV_8UC1); + + cv::Scalar mean = cv::Scalar(127.0f); + cv::Scalar stddev = cv::Scalar(40.f); + cv::randn(in_mat, mean, stddev); + + cv::Mat y_mat = cv::Mat(y_sz, CV_8UC1, in_mat.data); + cv::Mat uv_mat = cv::Mat(uv_sz, CV_8UC2, in_mat.data + in_mat.step1() * y_sz.height); + cv::Mat out_mat, out_mat_ocv; + + cv::GMat y, uv; + auto rgb = cv::gapi::NV12toRGB(y, uv); + auto out = cv::gapi::resize(rgb, out_sz, 0, 0, interp); + cv::GComputation c(cv::GIn(y, uv), cv::GOut(out)); + + auto pkg = cv::gapi::combine(fluidTestPackage, cv::gapi::core::fluid::kernels(), cv::unite_policy::KEEP); + + c.apply(cv::gin(y_mat, uv_mat), cv::gout(out_mat) + ,cv::compile_args(pkg, cv::GFluidOutputRois{{to_own(roi)}})); + + cv::Mat rgb_mat; + cv::cvtColor(in_mat, rgb_mat, cv::COLOR_YUV2RGB_NV12); + cv::resize(rgb_mat, out_mat_ocv, out_sz, 0, 0, interp); + + EXPECT_EQ(0, cv::countNonZero(out_mat(roi) != out_mat_ocv(roi))); +} + +INSTANTIATE_TEST_CASE_P(Fluid, NV12PlusResizeTest, + Values(std::make_tuple(cv::Size{8, 8}, + cv::Size{4, 4}, cv::Rect{0, 0, 4, 4}) + ,std::make_tuple(cv::Size{8, 8}, + cv::Size{4, 4}, cv::Rect{0, 0, 4, 1}) + ,std::make_tuple(cv::Size{8, 8}, + cv::Size{4, 4}, cv::Rect{0, 1, 4, 2}) + ,std::make_tuple(cv::Size{8, 8}, + cv::Size{4, 4}, cv::Rect{0, 2, 4, 2}) + ,std::make_tuple(cv::Size{64, 64}, + cv::Size{49, 49}, cv::Rect{0, 0, 49, 49}) + ,std::make_tuple(cv::Size{64, 64}, + cv::Size{49, 49}, cv::Rect{0, 0, 49, 12}) + ,std::make_tuple(cv::Size{64, 64}, + cv::Size{49, 49}, cv::Rect{0, 11, 49, 15}) + ,std::make_tuple(cv::Size{64, 64}, + cv::Size{49, 49}, cv::Rect{0, 39, 49, 10}) + ,std::make_tuple(cv::Size{1920, 1080}, + cv::Size{ 320, 256}, cv::Rect{0, 0, 320, 64}) + ,std::make_tuple(cv::Size{1920, 1080}, + cv::Size{ 320, 256}, cv::Rect{0, 64, 320, 64}) + ,std::make_tuple(cv::Size{1920, 1080}, + cv::Size{ 320, 256}, cv::Rect{0, 128, 320, 64}) + ,std::make_tuple(cv::Size{1920, 1080}, + cv::Size{ 320, 256}, cv::Rect{0, 192, 320, 64}) + ,std::make_tuple(cv::Size{256, 400}, + cv::Size{ 32, 64}, cv::Rect{0, 0, 32, 16}) + ,std::make_tuple(cv::Size{256, 400}, + cv::Size{ 32, 64}, cv::Rect{0, 16, 32, 16}) + ,std::make_tuple(cv::Size{256, 400}, + cv::Size{ 32, 64}, cv::Rect{0, 32, 32, 16}) + ,std::make_tuple(cv::Size{256, 400}, + cv::Size{ 32, 64}, cv::Rect{0, 48, 32, 16}) + )); + } // namespace opencv_test diff --git a/modules/gapi/test/gapi_fluid_test.cpp b/modules/gapi/test/gapi_fluid_test.cpp index 5b3501175e..0f5a58e0a9 100644 --- a/modules/gapi/test/gapi_fluid_test.cpp +++ b/modules/gapi/test/gapi_fluid_test.cpp @@ -710,4 +710,46 @@ TEST(FluidTwoIslands, SanityTest) EXPECT_EQ(0, countNonZero(in_mat2 != out_mat2)); } +struct NV12RoiTest : public TestWithParam > {}; +TEST_P(NV12RoiTest, Test) +{ + cv::Size y_sz; + cv::Rect roi; + std::tie(y_sz, roi) = GetParam(); + + cv::Size uv_sz(y_sz.width / 2, y_sz.height / 2); + cv::Size in_sz(y_sz.width, y_sz.height*3/2); + + cv::Mat in_mat = cv::Mat(in_sz, CV_8UC1); + + cv::Scalar mean = cv::Scalar(127.0f); + cv::Scalar stddev = cv::Scalar(40.f); + cv::randn(in_mat, mean, stddev); + + cv::Mat y_mat = cv::Mat(y_sz, CV_8UC1, in_mat.data); + cv::Mat uv_mat = cv::Mat(uv_sz, CV_8UC2, in_mat.data + in_mat.step1() * y_sz.height); + cv::Mat out_mat, out_mat_ocv; + + cv::GMat y, uv; + auto rgb = cv::gapi::NV12toRGB(y, uv); + cv::GComputation c(cv::GIn(y, uv), cv::GOut(rgb)); + + c.apply(cv::gin(y_mat, uv_mat), cv::gout(out_mat), cv::compile_args(fluidTestPackage, cv::GFluidOutputRois{{to_own(roi)}})); + + cv::cvtColor(in_mat, out_mat_ocv, cv::COLOR_YUV2RGB_NV12); + + EXPECT_EQ(0, cv::countNonZero(out_mat(roi) != out_mat_ocv(roi))); +} + +INSTANTIATE_TEST_CASE_P(Fluid, NV12RoiTest, + Values(std::make_pair(cv::Size{8, 8}, cv::Rect{0, 0, 8, 2}) + ,std::make_pair(cv::Size{8, 8}, cv::Rect{0, 2, 8, 2}) + ,std::make_pair(cv::Size{8, 8}, cv::Rect{0, 4, 8, 2}) + ,std::make_pair(cv::Size{8, 8}, cv::Rect{0, 6, 8, 2}) + ,std::make_pair(cv::Size{1920, 1080}, cv::Rect{0, 0, 1920, 270}) + ,std::make_pair(cv::Size{1920, 1080}, cv::Rect{0, 270, 1920, 270}) + ,std::make_pair(cv::Size{1920, 1080}, cv::Rect{0, 540, 1920, 270}) + ,std::make_pair(cv::Size{1920, 1080}, cv::Rect{0, 710, 1920, 270}) + )); + } // namespace opencv_test diff --git a/modules/gapi/test/gapi_fluid_test_kernels.cpp b/modules/gapi/test/gapi_fluid_test_kernels.cpp index 6bd06fe271..d490bafe2c 100644 --- a/modules/gapi/test/gapi_fluid_test_kernels.cpp +++ b/modules/gapi/test/gapi_fluid_test_kernels.cpp @@ -416,6 +416,76 @@ GAPI_FLUID_KERNEL(FSum2MatsAndScalar, TSum2MatsAndScalar, false) } }; +static const int ITUR_BT_601_CY = 1220542; +static const int ITUR_BT_601_CUB = 2116026; +static const int ITUR_BT_601_CUG = -409993; +static const int ITUR_BT_601_CVG = -852492; +static const int ITUR_BT_601_CVR = 1673527; +static const int ITUR_BT_601_SHIFT = 20; + +static inline void uvToRGBuv(const uchar u, const uchar v, int& ruv, int& guv, int& buv) +{ + int uu, vv; + uu = int(u) - 128; + vv = int(v) - 128; + + ruv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVR * vv; + guv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVG * vv + ITUR_BT_601_CUG * uu; + buv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CUB * uu; +} + +static inline void yRGBuvToRGB(const uchar vy, const int ruv, const int guv, const int buv, + uchar& r, uchar& g, uchar& b) +{ + int y = std::max(0, vy - 16) * ITUR_BT_601_CY; + r = saturate_cast((y + ruv) >> ITUR_BT_601_SHIFT); + g = saturate_cast((y + guv) >> ITUR_BT_601_SHIFT); + b = saturate_cast((y + buv) >> ITUR_BT_601_SHIFT); +} + +GAPI_FLUID_KERNEL(FNV12toRGB, cv::gapi::imgproc::GNV12toRGB, false) +{ + static const int Window = 1; + static const int LPI = 2; + static const auto Kind = GFluidKernel::Kind::NV12toRGB; + + static void run(const cv::gapi::fluid::View &in1, + const cv::gapi::fluid::View &in2, + cv::gapi::fluid::Buffer &out) + { + const auto w = out.length(); + + GAPI_Assert(w % 2 == 0); + GAPI_Assert(out.lpi() == 2); + + const uchar* uv_row = in2.InLineB(0); + const uchar* y_rows[] = {in1. InLineB(0), in1. InLineB(1)}; + uchar* out_rows[] = {out.OutLineB(0), out.OutLineB(1)}; + + for (int i = 0; i < w/2; i++) + { + uchar u = uv_row[2*i]; + uchar v = uv_row[2*i + 1]; + int ruv, guv, buv; + uvToRGBuv(u, v, ruv, guv, buv); + + for (int y = 0; y < 2; y++) + { + for (int x = 0; x < 2; x++) + { + uchar vy = y_rows[y][2*i + x]; + uchar r, g, b; + yRGBuvToRGB(vy, ruv, guv, buv, r, g, b); + + out_rows[y][3*(2*i + x)] = r; + out_rows[y][3*(2*i + x) + 1] = g; + out_rows[y][3*(2*i + x) + 2] = b; + } + } + } + } +}; + cv::gapi::GKernelPackage fluidTestPackage = cv::gapi::kernels