diff --git a/modules/gapi/include/opencv2/gapi/garg.hpp b/modules/gapi/include/opencv2/gapi/garg.hpp index 2f4b90fd30f7b466f01609bf8523cd5b025fd871..3f243a8d70610449472d7973b660063a1b938eea 100644 --- a/modules/gapi/include/opencv2/gapi/garg.hpp +++ b/modules/gapi/include/opencv2/gapi/garg.hpp @@ -102,6 +102,23 @@ using GRunArg = util::variant< >; using GRunArgs = std::vector; +// TODO: Think about the addition operator +/** + * @brief This operator allows to complement the input vector at runtime. + * + * It's an ordinary overload of addition assignment operator. + * + * Example of usage: + * @snippet dynamic_graph.cpp GRunArgs usage + * + */ +inline GRunArgs& operator += (GRunArgs &lhs, const GRunArgs &rhs) +{ + lhs.reserve(lhs.size() + rhs.size()); + lhs.insert(lhs.end(), rhs.begin(), rhs.end()); + return lhs; +} + namespace gapi { namespace wip @@ -133,10 +150,27 @@ using GRunArgP = util::variant< >; using GRunArgsP = std::vector; +// TODO: Think about the addition operator +/** + * @brief This operator allows to complement the output vector at runtime. + * + * It's an ordinary overload of addition assignment operator. + * + * Example of usage: + * @snippet dynamic_graph.cpp GRunArgsP usage + * + */ +inline GRunArgsP& operator += (GRunArgsP &lhs, const GRunArgsP &rhs) +{ + lhs.reserve(lhs.size() + rhs.size()); + lhs.insert(lhs.end(), rhs.begin(), rhs.end()); + return lhs; +} + namespace gapi { GAPI_EXPORTS cv::GRunArgsP bind(cv::GRunArgs &results); -} +} // namespace gapi template inline GRunArgs gin(const Ts&... args) { diff --git a/modules/gapi/include/opencv2/gapi/gproto.hpp b/modules/gapi/include/opencv2/gapi/gproto.hpp index 885819903b2c86909631aa0e1de50f5ef79d8a35..5319ae3b6dec4b7fcdf052afd683471d126775bb 100644 --- a/modules/gapi/include/opencv2/gapi/gproto.hpp +++ b/modules/gapi/include/opencv2/gapi/gproto.hpp @@ -61,8 +61,29 @@ public: explicit GIOProtoArgs(GProtoArgs &&args) : m_args(std::move(args)) {} GProtoArgs m_args; + + // TODO: Think about the addition operator + /** + * @brief This operator allows to complement the proto vectors at runtime. + * + * It's an ordinary overload of addition assignment operator. + * + * Example of usage: + * @snippet dynamic_graph.cpp GIOProtoArgs usage + * + */ + template + friend GIOProtoArgs& operator += (GIOProtoArgs &lhs, const GIOProtoArgs &rhs); }; +template +cv::GIOProtoArgs& operator += (cv::GIOProtoArgs &lhs, const cv::GIOProtoArgs &rhs) +{ + lhs.m_args.reserve(lhs.m_args.size() + rhs.m_args.size()); + lhs.m_args.insert(lhs.m_args.end(), rhs.m_args.begin(), rhs.m_args.end()); + return lhs; +} + struct In_Tag{}; struct Out_Tag{}; diff --git a/modules/gapi/samples/dynamic_graph.cpp b/modules/gapi/samples/dynamic_graph.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb8022c42f590596e9bcb97757165fac8ef476f5 --- /dev/null +++ b/modules/gapi/samples/dynamic_graph.cpp @@ -0,0 +1,68 @@ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + bool need_first_conversion = true; + bool need_second_conversion = false; + + cv::Size szOut(4, 4); + cv::GComputation cc([&](){ +// ! [GIOProtoArgs usage] + auto ins = cv::GIn(); + cv::GMat in1; + if (need_first_conversion) + ins += cv::GIn(in1); + + cv::GMat in2; + if (need_second_conversion) + ins += cv::GIn(in2); + + auto outs = cv::GOut(); + cv::GMat out1 = cv::gapi::resize(in1, szOut); + if (need_first_conversion) + outs += cv::GOut(out1); + + cv::GMat out2 = cv::gapi::resize(in2, szOut); + if (need_second_conversion) + outs += cv::GOut(out2); +// ! [GIOProtoArgs usage] + return cv::GComputation(std::move(ins), std::move(outs)); + }); + +// ! [GRunArgs usage] + auto in_vector = cv::gin(); + + cv::Mat in_mat1( 8, 8, CV_8UC3); + cv::Mat in_mat2(16, 16, CV_8UC3); + cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255)); + cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255)); + + if (need_first_conversion) + in_vector += cv::gin(in_mat1); + if (need_second_conversion) + in_vector += cv::gin(in_mat2); +// ! [GRunArgs usage] + +// ! [GRunArgsP usage] + auto out_vector = cv::gout(); + cv::Mat out_mat1, out_mat2; + if (need_first_conversion) + out_vector += cv::gout(out_mat1); + if (need_second_conversion) + out_vector += cv::gout(out_mat2); +// ! [GRunArgsP usage] + + auto stream = cc.compileStreaming(cv::compile_args(cv::gapi::core::cpu::kernels())); + stream.setSource(std::move(in_vector)); + + stream.start(); + stream.pull(std::move(out_vector)); + stream.stop(); + + return 0; +} diff --git a/modules/gapi/test/internal/gapi_int_dynamic_graph.cpp b/modules/gapi/test/internal/gapi_int_dynamic_graph.cpp new file mode 100644 index 0000000000000000000000000000000000000000..75e90d6c7ebef44063e295470ea6b542f7006a3e --- /dev/null +++ b/modules/gapi/test/internal/gapi_int_dynamic_graph.cpp @@ -0,0 +1,331 @@ +// 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) 2020 Intel Corporation + +#include "../test_precomp.hpp" + +#include +#include + +namespace opencv_test +{ + typedef ::testing::Types, + cv::GArray> VectorProtoTypes; + + template struct DynamicGraphProtoArgs: public ::testing::Test { using Type = T; }; + + TYPED_TEST_CASE(DynamicGraphProtoArgs, VectorProtoTypes); + + TYPED_TEST(DynamicGraphProtoArgs, AddProtoInputArgsSmoke) + { + using T = typename TestFixture::Type; + auto ins = GIn(); + T in; + EXPECT_NO_THROW(ins += GIn(in)); + } + + TYPED_TEST(DynamicGraphProtoArgs, AddProtoInputArgs) + { + using T = typename TestFixture::Type; + T in1, in2; + + auto ins1 = GIn(); + ins1 += GIn(in1); + ins1 += GIn(in2); + + auto ins2 = GIn(in1, in2); + + EXPECT_EQ(ins1.m_args.size(), ins2.m_args.size()); + } + + TYPED_TEST(DynamicGraphProtoArgs, AddProtoOutputArgsSmoke) + { + using T = typename TestFixture::Type; + auto outs = GOut(); + T out; + EXPECT_NO_THROW(outs += GOut(out)); + } + + TYPED_TEST(DynamicGraphProtoArgs, AddProtoOutputArgs) + { + using T = typename TestFixture::Type; + T out1, out2; + + auto outs1 = GOut(); + outs1 += GOut(out1); + outs1 += GOut(out2); + + auto outs2 = GOut(out1, out2); + + EXPECT_EQ(outs1.m_args.size(), outs2.m_args.size()); + } + + typedef ::testing::Types VectorRunTypes; + + template struct DynamicGraphRunArgs: public ::testing::Test { using Type = T; }; + + TYPED_TEST_CASE(DynamicGraphRunArgs, VectorRunTypes); + + TYPED_TEST(DynamicGraphRunArgs, AddRunArgsSmoke) + { + auto in_vector = cv::gin(); + + using T = typename TestFixture::Type; + T in; + EXPECT_NO_THROW(in_vector += cv::gin(in)); + } + + TYPED_TEST(DynamicGraphRunArgs, AddRunArgs) + { + using T = typename TestFixture::Type; + T in1, in2; + + auto in_vector1 = cv::gin(); + in_vector1 += cv::gin(in1); + in_vector1 += cv::gin(in2); + + auto in_vector2 = cv::gin(in1, in2); + + EXPECT_EQ(in_vector1.size(), in_vector2.size()); + } + + TYPED_TEST(DynamicGraphRunArgs, AddRunArgsPSmoke) + { + auto out_vector = cv::gout(); + + using T = typename TestFixture::Type; + T out; + EXPECT_NO_THROW(out_vector += cv::gout(out)); + } + + TYPED_TEST(DynamicGraphRunArgs, AddRunArgsP) + { + using T = typename TestFixture::Type; + T out1, out2; + + auto out_vector1 = cv::gout(); + out_vector1 += cv::gout(out1); + out_vector1 += cv::gout(out2); + + auto out_vector2 = cv::gout(out1, out2); + + EXPECT_EQ(out_vector1.size(), out_vector2.size()); + } + + TEST(DynamicGraph, ProtoInputArgsExecute) + { + cv::GComputation cc([]() { + cv::GMat in1; + auto ins = GIn(in1); + + cv::GMat in2; + ins += GIn(in2); + + cv::GMat out = cv::gapi::copy(in1 + in2); + + return cv::GComputation(std::move(ins), GOut(out)); + }); + + cv::Mat in_mat1 = cv::Mat::eye(32, 32, CV_8UC1); + cv::Mat in_mat2 = cv::Mat::eye(32, 32, CV_8UC1); + cv::Mat out_mat; + + EXPECT_NO_THROW(cc.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat))); + } + + TEST(DynamicGraph, ProtoOutputArgsExecute) + { + cv::GComputation cc([]() { + cv::GMat in; + cv::GMat out1 = cv::gapi::copy(in); + auto outs = GOut(out1); + + cv::GMat out2 = cv::gapi::copy(in); + outs += GOut(out2); + + return cv::GComputation(cv::GIn(in), std::move(outs)); + }); + + cv::Mat in_mat1 = cv::Mat::eye(32, 32, CV_8UC1); + cv::Mat out_mat1; + cv::Mat out_mat2; + + EXPECT_NO_THROW(cc.apply(cv::gin(in_mat1), cv::gout(out_mat1, out_mat1))); + } + + TEST(DynamicGraph, ProtoOutputInputArgsExecute) + { + cv::GComputation cc([]() { + cv::GMat in1; + auto ins = GIn(in1); + + cv::GMat in2; + ins += GIn(in2); + + cv::GMat out1 = cv::gapi::copy(in1 + in2); + auto outs = GOut(out1); + + cv::GMat out2 = cv::gapi::copy(in1 + in2); + outs += GOut(out2); + + return cv::GComputation(std::move(ins), std::move(outs)); + }); + + cv::Mat in_mat1 = cv::Mat::eye(32, 32, CV_8UC1); + cv::Mat in_mat2 = cv::Mat::eye(32, 32, CV_8UC1); + cv::Mat out_mat1, out_mat2; + + EXPECT_NO_THROW(cc.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat1, out_mat2))); + } + + TEST(DynamicGraph, ProtoArgsExecute) + { + cv::GComputation cc([]() { + cv::GMat in1; + auto ins = GIn(in1); + + cv::GMat in2; + ins += GIn(in2); + + cv::GMat out1 = cv::gapi::copy(in1 + in2); + auto outs = GOut(out1); + + cv::GMat out2 = cv::gapi::copy(in1 + in2); + outs += GOut(out2); + + return cv::GComputation(std::move(ins), std::move(outs)); + }); + + cv::Mat in_mat1 = cv::Mat::eye(32, 32, CV_8UC1); + cv::Mat in_mat2 = cv::Mat::eye(32, 32, CV_8UC1); + cv::Mat out_mat1, out_mat2; + + EXPECT_NO_THROW(cc.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_mat1, out_mat2))); + } + + TEST(DynamicGraph, ProtoOutputInputArgsAccuracy) + { + cv::Size szOut(4, 4); + cv::GComputation cc([&](){ + cv::GMat in1; + auto ins = GIn(in1); + + cv::GMat in2; + ins += GIn(in2); + + cv::GMat out1 = cv::gapi::resize(in1, szOut); + auto outs = GOut(out1); + + cv::GMat out2 = cv::gapi::resize(in2, szOut); + outs += GOut(out2); + + return cv::GComputation(std::move(ins), std::move(outs)); + }); + + // G-API test code + cv::Mat in_mat1( 8, 8, CV_8UC3); + cv::Mat in_mat2(16, 16, CV_8UC3); + cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255)); + cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255)); + + auto in_vector = cv::gin(); + in_vector += cv::gin(in_mat1); + in_vector += cv::gin(in_mat2); + + cv::Mat out_mat1, out_mat2; + auto out_vector = cv::gout(); + out_vector += cv::gout(out_mat1); + out_vector += cv::gout(out_mat2); + + cc.apply(std::move(in_vector), std::move(out_vector)); + + // OCV ref code + cv::Mat cv_out_mat1, cv_out_mat2; + cv::resize(in_mat1, cv_out_mat1, szOut); + cv::resize(in_mat2, cv_out_mat2, szOut); + + EXPECT_EQ(0, cvtest::norm(out_mat1, cv_out_mat1, NORM_INF)); + EXPECT_EQ(0, cvtest::norm(out_mat2, cv_out_mat2, NORM_INF)); + } + + TEST(DynamicGraph, Streaming) + { + cv::GComputation cc([&](){ + cv::Size szOut(4, 4); + + cv::GMat in1; + auto ins = GIn(in1); + + cv::GMat in2; + ins += GIn(in2); + + cv::GMat out1 = cv::gapi::resize(in1, szOut); + auto outs = GOut(out1); + + cv::GMat out2 = cv::gapi::resize(in2, szOut); + outs += GOut(out2); + + return cv::GComputation(std::move(ins), std::move(outs)); + }); + + EXPECT_NO_THROW(cc.compileStreaming(cv::compile_args(cv::gapi::core::cpu::kernels()))); + } + + TEST(DynamicGraph, StreamingAccuracy) + { + cv::Size szOut(4, 4); + cv::GComputation cc([&](){ + cv::GMat in1; + auto ins = GIn(in1); + + cv::GMat in2; + ins += GIn(in2); + + cv::GMat out1 = cv::gapi::resize(in1, szOut); + cv::GProtoOutputArgs outs = GOut(out1); + + cv::GMat out2 = cv::gapi::resize(in2, szOut); + outs += GOut(out2); + return cv::GComputation(std::move(ins), std::move(outs)); + }); + + // G-API test code + cv::Mat in_mat1( 8, 8, CV_8UC3); + cv::Mat in_mat2(16, 16, CV_8UC3); + cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255)); + cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255)); + + auto in_vector = cv::gin(); + in_vector += cv::gin(in_mat1); + in_vector += cv::gin(in_mat2); + + cv::Mat out_mat1, out_mat2; + auto out_vector = cv::gout(); + out_vector += cv::gout(out_mat1); + out_vector += cv::gout(out_mat2); + + auto stream = cc.compileStreaming(cv::compile_args(cv::gapi::core::cpu::kernels())); + stream.setSource(std::move(in_vector)); + + stream.start(); + stream.pull(std::move(out_vector)); + stream.stop(); + + // OCV ref code + cv::Mat cv_out_mat1, cv_out_mat2; + cv::resize(in_mat1, cv_out_mat1, szOut); + cv::resize(in_mat2, cv_out_mat2, szOut); + + EXPECT_EQ(0, cvtest::norm(out_mat1, cv_out_mat1, NORM_INF)); + EXPECT_EQ(0, cvtest::norm(out_mat2, cv_out_mat2, NORM_INF)); + } +} // namespace opencv_test