diff --git a/modules/gapi/src/backends/cpu/gcpubackend.cpp b/modules/gapi/src/backends/cpu/gcpubackend.cpp index dfcaf3d478..b1e716f3ba 100644 --- a/modules/gapi/src/backends/cpu/gcpubackend.cpp +++ b/modules/gapi/src/backends/cpu/gcpubackend.cpp @@ -2,7 +2,7 @@ // 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) 2018-2021 Intel Corporation +// Copyright (C) 2018-2022 Intel Corporation #include "precomp.hpp" @@ -88,7 +88,7 @@ cv::gimpl::GCPUExecutable::GCPUExecutable(const ade::Graph &g, { case NodeType::OP: { - m_script.push_back({nh, GModel::collectOutputMeta(m_gm, nh)}); + m_opNodes.push_back(nh); // If kernel is stateful then prepare storage for its state. GCPUKernel k = gcm.metadata(nh).get().k; @@ -107,19 +107,12 @@ cv::gimpl::GCPUExecutable::GCPUExecutable(const ade::Graph &g, auto rc = RcDesc{desc.rc, desc.shape, desc.ctor}; magazine::bindInArg(m_res, rc, m_gm.metadata(nh).get().arg); } - //preallocate internal Mats in advance - if (desc.storage == Data::Storage::INTERNAL && desc.shape == GShape::GMAT) - { - const auto mat_desc = util::get(desc.meta); - auto& mat = m_res.slot()[desc.rc]; - createMat(mat_desc, mat); - } break; } default: util::throw_error(std::logic_error("Unsupported NodeType type")); } } - + makeReshape(); // For each stateful kernel call 'setup' user callback to initialize state. setupKernelStates(); } @@ -176,8 +169,38 @@ void cv::gimpl::GCPUExecutable::setupKernelStates() } } +void cv::gimpl::GCPUExecutable::makeReshape() { + // Prepare the execution script + m_script.clear(); + for (auto &nh : m_opNodes) { + m_script.push_back({nh, GModel::collectOutputMeta(m_gm, nh)}); + } + + // Preallocate internal mats + for (auto& nh : m_dataNodes) { + const auto& desc = m_gm.metadata(nh).get(); + if (desc.storage == Data::Storage::INTERNAL && desc.shape == GShape::GMAT) { + const auto mat_desc = util::get(desc.meta); + auto& mat = m_res.slot()[desc.rc]; + createMat(mat_desc, mat); + } + } +} + +void cv::gimpl::GCPUExecutable::reshape(ade::Graph&, const GCompileArgs& args) { + m_compileArgs = args; + makeReshape(); + // Signal to reset stateful kernels` state. + // There can be no handleNewStream() call to set this flag + // if user didn't call GCompiled`s prepareForNewStream() + m_newStreamStarted = true; +} + void cv::gimpl::GCPUExecutable::handleNewStream() { + // Signal to reset stateful kernels` state. + // No need to call reshape() here since it'll + // be called automatically if input meta was changed m_newStreamStarted = true; } diff --git a/modules/gapi/src/backends/cpu/gcpubackend.hpp b/modules/gapi/src/backends/cpu/gcpubackend.hpp index 6328da03b0..6a7b41e3d4 100644 --- a/modules/gapi/src/backends/cpu/gcpubackend.hpp +++ b/modules/gapi/src/backends/cpu/gcpubackend.hpp @@ -2,7 +2,7 @@ // 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) 2018-2020 Intel Corporation +// Copyright (C) 2018-2022 Intel Corporation #ifndef OPENCV_GAPI_GCPUBACKEND_HPP @@ -33,7 +33,7 @@ class GCPUExecutable final: public GIslandExecutable { const ade::Graph &m_g; GModel::ConstGraph m_gm; - const cv::GCompileArgs m_compileArgs; + cv::GCompileArgs m_compileArgs; struct OperationInfo { @@ -51,6 +51,7 @@ class GCPUExecutable final: public GIslandExecutable // List of all resources in graph (both internal and external) std::vector m_dataNodes; + std::vector m_opNodes; // Actual data of all resources in graph (both internal and external) Mag m_res; @@ -61,19 +62,15 @@ class GCPUExecutable final: public GIslandExecutable GArg packArg(const GArg &arg); void setupKernelStates(); + void makeReshape(); + public: GCPUExecutable(const ade::Graph &graph, const cv::GCompileArgs &compileArgs, const std::vector &nodes); - virtual inline bool canReshape() const override { return false; } - virtual inline void reshape(ade::Graph&, const GCompileArgs&) override - { - // FIXME: CPU plugin is in fact reshapeable (as it was initially, - // even before outMeta() has been introduced), so this limitation - // should be dropped. - util::throw_error(std::logic_error("GCPUExecutable::reshape() should never be called")); - } + virtual inline bool canReshape() const override { return true; } + virtual void reshape(ade::Graph&, const GCompileArgs&) override; virtual void handleNewStream() override; diff --git a/modules/gapi/src/backends/fluid/gfluidbackend.cpp b/modules/gapi/src/backends/fluid/gfluidbackend.cpp index 0e33ca9c0f..ed4dda7d49 100644 --- a/modules/gapi/src/backends/fluid/gfluidbackend.cpp +++ b/modules/gapi/src/backends/fluid/gfluidbackend.cpp @@ -2,7 +2,7 @@ // 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) 2018-2020 Intel Corporation +// Copyright (C) 2018-2022 Intel Corporation #include "precomp.hpp" @@ -954,7 +954,7 @@ namespace GFluidModel fg(graph); for (const auto& node : g.nodes()) { - if (g.metadata(node).get().t == NodeType::DATA) + if (fg.metadata(node).contains()) { auto& fd = fg.metadata(node).get(); fd.latency = 0; diff --git a/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp b/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp index 239afc38c4..cf03430d55 100644 --- a/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp +++ b/modules/gapi/test/cpu/gapi_ocv_stateful_kernel_tests.cpp @@ -2,7 +2,7 @@ // 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) 2020 Intel Corporation +// Copyright (C) 2020-2022 Intel Corporation #include "gapi_ocv_stateful_kernel_test_utils.hpp" #include @@ -342,7 +342,79 @@ TEST(StatefulKernel, StateIsInitViaCompArgsInStreaming) // Allowing 5% difference of all pixels between G-API and reference OpenCV results testBackSubInStreaming(gapiBackSub, 5); } + +TEST(StatefulKernel, StateIsChangedViaCompArgsOnReshape) +{ + cv::GMat in; + cv::GComputation comp(in, GBackSub::on(in)); + + const auto pkg = cv::gapi::kernels(); + + // OpenCV reference substractor + auto pOCVBackSubKNN = createBackgroundSubtractorKNN(); + auto pOCVBackSubMOG2 = createBackgroundSubtractorMOG2(); + + const auto run = [&](const std::string& videoPath, const std::string& method) { + auto path = findDataFile(videoPath); + cv::gapi::wip::IStreamSource::Ptr source; + try { + source = gapi::wip::make_src(path); + } catch(...) { + throw SkipTestException("Video file can not be opened"); + } + cv::Mat inMat, gapiForeground, ocvForeground; + + for (int i = 0; i < 10; i++) { + cv::gapi::wip::Data inData; + source->pull(inData); + inMat = cv::util::get(inData); + comp.apply(inMat, gapiForeground, + cv::compile_args(pkg, BackSubStateParams{method})); + + if (method == "knn") { + pOCVBackSubKNN->apply(inMat, ocvForeground, -1); + // Allowing 1% difference among all pixels + compareBackSubResults(gapiForeground, ocvForeground, 1); + } else if (method == "mog2") { + pOCVBackSubMOG2->apply(inMat, ocvForeground, -1); + compareBackSubResults(gapiForeground, ocvForeground, 5); + } else { + CV_Assert(false && "Unknown BackSub method"); + } + } + }; + + run("cv/video/768x576.avi", "knn"); + run("cv/video/1920x1080.avi", "mog2"); +} #endif + +TEST(StatefulKernel, StateIsAutoResetOnReshape) +{ + cv::GMat in; + cv::GOpaque up_to_date = GIsStateUpToDate::on(in); + cv::GOpaque calls_count = GCountCalls::on(in); + cv::GComputation comp(cv::GIn(in), cv::GOut(up_to_date, calls_count)); + + auto run = [&comp](const cv::Mat& in_mat) { + const auto pkg = cv::gapi::kernels(); + bool stateIsUpToDate = false; + int callsCount = 0; + for (int i = 0; i < 3; i++) { + comp.apply(cv::gin(in_mat), cv::gout(stateIsUpToDate, callsCount), + cv::compile_args(pkg)); + EXPECT_TRUE(stateIsUpToDate); + EXPECT_EQ(i+1, callsCount); + } + }; + + cv::Mat in_mat1(32, 32, CV_8UC1); + run(in_mat1); + + cv::Mat in_mat2(16, 16, CV_8UC1); + run(in_mat2); +} + //------------------------------------------------------------------------------------------------------------- diff --git a/modules/gapi/test/internal/gapi_int_executor_tests.cpp b/modules/gapi/test/internal/gapi_int_executor_tests.cpp index 4745213909..b8f0e18e0b 100644 --- a/modules/gapi/test/internal/gapi_int_executor_tests.cpp +++ b/modules/gapi/test/internal/gapi_int_executor_tests.cpp @@ -2,12 +2,14 @@ // 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) 2018 Intel Corporation +// Copyright (C) 2018-2022 Intel Corporation #include "../test_precomp.hpp" #include "../gapi_mock_kernels.hpp" +#include + namespace opencv_test { @@ -294,6 +296,28 @@ TEST_F(GExecutorReshapeTest, ReshapeCallAllocate) EXPECT_EQ(1, island1.getReshapeCounter()); } +TEST_F(GExecutorReshapeTest, CPUBackendIsReshapable) +{ + comp = cv::GComputation([](){ + cv::GMat in; + cv::GMat foo = I::Foo::on(in); + cv::GMat out = cv::gapi::bitwise_not(cv::gapi::bitwise_not(in)); + return cv::GComputation(cv::GIn(in), cv::GOut(foo, out)); + }); + // NB: Initial state + EXPECT_EQ(0, island1.getReshapeCounter()); + + // NB: First compilation. + cv::Mat out_mat2; + comp.apply(cv::gin(in_mat1), cv::gout(out_mat, out_mat2), cv::compile_args(pkg)); + EXPECT_EQ(0, island1.getReshapeCounter()); + + // NB: The entire graph is reshapable, so it won't be recompiled, but reshaped. + comp.apply(cv::gin(in_mat2), cv::gout(out_mat, out_mat2), cv::compile_args(pkg)); + EXPECT_EQ(1, island1.getReshapeCounter()); + EXPECT_EQ(0, cvtest::norm(out_mat2, in_mat2, NORM_INF)); +} + // FIXME: Add explicit tests on GMat/GScalar/GArray being connectors // between executed islands