feat: 切换后端至PaddleOCR-NCNN,切换工程为CMake

1.项目后端整体迁移至PaddleOCR-NCNN算法,已通过基本的兼容性测试
2.工程改为使用CMake组织,后续为了更好地兼容第三方库,不再提供QMake工程
3.重整权利声明文件,重整代码工程,确保最小化侵权风险

Log: 切换后端至PaddleOCR-NCNN,切换工程为CMake
Change-Id: I4d5d2c5d37505a4a24b389b1a4c5d12f17bfa38c
This commit is contained in:
wangzhengyang
2022-05-10 09:54:44 +08:00
parent ecdd171c6f
commit 718c41634f
10018 changed files with 3593797 additions and 186748 deletions

View File

@ -0,0 +1,86 @@
// 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) 2018 Intel Corporation
#include "../test_precomp.hpp"
#include "../gapi_mock_kernels.hpp"
#include "compiler/gmodel.hpp"
#include "compiler/gcompiler.hpp"
namespace opencv_test {
namespace {
struct MockMeta
{
static const char* name() { return "MockMeta"; }
};
class GMockBackendImpl final: public cv::gapi::GBackend::Priv
{
virtual void unpackKernel(ade::Graph &,
const ade::NodeHandle &,
const cv::GKernelImpl &) override
{
// Do nothing here
}
virtual EPtr compile(const ade::Graph &,
const cv::GCompileArgs &,
const std::vector<ade::NodeHandle> &) const override
{
// Do nothing here as well
return {};
}
virtual void addBackendPasses(ade::ExecutionEngineSetupContext &ectx) override
{
ectx.addPass("transform", "set_mock_meta", [](ade::passes::PassContext &ctx) {
ade::TypedGraph<MockMeta> me(ctx.graph);
for (const auto &nh : me.nodes())
{
me.metadata(nh).set(MockMeta{});
}
});
}
};
static cv::gapi::GBackend mock_backend(std::make_shared<GMockBackendImpl>());
GAPI_OCV_KERNEL(MockFoo, I::Foo)
{
static void run(const cv::Mat &, cv::Mat &) { /*Do nothing*/ }
static cv::gapi::GBackend backend() { return mock_backend; } // FIXME: Must be removed
};
} // anonymous namespace
TEST(GBackend, CustomPassesExecuted)
{
cv::GMat in;
cv::GMat out = I::Foo::on(in);
cv::GComputation c(in, out);
// Prepare compilation parameters manually
const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
const auto pkg = cv::gapi::kernels<MockFoo>();
// Directly instantiate G-API graph compiler and run partial compilation
cv::gimpl::GCompiler compiler(c, {in_meta}, cv::compile_args(pkg));
cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
compiler.runPasses(*graph);
// Inspect the graph and verify the metadata written by Mock backend
ade::TypedGraph<MockMeta> me(*graph);
EXPECT_LT(0u, static_cast<std::size_t>(me.nodes().size()));
for (const auto &nh : me.nodes())
{
EXPECT_TRUE(me.metadata(nh).contains<MockMeta>());
}
}
} // namespace opencv_test

View File

@ -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 <opencv2/gapi/cpu/core.hpp>
#include <opencv2/gapi/cpu/imgproc.hpp>
namespace opencv_test
{
typedef ::testing::Types<cv::GMat, cv::GMatP, cv::GFrame,
cv::GScalar, cv::GOpaque<int>,
cv::GArray<int>> VectorProtoTypes;
template<typename T> 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<cv::Mat,
#if !defined(GAPI_STANDALONE)
cv::UMat,
#endif // !defined(GAPI_STANDALONE)
cv::Scalar,
cv::detail::VectorRef,
cv::detail::OpaqueRef> VectorRunTypes;
template<typename T> 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

View File

@ -0,0 +1,300 @@
// 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) 2018 Intel Corporation
#include "../test_precomp.hpp"
#include "../gapi_mock_kernels.hpp"
namespace opencv_test
{
namespace
{
class GMockExecutable final: public cv::gimpl::GIslandExecutable
{
virtual inline bool canReshape() const override {
return m_priv->m_can_reshape;
}
virtual void reshape(ade::Graph&, const GCompileArgs&) override
{
m_priv->m_reshape_counter++;
}
virtual void handleNewStream() override { }
virtual void run(std::vector<InObj>&&, std::vector<OutObj>&&) { }
virtual bool allocatesOutputs() const override
{
return true;
}
virtual cv::RMat allocate(const cv::GMatDesc&) const override
{
m_priv->m_allocate_counter++;
return cv::RMat();
}
// NB: GMockBackendImpl creates new unique_ptr<GMockExecutable>
// on every compile call. Need to share counters between instances in order
// to validate it in tests.
struct Priv
{
bool m_can_reshape;
int m_reshape_counter;
int m_allocate_counter;
};
std::shared_ptr<Priv> m_priv;
public:
GMockExecutable(bool can_reshape = true)
: m_priv(new Priv{can_reshape, 0, 0})
{
};
void setReshape(bool can_reshape) { m_priv->m_can_reshape = can_reshape; }
int getReshapeCounter() const { return m_priv->m_reshape_counter; }
int getAllocateCounter() const { return m_priv->m_allocate_counter; }
};
class GMockBackendImpl final: public cv::gapi::GBackend::Priv
{
virtual void unpackKernel(ade::Graph &,
const ade::NodeHandle &,
const cv::GKernelImpl &) override { }
virtual EPtr compile(const ade::Graph &,
const cv::GCompileArgs &,
const std::vector<ade::NodeHandle> &) const override
{
++m_compile_counter;
return EPtr{new GMockExecutable(m_exec)};
}
mutable int m_compile_counter = 0;
GMockExecutable m_exec;
virtual bool controlsMerge() const override {
return true;
}
virtual bool allowsMerge(const cv::gimpl::GIslandModel::Graph &,
const ade::NodeHandle &,
const ade::NodeHandle &,
const ade::NodeHandle &) const override {
return false;
}
public:
GMockBackendImpl(const GMockExecutable& exec) : m_exec(exec) { };
int getCompileCounter() const { return m_compile_counter; }
};
class GMockFunctor : public gapi::cpu::GOCVFunctor
{
public:
GMockFunctor(cv::gapi::GBackend backend,
const char* id,
const Meta &meta,
const Impl& impl)
: gapi::cpu::GOCVFunctor(id, meta, impl), m_backend(backend)
{
}
cv::gapi::GBackend backend() const override { return m_backend; }
private:
cv::gapi::GBackend m_backend;
};
template<typename K, typename Callable>
GMockFunctor mock_kernel(const cv::gapi::GBackend& backend, Callable c)
{
using P = cv::detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
return GMockFunctor{ backend
, K::id()
, &K::getOutMeta
, std::bind(&P::callFunctor, std::placeholders::_1, c)
};
}
void dummyFooImpl(const cv::Mat&, cv::Mat&) { };
void dummyBarImpl(const cv::Mat&, const cv::Mat&, cv::Mat&) { };
struct GExecutorReshapeTest: public ::testing::Test
{
GExecutorReshapeTest()
: comp([](){
cv::GMat in;
cv::GMat out = I::Bar::on(I::Foo::on(in), in);
return cv::GComputation(in, out);
})
{
backend_impl1 = std::make_shared<GMockBackendImpl>(island1);
backend1 = cv::gapi::GBackend{backend_impl1};
backend_impl2 = std::make_shared<GMockBackendImpl>(island2);
backend2 = cv::gapi::GBackend{backend_impl2};
auto kernel1 = mock_kernel<I::Foo>(backend1, dummyFooImpl);
auto kernel2 = mock_kernel<I::Bar>(backend2, dummyBarImpl);
pkg = cv::gapi::kernels(kernel1, kernel2);
in_mat1 = cv::Mat::eye(32, 32, CV_8UC1);
in_mat2 = cv::Mat::eye(64, 64, CV_8UC1);
}
cv::GComputation comp;
GMockExecutable island1;
std::shared_ptr<GMockBackendImpl> backend_impl1;
cv::gapi::GBackend backend1;
GMockExecutable island2;
std::shared_ptr<GMockBackendImpl> backend_impl2;
cv::gapi::GBackend backend2;
cv::gapi::GKernelPackage pkg;
cv::Mat in_mat1, in_mat2, out_mat;;
};
} // anonymous namespace
// FIXME: avoid code duplication
// The below graph and config is taken from ComplexIslands test suite
TEST(GExecutor, SmokeTest)
{
cv::GMat in[2];
cv::GMat tmp[4];
cv::GScalar scl;
cv::GMat out[2];
tmp[0] = cv::gapi::bitwise_not(cv::gapi::bitwise_not(in[0]));
tmp[1] = cv::gapi::boxFilter(in[1], -1, cv::Size(3,3));
tmp[2] = tmp[0] + tmp[1]; // FIXME: handle tmp[2] = tmp[0]+tmp[2] typo
scl = cv::gapi::sum(tmp[1]);
tmp[3] = cv::gapi::medianBlur(tmp[1], 3);
out[0] = tmp[2] + scl;
out[1] = cv::gapi::boxFilter(tmp[3], -1, cv::Size(3,3));
// isl0 #internal1
// ........................... .........
// (in1) -> NotNot ->(tmp0) --> Add ---------> (tmp2) --> AddC -------> (out1)
// :.....................^...: :..^....:
// : :
// : :
// #internal0 : :
// .....................:......... :
// (in2) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
// :..........:..................: isl1
// : ..............................
// `------------> Median -> (tmp3) --> Blur -------> (out2)
// :............................:
cv::gapi::island("isl0", cv::GIn(in[0], tmp[1]), cv::GOut(tmp[2]));
cv::gapi::island("isl1", cv::GIn(tmp[1]), cv::GOut(out[1]));
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_gapi[2];
// Run G-API:
cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
.apply(cv::gin(in_mat1, in_mat2), cv::gout(out_gapi[0], out_gapi[1]));
// Run OpenCV
cv::Mat out_ocv[2];
{
cv::Mat ocv_tmp0;
cv::Mat ocv_tmp1;
cv::Mat ocv_tmp2;
cv::Mat ocv_tmp3;
cv::Scalar ocv_scl;
ocv_tmp0 = in_mat1; // skip !(!)
cv::boxFilter(in_mat2, ocv_tmp1, -1, cv::Size(3,3));
ocv_tmp2 = ocv_tmp0 + ocv_tmp1;
ocv_scl = cv::sum(ocv_tmp1);
cv::medianBlur(ocv_tmp1, ocv_tmp3, 3);
out_ocv[0] = ocv_tmp2 + ocv_scl;
cv::boxFilter(ocv_tmp3, out_ocv[1], -1, cv::Size(3,3));
}
EXPECT_EQ(0, cvtest::norm(out_gapi[0], out_ocv[0], NORM_INF));
EXPECT_EQ(0, cvtest::norm(out_gapi[1], out_ocv[1], NORM_INF));
// FIXME: check that GIslandModel has more than 1 island (e.g. fusion
// with breakdown worked)
}
TEST_F(GExecutorReshapeTest, ReshapeInsteadOfRecompile)
{
// NB: Initial state
EXPECT_EQ(0, backend_impl1->getCompileCounter());
EXPECT_EQ(0, backend_impl2->getCompileCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
EXPECT_EQ(0, island2.getReshapeCounter());
// NB: First compilation.
comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(1, backend_impl1->getCompileCounter());
EXPECT_EQ(1, backend_impl2->getCompileCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
EXPECT_EQ(0, island2.getReshapeCounter());
// NB: GMockBackendImpl implements "reshape" method,
// so it won't be recompiled if the meta is changed.
comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(1, backend_impl1->getCompileCounter());
EXPECT_EQ(1, backend_impl2->getCompileCounter());
EXPECT_EQ(1, island1.getReshapeCounter());
EXPECT_EQ(1, island2.getReshapeCounter());
}
TEST_F(GExecutorReshapeTest, OneBackendNotReshapable)
{
// NB: Make first island not reshapable
island1.setReshape(false);
// NB: Initial state
EXPECT_EQ(0, backend_impl1->getCompileCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
EXPECT_EQ(0, backend_impl2->getCompileCounter());
EXPECT_EQ(0, island2.getReshapeCounter());
// NB: First compilation.
comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(1, backend_impl1->getCompileCounter());
EXPECT_EQ(1, backend_impl2->getCompileCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
EXPECT_EQ(0, island2.getReshapeCounter());
// NB: Since one of islands isn't reshapable
// the entire graph isn't reshapable as well.
comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(2, backend_impl1->getCompileCounter());
EXPECT_EQ(2, backend_impl2->getCompileCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
EXPECT_EQ(0, island2.getReshapeCounter());
}
TEST_F(GExecutorReshapeTest, ReshapeCallAllocate)
{
// NB: Initial state
EXPECT_EQ(0, island1.getAllocateCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
// NB: First compilation.
comp.apply(cv::gin(in_mat1), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(1, island1.getAllocateCounter());
EXPECT_EQ(0, island1.getReshapeCounter());
// NB: The entire graph is reshapable, so it won't be recompiled, but reshaped.
// Check that reshape call "allocate" to reallocate buffers.
comp.apply(cv::gin(in_mat2), cv::gout(out_mat), cv::compile_args(pkg));
EXPECT_EQ(2, island1.getAllocateCounter());
EXPECT_EQ(1, island1.getReshapeCounter());
}
// FIXME: Add explicit tests on GMat/GScalar/GArray<T> being connectors
// between executed islands
} // namespace opencv_test

View File

@ -0,0 +1,118 @@
// 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) 2018 Intel Corporation
#include "../test_precomp.hpp"
namespace opencv_test {
// Tests on T/Spec/Kind matching ///////////////////////////////////////////////
// {{
template<class T, cv::detail::ArgKind Exp>
struct Expected
{
using type = T;
static const constexpr cv::detail::ArgKind kind = Exp;
};
template<typename T>
struct GArgKind: public ::testing::Test
{
using Type = typename T::type;
const cv::detail::ArgKind Kind = T::kind;
};
// The reason here is to _manually_ list types and their kinds
// (and NOT reuse cv::detail::ArgKind::Traits<>, since it is a subject of testing)
using GArg_Test_Types = ::testing::Types
<
// G-API types
Expected<cv::GMat, cv::detail::ArgKind::GMAT>
, Expected<cv::GMatP, cv::detail::ArgKind::GMATP>
, Expected<cv::GFrame, cv::detail::ArgKind::GFRAME>
, Expected<cv::GScalar, cv::detail::ArgKind::GSCALAR>
, Expected<cv::GArray<int>, cv::detail::ArgKind::GARRAY>
, Expected<cv::GArray<float>, cv::detail::ArgKind::GARRAY>
, Expected<cv::GArray<cv::Point>, cv::detail::ArgKind::GARRAY>
, Expected<cv::GArray<cv::Rect>, cv::detail::ArgKind::GARRAY>
, Expected<cv::GOpaque<int>, cv::detail::ArgKind::GOPAQUE>
, Expected<cv::GOpaque<float>, cv::detail::ArgKind::GOPAQUE>
, Expected<cv::GOpaque<cv::Point>, cv::detail::ArgKind::GOPAQUE>
, Expected<cv::GOpaque<cv::Rect>, cv::detail::ArgKind::GOPAQUE>
// Built-in types
, Expected<int, cv::detail::ArgKind::OPAQUE_VAL>
, Expected<float, cv::detail::ArgKind::OPAQUE_VAL>
, Expected<int*, cv::detail::ArgKind::OPAQUE_VAL>
, Expected<cv::Point, cv::detail::ArgKind::OPAQUE_VAL>
, Expected<std::string, cv::detail::ArgKind::OPAQUE_VAL>
, Expected<cv::Mat, cv::detail::ArgKind::OPAQUE_VAL>
, Expected<std::vector<int>, cv::detail::ArgKind::OPAQUE_VAL>
, Expected<std::vector<cv::Point>, cv::detail::ArgKind::OPAQUE_VAL>
>;
TYPED_TEST_CASE(GArgKind, GArg_Test_Types);
TYPED_TEST(GArgKind, LocalVar)
{
typename TestFixture::Type val{};
cv::GArg arg(val);
EXPECT_EQ(TestFixture::Kind, arg.kind);
}
TYPED_TEST(GArgKind, ConstLocalVar)
{
const typename TestFixture::Type val{};
cv::GArg arg(val);
EXPECT_EQ(TestFixture::Kind, arg.kind);
}
TYPED_TEST(GArgKind, RValue)
{
cv::GArg arg = cv::GArg(typename TestFixture::Type());
EXPECT_EQ(TestFixture::Kind, arg.kind);
}
////////////////////////////////////////////////////////////////////////////////
TEST(GArg, HasWrap)
{
static_assert(!cv::detail::has_custom_wrap<cv::GMat>::value,
"GMat has no custom marshalling logic");
static_assert(!cv::detail::has_custom_wrap<cv::GScalar>::value,
"GScalar has no custom marshalling logic");
static_assert(cv::detail::has_custom_wrap<cv::GArray<int> >::value,
"GArray<int> has custom marshalling logic");
static_assert(cv::detail::has_custom_wrap<cv::GArray<std::string> >::value,
"GArray<int> has custom marshalling logic");
static_assert(cv::detail::has_custom_wrap<cv::GOpaque<int> >::value,
"GOpaque<int> has custom marshalling logic");
static_assert(cv::detail::has_custom_wrap<cv::GOpaque<std::string> >::value,
"GOpaque<int> has custom marshalling logic");
}
TEST(GArg, GArrayU)
{
// Placing a GArray<T> into GArg automatically strips it to GArrayU
cv::GArg arg1 = cv::GArg(cv::GArray<int>());
EXPECT_NO_THROW(arg1.get<cv::detail::GArrayU>());
cv::GArg arg2 = cv::GArg(cv::GArray<cv::Point>());
EXPECT_NO_THROW(arg2.get<cv::detail::GArrayU>());
}
TEST(GArg, GOpaqueU)
{
// Placing a GOpaque<T> into GArg automatically strips it to GOpaqueU
cv::GArg arg1 = cv::GArg(cv::GOpaque<int>());
EXPECT_NO_THROW(arg1.get<cv::detail::GOpaqueU>());
cv::GArg arg2 = cv::GArg(cv::GOpaque<cv::Point>());
EXPECT_NO_THROW(arg2.get<cv::detail::GOpaqueU>());
}
} // namespace opencv_test

View File

@ -0,0 +1,201 @@
// 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) 2018 Intel Corporation
#include "../test_precomp.hpp"
#include "api/gcomputation_priv.hpp"
namespace opencv_test
{
TEST(GMetaArg, Traits_Is_Positive)
{
using namespace cv::detail;
static_assert(is_meta_descr<cv::GScalarDesc>::value,
"GScalarDesc is a meta description type");
static_assert(is_meta_descr<cv::GMatDesc>::value,
"GMatDesc is a meta description type");
}
TEST(GMetaArg, Traits_Is_Negative)
{
using namespace cv::detail;
static_assert(!is_meta_descr<cv::GCompileArgs>::value,
"GCompileArgs is NOT a meta description type");
static_assert(!is_meta_descr<int>::value,
"int is NOT a meta description type");
static_assert(!is_meta_descr<std::string>::value,
"str::string is NOT a meta description type");
}
TEST(GMetaArg, Traits_Are_EntireList_Positive)
{
using namespace cv::detail;
static_assert(are_meta_descrs<cv::GScalarDesc>::value,
"GScalarDesc is a meta description type");
static_assert(are_meta_descrs<cv::GMatDesc>::value,
"GMatDesc is a meta description type");
static_assert(are_meta_descrs<cv::GMatDesc, cv::GScalarDesc>::value,
"Both GMatDesc and GScalarDesc are meta types");
}
TEST(GMetaArg, Traits_Are_EntireList_Negative)
{
using namespace cv::detail;
static_assert(!are_meta_descrs<cv::GCompileArgs>::value,
"GCompileArgs is NOT among meta types");
static_assert(!are_meta_descrs<int, std::string>::value,
"Both int and std::string is NOT among meta types");
static_assert(!are_meta_descrs<cv::GMatDesc, cv::GScalarDesc, int>::value,
"List of type is not valid for meta as there\'s int");
static_assert(!are_meta_descrs<cv::GMatDesc, cv::GScalarDesc, cv::GCompileArgs>::value,
"List of type is not valid for meta as there\'s GCompileArgs");
}
TEST(GMetaArg, Traits_Are_ButLast_Positive)
{
using namespace cv::detail;
static_assert(are_meta_descrs_but_last<cv::GScalarDesc, int>::value,
"List is valid (int is omitted)");
static_assert(are_meta_descrs_but_last<cv::GMatDesc, cv::GScalarDesc, cv::GCompileArgs>::value,
"List is valid (GCompileArgs are omitted)");
}
TEST(GMetaArg, Traits_Are_ButLast_Negative)
{
using namespace cv::detail;
static_assert(!are_meta_descrs_but_last<int, std::string>::value,
"Both int is NOT among meta types (std::string is omitted)");
static_assert(!are_meta_descrs_but_last<cv::GMatDesc, cv::GScalarDesc, int, int>::value,
"List of type is not valid for meta as there\'s two ints");
static_assert(!are_meta_descrs_but_last<cv::GMatDesc, cv::GScalarDesc, cv::GCompileArgs, float>::value,
"List of type is not valid for meta as there\'s GCompileArgs");
}
TEST(GMetaArg, Can_Get_Metas_From_Input_Run_Args)
{
cv::Mat m(3, 3, CV_8UC3);
cv::Scalar s;
std::vector<int> v;
GMatDesc m_desc;
GMetaArgs meta_args = descr_of(cv::gin(m, s, v));
EXPECT_EQ(meta_args.size(), 3u);
EXPECT_NO_THROW(m_desc = util::get<cv::GMatDesc>(meta_args[0]));
EXPECT_NO_THROW(util::get<cv::GScalarDesc>(meta_args[1]));
EXPECT_NO_THROW(util::get<cv::GArrayDesc>(meta_args[2]));
EXPECT_EQ(CV_8U, m_desc.depth);
EXPECT_EQ(3, m_desc.chan);
EXPECT_EQ(cv::gapi::own::Size(3, 3), m_desc.size);
}
TEST(GMetaArg, Can_Get_Metas_From_Output_Run_Args)
{
cv::Mat m(3, 3, CV_8UC3);
cv::Scalar s;
std::vector<int> v;
GMatDesc m_desc;
GRunArgsP out_run_args = cv::gout(m, s, v);
GMetaArg m_meta = descr_of(out_run_args[0]);
GMetaArg s_meta = descr_of(out_run_args[1]);
GMetaArg v_meta = descr_of(out_run_args[2]);
EXPECT_NO_THROW(m_desc = util::get<cv::GMatDesc>(m_meta));
EXPECT_NO_THROW(util::get<cv::GScalarDesc>(s_meta));
EXPECT_NO_THROW(util::get<cv::GArrayDesc>(v_meta));
EXPECT_EQ(CV_8U, m_desc.depth);
EXPECT_EQ(3, m_desc.chan);
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::Scalar os;
std::vector<int> 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<int> 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::Scalar os;
std::vector<int> 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

View File

@ -0,0 +1,378 @@
// 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) 2018-2020 Intel Corporation
#include "../test_precomp.hpp"
#include <ade/util/zip_range.hpp> // util::indexed
#include <opencv2/gapi/gkernel.hpp>
#include <opencv2/gapi/gcommon.hpp>
#include "compiler/gmodelbuilder.hpp"
#include "compiler/gmodel.hpp" // RcDesc, GModel::init
namespace opencv_test
{
namespace test
{
namespace
{
namespace D = cv::detail;
cv::GMat unaryOp(cv::GMat m)
{
return cv::GCall(cv::GKernel{ "gapi.test.unaryop"
, ""
, nullptr
, { GShape::GMAT }
, { D::OpaqueKind::CV_UNKNOWN }
, { cv::detail::HostCtor{cv::util::monostate{}} }
}).pass(m).yield(0);
}
cv::GMat binaryOp(cv::GMat m1, cv::GMat m2)
{
return cv::GCall(cv::GKernel{ "gapi.test.binaryOp"
, ""
, nullptr
, { GShape::GMAT }
, { D::OpaqueKind::CV_UNKNOWN, D::OpaqueKind::CV_UNKNOWN }
, { cv::detail::HostCtor{cv::util::monostate{}} }
}).pass(m1, m2).yield(0);
}
std::vector<ade::NodeHandle> collectOperations(const cv::gimpl::GModel::Graph& gr)
{
std::vector<ade::NodeHandle> ops;
for (const auto& nh : gr.nodes())
{
if (gr.metadata(nh).get<cv::gimpl::NodeType>().t == cv::gimpl::NodeType::OP)
ops.push_back(nh);
}
return ops;
}
ade::NodeHandle inputOf(cv::gimpl::GModel::Graph& gm, ade::NodeHandle nh, std::size_t port)
{
for (const auto& eh : nh->inEdges())
{
if (gm.metadata(eh).get<cv::gimpl::Input>().port == port)
{
return eh->srcNode();
}
}
util::throw_error(std::logic_error("port " + std::to_string(port) + " not found"));
}
}
}// namespace opencv_test::test
TEST(GModelBuilder, Unroll_TestUnary)
{
cv::GMat in;
cv::GMat out = test::unaryOp(in);
auto unrolled = cv::gimpl::unrollExpr(cv::GIn(in).m_args, cv::GOut(out).m_args);
EXPECT_EQ(1u, unrolled.all_ops.size()); // There is one operation
EXPECT_EQ(2u, unrolled.all_data.size()); // And two data objects (in, out)
// TODO check what the operation is, and so on, and so on
}
TEST(GModelBuilder, Unroll_TestUnaryOfUnary)
{
cv::GMat in;
cv::GMat out = test::unaryOp(test::unaryOp(in));
auto unrolled = cv::gimpl::unrollExpr(cv::GIn(in).m_args, cv::GOut(out).m_args);
EXPECT_EQ(2u, unrolled.all_ops.size()); // There're two operations
EXPECT_EQ(3u, unrolled.all_data.size()); // And three data objects (in, out)
// TODO check what the operation is, and so on, and so on
}
TEST(GModelBuilder, Unroll_Not_All_Protocol_Inputs_Are_Reached)
{
cv::GMat in1, in2; // in1 -> unaryOp() -> u_op1 -> unaryOp() -> out
auto u_op1 = test::unaryOp(in1); // in2 -> unaryOp() -> u_op2
auto u_op2 = test::unaryOp(in2);
auto out = test::unaryOp(u_op1);
EXPECT_THROW(cv::gimpl::unrollExpr(cv::GIn(in1, in2).m_args, cv::GOut(out).m_args), std::logic_error);
}
TEST(GModelBuilder, Unroll_Parallel_Path)
{
cv::GMat in1, in2; // in1 -> unaryOp() -> out1
auto out1 = test::unaryOp(in1); // in2 -> unaryOp() -> out2
auto out2 = test::unaryOp(in2);
auto unrolled = cv::gimpl::unrollExpr(cv::GIn(in1, in2).m_args, cv::GOut(out1, out2).m_args);
EXPECT_EQ(unrolled.all_ops.size(), 2u);
EXPECT_EQ(unrolled.all_data.size(), 4u);
}
TEST(GModelBuilder, Unroll_WithBranch)
{
// in -> unaryOp() -> tmp -->unaryOp() -> out1
// `---->unaryOp() -> out2
GMat in;
auto tmp = test::unaryOp(in);
auto out1 = test::unaryOp(tmp);
auto out2 = test::unaryOp(tmp);
auto unrolled = cv::gimpl::unrollExpr(cv::GIn(in).m_args, cv::GOut(out1, out2).m_args);
EXPECT_EQ(unrolled.all_ops.size(), 3u);
EXPECT_EQ(unrolled.all_data.size(), 4u);
}
TEST(GModelBuilder, Build_Unary)
{
cv::GMat in;
cv::GMat out = test::unaryOp(in);
ade::Graph g;
cv::gimpl::GModel::Graph gm(g);
cv::gimpl::GModel::init(gm);
cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(out).m_args);
EXPECT_EQ(3u, static_cast<std::size_t>(g.nodes().size())); // Generated graph should have three nodes
// TODO: Check what the nodes are
}
TEST(GModelBuilder, Constant_GScalar)
{
// in -> addC()-----(GMat)---->mulC()-----(GMat)---->unaryOp()----out
// ^ ^
// | |
// 3-------` c_s-------'
cv::GMat in;
cv::GScalar c_s = 5;
auto out = test::unaryOp((in + 3) * c_s); // 3 converted to GScalar
ade::Graph g;
cv::gimpl::GModel::Graph gm(g);
cv::gimpl::GModel::init(gm);
auto proto_slots = cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(out).m_args);
cv::gimpl::Protocol p;
std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
auto in_nh = p.in_nhs.front();
auto addC_nh = in_nh->outNodes().front();
auto mulC_nh = addC_nh->outNodes().front()->outNodes().front();
ASSERT_TRUE(gm.metadata(addC_nh).get<cv::gimpl::NodeType>().t == cv::gimpl::NodeType::OP);
ASSERT_TRUE(gm.metadata(mulC_nh).get<cv::gimpl::NodeType>().t == cv::gimpl::NodeType::OP);
auto s_3 = test::inputOf(gm, addC_nh, 1);
auto s_5 = test::inputOf(gm, mulC_nh, 1);
EXPECT_EQ(9u, static_cast<std::size_t>(g.nodes().size())); // 6 data nodes (1 -input, 1 output, 2 constant, 2 temp) and 3 op nodes
EXPECT_EQ(2u, static_cast<std::size_t>(addC_nh->inNodes().size())); // in and 3
EXPECT_EQ(2u, static_cast<std::size_t>(mulC_nh->inNodes().size())); // addC output and c_s
EXPECT_EQ(3, (util::get<cv::Scalar>(gm.metadata(s_3).get<cv::gimpl::ConstValue>().arg))[0]);
EXPECT_EQ(5, (util::get<cv::Scalar>(gm.metadata(s_5).get<cv::gimpl::ConstValue>().arg))[0]);
}
TEST(GModelBuilder, Check_Multiple_Outputs)
{
// ------------------------------> r
// '
// ' -----------> i_out1
// ' '
// in ----> split3() ---> g ---> integral()
// ' '
// ' -----------> i_out2
// '
// '---------> b ---> unaryOp() ---> u_out
cv::GMat in, r, g, b, i_out1, i_out2, u_out;
std::tie(r, g, b) = cv::gapi::split3(in);
std::tie(i_out1, i_out2) = cv::gapi::integral(g, 1, 1);
u_out = test::unaryOp(b);
ade::Graph gr;
cv::gimpl::GModel::Graph gm(gr);
cv::gimpl::GModel::init(gm);
auto proto_slots = cv::gimpl::GModelBuilder(gr).put(cv::GIn(in).m_args, cv::GOut(r, i_out1, i_out2, u_out).m_args);
cv::gimpl::Protocol p;
std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
EXPECT_EQ(4u, static_cast<std::size_t>(p.out_nhs.size()));
EXPECT_EQ(0u, gm.metadata(p.out_nhs[0]->inEdges().front()).get<cv::gimpl::Output>().port);
EXPECT_EQ(0u, gm.metadata(p.out_nhs[1]->inEdges().front()).get<cv::gimpl::Output>().port);
EXPECT_EQ(1u, gm.metadata(p.out_nhs[2]->inEdges().front()).get<cv::gimpl::Output>().port);
EXPECT_EQ(0u, gm.metadata(p.out_nhs[3]->inEdges().front()).get<cv::gimpl::Output>().port);
for (const auto it : ade::util::indexed(p.out_nhs))
{
const auto& out_nh = ade::util::value(it);
EXPECT_EQ(cv::gimpl::NodeType::DATA, gm.metadata(out_nh).get<cv::gimpl::NodeType>().t);
EXPECT_EQ(GShape::GMAT, gm.metadata(out_nh).get<cv::gimpl::Data>().shape);
}
}
TEST(GModelBuilder, Unused_Outputs)
{
cv::GMat in;
auto yuv_p = cv::gapi::split3(in);
ade::Graph g;
cv::gimpl::GModel::Graph gm(g);
cv::gimpl::GModel::init(gm);
cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(std::get<0>(yuv_p)).m_args);
EXPECT_EQ(5u, static_cast<std::size_t>(g.nodes().size())); // 1 input, 1 operation, 3 outputs
}
TEST(GModelBuilder, Work_With_One_Channel_From_Split3)
{
cv::GMat in, y, u, v;
std::tie(y, u, v) = cv::gapi::split3(in);
auto y_blur = cv::gapi::gaussianBlur(y, cv::Size(3, 3), 1);
ade::Graph g;
cv::gimpl::GModel::Graph gm(g);
cv::gimpl::GModel::init(gm);
cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(y_blur).m_args);
EXPECT_EQ(7u, static_cast<std::size_t>(g.nodes().size())); // 1 input, 2 operation, 3 nodes from split3, 1 output
}
TEST(GModelBuilder, Add_Nodes_To_Unused_Nodes)
{
cv::GMat in, y, u, v;
std::tie(y, u, v) = cv::gapi::split3(in);
auto y_blur = cv::gapi::gaussianBlur(y, cv::Size(3, 3), 1);
// unused nodes
auto u_blur = cv::gapi::gaussianBlur(y, cv::Size(3, 3), 1);
auto v_blur = cv::gapi::gaussianBlur(y, cv::Size(3, 3), 1);
ade::Graph g;
cv::gimpl::GModel::Graph gm(g);
cv::gimpl::GModel::init(gm);
cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(y_blur).m_args);
EXPECT_EQ(7u, static_cast<std::size_t>(g.nodes().size())); // 1 input, 2 operation, 3 nodes from split3, 1 output
}
TEST(GModelBuilder, Unlisted_Inputs)
{
// in1 -> binaryOp() -> out
// ^
// |
// in2 ----'
cv::GMat in1, in2;
auto out = test::binaryOp(in1, in2);
ade::Graph g;
cv::gimpl::GModel::Graph gm(g);
cv::gimpl::GModel::init(gm);
// add required 2 inputs but pass 1
EXPECT_THROW(cv::gimpl::GModelBuilder(g).put(cv::GIn(in1).m_args, cv::GOut(out).m_args), std::logic_error);
}
TEST(GModelBuilder, Unroll_No_Link_Between_In_And_Out)
{
// in -> unaryOp() -> u_op
// other -> unaryOp() -> out
cv::GMat in, other;
auto u_op = test::unaryOp(in);
auto out = test::unaryOp(other);
EXPECT_THROW(cv::gimpl::unrollExpr(cv::GIn(in).m_args, cv::GOut(out).m_args), std::logic_error);
}
TEST(GModel_builder, Check_Binary_Op)
{
// in1 -> binaryOp() -> out
// ^
// |
// in2 -----'
cv::GMat in1, in2;
auto out = test::binaryOp(in1, in2);
ade::Graph g;
cv::gimpl::GModel::Graph gm(g);
cv::gimpl::GModel::init(gm);
auto proto_slots = cv::gimpl::GModelBuilder(g).put(cv::GIn(in1, in2).m_args, cv::GOut(out).m_args);
cv::gimpl::Protocol p;
std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
auto ops = test::collectOperations(g);
EXPECT_EQ(1u, ops.size());
EXPECT_EQ("gapi.test.binaryOp", gm.metadata(ops.front()).get<cv::gimpl::Op>().k.name);
EXPECT_EQ(2u, static_cast<std::size_t>(ops.front()->inEdges().size()));
EXPECT_EQ(1u, static_cast<std::size_t>(ops.front()->outEdges().size()));
EXPECT_EQ(1u, static_cast<std::size_t>(ops.front()->outNodes().size()));
}
TEST(GModelBuilder, Add_Operation_With_Two_Out_One_Time)
{
// in -> integral() --> out_b1 -> unaryOp() -> out1
// |
// '-------> out_b2 -> unaryOp() -> out2
cv::GMat in, out_b1, out_b2;
std::tie(out_b1, out_b2) = cv::gapi::integral(in, 1, 1);
auto out1 = test::unaryOp(out_b1);
auto out2 = test::unaryOp(out_b1);
ade::Graph g;
cv::gimpl::GModel::Graph gm(g);
cv::gimpl::GModel::init(gm);
auto proto_slots = cv::gimpl::GModelBuilder(g).put(cv::GIn(in).m_args, cv::GOut(out1, out2).m_args);
auto ops = test::collectOperations(gm);
cv::gimpl::Protocol p;
std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
auto integral_nh = p.in_nhs.front()->outNodes().front();
EXPECT_EQ(3u, ops.size());
EXPECT_EQ("org.opencv.core.matrixop.integral", gm.metadata(integral_nh).get<cv::gimpl::Op>().k.name);
EXPECT_EQ(1u, static_cast<std::size_t>(integral_nh->inEdges().size()));
EXPECT_EQ(2u, static_cast<std::size_t>(integral_nh->outEdges().size()));
EXPECT_EQ(2u, static_cast<std::size_t>(integral_nh->outNodes().size()));
}
TEST(GModelBuilder, Add_Operation_With_One_Out_One_Time)
{
// in1 -> binaryOp() -> b_out -> unaryOp() -> out1
// ^ |
// | |
// in2 ------- '----> unaryOp() -> out2
cv::GMat in1, in2;
auto b_out = test::binaryOp(in1, in2);
auto out1 = test::unaryOp(b_out);
auto out2 = test::unaryOp(b_out);
ade::Graph g;
cv::gimpl::GModel::Graph gm(g);
cv::gimpl::GModel::init(gm);
auto proto_slots = cv::gimpl::GModelBuilder(g).put(cv::GIn(in1, in2).m_args, cv::GOut(out1, out2).m_args);
cv::gimpl::Protocol p;
std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
cv::gimpl::GModel::Graph gr(g);
auto binaryOp_nh = p.in_nhs.front()->outNodes().front();
EXPECT_EQ(2u, static_cast<std::size_t>(binaryOp_nh->inEdges().size()));
EXPECT_EQ(1u, static_cast<std::size_t>(binaryOp_nh->outEdges().size()));
EXPECT_EQ(8u, static_cast<std::size_t>(g.nodes().size()));
}
} // namespace opencv_test

View File

@ -0,0 +1,588 @@
// 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) 2018 Intel Corporation
#include "../test_precomp.hpp"
#include "compiler/transactions.hpp"
#include "../gapi_mock_kernels.hpp"
#include "compiler/gislandmodel.hpp"
#include "compiler/gcompiler.hpp"
#include "compiler/gmodel_priv.hpp"
namespace opencv_test
{
TEST(IslandFusion, TwoOps_OneIsland)
{
namespace J = Jupiter; // see mock_kernels.cpp
// Define a computation:
//
// (in) -> J::Foo1 -> (tmp0) -> J::Foo2 -> (out)
// : :
// : "island0" :
// :<----------------------------->:
cv::GMat in;
cv::GMat tmp0 = I::Foo::on(in);
cv::GMat out = I::Foo::on(tmp0);
cv::GComputation cc(in, out);
// Prepare compilation parameters manually
const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
const auto pkg = cv::gapi::kernels<J::Foo>();
// Directly instantiate G-API graph compiler and run partial compilation
cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
compiler.runPasses(*graph);
// Inspect the graph and verify the islands configuration
cv::gimpl::GModel::ConstGraph gm(*graph);
cv::gimpl::GModel::ConstLayoutGraph glm(*graph);
auto in_nh = cv::gimpl::GModel::dataNodeOf(glm, in);
auto tmp_nh = cv::gimpl::GModel::dataNodeOf(glm, tmp0);
auto out_nh = cv::gimpl::GModel::dataNodeOf(glm, out);
// in/out mats shouldn't be assigned to any Island
EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
// Since tmp is surrounded by two J kernels, tmp should be assigned
// to island J
EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
}
TEST(IslandFusion, TwoOps_TwoIslands)
{
namespace J = Jupiter; // see mock_kernels.cpp
namespace S = Saturn; // see mock_kernels.cpp
// Define a computation:
//
// (in) -> J::Foo --> (tmp0) -> S::Bar --> (out)
// : : -> :
// : : : :
// :<-------->: :<-------->:
cv::GMat in;
cv::GMat tmp0 = I::Foo::on(in);
cv::GMat out = I::Bar::on(tmp0, tmp0);
cv::GComputation cc(in, out);
// Prepare compilation parameters manually
const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
const auto pkg = cv::gapi::kernels<J::Foo, S::Bar>();
// Directly instantiate G-API graph compiler and run partial compilation
cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
compiler.runPasses(*graph);
// Inspect the graph and verify the islands configuration
cv::gimpl::GModel::ConstGraph gm(*graph);
auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);
auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
// in/tmp/out mats shouldn't be assigned to any Island
EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
// There should be two islands in the GIslandModel
const auto is_island = [&](ade::NodeHandle nh) {
return (cv::gimpl::NodeKind::ISLAND
== gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
};
const std::size_t num_isl = std::count_if(gim.nodes().begin(),
gim.nodes().end(),
is_island);
EXPECT_EQ(2u, num_isl);
auto isl_foo_nh = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);
auto isl_bar_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
ASSERT_NE(nullptr, isl_foo_nh);
ASSERT_NE(nullptr, isl_bar_nh);
// Islands should be different
auto isl_foo_obj = gim.metadata(isl_foo_nh).get<cv::gimpl::FusedIsland>().object;
auto isl_bar_obj = gim.metadata(isl_bar_nh).get<cv::gimpl::FusedIsland>().object;
EXPECT_FALSE(isl_foo_obj == isl_bar_obj);
}
TEST(IslandFusion, ConsumerHasTwoInputs)
{
namespace J = Jupiter; // see mock_kernels.cpp
// Define a computation: island
// ............................
// (in0) ->:J::Foo -> (tmp) -> S::Bar :--> (out)
// :....................^.....:
// |
// (in1) -----------------------`
//
// Check that island is build correctly, when consumer has two inputs
GMat in[2];
GMat tmp = I::Foo::on(in[0]);
GMat out = I::Bar::on(tmp, in[1]);
cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));
// Prepare compilation parameters manually
cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)}),
GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)})};
const auto pkg = cv::gapi::kernels<J::Foo, J::Bar>();
// Directly instantiate G-API graph compiler and run partial compilation
cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));
cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
compiler.runPasses(*graph);
cv::gimpl::GModel::ConstGraph gm(*graph);
auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);
auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
EXPECT_FALSE(gm.metadata(in0_nh ).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(in1_nh ).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
const auto is_island = [&](ade::NodeHandle nh) {
return (cv::gimpl::NodeKind::ISLAND
== gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
};
const std::size_t num_isl = std::count_if(gim.nodes().begin(),
gim.nodes().end(),
is_island);
EXPECT_EQ(1u, num_isl);
auto isl_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;
EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp_nh));
EXPECT_EQ(2u, static_cast<std::size_t>(isl_nh->inNodes().size()));
EXPECT_EQ(1u, static_cast<std::size_t>(isl_nh->outNodes().size()));
}
TEST(IslandFusion, DataNodeUsedDifferentBackend)
{
// Define a computation:
//
// internal isl isl0
// ...........................
// (in1) -> :J::Foo--> (tmp) -> J::Foo: --> (out0)
// :............|............:
// | ........
// `---->:S::Baz: --> (out1)
// :......:
// Check that the node was not dropped out of the island
// because it is used by the kernel from another backend
namespace J = Jupiter;
namespace S = Saturn;
cv::GMat in, tmp, out0;
cv::GScalar out1;
tmp = I::Foo::on(in);
out0 = I::Foo::on(tmp);
out1 = I::Baz::on(tmp);
cv::GComputation cc(cv::GIn(in), cv::GOut(out0, out1));
// Prepare compilation parameters manually
const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
const auto pkg = cv::gapi::kernels<J::Foo, S::Baz>();
// Directly instantiate G-API graph compiler and run partial compilation
cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
compiler.runPasses(*graph);
// Inspect the graph and verify the islands configuration
cv::gimpl::GModel::ConstGraph gm(*graph);
auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);
auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);
auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out0);
auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out1);
EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
auto isl_nh = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);
auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;
EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp_nh));
EXPECT_EQ(2u, static_cast<std::size_t>(isl_nh->outNodes().size()));
EXPECT_EQ(7u, static_cast<std::size_t>(gm.nodes().size()));
EXPECT_EQ(6u, static_cast<std::size_t>(gim.nodes().size()));
}
TEST(IslandFusion, LoopBetweenDifferentBackends)
{
// Define a computation:
//
//
// .............................
// (in) -> :J::Baz -> (tmp0) -> J::Quux: -> (out0)
// | :............|..........^....
// | ........ | | ........
// `---->:S::Foo: `----------|-------->:S::Qux:-> (out1)
// :....|.: | :....^.:
// | | |
// `-------------- (tmp1) -----------`
// Kernels S::Foo and S::Qux cannot merge, because there will be a cycle between islands
namespace J = Jupiter;
namespace S = Saturn;
cv::GScalar tmp0;
cv::GMat in, tmp1, out0, out1;
tmp0 = I::Baz::on(in);
tmp1 = I::Foo::on(in);
out1 = I::Qux::on(tmp1, tmp0);
out0 = I::Quux::on(tmp0, tmp1);
cv::GComputation cc(cv::GIn(in), cv::GOut(out1, out0));
// Prepare compilation parameters manually
const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
const auto pkg = cv::gapi::kernels<J::Baz, J::Quux, S::Foo, S::Qux>();
// Directly instantiate G-API graph compiler and run partial compilation
cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
compiler.runPasses(*graph);
cv::gimpl::GModel::ConstGraph gm(*graph);
auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);
auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp1);
auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out0);
auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out1);
EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>());
// The node does not belong to the island so as not to form a cycle
EXPECT_FALSE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());
EXPECT_TRUE(gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());
// There should be three islands in the GIslandModel
const auto is_island = [&](ade::NodeHandle nh) {
return (cv::gimpl::NodeKind::ISLAND
== gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
};
const std::size_t num_isl = std::count_if(gim.nodes().begin(),
gim.nodes().end(),
is_island);
EXPECT_EQ(3u, num_isl);
}
TEST(IslandsFusion, PartionOverlapUserIsland)
{
// Define a computation:
//
// internal isl isl0
// ........ ........
// (in0) -> :J::Foo:--> (tmp) ->:S::Bar: --> (out)
// :......: :......:
// ^
// |
// (in1) --------------------------`
// Check that internal islands doesn't overlap user island
namespace J = Jupiter;
namespace S = Saturn;
GMat in[2];
GMat tmp = I::Foo::on(in[0]);
GMat out = I::Bar::on(tmp, in[1]);
cv::gapi::island("isl0", cv::GIn(tmp, in[1]), cv::GOut(out));
cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));
// Prepare compilation parameters manually
cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)}),
GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)})};
const auto pkg = cv::gapi::kernels<J::Foo, J::Bar>();
// Directly instantiate G-API graph compiler and run partial compilation
cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));
cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
compiler.runPasses(*graph);
cv::gimpl::GModel::ConstGraph gm(*graph);
auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;
cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);
auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
auto foo_nh = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);
auto foo_obj = gim.metadata(foo_nh).get<cv::gimpl::FusedIsland>().object;
auto bar_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
auto bar_obj = gim.metadata(bar_nh).get<cv::gimpl::FusedIsland>().object;
EXPECT_FALSE(gm.metadata(in0_nh ).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(in1_nh ).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(foo_obj->is_user_specified());
EXPECT_TRUE(bar_obj->is_user_specified());
}
TEST(IslandsFusion, DISABLED_IslandContainsDifferentBackends)
{
// Define a computation:
//
// isl0
// ............................
// (in0) -> :J::Foo:--> (tmp) -> S::Bar: --> (out)
// :..........................:
// ^
// |
// (in1) --------------------------`
// Try create island contains different backends
namespace J = Jupiter;
namespace S = Saturn;
GMat in[2];
GMat tmp = I::Foo::on(in[0]);
GMat out = I::Bar::on(tmp, in[1]);
cv::gapi::island("isl0", cv::GIn(in[0], in[1]), cv::GOut(out));
cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));
// Prepare compilation parameters manually
cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)}),
GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)})};
const auto pkg = cv::gapi::kernels<J::Foo, S::Bar>();
// Directly instantiate G-API graph compiler and run partial compilation
cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));
cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
EXPECT_ANY_THROW(compiler.runPasses(*graph));
}
TEST(IslandFusion, WithLoop)
{
namespace J = Jupiter; // see mock_kernels.cpp
// Define a computation:
//
// (in) -> J::Foo --> (tmp0) -> J::Foo --> (tmp1) -> J::Qux -> (out)
// : ^
// '--> J::Baz --> (scl0) --'
//
// The whole thing should be merged to a single island
// There's a cycle warning if Foo/Foo/Qux are merged first
// Then this island both produces data for Baz and consumes data
// from Baz. This is a cycle and it should be avoided by the merging code.
//
cv::GMat in;
cv::GMat tmp0 = I::Foo::on(in);
cv::GMat tmp1 = I::Foo::on(tmp0);
cv::GScalar scl0 = I::Baz::on(tmp0);
cv::GMat out = I::Qux::on(tmp1, scl0);
cv::GComputation cc(in, out);
// Prepare compilation parameters manually
const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
const auto pkg = cv::gapi::kernels<J::Foo, J::Baz, J::Qux>();
// Directly instantiate G-API graph compiler and run partial compilation
cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));
cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
compiler.runPasses(*graph);
// Inspect the graph and verify the islands configuration
cv::gimpl::GModel::ConstGraph gm(*graph);
auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);
auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);
auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp1);
auto scl0_nh = cv::gimpl::GModel::dataNodeOf(gm, scl0);
auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
// in/out mats shouldn't be assigned to any Island
EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());
// tmp0/tmp1/scl should be assigned to island
EXPECT_TRUE(gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());
EXPECT_TRUE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());
EXPECT_TRUE(gm.metadata(scl0_nh).contains<cv::gimpl::Island>());
// Check that there's a single island object and it contains all
// that data object handles
cv::gimpl::GModel::ConstGraph cg(*graph);
auto isl_model = cg.metadata().get<cv::gimpl::IslandModel>().model;
cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
const auto is_island = [&](ade::NodeHandle nh) {
return (cv::gimpl::NodeKind::ISLAND
== gim.metadata(nh).get<cv::gimpl::NodeKind>().k);
};
const std::size_t num_isl = std::count_if(gim.nodes().begin(),
gim.nodes().end(),
is_island);
EXPECT_EQ(1u, num_isl);
auto isl_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);
auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;
EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp0_nh));
EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp1_nh));
EXPECT_TRUE(ade::util::contains(isl_obj->contents(), scl0_nh));
}
TEST(IslandFusion, Regression_ShouldFuseAll)
{
// Initially the merge procedure didn't work as expected and
// stopped fusion even if it could be continued (e.g. full
// GModel graph could be fused into a single GIsland node).
// Example of this is custom RGB 2 YUV pipeline as shown below:
cv::GMat r, g, b;
cv::GMat y = 0.299f*r + 0.587f*g + 0.114f*b;
cv::GMat u = 0.492f*(b - y);
cv::GMat v = 0.877f*(r - y);
cv::GComputation customCvt({r, g, b}, {y, u, v});
const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
// Directly instantiate G-API graph compiler and run partial compilation
cv::gimpl::GCompiler compiler(customCvt, {in_meta,in_meta,in_meta}, cv::compile_args());
cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();
compiler.runPasses(*graph);
cv::gimpl::GModel::ConstGraph cg(*graph);
auto isl_model = cg.metadata().get<cv::gimpl::IslandModel>().model;
cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);
std::vector<ade::NodeHandle> data_nhs;
std::vector<ade::NodeHandle> isl_nhs;
for (auto &&nh : gim.nodes())
{
if (gim.metadata(nh).contains<cv::gimpl::FusedIsland>())
isl_nhs.push_back(std::move(nh));
else if (gim.metadata(nh).contains<cv::gimpl::DataSlot>())
data_nhs.push_back(std::move(nh));
else FAIL() << "GIslandModel node with unexpected metadata type";
}
EXPECT_EQ(6u, data_nhs.size()); // 3 input nodes + 3 output nodes
EXPECT_EQ(1u, isl_nhs.size()); // 1 island
}
TEST(IslandFusion, Test_Desync_NoFuse)
{
cv::GMat in;
cv::GMat tmp1 = in*0.5f;
cv::GMat tmp2 = tmp1 + in;
cv::GMat tmp3 = cv::gapi::streaming::desync(tmp1);
cv::GMat tmp4 = tmp3*0.1f;
const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});
cv::GComputation comp(cv::GIn(in), cv::GOut(tmp2, tmp4));
//////////////////////////////////////////////////////////////////
// Compile the graph in "regular" mode, it should produce a single island
// Note: with copy moved to a separate backend there is always 3 islands in this test
{
using namespace cv::gimpl;
GCompiler compiler(comp, {in_meta}, cv::compile_args());
GCompiler::GPtr graph = compiler.generateGraph();
compiler.runPasses(*graph);
auto isl_model = GModel::ConstGraph(*graph).metadata()
.get<IslandModel>().model;
GIslandModel::ConstGraph gim(*isl_model);
const auto is_island = [&](ade::NodeHandle nh) {
return (NodeKind::ISLAND == gim.metadata(nh).get<NodeKind>().k);
};
const auto num_isl = std::count_if(gim.nodes().begin(),
gim.nodes().end(),
is_island);
EXPECT_EQ(3, num_isl);
}
//////////////////////////////////////////////////////////////////
// Now compile the graph in the streaming mode.
// It has to produce two islands
// Note: with copy moved to a separate backend there is always 3 islands in this test
{
using namespace cv::gimpl;
GCompiler compiler(comp, {in_meta}, cv::compile_args());
GCompiler::GPtr graph = compiler.generateGraph();
GModel::Graph(*graph).metadata().set(Streaming{});
compiler.runPasses(*graph);
auto isl_model = GModel::ConstGraph(*graph).metadata()
.get<IslandModel>().model;
GIslandModel::ConstGraph gim(*isl_model);
const auto is_island = [&](ade::NodeHandle nh) {
return (NodeKind::ISLAND == gim.metadata(nh).get<NodeKind>().k);
};
const auto num_isl = std::count_if(gim.nodes().begin(),
gim.nodes().end(),
is_island);
EXPECT_EQ(3, num_isl);
}
}
// Fixme: add more tests on mixed (hetero) graphs
// ADE-222, ADE-223
// FIXME: add test on combination of user-specified island
// which should be heterogeneous (based on kernel availability)
// but as we don't support this, compilation should fail
// FIXME: add tests on automatic inferred islands which are
// connected via 1) gmat 2) gscalar 3) garray,
// check the case with executor
// check the case when this 1/2/3 interim object is also gcomputation output
} // namespace opencv_test

View File

@ -0,0 +1,654 @@
// 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) 2018 Intel Corporation
#include "../test_precomp.hpp"
#include "compiler/gmodel.hpp"
#include "compiler/gcompiled_priv.hpp"
#include "compiler/gmodel_priv.hpp"
namespace opencv_test
{
////////////////////////////////////////////////////////////////////////////////
// Tests on a plain graph
//
// (in) -> Blur1 -> (tmp0) -> Blur2 -> (tmp1) -> Blur3 -> (tmp2) -> Blur4 -> (out)
//
namespace
{
struct PlainIslandsFixture
{
cv::GMat in;
cv::GMat tmp[3];
cv::GMat out;
PlainIslandsFixture()
{
tmp[0] = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
tmp[1] = cv::gapi::boxFilter(tmp[0], -1, cv::Size(3,3));
tmp[2] = cv::gapi::boxFilter(tmp[1], -1, cv::Size(3,3));
out = cv::gapi::boxFilter(tmp[2], -1, cv::Size(3,3));
}
};
struct Islands: public ::testing::Test, public PlainIslandsFixture {};
using GIntArray = GArray<int>;
G_TYPED_KERNEL(CreateMatWithDiag, <GMat(GIntArray)>, "test.array.create_mat_with_diag")
{
static GMatDesc outMeta(const GArrayDesc&) { return cv::GMatDesc{CV_32S, 1,{3, 3}}; }
};
GAPI_OCV_KERNEL(CreateMatWithDiagImpl, CreateMatWithDiag)
{
static void run(const std::vector<int> &in, cv::Mat& out)
{
auto size = static_cast<int>(in.size());
out = Mat::zeros(size, size, CV_32SC1);
for(int i = 0; i < out.rows; i++)
{
auto* row = out.ptr<int>(i);
row[i] = in[i];
}
}
};
G_TYPED_KERNEL(Mat2Array, <GIntArray(GMat)>, "test.array.mat2array")
{
static GArrayDesc outMeta(const GMatDesc&) { return empty_array_desc(); }
};
GAPI_OCV_KERNEL(Mat2ArrayImpl, Mat2Array)
{
static void run(const cv::Mat& in, std::vector<int> &out)
{
GAPI_Assert(in.depth() == CV_32S && in.isContinuous());
out.reserve(in.cols * in.rows);
out.assign((int*)in.datastart, (int*)in.dataend);
}
};
}
TEST_F(Islands, SmokeTest)
{
// (in) -> Blur1 -> (tmp0) -> Blur2 -> (tmp1) -> Blur3 -> (tmp2) -> Blur4 -> (out)
// : "test" :
// :<------------------------->:
cv::gapi::island("test", cv::GIn(tmp[0]), cv::GOut(tmp[2]));
auto cc = cv::GComputation(in, out).compile(cv::GMatDesc{CV_8U,1,{640,480}});
const auto &gm = cc.priv().model();
const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
// tmp1 and tmp3 is not a part of any island
EXPECT_FALSE(gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp2_nh).contains<cv::gimpl::Island>());
// tmp2 is part of "test" island
EXPECT_TRUE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());
EXPECT_EQ("test", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
}
TEST_F(Islands, TwoIslands)
{
// (in) -> Blur1 -> (tmp0) -> Blur2 -> (tmp1) -> Blur3 -> (tmp2) -> Blur4 -> (out)
// : "test1" : : "test2" :
// :<---------------------------->: :<--------------------------------->
EXPECT_NO_THROW(cv::gapi::island("test1", cv::GIn(in), cv::GOut(tmp[1])));
EXPECT_NO_THROW(cv::gapi::island("test2", cv::GIn(tmp[1]), cv::GOut(out)));
auto cc = cv::GComputation(in, out).compile(cv::GMatDesc{CV_8U,1,{640,480}});
const auto &gm = cc.priv().model();
const auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);
const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
const auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);
// Only tmp0 and tmp2 should be listed in islands.
EXPECT_TRUE (gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());
EXPECT_TRUE (gm.metadata(tmp2_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(in_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out_nh) .contains<cv::gimpl::Island>());
EXPECT_EQ("test1", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island);
EXPECT_EQ("test2", gm.metadata(tmp2_nh).get<cv::gimpl::Island>().island);
}
// FIXME: Disabled since currently merge procedure merges two into one
// successfully
TEST_F(Islands, DISABLED_Two_Islands_With_Same_Name_Should_Fail)
{
// (in) -> Blur1 -> (tmp0) -> Blur2 -> (tmp1) -> Blur3 -> (tmp2) -> Blur4 -> (out)
// : "test1" : : "test1" :
// :<---------------------------->: :<--------------------------------->
EXPECT_NO_THROW(cv::gapi::island("test1", cv::GIn(in), cv::GOut(tmp[1])));
EXPECT_NO_THROW(cv::gapi::island("test1", cv::GIn(tmp[1]), cv::GOut(out)));
EXPECT_ANY_THROW(cv::GComputation(in, out).compile(cv::GMatDesc{CV_8U,1,{640,480}}));
}
// (in) -> Blur1 -> (tmp0) -> Blur2 -> (tmp1) -> Blur3 -> (tmp2) -> Blur4 -> (out)
// : "test1": : :
// :<----------------:----------->: :
// : :
// : "test2" :
// :<------------------------->:
TEST_F(Islands, OverlappingIslands1)
{
EXPECT_NO_THROW (cv::gapi::island("test1", cv::GIn(in), cv::GOut(tmp[1])));
EXPECT_ANY_THROW(cv::gapi::island("test2", cv::GIn(tmp[0]), cv::GOut(tmp[2])));
}
TEST_F(Islands, OverlappingIslands2)
{
EXPECT_NO_THROW (cv::gapi::island("test2", cv::GIn(tmp[0]), cv::GOut(tmp[2])));
EXPECT_ANY_THROW(cv::gapi::island("test1", cv::GIn(in), cv::GOut(tmp[1])));
}
////////////////////////////////////////////////////////////////////////////////
// Tests on a complex graph
//
// (in0) -> Not -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
// ^ ^
// (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
// :
// `------------> Median -> (tmp3) --> Blur -------> (out1)
//
namespace
{
struct ComplexIslandsFixture
{
cv::GMat in[2];
cv::GMat tmp[4];
cv::GScalar scl;
cv::GMat out[2];
ComplexIslandsFixture()
{
tmp[0] = cv::gapi::bitwise_not(in[0]);
tmp[1] = cv::gapi::boxFilter(in[1], -1, cv::Size(3,3));
tmp[2] = tmp[0] + tmp[1]; // FIXME: handle tmp[2] = tmp[0]+tmp[2] typo
scl = cv::gapi::sum(tmp[1]);
tmp[3] = cv::gapi::medianBlur(tmp[1], 3);
out[0] = tmp[2] + scl;
out[1] = cv::gapi::boxFilter(tmp[3], -1, cv::Size(3,3));
}
};
struct ComplexIslands: public ::testing::Test, public ComplexIslandsFixture {};
} // namespace
TEST_F(ComplexIslands, SmokeTest)
{
// isl0 #internal1
// ........................... ........
// (in0) -> Not -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
// :............ ........^...: :.^....:
// ... : :
// (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
// : isl1
// : ..............................
// `------------> Median -> (tmp3) --> Blur -------> (out1)
// :............................:
cv::gapi::island("isl0", cv::GIn(in[0], tmp[1]), cv::GOut(tmp[2]));
cv::gapi::island("isl1", cv::GIn(tmp[1]), cv::GOut(out[1]));
auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
.compile(cv::GMatDesc{CV_8U,1,{640,480}},
cv::GMatDesc{CV_8U,1,{640,480}});
const auto &gm = cc.priv().model();
const auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
const auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[3]);
const auto scl_nh = cv::gimpl::GModel::dataNodeOf(gm, scl);
const auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out[0]);
const auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out[1]);
// tmp0, tmp3 are in islands, others are not
EXPECT_TRUE(gm.metadata(tmp0_nh) .contains<cv::gimpl::Island>()); // isl0
EXPECT_TRUE(gm.metadata(tmp3_nh) .contains<cv::gimpl::Island>()); // isl1
EXPECT_FALSE(gm.metadata(in0_nh) .contains<cv::gimpl::Island>()); // (input is never fused)
EXPECT_FALSE(gm.metadata(in1_nh) .contains<cv::gimpl::Island>()); // (input is never fused)
EXPECT_TRUE (gm.metadata(tmp1_nh).contains<cv::gimpl::Island>()); // <internal island>
EXPECT_FALSE(gm.metadata(tmp2_nh).contains<cv::gimpl::Island>()); // #not fused as cycle-causing#
EXPECT_FALSE(gm.metadata(scl_nh) .contains<cv::gimpl::Island>()); // #not fused as cycle-causing#
EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>()); // (output is never fused)
EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>()); // (output is never fused)
EXPECT_EQ("isl0", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island);
EXPECT_EQ("isl1", gm.metadata(tmp3_nh).get<cv::gimpl::Island>().island);
EXPECT_NE("isl0", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
EXPECT_NE("isl1", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
// FIXME: Add a test with same graph for Fusion and check GIslandModel
}
TEST_F(ComplexIslands, DistinictIslandsWithSameName)
{
// isl0
// ...........................
// (in0) -> Not -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
// :............ ........^...: ^
// ... : :
// (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
// : isl0
// : ..............................
// `------------> Median -> (tmp3) --> Blur -------> (out1)
// :............................:
cv::gapi::island("isl0", cv::GIn(in[0], tmp[1]), cv::GOut(tmp[2]));
cv::gapi::island("isl0", cv::GIn(tmp[1]), cv::GOut(out[1]));
auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]));
EXPECT_ANY_THROW(cc.compile(cv::GMatDesc{CV_8U,1,{640,480}},
cv::GMatDesc{CV_8U,1,{640,480}}));
}
TEST_F(ComplexIslands, FullGraph)
{
cv::gapi::island("isl0", cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]));
auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
.compile(cv::GMatDesc{CV_8U,1,{640,480}},
cv::GMatDesc{CV_8U,1,{640,480}});
const auto &gm = cc.priv().model();
std::vector<ade::NodeHandle> handles_inside = {
cv::gimpl::GModel::dataNodeOf(gm, tmp[0]),
cv::gimpl::GModel::dataNodeOf(gm, tmp[1]),
cv::gimpl::GModel::dataNodeOf(gm, tmp[2]),
cv::gimpl::GModel::dataNodeOf(gm, tmp[3]),
cv::gimpl::GModel::dataNodeOf(gm, scl),
};
std::vector<ade::NodeHandle> handles_outside = {
cv::gimpl::GModel::dataNodeOf(gm, in[0]),
cv::gimpl::GModel::dataNodeOf(gm, in[1]),
cv::gimpl::GModel::dataNodeOf(gm, out[0]),
cv::gimpl::GModel::dataNodeOf(gm, out[1]),
};
for (auto nh_inside : handles_inside)
{
EXPECT_EQ("isl0", gm.metadata(nh_inside).get<cv::gimpl::Island>().island);
}
for (auto nh_outside : handles_outside)
{
EXPECT_FALSE(gm.metadata(nh_outside).contains<cv::gimpl::Island>());
}
}
TEST_F(ComplexIslands, ViaScalar)
{
//
// .........................................#internal0.
// (in0) -> Not -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
// :....................^.........................^...:
// : :
// .....................:.........(isl0). :
// (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
// :..........:.........................:
// :
// : ..................#internal1.
// `------------> Median -> (tmp3) --> Blur -------> (out1)
// :...........................:
cv::gapi::island("isl0", cv::GIn(in[1]), cv::GOut(scl));
auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
.compile(cv::GMatDesc{CV_8U,1,{640,480}},
cv::GMatDesc{CV_8U,1,{640,480}});
const auto &gm = cc.priv().model();
const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[3]);
EXPECT_NE("isl0", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island); // <internal>
EXPECT_EQ("isl0", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island); // isl0
EXPECT_NE("isl0", gm.metadata(tmp2_nh).get<cv::gimpl::Island>().island); // <internal>
EXPECT_NE("isl0", gm.metadata(tmp3_nh).get<cv::gimpl::Island>().island); // <internal>
std::vector<ade::NodeHandle> handles_outside = {
cv::gimpl::GModel::dataNodeOf(gm, in[0]),
cv::gimpl::GModel::dataNodeOf(gm, in[1]),
cv::gimpl::GModel::dataNodeOf(gm, scl),
cv::gimpl::GModel::dataNodeOf(gm, out[0]),
cv::gimpl::GModel::dataNodeOf(gm, out[1]),
};
for (auto nh_outside : handles_outside)
{
EXPECT_FALSE(gm.metadata(nh_outside).contains<cv::gimpl::Island>());
}
}
TEST_F(ComplexIslands, BorderDataIsland)
{
// .................................(isl0)..
// : :
// (in0) -> Not -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
// : ^ : ^
// : : : :
// (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
// :...........:...........................:
// : : :
// : : :.........................................(isl1)..
// : `------------> Median -> (tmp3) --> Blur -------> (out1)
// : :
// :......................................................:
cv::gapi::island("isl0", cv::GIn(in[0], in[1]), cv::GOut(tmp[2], scl));
cv::gapi::island("isl1", cv::GIn(tmp[1]), cv::GOut(out[1]));
auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
.compile(cv::GMatDesc{CV_8U,1,{640,480}},
cv::GMatDesc{CV_8U,1,{640,480}});
const auto &gm = cc.priv().model();
const auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
const auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[3]);
const auto scl_nh = cv::gimpl::GModel::dataNodeOf(gm, scl);
const auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out[0]);
const auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out[1]);
// Check handles inside isl0
EXPECT_EQ("isl0", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island);
EXPECT_EQ("isl0", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
// ^^^ Important - tmp1 is assigned to isl0, not isl1
// Check handles inside isl1
EXPECT_EQ("isl1", gm.metadata(tmp3_nh).get<cv::gimpl::Island>().island);
// Check outside handles
EXPECT_FALSE(gm.metadata(in0_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(in1_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp2_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(scl_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>());
}
TEST_F(ComplexIslands, IncompleteSpec)
{
// isl0
// ...........................
// (in0) -> Not -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
// :...........xxx.......^...: ^
// : :
// (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
// :
// :
// `------------> Median -> (tmp3) --> Blur -------> (out1)
//
// tmp1 is missing in the below spec
EXPECT_ANY_THROW(cv::gapi::island("isl0", cv::GIn(in[0]), cv::GOut(tmp[2])));
// empty range
EXPECT_ANY_THROW(cv::gapi::island("isl1", cv::GIn(tmp[2]), cv::GOut(tmp[2])));
}
TEST_F(ComplexIslands, InputOperationFromDifferentIslands)
{
// isl1
// ........................... ........
// (in0)--> Not -> (tmp0) --> Add :--------> (tmp2)-->: AddC : -------> (out0)
// :......................^..: : ^ :
// isl0 : : : :
// .......................:....................... : :
// (in1) :-> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----- :
// :....................................................:
// isl0 :
// `------------> Median -> (tmp3) --> Blur -------> (out1)
//
cv::gapi::island("isl0", cv::GIn(in[1], tmp[2]), cv::GOut(out[0]));
cv::gapi::island("isl1", cv::GIn(in[0], tmp[1]), cv::GOut(tmp[2]));
auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
.compile(cv::GMatDesc{CV_8U,1,{640,480}},
cv::GMatDesc{CV_8U,1,{640,480}});
const auto &gm = cc.priv().model();
const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
EXPECT_EQ("isl1", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island);
EXPECT_EQ("isl0", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
EXPECT_FALSE(gm.metadata(tmp2_nh).contains<cv::gimpl::Island>());
}
TEST_F(ComplexIslands, NoWayBetweenNodes)
{
// (in0) -> Not -> (tmp0) --> Add ---------> (tmp2) --> AddC -------> (out0)
// ^ ^
// (in1) -> Blur -> (tmp1) ----'--> Sum ----> (scl0) ----'
// :
// `------------> Median -> (tmp3) --> Blur -------> (out1)
EXPECT_ANY_THROW(cv::gapi::island("isl0", cv::GIn(in[1]), cv::GOut(tmp[0])));
}
TEST_F(ComplexIslands, IslandsContainUnusedPart)
{
// Unused part of the graph
// x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
// x x
// x(in0) -> Not -> (tmp0) --> Add ---------> (tmp2)---> AddC ---------> (out0) x
// x ^ ^ x
// x x x x x x x x x x x x x x x | x x | x
// | x | x
// ...... | x | x
// (in1) -> :Blur:----------> (tmp1) x-----> Sum ------> (scl0) x
// ...... : x x x x x x x x x x x x x x x x x x x x x x x x
// isl0
// :
// `------------> Median -> (tmp3) --> Blur -------> (out1)
cv::gapi::island("isl0", cv::GIn(in[1]), cv::GOut(scl));
auto cc = cv::GComputation(cv::GIn(in[1]), cv::GOut(out[1]))
.compile(cv::GMatDesc{CV_8U,1,{640,480}});
const auto &gm = cc.priv().model();
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
//The output 0 is not specified in the graph
//means that there will not be a node scl, so that tmp1 will not assign to the island
// FIXME Check that blur assigned to island using the function producerOf
// After merge islands fusion
EXPECT_FALSE(gm.metadata(tmp1_nh) .contains<cv::gimpl::Island>());
}
TEST_F(ComplexIslands, FullGraphInTwoIslands)
{
// isl0
// ..................................................
// (in0) -> :Not -> (tmp0) --> Add ---------> (tmp2) --> AddC: -------> (out0)
// ...................^.... ^ :
// ............... | : : :
// (in1) -> :Blur-> (tmp1):----'-->:Sum ----> (scl0) ----' :
// ........ | : ...........................
// isl1 : | :............................................
// : `------------> Median -> (tmp3) --> Blur ------->:(out1)
// ....................................................
cv::gapi::island("isl0", cv::GIn(in[0], tmp[1]), cv::GOut(out[0]));
cv::gapi::island("isl1", cv::GIn(in[1]), cv::GOut(out[1]));
auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
.compile(cv::GMatDesc{CV_8U,1,{640,480}},
cv::GMatDesc{CV_8U,1,{640,480}});
const auto &gm = cc.priv().model();
const auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
const auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[3]);
const auto scl_nh = cv::gimpl::GModel::dataNodeOf(gm, scl);
const auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out[0]);
const auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out[1]);
// Check handles inside isl0
EXPECT_EQ("isl0", gm.metadata(tmp0_nh).get<cv::gimpl::Island>().island);
EXPECT_EQ("isl0", gm.metadata(tmp2_nh).get<cv::gimpl::Island>().island);
EXPECT_EQ("isl0", gm.metadata(scl_nh).get<cv::gimpl::Island>().island);
// Check handles inside isl1
EXPECT_EQ("isl1", gm.metadata(tmp1_nh).get<cv::gimpl::Island>().island);
EXPECT_EQ("isl1", gm.metadata(tmp3_nh).get<cv::gimpl::Island>().island);
// Check outside handles
EXPECT_FALSE(gm.metadata(in0_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(in1_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>());
}
TEST_F(ComplexIslands, OnlyOperationsAssignedToIslands)
{
cv::gapi::island("isl0", cv::GIn(in[1]), cv::GOut(tmp[1]));
cv::gapi::island("isl1", cv::GIn(tmp[1]), cv::GOut(scl));
cv::gapi::island("isl2", cv::GIn(scl, tmp[2]), cv::GOut(out[0]));
cv::gapi::island("isl3", cv::GIn(in[0]), cv::GOut(tmp[0]));
cv::gapi::island("isl4", cv::GIn(tmp[0], tmp[1]), cv::GOut(tmp[2]));
cv::gapi::island("isl5", cv::GIn(tmp[1]), cv::GOut(tmp[3]));
cv::gapi::island("isl6", cv::GIn(tmp[3]), cv::GOut(out[1]));
auto cc = cv::GComputation(cv::GIn(in[0], in[1]), cv::GOut(out[0], out[1]))
.compile(cv::GMatDesc{CV_8U,1,{640,480}},
cv::GMatDesc{CV_8U,1,{640,480}});
const auto &gm = cc.priv().model();
//FIXME: Check that operation handles are really assigned to isl0..isl6
const auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);
const auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);
const auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[0]);
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[1]);
const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[2]);
const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp[3]);
const auto scl_nh = cv::gimpl::GModel::dataNodeOf(gm, scl);
const auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out[0]);
const auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out[1]);
EXPECT_FALSE(gm.metadata(in0_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(in1_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp0_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp1_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp2_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp3_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(scl_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>());
}
namespace
{
struct IslandStructureWithGArray
{
GIntArray in, out;
GMat tmp;
IslandStructureWithGArray()
{
tmp = CreateMatWithDiag::on(in);
out = Mat2Array::on(tmp);
}
};
struct IslandsWithGArray: public ::testing::Test, public IslandStructureWithGArray {};
} // namespace
TEST_F(IslandsWithGArray, IslandWithGArrayAsInput)
{
cv::gapi::island("isl0", cv::GIn(in), cv::GOut(tmp));
const auto pkg = cv::gapi::kernels<CreateMatWithDiagImpl, Mat2ArrayImpl>();
auto cc = cv::GComputation(cv::GIn(in), GOut(out)).compile(cv::empty_array_desc(), cv::compile_args(pkg));
const auto &gm = cc.priv().model();
const auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in.strip());
const auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out.strip());
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);
GAPI_Assert(tmp_nh->inNodes().size() == 1);
const auto create_diag_mat_nh = tmp_nh->inNodes().front();
EXPECT_EQ("isl0", gm.metadata(create_diag_mat_nh).get<cv::gimpl::Island>().island);
EXPECT_FALSE(gm.metadata(in_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp_nh) .contains<cv::gimpl::Island>());
}
TEST_F(IslandsWithGArray, IslandWithGArrayAsOutput)
{
cv::gapi::island("isl0", cv::GIn(tmp), cv::GOut(out));
const auto pkg = cv::gapi::kernels<CreateMatWithDiagImpl, Mat2ArrayImpl>();
auto cc = cv::GComputation(cv::GIn(in), GOut(out)).compile(cv::empty_array_desc(), cv::compile_args(pkg));
const auto &gm = cc.priv().model();
const auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in.strip());
const auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out.strip());
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);
GAPI_Assert(tmp_nh->inNodes().size() == 1);
const auto mat2array_nh = out_nh->inNodes().front();
EXPECT_EQ("isl0", gm.metadata(mat2array_nh).get<cv::gimpl::Island>().island);
EXPECT_FALSE(gm.metadata(in_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(out_nh) .contains<cv::gimpl::Island>());
EXPECT_FALSE(gm.metadata(tmp_nh) .contains<cv::gimpl::Island>());
}
////////////////////////////////////////////////////////////////////////////////
// Wrong input tests on island name
//
namespace
{
struct CheckName : public TestWithParam<std::tuple<bool, const char*> >,
public PlainIslandsFixture
{
void assignIsland(const std::string &s)
{
cv::gapi::island(s, cv::GIn(tmp[0]), cv::GOut(tmp[2]));
};
};
TEST_P(CheckName, Test)
{
bool correct = false;
const char *name = "";
std::tie(correct, name) = GetParam();
if (correct) EXPECT_NO_THROW(assignIsland(name));
else EXPECT_ANY_THROW(assignIsland(name));
}
} // namespace
INSTANTIATE_TEST_CASE_P(IslandName, CheckName,
Values(std::make_tuple(true, "name"),
std::make_tuple(true, " name "),
std::make_tuple(true, " n a m e "),
std::make_tuple(true, " 123 $$ %%"),
std::make_tuple(true, ".: -"),
std::make_tuple(false, ""),
std::make_tuple(false, " "),
std::make_tuple(false, " \t "),
std::make_tuple(false, " \t \t ")));
// FIXME: add <internal> test on unrollExpr() use for islands
} // opencv_test

View File

@ -0,0 +1,997 @@
// 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 <stdexcept>
#include "compiler/gmodel.hpp"
#include "compiler/gmodel_priv.hpp"
#include "api/gcomputation_priv.hpp"
#include "compiler/gcompiler.hpp"
#include "compiler/gmodelbuilder.hpp"
#include "compiler/passes/passes.hpp"
#include "compiler/passes/pattern_matching.hpp"
#include "../common/gapi_tests_common.hpp"
#include "logger.hpp"
namespace opencv_test
{
namespace matching_test {
namespace {
using V = std::vector<ade::NodeHandle>;
using S = std::unordered_set< ade::NodeHandle
, ade::HandleHasher<ade::Node>
>;
void initGModel(ade::Graph& gr,
cv::GProtoInputArgs&& in,
cv::GProtoOutputArgs&& out) {
cv::gimpl::GModel::Graph gm(gr);
cv::gimpl::GModel::init(gm);
auto proto_slots = cv::gimpl::GModelBuilder(gr)
.put(in.m_args, out.m_args);
cv::gimpl::Protocol p;
std::tie(p.inputs, p.outputs, p.in_nhs, p.out_nhs) = proto_slots;
gm.metadata().set(p);
}
bool isConsumedBy(const cv::gimpl::GModel::ConstGraph &gm, ade::NodeHandle data_nh, ade::NodeHandle op_nh) {
auto oi = cv::gimpl::GModel::orderedInputs(gm, op_nh);
return std::find(oi.begin(), oi.end(), data_nh) != oi.end();
}
std::string opName(const cv::gimpl::GModel::ConstGraph &gm, ade::NodeHandle op_nh) {
return gm.metadata(op_nh).get<cv::gimpl::Op>().k.name;
}
}
} // matching_test
TEST(PatternMatching, TestFuncDoesNotChangeTestGraph)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat out = cv::gapi::bitwise_not(in);
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in;
GMat out = cv::gapi::bitwise_not(in);
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::findMatches(pg, tg);
// Inspecting results:
matching_test::S nodes{ tgm.nodes().begin(), tgm.nodes().end() };
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in);
const auto out_nh = cv::gimpl::GModel::dataNodeOf(tgm, out);
auto input_data_nhs = tgm.metadata().get<cv::gimpl::Protocol>().in_nhs;
auto output_data_nhs = tgm.metadata().get<cv::gimpl::Protocol>().out_nhs;
EXPECT_EQ(1u, input_data_nhs.size());
EXPECT_EQ(1u, output_data_nhs.size());
EXPECT_EQ(in_nh, *input_data_nhs.begin());
EXPECT_EQ(out_nh, *output_data_nhs.begin());
EXPECT_EQ(0u, in_nh->inEdges().size());
EXPECT_EQ(0u, out_nh->outEdges().size());
EXPECT_EQ(1u, in_nh->outEdges().size());
EXPECT_EQ(1u, out_nh->inEdges().size());
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, out_nh); //bitwise_not
EXPECT_EQ(cv::gapi::core::GNot::id(), matching_test::opName(tgm, op_nh));
EXPECT_EQ(1u, op_nh->inEdges().size());
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in_nh, op_nh));
EXPECT_EQ(1u, op_nh->outEdges().size());
}
TEST(PatternMatching, TestSimple1)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat out = cv::gapi::bitwise_not(in);
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in;
GMat out = cv::gapi::bitwise_not(in);
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(3u, nodes.size());
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in);
const auto out_nh = cv::gimpl::GModel::dataNodeOf(tgm, out);
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, out_nh);
EXPECT_EQ(matching_test::S({in_nh, out_nh, op_nh}), nodes);
EXPECT_EQ(cv::gapi::core::GNot::id(), matching_test::opName(tgm, op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in_nh, op_nh));
EXPECT_EQ(matching_test::S{op_nh}, match.startOps());
EXPECT_EQ(matching_test::S{op_nh}, match.finishOps());
EXPECT_EQ(matching_test::V{in_nh}, match.protoIns());
EXPECT_EQ(matching_test::V{out_nh}, match.protoOuts());
}
TEST(PatternMatching, TestSimple2)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat out = cv::gapi::bitwise_not(in);
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in;
GMat tmp = cv::gapi::bitwise_not(in);
GMat out = cv::gapi::blur(tmp, cv::Size(3, 3));
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(3u, nodes.size());
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in);
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp);
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, tmp_nh);
EXPECT_EQ(matching_test::S({in_nh, tmp_nh, op_nh}), nodes);
EXPECT_EQ(cv::gapi::core::GNot::id(), matching_test::opName(tgm, op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in_nh, op_nh));
EXPECT_EQ(matching_test::S{op_nh}, match.startOps());
EXPECT_EQ(matching_test::S{op_nh}, match.finishOps());
EXPECT_EQ(matching_test::V{in_nh}, match.protoIns());
EXPECT_EQ(matching_test::V{tmp_nh}, match.protoOuts());
}
TEST(PatternMatching, TestSimple3)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat out = cv::gapi::bitwise_not(in);
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in;
GMat tmp = cv::gapi::blur(in, cv::Size(3, 3));
GMat out = cv::gapi::bitwise_not(tmp);
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(3u, nodes.size());
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp);
const auto out_nh = cv::gimpl::GModel::dataNodeOf(tgm, out);
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, out_nh);
EXPECT_EQ(matching_test::S({tmp_nh, out_nh, op_nh}), nodes);
EXPECT_EQ(cv::gapi::core::GNot::id(), matching_test::opName(tgm, op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, tmp_nh, op_nh));
EXPECT_EQ(matching_test::S{op_nh}, match.startOps());
EXPECT_EQ(matching_test::S{op_nh}, match.finishOps());
EXPECT_EQ(matching_test::V{tmp_nh}, match.protoIns());
EXPECT_EQ(matching_test::V{out_nh}, match.protoOuts());
}
TEST(PatternMatching, TestMultiplePatternOuts)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat dx, dy;
std::tie(dx, dy) = cv::gapi::SobelXY(in, -1, 1);
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(dx, dy));
}
// Test
ade::Graph tg;
GMat in;
GMat dx, dy;
std::tie(dx, dy) = cv::gapi::SobelXY(in, -1, 1);
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(dx, dy));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(4u, nodes.size());
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in);
const auto dx_nh = cv::gimpl::GModel::dataNodeOf(tgm, dx);
const auto dy_nh = cv::gimpl::GModel::dataNodeOf(tgm, dy);
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, dx_nh);
EXPECT_EQ(op_nh, cv::gimpl::GModel::producerOf(tgm, dy_nh));
EXPECT_EQ(matching_test::S({in_nh, dx_nh, dy_nh, op_nh}), nodes);
EXPECT_EQ(cv::gapi::imgproc::GSobelXY::id(), matching_test::opName(tgm, op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in_nh, op_nh));
EXPECT_EQ(matching_test::S{op_nh}, match.startOps());
EXPECT_EQ(matching_test::S{op_nh}, match.finishOps());
EXPECT_EQ(matching_test::V{in_nh}, match.protoIns());
EXPECT_EQ(matching_test::V({dx_nh, dy_nh}), match.protoOuts());
}
TEST(PatternMatching, TestPrepResizeSplit3)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat tmp = cv::gapi::resize(in, cv::Size{224, 224});
GMat b, g, r;
std::tie(b, g, r) = cv::gapi::split3(tmp);
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(b, g, r));
}
// Test
ade::Graph tg;
GMat y, uv;
GMat bgr = cv::gapi::NV12toBGR(y, uv);
GMat tmp = cv::gapi::resize(bgr, cv::Size{224, 224});
GMat b, g, r;
std::tie(b, g, r) = cv::gapi::split3(tmp);
matching_test::initGModel(tg, cv::GIn(y, uv), cv::GOut(b, g, r));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(7u, nodes.size());
const auto bgr_nh = cv::gimpl::GModel::dataNodeOf(tgm, bgr);
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp);
const auto b_nh = cv::gimpl::GModel::dataNodeOf(tgm, b);
const auto g_nh = cv::gimpl::GModel::dataNodeOf(tgm, g);
const auto r_nh = cv::gimpl::GModel::dataNodeOf(tgm, r);
const auto op1_nh = cv::gimpl::GModel::producerOf(tgm, tmp_nh); // 1st resize
const auto op2_nh = cv::gimpl::GModel::producerOf(tgm, b_nh); // 2nd split3
EXPECT_EQ(op2_nh, cv::gimpl::GModel::producerOf(tgm, g_nh));
EXPECT_EQ(op2_nh, cv::gimpl::GModel::producerOf(tgm, r_nh));
EXPECT_EQ(matching_test::S({bgr_nh, tmp_nh, b_nh, g_nh,
r_nh, op1_nh, op2_nh}),
nodes);
EXPECT_EQ(cv::gapi::core::GResize::id(), matching_test::opName(tgm, op1_nh));
EXPECT_EQ(cv::gapi::core::GSplit3::id(), matching_test::opName(tgm, op2_nh));
EXPECT_EQ(1u, tmp_nh->outEdges().size());
EXPECT_TRUE(matching_test::isConsumedBy(tgm, bgr_nh, op1_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, tmp_nh, op2_nh));
EXPECT_EQ(matching_test::S{ op1_nh }, match.startOps());
EXPECT_EQ(matching_test::S{ op2_nh }, match.finishOps());
EXPECT_EQ(matching_test::V{ bgr_nh }, match.protoIns());
EXPECT_EQ(matching_test::V({ b_nh, g_nh, r_nh }), match.protoOuts());
}
G_TYPED_KERNEL(GToNCHW, <GMatP(GMat)>, "test.toNCHW") {
static GMatDesc outMeta(GMatDesc in) {
GAPI_Assert(in.depth == CV_8U);
GAPI_Assert(in.chan == 3);
GAPI_Assert(in.planar == false);
return in.asPlanar();
}
};
static GMatP toNCHW(const GMat& src)
{
return GToNCHW::on(src);
}
TEST(PatternMatching, TestPrepResizeToNCHW)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat tmp = cv::gapi::resize(in, cv::Size{224, 224});
GMatP plr = toNCHW(tmp);
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(plr));
}
// Test
ade::Graph tg;
GMat y, uv;
GMat bgr = cv::gapi::NV12toBGR(y, uv);
GMat tmp = cv::gapi::resize(bgr, cv::Size{224, 224});
GMatP plr = toNCHW(tmp);
matching_test::initGModel(tg, cv::GIn(y, uv), cv::GOut(plr));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(5u, nodes.size());
const auto bgr_nh = cv::gimpl::GModel::dataNodeOf(tgm, bgr);
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp);
const auto plr_nh = cv::gimpl::GModel::dataNodeOf(tgm, plr);
const auto op1_nh = cv::gimpl::GModel::producerOf(tgm, tmp_nh); // 1st resize
const auto op2_nh = cv::gimpl::GModel::producerOf(tgm, plr_nh); // 2nd toNCHW
EXPECT_EQ(matching_test::S({bgr_nh, tmp_nh, plr_nh, op1_nh, op2_nh}),
nodes);
EXPECT_EQ(cv::gapi::core::GResize::id(), matching_test::opName(tgm, op1_nh));
EXPECT_EQ(GToNCHW::id(), matching_test::opName(tgm, op2_nh));
EXPECT_EQ(1u, tmp_nh->outEdges().size());
EXPECT_TRUE(matching_test::isConsumedBy(tgm, bgr_nh, op1_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, tmp_nh, op2_nh));
EXPECT_EQ(matching_test::S{ op1_nh }, match.startOps());
EXPECT_EQ(matching_test::S{ op2_nh }, match.finishOps());
EXPECT_EQ(matching_test::V{ bgr_nh }, match.protoIns());
EXPECT_EQ(matching_test::V{ plr_nh }, match.protoOuts());
}
TEST(PatternMatching, TestPrepNV12toBGRToNCHW)
{
// Pattern
ade::Graph pg;
{
GMat y, uv;
GMat bgr = cv::gapi::NV12toBGR(y, uv);
GMatP plr = toNCHW(bgr);
matching_test::initGModel(pg, cv::GIn(y, uv), cv::GOut(plr));
}
// Test
ade::Graph tg;
GMat y, uv;
GMat bgr = cv::gapi::NV12toBGR(y, uv);
GMatP plr = toNCHW(bgr);
GMat rsz = cv::gapi::resizeP(plr, cv::Size{224, 224});
matching_test::initGModel(tg, cv::GIn(y, uv), cv::GOut(rsz));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(6u, nodes.size());
const auto y_nh = cv::gimpl::GModel::dataNodeOf(tgm, y);
const auto uv_nh = cv::gimpl::GModel::dataNodeOf(tgm, uv);
const auto bgr_nh = cv::gimpl::GModel::dataNodeOf(tgm, bgr);
const auto plr_nh = cv::gimpl::GModel::dataNodeOf(tgm, plr);
const auto op1_nh = cv::gimpl::GModel::producerOf(tgm, bgr_nh); // 1st NV12toBGR
const auto op2_nh = cv::gimpl::GModel::producerOf(tgm, plr_nh); // 2nd toNCHW
EXPECT_EQ(matching_test::S({y_nh, uv_nh, bgr_nh, plr_nh, op1_nh, op2_nh}),
nodes);
EXPECT_EQ(cv::gapi::imgproc::GNV12toBGR::id(), matching_test::opName(tgm, op1_nh));
EXPECT_EQ(GToNCHW::id(), matching_test::opName(tgm, op2_nh));
EXPECT_EQ(1u, bgr_nh->outEdges().size());
EXPECT_TRUE(matching_test::isConsumedBy(tgm, y_nh, op1_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, uv_nh, op1_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, bgr_nh, op2_nh));
EXPECT_EQ(matching_test::S{ op1_nh }, match.startOps());
EXPECT_EQ(matching_test::S{ op2_nh }, match.finishOps());
EXPECT_EQ(matching_test::V({ y_nh, uv_nh }), match.protoIns());
EXPECT_EQ(matching_test::V{ plr_nh }, match.protoOuts());
}
//FIXME: To switch from filter2d kernel (which shall be matched by params too) to another one
TEST(PatternMatching, MatchChainInTheMiddle)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat tmp = cv::gapi::filter2D(in, -1, {});
GMat out = cv::gapi::filter2D(tmp, -1, {});
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in;
GMat tmp1 = cv::gapi::erode3x3(in);
GMat tmp2 = cv::gapi::filter2D(tmp1, -1, {});
GMat tmp3 = cv::gapi::filter2D(tmp2, -1, {});
GMat out = cv::gapi::dilate3x3(tmp3);
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(5u, nodes.size());
const auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp1);
const auto tmp2_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp2);
const auto tmp3_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp3);
const auto op1_nh = cv::gimpl::GModel::producerOf(tgm, tmp2_nh); // 1st filter2D
const auto op2_nh = cv::gimpl::GModel::producerOf(tgm, tmp3_nh); // 2nd filter2D
EXPECT_EQ(matching_test::S({tmp1_nh, tmp2_nh, tmp3_nh, op1_nh, op2_nh}), nodes);
EXPECT_EQ(cv::gapi::imgproc::GFilter2D::id(), matching_test::opName(tgm, op1_nh));
EXPECT_EQ(cv::gapi::imgproc::GFilter2D::id(), matching_test::opName(tgm, op2_nh));
EXPECT_EQ(1u, tmp2_nh->outEdges().size());
EXPECT_TRUE(matching_test::isConsumedBy(tgm, tmp1_nh, op1_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, tmp2_nh, op2_nh));
EXPECT_EQ(matching_test::S({op1_nh}), match.startOps());
EXPECT_EQ(matching_test::S({op2_nh}), match.finishOps());
EXPECT_EQ(matching_test::V{ tmp1_nh }, match.protoIns());
EXPECT_EQ(matching_test::V{ tmp3_nh }, match.protoOuts());
}
TEST(PatternMatching, TestMultipleStartOps1)
{
// Pattern
ade::Graph pg;
{
GMat in1, in2;
GMat er = cv::gapi::erode3x3(in1);
GMat dil = cv::gapi::dilate3x3(in2);
GMat out = cv::gapi::add(er, dil);
matching_test::initGModel(pg, cv::GIn(in1, in2), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in1, in2, in3, in4, in5, in6;
GMat er1 = cv::gapi::erode3x3(in1);
GMat er2 = cv::gapi::erode3x3(in2);
GMat er3 = cv::gapi::erode3x3(in3);
GMat er4 = cv::gapi::erode3x3(in4);
GMat dil1 = cv::gapi::dilate3x3(in5);
GMat dil2 = cv::gapi::dilate3x3(in6);
GMat out1 = cv::gapi::add(er1, er2);
GMat out2 = cv::gapi::add(er3, dil2);
matching_test::initGModel(tg, cv::GIn(in1, in2, in3, in4, in5, in6), cv::GOut(out1, out2, er4, dil1));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(8u, nodes.size());
const auto in3_nh = cv::gimpl::GModel::dataNodeOf(tgm, in3);
const auto in6_nh = cv::gimpl::GModel::dataNodeOf(tgm, in6);
const auto er3_nh = cv::gimpl::GModel::dataNodeOf(tgm, er3);
const auto dil2_nh = cv::gimpl::GModel::dataNodeOf(tgm, dil2);
const auto out2_nh = cv::gimpl::GModel::dataNodeOf(tgm, out2);
const auto er_op_nh = cv::gimpl::GModel::producerOf(tgm, er3_nh);
const auto dil_op_nh = cv::gimpl::GModel::producerOf(tgm, dil2_nh);
const auto add_op_nh = cv::gimpl::GModel::producerOf(tgm, out2_nh);
EXPECT_EQ(matching_test::S({in3_nh, in6_nh, er3_nh, dil2_nh, out2_nh,
er_op_nh, dil_op_nh, add_op_nh}),
nodes);
EXPECT_EQ(cv::gapi::imgproc::GErode::id(), matching_test::opName(tgm, er_op_nh));
EXPECT_EQ(cv::gapi::imgproc::GDilate::id(), matching_test::opName(tgm, dil_op_nh));
EXPECT_EQ(cv::gapi::core::GAdd::id(), matching_test::opName(tgm, add_op_nh));
EXPECT_EQ(1u, er3_nh->outEdges().size());
EXPECT_EQ(1u, dil2_nh->outEdges().size());
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in3_nh, er_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in6_nh, dil_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, er3_nh, add_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, dil2_nh, add_op_nh));
EXPECT_EQ(matching_test::S({ er_op_nh, dil_op_nh }), match.startOps());
EXPECT_EQ(matching_test::S{ add_op_nh }, match.finishOps());
EXPECT_EQ(matching_test::V({ in3_nh, in6_nh }), match.protoIns());
EXPECT_EQ(matching_test::V{ out2_nh }, match.protoOuts());
}
TEST(PatternMatching, TestMultipleStartOps2)
{
// Pattern
ade::Graph pg;
{
GMat in1, in2;
GMat er = cv::gapi::erode3x3(in1);
GMat dil = cv::gapi::dilate3x3(in2);
GMat out = cv::gapi::add(er, dil);
matching_test::initGModel(pg, cv::GIn(in1, in2), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in1, in2;
GMat er = cv::gapi::erode3x3(in1);
GMat dil1 = cv::gapi::dilate3x3(in2);
GMat dil2 = cv::gapi::dilate3x3(dil1);
GMat out = cv::gapi::add(er, dil2);
matching_test::initGModel(tg, cv::GIn(in1, in2), cv::GOut(out));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(8u, nodes.size());
const auto in1_nh = cv::gimpl::GModel::dataNodeOf(tgm, in1);
const auto dil1_nh = cv::gimpl::GModel::dataNodeOf(tgm, dil1);
const auto er_nh = cv::gimpl::GModel::dataNodeOf(tgm, er);
const auto dil2_nh = cv::gimpl::GModel::dataNodeOf(tgm, dil2);
const auto out_nh = cv::gimpl::GModel::dataNodeOf(tgm, out);
const auto er_op_nh = cv::gimpl::GModel::producerOf(tgm, er_nh);
const auto dil_op_nh = cv::gimpl::GModel::producerOf(tgm, dil2_nh);
const auto add_op_nh = cv::gimpl::GModel::producerOf(tgm, out_nh);
EXPECT_EQ(matching_test::S({in1_nh, dil1_nh, er_nh, dil2_nh, out_nh,
er_op_nh, dil_op_nh, add_op_nh}),
nodes);
EXPECT_EQ(cv::gapi::imgproc::GErode::id(), matching_test::opName(tgm, er_op_nh));
EXPECT_EQ(cv::gapi::imgproc::GDilate::id(), matching_test::opName(tgm, dil_op_nh));
EXPECT_EQ(cv::gapi::core::GAdd::id(), matching_test::opName(tgm, add_op_nh));
EXPECT_EQ(1u, er_nh->outEdges().size());
EXPECT_EQ(1u, dil2_nh->outEdges().size());
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in1_nh, er_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, dil1_nh, dil_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, er_nh, add_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, dil2_nh, add_op_nh));
EXPECT_EQ(matching_test::S({ er_op_nh, dil_op_nh }), match.startOps());
EXPECT_EQ(matching_test::S{ add_op_nh }, match.finishOps());
EXPECT_EQ(matching_test::V({ in1_nh, dil1_nh }), match.protoIns());
EXPECT_EQ(matching_test::V{ out_nh }, match.protoOuts());
}
TEST(PatternMatching, TestInexactMatchOfInOutData)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat out = cv::gapi::dilate3x3(in);
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in;
GMat out1 = cv::gapi::erode3x3(in);
GMat out2 = cv::gapi::boxFilter(in, -1, cv::Size(3, 3));
GMat tmp = cv::gapi::dilate3x3(in);
GScalar out3 = cv::gapi::sum(tmp);
GScalar out4 = cv::gapi::mean(tmp);
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out1, out2, out3, out4));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(3u, nodes.size());
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in);
const auto tmp_nh = cv::gimpl::GModel::dataNodeOf(tgm, tmp);
const auto op_nh = cv::gimpl::GModel::producerOf(tgm, tmp_nh); // dilate3x3
EXPECT_EQ(matching_test::S({in_nh, tmp_nh, op_nh}),
nodes);
EXPECT_EQ(cv::gapi::imgproc::GDilate::id(), matching_test::opName(tgm, op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, in_nh, op_nh));
EXPECT_EQ(matching_test::S{ op_nh }, match.startOps());
EXPECT_EQ(matching_test::S{ op_nh }, match.finishOps());
EXPECT_EQ(matching_test::V{ in_nh }, match.protoIns());
EXPECT_EQ(matching_test::V{ tmp_nh }, match.protoOuts());
EXPECT_GT(in_nh->outEdges().size(), 1u);
EXPECT_GT(tmp_nh->outEdges().size(), 1u);
}
//FIXME: The start ops matching shall be reworked to more smarter way.
// Start ops matching shall get rid of non valid matchings sample,
// where two identical start ops in the pattern refer to the only one in the test.
TEST(PatternMatching, TestManySameStartOpsAndHinge)
{
// Pattern
ade::Graph pg;
{
GMat in1, in2, in3;
GMat er1 = cv::gapi::erode3x3(in1);
GMat er2 = cv::gapi::erode3x3(in2);
GMat er3 = cv::gapi::erode3x3(in3);
GMat mrg = cv::gapi::merge3(er1, er2, er3);
matching_test::initGModel(pg, cv::GIn(in1, in2, in3), cv::GOut(mrg));
}
// Test
ade::Graph tg;
GMat in1, in2, in3;
GMat er1 = cv::gapi::erode3x3(in1);
GMat er2 = cv::gapi::erode3x3(in2);
GMat er3 = cv::gapi::erode3x3(in3);
GMat mrg = cv::gapi::merge3(er1, er2, er3);
matching_test::initGModel(tg, cv::GIn(in1, in2, in3), cv::GOut(mrg));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(11u, nodes.size());
EXPECT_EQ(matching_test::S(tgm.nodes().begin(), tgm.nodes().end()),
nodes);
}
//FIXME: The start ops matching shall be reworked to more smarter way.
// Start ops matching shall get rid of non valid matchings sample,
// where two identical start ops in the pattern refer to the only one in the test.
TEST(PatternMatching, TestManySameStartOpsAndHinge2)
{
// Pattern
ade::Graph pg;
{
GMat in1, in2, in3;
GMat er1 = cv::gapi::erode3x3(in1);
GMat er2 = cv::gapi::erode3x3(in2);
GMat er3 = cv::gapi::erode3x3(in3);
GMat dil1 = cv::gapi::dilate3x3(er1);
GMat dil2 = cv::gapi::dilate3x3(er2);
GMat dil3 = cv::gapi::dilate3x3(er3);
GMat mrg = cv::gapi::merge3(dil1, dil2, dil3);
matching_test::initGModel(pg, cv::GIn(in1, in2, in3), cv::GOut(mrg));
}
// Test
ade::Graph tg;
GMat in1, in2, in3;
GMat er1 = cv::gapi::erode3x3(in1);
GMat er2 = cv::gapi::erode3x3(in2);
GMat er3 = cv::gapi::erode3x3(in3);
GMat dil1 = cv::gapi::dilate3x3(er1);
GMat dil2 = cv::gapi::dilate3x3(er2);
GMat dil3 = cv::gapi::dilate3x3(er3);
GMat mrg = cv::gapi::merge3(dil1, dil2, dil3);
matching_test::initGModel(tg, cv::GIn(in1, in2, in3), cv::GOut(mrg));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(17u, nodes.size());
EXPECT_EQ(matching_test::S(tgm.nodes().begin(), tgm.nodes().end()),
nodes);
}
//FIXME: The start ops matching shall be reworked to more smarter way.
// Start ops matching shall get rid of non valid matchings sample,
// where two identical start ops in the pattern refer to the only one in the test.
TEST(PatternMatching, TestTwoChainsOnTheHingeIsomorphism)
{
// Pattern
ade::Graph pg;
{
GMat in1, in2;
GMat er1 = cv::gapi::erode3x3(in1);
GMat er2 = cv::gapi::erode3x3(in2);
GMat mdb = cv::gapi::medianBlur(er1, 3);
GMat gb = cv::gapi::gaussianBlur(er2, cv::Size(5, 5), 0.12);
GMat conc = cv::gapi::concatVert(mdb, gb);
matching_test::initGModel(pg, cv::GIn(in1, in2), cv::GOut(conc));
}
// Test
ade::Graph tg;
GMat in1, in2;
GMat er1 = cv::gapi::erode3x3(in1);
GMat er2 = cv::gapi::erode3x3(in2);
GMat gb = cv::gapi::gaussianBlur(er1, cv::Size(5, 5), 0.12);
GMat mdb = cv::gapi::medianBlur(er2, 3);
GMat conc = cv::gapi::concatVert(mdb, gb);
matching_test::initGModel(tg, cv::GIn(in1, in2), cv::GOut(conc));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(12u, nodes.size());
EXPECT_EQ(matching_test::S(tgm.nodes().begin(), tgm.nodes().end()),
nodes);
const auto in1_nh = cv::gimpl::GModel::dataNodeOf(tgm, in1);
const auto in2_nh = cv::gimpl::GModel::dataNodeOf(tgm, in2);
EXPECT_EQ(matching_test::V({ in2_nh, in1_nh }), match.protoIns());
}
TEST(PatternMatching, TestPatternHasMoreInDataNodes)
{
// Pattern
ade::Graph pg;
{
GMat in1, in2, in3;
GMat out = cv::gapi::merge3(in1, in2, in3);
matching_test::initGModel(pg, cv::GIn(in1, in2, in3), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in;
GMat out = cv::gapi::merge3(in, in, in);
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(3u, nodes.size());
EXPECT_EQ(matching_test::S(tgm.nodes().begin(), tgm.nodes().end()),
nodes);
const auto in_nh = cv::gimpl::GModel::dataNodeOf(tgm, in);
EXPECT_EQ(matching_test::V({ in_nh, in_nh, in_nh }), match.protoIns());
}
TEST(PatternMatching, TestPatternHasFewerInDataNodes)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat out = cv::gapi::merge3(in, in, in);
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in1, in2, in3;
GMat out = cv::gapi::merge3(in1, in2, in3);
matching_test::initGModel(tg, cv::GIn(in1, in2, in3), cv::GOut(out));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_FALSE(match.ok());
}
TEST(PatternMatching, TestTwoMatchingsOneCorrect)
{
// Pattern
ade::Graph pg;
{
GMat in1, in2;
GMat n = cv::gapi::bitwise_not(in1);
GMat e = cv::gapi::erode3x3(in1);
GMat d = cv::gapi::dilate3x3(in2);
GMat out = cv::gapi::merge3(n, e, d);
matching_test::initGModel(pg, cv::GIn(in1, in2), cv::GOut(out));
}
// Test
ade::Graph tg;
GMat in1, in2;
GMat n = cv::gapi::bitwise_not(in1);
GMat e = cv::gapi::erode3x3(in2);
GMat d = cv::gapi::dilate3x3(in2);
GMat mrg = cv::gapi::merge3(n, e, d);
GMat i, sqi;
std::tie(i, sqi) = cv::gapi::integral(mrg);
GMat n1 = cv::gapi::bitwise_not(i);
GMat e1 = cv::gapi::erode3x3(i);
GMat d1 = cv::gapi::dilate3x3(sqi);
GMat out = cv::gapi::merge3(n1, e1, d1);
matching_test::initGModel(tg, cv::GIn(in1, in2), cv::GOut(out));
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_TRUE(match.ok());
auto nodes = match.nodes();
EXPECT_EQ(10u, nodes.size());
const auto i_nh = cv::gimpl::GModel::dataNodeOf(tgm, i);
const auto sqi_nh = cv::gimpl::GModel::dataNodeOf(tgm, sqi);
const auto n1_nh = cv::gimpl::GModel::dataNodeOf(tgm, n1);
const auto e1_nh = cv::gimpl::GModel::dataNodeOf(tgm, e1);
const auto d1_nh = cv::gimpl::GModel::dataNodeOf(tgm, d1);
const auto out_nh = cv::gimpl::GModel::dataNodeOf(tgm, out);
const auto n_op_nh = cv::gimpl::GModel::producerOf(tgm, n1_nh);
const auto e_op_nh = cv::gimpl::GModel::producerOf(tgm, e1_nh);
const auto d_op_nh = cv::gimpl::GModel::producerOf(tgm, d1_nh);
const auto m_op_nh = cv::gimpl::GModel::producerOf(tgm, out_nh);
EXPECT_EQ(matching_test::S({i_nh, sqi_nh, n1_nh, e1_nh, d1_nh, out_nh,
n_op_nh, e_op_nh, d_op_nh, m_op_nh}), nodes);
EXPECT_EQ(cv::gapi::core::GNot::id(), matching_test::opName(tgm, n_op_nh));
EXPECT_EQ(cv::gapi::imgproc::GErode::id(), matching_test::opName(tgm, e_op_nh));
EXPECT_EQ(cv::gapi::imgproc::GDilate::id(), matching_test::opName(tgm, d_op_nh));
EXPECT_EQ(cv::gapi::core::GMerge3::id(), matching_test::opName(tgm, m_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, i_nh, n_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, i_nh, e_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, sqi_nh, d_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, n1_nh, m_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, e1_nh, m_op_nh));
EXPECT_TRUE(matching_test::isConsumedBy(tgm, d1_nh, m_op_nh));
EXPECT_EQ(1u, n1_nh->outEdges().size());
EXPECT_EQ(1u, e1_nh->outEdges().size());
EXPECT_EQ(1u, d1_nh->outEdges().size());
EXPECT_EQ(matching_test::S({n_op_nh, e_op_nh, d_op_nh}), match.startOps());
EXPECT_EQ(matching_test::S{m_op_nh}, match.finishOps());
EXPECT_EQ(matching_test::V({i_nh, sqi_nh}), match.protoIns());
EXPECT_EQ(matching_test::V{out_nh}, match.protoOuts());}
TEST(PatternMatching, CheckNoMatch)
{
// Pattern
ade::Graph pg;
{
GMat in;
GMat tmp = cv::gapi::filter2D(in, -1, {});
GMat out = cv::gapi::filter2D(tmp, -1, {});
matching_test::initGModel(pg, cv::GIn(in), cv::GOut(out));
}
// Test
ade::Graph tg;
{
GMat in;
GMat tmp1 = cv::gapi::erode3x3(in);
GMat out = cv::gapi::dilate3x3(tmp1);
matching_test::initGModel(tg, cv::GIn(in), cv::GOut(out));
}
// Pattern Matching
cv::gimpl::GModel::Graph pgm(pg);
cv::gimpl::GModel::Graph tgm(tg);
cv::gimpl::SubgraphMatch match = cv::gimpl::findMatches(pg, tg);
// Inspecting results:
EXPECT_FALSE(match.ok());
}
TEST(PatternMatching, adeSmokeTest)
{
ade::Graph g;
ade::NodeHandle src = g.createNode();
ade::NodeHandle dst = g.createNode();
g.link(src, dst);
g.link(src, dst);
EXPECT_EQ(2u, dst->inNodes().size());
}
} // namespace opencv_test

View File

@ -0,0 +1,649 @@
// 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 <stdexcept>
#include <opencv2/gapi/gtransform.hpp>
#include <opencv2/gapi/cpu/core.hpp>
#include <opencv2/gapi/cpu/imgproc.hpp>
#include "compiler/gmodel.hpp"
#include "compiler/gmodel_priv.hpp"
#include "api/gcomputation_priv.hpp"
#include "compiler/gcompiler.hpp"
#include "compiler/gmodelbuilder.hpp"
#include "compiler/passes/passes.hpp"
#include "compiler/passes/pattern_matching.hpp"
#include "../common/gapi_tests_common.hpp"
#include "logger.hpp"
namespace opencv_test
{
// --------------------------------------------------------------------------------------
// Accuracy integration tests (GComputation-level)
namespace {
// FIXME: replace listener with something better (e.g. check graph via GModel?)
// custom "listener" to check what kernels are called within the test
struct KernelListener { std::map<std::string, size_t> counts; };
KernelListener& getListener() {
static KernelListener l;
return l;
}
using CompCreator = std::function<cv::GComputation()>;
using CompileArgsCreator = std::function<cv::GCompileArgs()>;
using Verifier = std::function<void(KernelListener)>;
} // anonymous namespace
// Custom kernels && transformations below:
G_TYPED_KERNEL(MyNV12toBGR, <GMat(GMat, GMat)>, "test.my_nv12_to_bgr") {
static GMatDesc outMeta(GMatDesc in_y, GMatDesc in_uv) {
return cv::gapi::imgproc::GNV12toBGR::outMeta(in_y, in_uv);
}
};
GAPI_OCV_KERNEL(MyNV12toBGRImpl, MyNV12toBGR)
{
static void run(const cv::Mat& in_y, const cv::Mat& in_uv, cv::Mat &out)
{
getListener().counts[MyNV12toBGR::id()]++;
cv::cvtColorTwoPlane(in_y, in_uv, out, cv::COLOR_YUV2BGR_NV12);
}
};
G_TYPED_KERNEL(MyPlanarResize, <GMatP(GMatP, Size, int)>, "test.my_planar_resize") {
static GMatDesc outMeta(GMatDesc in, Size sz, int interp) {
return cv::gapi::core::GResizeP::outMeta(in, sz, interp);
}
};
GAPI_OCV_KERNEL(MyPlanarResizeImpl, MyPlanarResize) {
static void run(const cv::Mat& in, cv::Size out_sz, int interp, cv::Mat &out)
{
getListener().counts[MyPlanarResize::id()]++;
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++) {
auto in_plane = in(cv::Rect(0, i*inH, inW, inH));
auto out_plane = out(cv::Rect(0, i*outH, outW, outH));
cv::resize(in_plane, out_plane, out_sz, 0, 0, interp);
}
}
};
G_TYPED_KERNEL(MyInterleavedResize, <GMat(GMat, Size, int)>, "test.my_interleaved_resize") {
static GMatDesc outMeta(GMatDesc in, Size sz, int interp) {
return cv::gapi::core::GResize::outMeta(in, sz, 0.0, 0.0, interp);
}
};
GAPI_OCV_KERNEL(MyInterleavedResizeImpl, MyInterleavedResize) {
static void run(const cv::Mat& in, cv::Size out_sz, int interp, cv::Mat &out)
{
getListener().counts[MyInterleavedResize::id()]++;
cv::resize(in, out, out_sz, 0.0, 0.0, interp);
}
};
G_TYPED_KERNEL(MyToNCHW, <GMatP(GMat)>, "test.my_to_nchw") {
static GMatDesc outMeta(GMatDesc in) {
GAPI_Assert(in.depth == CV_8U);
GAPI_Assert(in.chan == 3);
GAPI_Assert(in.planar == false);
return in.asPlanar();
}
};
GAPI_OCV_KERNEL(MyToNCHWImpl, MyToNCHW) {
static void run(const cv::Mat& in, cv::Mat& out)
{
getListener().counts[MyToNCHW::id()]++;
auto sz = in.size();
auto w = sz.width;
auto h = sz.height;
cv::Mat ins[3] = {};
cv::split(in, ins);
for (int i = 0; i < 3; i++) {
auto in_plane = ins[i];
auto out_plane = out(cv::Rect(0, i*h, w, h));
in_plane.copyTo(out_plane);
}
}
};
using GMat4 = std::tuple<GMat, GMat, GMat, GMat>;
G_TYPED_KERNEL_M(MySplit4, <GMat4(GMat)>, "test.my_split4") {
static std::tuple<GMatDesc, GMatDesc, GMatDesc, GMatDesc> outMeta(GMatDesc in) {
const auto out_depth = in.depth;
const auto out_desc = in.withType(out_depth, 1);
return std::make_tuple(out_desc, out_desc, out_desc, out_desc);
}
};
GAPI_OCV_KERNEL(MySplit4Impl, MySplit4) {
static void run(const cv::Mat& in, cv::Mat& out1, cv::Mat& out2, cv::Mat& out3, cv::Mat& out4)
{
getListener().counts[MySplit4::id()]++;
cv::Mat outs[] = { out1, out2, out3, out4 };
cv::split(in, outs);
}
};
GAPI_TRANSFORM(NV12Transform, <cv::GMat(cv::GMat, cv::GMat)>, "test.nv12_transform")
{
static cv::GMat pattern(const cv::GMat& y, const cv::GMat& uv)
{
GMat out = cv::gapi::NV12toBGR(y, uv);
return out;
}
static cv::GMat substitute(const cv::GMat& y, const cv::GMat& uv)
{
GMat out = MyNV12toBGR::on(y, uv);
return out;
}
};
GAPI_TRANSFORM(ResizeTransform, <cv::GMat(cv::GMat)>, "3 x Resize -> Interleaved Resize")
{
static cv::GMat pattern(const cv::GMat& in)
{
GMat b, g, r;
std::tie(b, g, r) = cv::gapi::split3(in);
const auto resize = std::bind(&cv::gapi::resize, std::placeholders::_1,
cv::Size(100, 100), 0, 0, cv::INTER_AREA);
return cv::gapi::merge3(resize(b), resize(g), resize(r));
}
static cv::GMat substitute(const cv::GMat& in)
{
return MyInterleavedResize::on(in, cv::Size(100, 100), cv::INTER_AREA);
}
};
GAPI_TRANSFORM(ResizeTransformToCustom, <cv::GMat(cv::GMat)>, "Resize -> Custom Resize")
{
static cv::GMat pattern(const cv::GMat& in)
{
return cv::gapi::resize(in, cv::Size(100, 100), 0, 0, cv::INTER_AREA);
}
static cv::GMat substitute(const cv::GMat& in)
{
return MyInterleavedResize::on(in, cv::Size(100, 100), cv::INTER_AREA);
}
};
GAPI_TRANSFORM(ChainTransform1, <GMatP(GMat)>, "Resize + toNCHW -> toNCHW + PlanarResize")
{
static GMatP pattern(const cv::GMat& in)
{
return MyToNCHW::on(cv::gapi::resize(in, cv::Size(60, 60)));
}
static GMatP substitute(const cv::GMat& in)
{
return MyPlanarResize::on(MyToNCHW::on(in), cv::Size(60, 60), cv::INTER_LINEAR);
}
};
GAPI_TRANSFORM(ChainTransform2, <GMatP(GMat, GMat)>, "NV12toBGR + toNCHW -> NV12toBGRp")
{
static GMatP pattern(const GMat& y, const GMat& uv)
{
return MyToNCHW::on(MyNV12toBGR::on(y, uv));
}
static GMatP substitute(const GMat& y, const GMat& uv)
{
return cv::gapi::NV12toBGRp(y, uv);
}
};
GAPI_TRANSFORM(Split4Transform, <GMat4(GMat)>, "Split4 -> Custom Split4")
{
static GMat4 pattern(const GMat& in)
{
return cv::gapi::split4(in);
}
static GMat4 substitute(const GMat& in)
{
return MySplit4::on(in);
}
};
GAPI_TRANSFORM(Split4Merge3Transform, <GMat(GMat)>, "Split4 + Merge3 -> Custom Split4 + Merge3")
{
static GMat pattern(const GMat& in)
{
GMat tmp1, tmp2, tmp3, unused;
std::tie(tmp1, tmp2, tmp3, unused) = cv::gapi::split4(in);
return cv::gapi::merge3(tmp1, tmp2, tmp3);
}
static GMat substitute(const GMat& in)
{
GMat tmp1, tmp2, tmp3, unused;
std::tie(tmp1, tmp2, tmp3, unused) = MySplit4::on(in);
return cv::gapi::merge3(tmp1, tmp2, tmp3);
}
};
GAPI_TRANSFORM(Merge4Split4Transform, <GMat4(GMat, GMat, GMat, GMat)>,
"Merge4 + Split4 -> Merge4 + Custom Split4")
{
static GMat4 pattern(const GMat& in1, const GMat& in2, const GMat& in3,
const GMat& in4)
{
return cv::gapi::split4(cv::gapi::merge4(in1, in2, in3, in4));
}
static GMat4 substitute(const GMat& in1, const GMat& in2, const GMat& in3,
const GMat& in4)
{
return MySplit4::on(cv::gapi::merge4(in1, in2, in3, in4));
}
};
// --------------------------------------------------------------------------------------
// Integration tests
TEST(PatternMatchingIntegrationBasic, OneTransformationApplied)
{
cv::Size in_sz(640, 480);
cv::Mat input(in_sz, CV_8UC3);
cv::randu(input, cv::Scalar::all(0), cv::Scalar::all(100));
cv::Mat orig_graph_output, transformed_graph_output;
auto orig_args = cv::compile_args();
auto transform_args = cv::compile_args(
cv::gapi::kernels<MyInterleavedResizeImpl, ResizeTransform>());
auto& listener = getListener();
listener.counts.clear(); // clear counters before testing
const auto make_computation = [] () {
GMat in;
GMat b, g, r;
std::tie(b, g, r) = cv::gapi::split3(in);
const auto resize = std::bind(&cv::gapi::resize, std::placeholders::_1,
cv::Size(100, 100), 0, 0, cv::INTER_AREA);
GMat out = cv::gapi::merge3(resize(b), resize(g), resize(r));
return cv::GComputation(cv::GIn(in), cv::GOut(out));
};
{
// Run original graph
auto mainC = make_computation();
mainC.apply(cv::gin(input), cv::gout(orig_graph_output), std::move(orig_args));
}
// Generate transformed graph (passing transformations via compile args)
auto mainC = make_computation(); // get new copy with new Priv
mainC.apply(cv::gin(input), cv::gout(transformed_graph_output), std::move(transform_args));
// Compare
ASSERT_TRUE(AbsExact()(orig_graph_output, transformed_graph_output));
// Custom verification via listener
ASSERT_EQ(1u, listener.counts.size());
// called in transformed graph:
ASSERT_NE(listener.counts.cend(), listener.counts.find(MyInterleavedResize::id()));
ASSERT_EQ(1u, listener.counts.at(MyInterleavedResize::id()));
}
TEST(PatternMatchingIntegrationBasic, SameTransformationAppliedSeveralTimes)
{
cv::Size in_sz(640, 480);
cv::Mat input(in_sz, CV_8UC3);
cv::randu(input, cv::Scalar::all(0), cv::Scalar::all(100));
cv::Mat orig_graph_output, transformed_graph_output;
auto orig_args = cv::compile_args();
auto transform_args = cv::compile_args(
cv::gapi::kernels<MyInterleavedResizeImpl, ResizeTransformToCustom>());
auto& listener = getListener();
listener.counts.clear(); // clear counters before testing
const auto make_computation = [] () {
GMat in;
GMat b, g, r;
std::tie(b, g, r) = cv::gapi::split3(in);
const auto resize = std::bind(&cv::gapi::resize, std::placeholders::_1,
cv::Size(100, 100), 0, 0, cv::INTER_AREA);
GMat out = cv::gapi::merge3(resize(b), resize(g), resize(r));
return cv::GComputation(cv::GIn(in), cv::GOut(out));
};
{
// Run original graph
auto mainC = make_computation();
mainC.apply(cv::gin(input), cv::gout(orig_graph_output), std::move(orig_args));
}
// Generate transformed graph (passing transformations via compile args)
auto mainC = make_computation(); // get new copy with new Priv
mainC.apply(cv::gin(input), cv::gout(transformed_graph_output), std::move(transform_args));
// Compare
ASSERT_TRUE(AbsExact()(orig_graph_output, transformed_graph_output));
// Custom verification via listener
ASSERT_EQ(1u, listener.counts.size());
// called in transformed graph:
ASSERT_NE(listener.counts.cend(), listener.counts.find(MyInterleavedResize::id()));
ASSERT_EQ(3u, listener.counts.at(MyInterleavedResize::id()));
}
TEST(PatternMatchingIntegrationBasic, OneNV12toBGRTransformationApplied)
{
cv::Size in_sz(640, 480);
cv::Mat y(in_sz, CV_8UC1), uv(cv::Size(in_sz.width / 2, in_sz.height / 2), CV_8UC2);
cv::randu(y, cv::Scalar::all(0), cv::Scalar::all(100));
cv::randu(uv, cv::Scalar::all(100), cv::Scalar::all(200));
cv::Mat orig_graph_output, transformed_graph_output;
auto orig_args = cv::compile_args();
auto transform_args = cv::compile_args(cv::gapi::kernels<MyNV12toBGRImpl, NV12Transform>());
auto& listener = getListener();
listener.counts.clear(); // clear counters before testing
const auto make_computation = [] () {
GMat in1, in2;
GMat bgr = cv::gapi::NV12toBGR(in1, in2);
GMat out = cv::gapi::resize(bgr, cv::Size(100, 100));
return cv::GComputation(cv::GIn(in1, in2), cv::GOut(out));
};
{
// Run original graph
auto mainC = make_computation();
mainC.apply(cv::gin(y, uv), cv::gout(orig_graph_output), std::move(orig_args));
}
// Generate transformed graph (passing transformations via compile args)
auto mainC = make_computation(); // get new copy with new Priv
mainC.apply(cv::gin(y, uv), cv::gout(transformed_graph_output), std::move(transform_args));
// Compare
ASSERT_TRUE(AbsExact()(orig_graph_output, transformed_graph_output));
// Custom verification via listener
ASSERT_EQ(1u, listener.counts.size());
// called in transformed graph:
ASSERT_NE(listener.counts.cend(), listener.counts.find(MyNV12toBGR::id()));
ASSERT_EQ(1u, listener.counts.at(MyNV12toBGR::id()));
}
TEST(PatternMatchingIntegrationBasic, TwoTransformationsApplied)
{
cv::Size in_sz(640, 480);
cv::Mat y(in_sz, CV_8UC1), uv(cv::Size(in_sz.width / 2, in_sz.height / 2), CV_8UC2);
cv::randu(y, cv::Scalar::all(0), cv::Scalar::all(100));
cv::randu(uv, cv::Scalar::all(100), cv::Scalar::all(200));
cv::Mat orig_graph_output, transformed_graph_output;
auto orig_args = cv::compile_args();
auto transform_args = cv::compile_args(
cv::gapi::kernels<MyNV12toBGRImpl, MyInterleavedResizeImpl, ResizeTransform,
NV12Transform>()); // compile args with transformations
auto& listener = getListener();
listener.counts.clear(); // clear counters before testing
const auto make_computation = [] () {
GMat in1, in2;
GMat bgr = cv::gapi::NV12toBGR(in1, in2);
GMat b, g, r;
std::tie(b, g, r) = cv::gapi::split3(bgr);
const auto resize = std::bind(&cv::gapi::resize, std::placeholders::_1,
cv::Size(100, 100), 0, 0, cv::INTER_AREA);
GMat tmp1 = cv::gapi::resize(bgr, cv::Size(90, 90));
GMat tmp2 = cv::gapi::bitwise_not(cv::gapi::merge3(resize(b), resize(g), resize(r)));
GMat out = cv::gapi::resize(tmp1 + GScalar(10.0), cv::Size(100, 100)) + tmp2;
return cv::GComputation(cv::GIn(in1, in2), cv::GOut(out));
};
{
// Run original graph
auto mainC = make_computation();
mainC.apply(cv::gin(y, uv), cv::gout(orig_graph_output), std::move(orig_args));
}
// Generate transformed graph (passing transformations via compile args)
auto mainC = make_computation(); // get new copy with new Priv
mainC.apply(cv::gin(y, uv), cv::gout(transformed_graph_output), std::move(transform_args));
// Compare
ASSERT_TRUE(AbsExact()(orig_graph_output, transformed_graph_output));
// Custom verification via listener
ASSERT_EQ(2u, listener.counts.size());
// called in transformed graph:
ASSERT_NE(listener.counts.cend(), listener.counts.find(MyNV12toBGR::id()));
ASSERT_EQ(1u, listener.counts.at(MyNV12toBGR::id()));
ASSERT_NE(listener.counts.cend(), listener.counts.find(MyInterleavedResize::id()));
ASSERT_EQ(1u, listener.counts.at(MyInterleavedResize::id()));
}
struct PatternMatchingIntegrationE2E : testing::Test
{
cv::GComputation makeComputation() {
GMat in1, in2;
GMat bgr = MyNV12toBGR::on(in1, in2);
GMat resized = cv::gapi::resize(bgr, cv::Size(60, 60));
GMatP out = MyToNCHW::on(resized);
return cv::GComputation(cv::GIn(in1, in2), cv::GOut(out));
}
void runTest(cv::GCompileArgs&& transform_args) {
cv::Size in_sz(640, 480);
cv::Mat y(in_sz, CV_8UC1), uv(cv::Size(in_sz.width / 2, in_sz.height / 2), CV_8UC2);
cv::randu(y, cv::Scalar::all(0), cv::Scalar::all(100));
cv::randu(uv, cv::Scalar::all(100), cv::Scalar::all(200));
cv::Mat orig_graph_output, transformed_graph_output;
auto& listener = getListener();
listener.counts.clear(); // clear counters before testing
{
// Run original graph
auto mainC = makeComputation();
mainC.apply(cv::gin(y, uv), cv::gout(orig_graph_output),
cv::compile_args(cv::gapi::kernels<MyNV12toBGRImpl, MyToNCHWImpl>()));
}
// Generate transformed graph (passing transformations via compile args)
auto mainC = makeComputation(); // get new copy with new Priv
mainC.apply(cv::gin(y, uv), cv::gout(transformed_graph_output), std::move(transform_args));
// Compare
ASSERT_TRUE(AbsExact()(orig_graph_output, transformed_graph_output));
// Custom verification via listener
ASSERT_EQ(3u, listener.counts.size());
// called in original graph:
ASSERT_NE(listener.counts.cend(), listener.counts.find(MyNV12toBGR::id()));
ASSERT_NE(listener.counts.cend(), listener.counts.find(MyToNCHW::id()));
ASSERT_EQ(1u, listener.counts.at(MyNV12toBGR::id()));
ASSERT_EQ(1u, listener.counts.at(MyToNCHW::id()));
// called in transformed graph:
ASSERT_NE(listener.counts.cend(), listener.counts.find(MyPlanarResize::id()));
ASSERT_EQ(1u, listener.counts.at(MyPlanarResize::id()));
}
};
TEST_F(PatternMatchingIntegrationE2E, ChainTransformationsApplied)
{
runTest(cv::compile_args(
cv::gapi::kernels<MyPlanarResizeImpl, ChainTransform1, ChainTransform2>()));
}
TEST_F(PatternMatchingIntegrationE2E, ReversedChainTransformationsApplied)
{
runTest(cv::compile_args(
cv::gapi::kernels<ChainTransform2, MyPlanarResizeImpl, ChainTransform1>()));
}
struct PatternMatchingIntegrationUnusedNodes : testing::Test
{
cv::GComputation makeComputation() {
GMat in1, in2;
GMat bgr = cv::gapi::NV12toBGR(in1, in2);
GMat b1, g1, r1;
std::tie(b1, g1, r1) = cv::gapi::split3(bgr);
// FIXME: easier way to call split4??
GMat merged4 = cv::gapi::merge4(b1, g1, r1, b1);
GMat b2, g2, r2, unused;
std::tie(b2, g2, r2, unused) = cv::gapi::split4(merged4);
GMat out = cv::gapi::merge3(b2, g2, r2);
return cv::GComputation(cv::GIn(in1, in2), cv::GOut(out));
}
void runTest(cv::GCompileArgs&& transform_args) {
cv::Size in_sz(640, 480);
cv::Mat y(in_sz, CV_8UC1), uv(cv::Size(in_sz.width / 2, in_sz.height / 2), CV_8UC2);
cv::randu(y, cv::Scalar::all(0), cv::Scalar::all(100));
cv::randu(uv, cv::Scalar::all(100), cv::Scalar::all(200));
cv::Mat orig_graph_output, transformed_graph_output;
auto& listener = getListener();
listener.counts.clear(); // clear counters before testing
{
// Run original graph
auto mainC = makeComputation();
mainC.apply(cv::gin(y, uv), cv::gout(orig_graph_output),
cv::compile_args(cv::gapi::kernels<MyNV12toBGRImpl, MyToNCHWImpl>()));
}
// Generate transformed graph (passing transformations via compile args)
auto mainC = makeComputation(); // get new copy with new Priv
mainC.apply(cv::gin(y, uv), cv::gout(transformed_graph_output), std::move(transform_args));
// Compare
ASSERT_TRUE(AbsExact()(orig_graph_output, transformed_graph_output));
// Custom verification via listener
ASSERT_EQ(1u, listener.counts.size());
// called in transformed graph:
ASSERT_NE(listener.counts.cend(), listener.counts.find(MySplit4::id()));
ASSERT_EQ(1u, listener.counts.at(MySplit4::id()));
}
};
TEST_F(PatternMatchingIntegrationUnusedNodes, SingleOpTransformApplied)
{
runTest(cv::compile_args(cv::gapi::kernels<MySplit4Impl, Split4Transform>()));
}
// FIXME: enable once unused nodes are properly handled by Transformation API
TEST_F(PatternMatchingIntegrationUnusedNodes, DISABLED_TransformWithInternalUnusedNodeApplied)
{
runTest(cv::compile_args(cv::gapi::kernels<MySplit4Impl, Split4Merge3Transform>()));
}
TEST_F(PatternMatchingIntegrationUnusedNodes, TransformWithOutputUnusedNodeApplied)
{
runTest(cv::compile_args(cv::gapi::kernels<MySplit4Impl, Merge4Split4Transform>()));
}
// --------------------------------------------------------------------------------------
// Bad arg integration tests (GCompiler-level) - General
struct PatternMatchingIntegrationBadArgTests : testing::Test
{
cv::GComputation makeComputation() {
GMat in;
GMat a, b, c, d;
std::tie(a, b, c, d) = MySplit4::on(in); // using custom Split4 to check if it's called
GMat out = cv::gapi::merge3(a + b, cv::gapi::bitwise_not(c), d * cv::GScalar(2.0));
return cv::GComputation(cv::GIn(in), cv::GOut(out));
}
void runTest(cv::GCompileArgs&& transform_args) {
cv::Size in_sz(640, 480);
cv::Mat input(in_sz, CV_8UC4);
cv::randu(input, cv::Scalar::all(70), cv::Scalar::all(140));
cv::Mat output;
// Generate transformed graph (passing transformations via compile args)
auto mainC = makeComputation(); // get new copy with new Priv
ASSERT_NO_THROW(mainC.apply(cv::gin(input), cv::gout(output), std::move(transform_args)));
}
};
TEST_F(PatternMatchingIntegrationBadArgTests, NoTransformations)
{
auto transform_args = cv::compile_args(cv::gapi::kernels<MySplit4Impl>());
auto& listener = getListener();
listener.counts.clear(); // clear counters before testing
runTest(std::move(transform_args));
// Custom verification via listener
ASSERT_EQ(1u, listener.counts.size());
ASSERT_NE(listener.counts.cend(), listener.counts.find(MySplit4::id()));
ASSERT_EQ(1u, listener.counts.at(MySplit4::id()));
}
TEST_F(PatternMatchingIntegrationBadArgTests, WrongTransformation)
{
// Here Split4Transform::pattern is "looking for" cv::gapi::split4 but it's not used
auto transform_args = cv::compile_args(cv::gapi::kernels<MySplit4Impl, Split4Transform>());
auto& listener = getListener();
listener.counts.clear(); // clear counters before testing
runTest(std::move(transform_args));
// Custom verification via listener
ASSERT_EQ(1u, listener.counts.size());
ASSERT_NE(listener.counts.cend(), listener.counts.find(MySplit4::id()));
ASSERT_EQ(1u, listener.counts.at(MySplit4::id()));
}
// --------------------------------------------------------------------------------------
// Bad arg integration tests (GCompiler-level) - Endless Loops
GAPI_TRANSFORM(EndlessLoopTransform, <cv::GMat(cv::GMat)>, "pattern in substitute")
{
static cv::GMat pattern(const cv::GMat& in)
{
return cv::gapi::resize(in, cv::Size(100, 100), 0, 0, cv::INTER_LINEAR);
}
static cv::GMat substitute(const cv::GMat& in)
{
cv::GMat b, g, r;
std::tie(b, g, r) = cv::gapi::split3(in);
auto resize = std::bind(&cv::gapi::resize,
std::placeholders::_1, cv::Size(100, 100), 0, 0, cv::INTER_LINEAR);
cv::GMat out = cv::gapi::merge3(resize(b), resize(g), resize(r));
return out;
}
};
TEST(PatternMatchingIntegrationEndlessLoops, PatternInSubstituteInOneTransform)
{
cv::Size in_sz(640, 480);
cv::Mat input(in_sz, CV_8UC3);
cv::randu(input, cv::Scalar::all(0), cv::Scalar::all(100));
auto c = [] () {
GMat in;
GMat tmp = cv::gapi::resize(in, cv::Size(100, 100), 0, 0, cv::INTER_LINEAR);
GMat out = cv::gapi::bitwise_not(tmp);
return cv::GComputation(cv::GIn(in), cv::GOut(out));
}();
EXPECT_THROW(
cv::gimpl::GCompiler(c, cv::descr_of(cv::gin(input)),
cv::compile_args(cv::gapi::kernels<EndlessLoopTransform>())),
std::exception);
}
} // namespace opencv_test

View File

@ -0,0 +1,35 @@
// 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 "../src/api/gproto_priv.hpp"
namespace opencv_test {
template<typename T>
struct ProtoPtrTest : public ::testing::Test { using Type = T; };
using ProtoPtrTestTypes = ::testing::Types< cv::Mat
, cv::UMat
, cv::gapi::own::Mat
, cv::RMat
, cv::Scalar
, std::vector<int>
, int
>;
TYPED_TEST_CASE(ProtoPtrTest, ProtoPtrTestTypes);
TYPED_TEST(ProtoPtrTest, NonZero)
{
typename TestFixture::Type value;
const auto arg = cv::gout(value).front();
const auto ptr = cv::gimpl::proto::ptr(arg);
EXPECT_EQ(ptr, &value);
}
} // namespace opencv_test

View File

@ -0,0 +1,232 @@
// 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) 2018 Intel Corporation
#include "../test_precomp.hpp"
#include "api/gcomputation_priv.hpp"
#include <opencv2/gapi/fluid/gfluidkernel.hpp>
#include <opencv2/gapi/fluid/core.hpp>
#include <opencv2/gapi/fluid/imgproc.hpp>
namespace opencv_test
{
TEST(GComputationCompile, NoRecompileWithSameMeta)
{
cv::GMat in;
cv::GComputation cc(in, in+in);
cv::Mat in_mat1 = cv::Mat::eye (32, 32, CV_8UC1);
cv::Mat in_mat2 = cv::Mat::zeros(32, 32, CV_8UC1);
cv::Mat out_mat;
cc.apply(in_mat1, out_mat);
auto comp1 = cc.priv().m_lastCompiled;
cc.apply(in_mat2, out_mat);
auto comp2 = cc.priv().m_lastCompiled;
// Both compiled objects are actually the same unique executable
EXPECT_EQ(&comp1.priv(), &comp2.priv());
}
TEST(GComputationCompile, NoRecompileWithWrongMeta)
{
cv::GMat in;
cv::GComputation cc(in, in+in);
cv::Mat in_mat1 = cv::Mat::eye (32, 32, CV_8UC1);
cv::Mat in_mat2 = cv::Mat::zeros(32, 32, CV_8UC1);
cv::Mat out_mat;
cc.apply(in_mat1, out_mat);
auto comp1 = cc.priv().m_lastCompiled;
EXPECT_THROW(cc.apply(cv::gin(cv::Scalar(128)), cv::gout(out_mat)), std::logic_error);
auto comp2 = cc.priv().m_lastCompiled;
// Both compiled objects are actually the same unique executable
EXPECT_EQ(&comp1.priv(), &comp2.priv());
}
TEST(GComputationCompile, RecompileWithDifferentMeta)
{
cv::GMat in;
cv::GComputation cc(in, in+in);
cv::Mat in_mat1 = cv::Mat::eye (32, 32, CV_8UC1);
cv::Mat in_mat2 = cv::Mat::zeros(64, 64, CV_32F);
cv::Mat out_mat;
cc.apply(in_mat1, out_mat);
auto comp1 = cc.priv().m_lastCompiled;
cc.apply(in_mat2, out_mat);
auto comp2 = cc.priv().m_lastCompiled;
// Both compiled objects are different
EXPECT_NE(&comp1.priv(), &comp2.priv());
}
TEST(GComputationCompile, FluidReshapeWithDifferentDims)
{
cv::GMat in;
cv::GComputation cc(in, in+in);
cv::Mat in_mat1 = cv::Mat::eye (32, 32, CV_8UC1);
cv::Mat in_mat2 = cv::Mat::zeros(64, 64, CV_8UC1);
cv::Mat out_mat;
cc.apply(in_mat1, out_mat, cv::compile_args(cv::gapi::core::fluid::kernels()));
auto comp1 = cc.priv().m_lastCompiled;
cc.apply(in_mat2, out_mat);
auto comp2 = cc.priv().m_lastCompiled;
// Both compiled objects are actually the same unique executable
EXPECT_EQ(&comp1.priv(), &comp2.priv());
}
TEST(GComputationCompile, FluidReshapeResizeDownScale)
{
cv::Size szOut(4, 4);
cv::GMat in;
cv::GComputation cc(in, cv::gapi::resize(in, szOut));
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));
cv::Mat out_mat1, out_mat2;
cc.apply(in_mat1, out_mat1, cv::compile_args(cv::gapi::core::fluid::kernels()));
auto comp1 = cc.priv().m_lastCompiled;
cc.apply(in_mat2, out_mat2);
auto comp2 = cc.priv().m_lastCompiled;
// Both compiled objects are actually the same unique executable
EXPECT_EQ(&comp1.priv(), &comp2.priv());
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(GComputationCompile, FluidReshapeSwitchToUpscaleFromDownscale)
{
cv::Size szOut(4, 4);
cv::GMat in;
cv::GComputation cc(in, cv::gapi::resize(in, szOut));
cv::Mat in_mat1( 8, 8, CV_8UC3);
cv::Mat in_mat2( 2, 2, CV_8UC3);
cv::Mat in_mat3(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));
cv::randu(in_mat3, cv::Scalar::all(0), cv::Scalar::all(255));
cv::Mat out_mat1, out_mat2, out_mat3;
cc.apply(in_mat1, out_mat1, cv::compile_args(cv::gapi::core::fluid::kernels()));
auto comp1 = cc.priv().m_lastCompiled;
cc.apply(in_mat2, out_mat2);
auto comp2 = cc.priv().m_lastCompiled;
cc.apply(in_mat3, out_mat3);
auto comp3 = cc.priv().m_lastCompiled;
EXPECT_EQ(&comp1.priv(), &comp2.priv());
EXPECT_EQ(&comp1.priv(), &comp3.priv());
cv::Mat cv_out_mat1, cv_out_mat2, cv_out_mat3;
cv::resize(in_mat1, cv_out_mat1, szOut);
cv::resize(in_mat2, cv_out_mat2, szOut);
cv::resize(in_mat3, cv_out_mat3, 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));
EXPECT_EQ(0, cvtest::norm(out_mat3, cv_out_mat3, NORM_INF));
}
TEST(GComputationCompile, ReshapeBlur)
{
cv::Size kernelSize{3, 3};
cv::GMat in;
cv::GComputation cc(in, cv::gapi::blur(in, kernelSize));
cv::Mat in_mat1( 8, 8, CV_8UC1);
cv::Mat in_mat2(16, 16, CV_8UC1);
cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255));
cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255));
cv::Mat out_mat1, out_mat2;
cc.apply(in_mat1, out_mat1, cv::compile_args(cv::gapi::imgproc::fluid::kernels()));
auto comp1 = cc.priv().m_lastCompiled;
cc.apply(in_mat2, out_mat2);
auto comp2 = cc.priv().m_lastCompiled;
// Both compiled objects are actually the same unique executable
EXPECT_EQ(&comp1.priv(), &comp2.priv());
cv::Mat cv_out_mat1, cv_out_mat2;
cv::blur(in_mat1, cv_out_mat1, kernelSize);
cv::blur(in_mat2, cv_out_mat2, kernelSize);
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(GComputationCompile, ReshapeRois)
{
cv::Size kernelSize{3, 3};
cv::Size szOut(8, 8);
cv::GMat in;
auto blurred = cv::gapi::blur(in, kernelSize);
cv::GComputation cc(in, cv::gapi::resize(blurred, szOut));
cv::Mat first_in_mat(8, 8, CV_8UC3);
cv::randn(first_in_mat, cv::Scalar::all(127), cv::Scalar::all(40.f));
cv::Mat first_out_mat;
auto fluidKernels = cv::gapi::combine(gapi::imgproc::fluid::kernels(),
gapi::core::fluid::kernels());
cc.apply(first_in_mat, first_out_mat, cv::compile_args(fluidKernels));
auto first_comp = cc.priv().m_lastCompiled;
constexpr int niter = 4;
for (int i = 0; i < niter; i++)
{
int width = 4 + 2*i;
int height = width;
cv::Mat in_mat(width, height, CV_8UC3);
cv::randn(in_mat, cv::Scalar::all(127), cv::Scalar::all(40.f));
cv::Mat out_mat = cv::Mat::zeros(szOut, CV_8UC3);
int x = 0;
int y = szOut.height * i / niter;
int roiW = szOut.width;
int roiH = szOut.height / niter;
cv::Rect roi{x, y, roiW, roiH};
cc.apply(in_mat, out_mat, cv::compile_args(cv::GFluidOutputRois{{roi}}));
auto comp = cc.priv().m_lastCompiled;
EXPECT_EQ(&first_comp.priv(), &comp.priv());
cv::Mat blur_mat, cv_out_mat;
cv::blur(in_mat, blur_mat, kernelSize);
cv::resize(blur_mat, cv_out_mat, szOut);
EXPECT_EQ(0, cvtest::norm(out_mat(roi), cv_out_mat(roi), NORM_INF));
}
}
} // opencv_test

View File

@ -0,0 +1,207 @@
// 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) 2018 Intel Corporation
#include "../test_precomp.hpp"
namespace opencv_test
{
typedef ::testing::Types<int, cv::Point, cv::Rect> VectorRef_Test_Types;
template<typename T> struct VectorRefT: public ::testing::Test { using Type = T; };
TYPED_TEST_CASE(VectorRefT, VectorRef_Test_Types);
TYPED_TEST(VectorRefT, Reset_Valid)
{
using T = typename TestFixture::Type;
cv::detail::VectorRefT<T> ref; // vector ref created empty
EXPECT_NO_THROW(ref.reset()); // 1st reset is OK (initializes)
EXPECT_NO_THROW(ref.reset()); // 2nd reset is also OK (resets)
}
TYPED_TEST(VectorRefT, Reset_Invalid)
{
using T = typename TestFixture::Type;
std::vector<T> vec(42); // create a std::vector of 42 elements
cv::detail::VectorRefT<T> ref(vec); // RO_EXT (since reference is const)
EXPECT_ANY_THROW(ref.reset()); // data-bound vector ref can't be reset
}
TYPED_TEST(VectorRefT, ReadRef_External)
{
using T = typename TestFixture::Type;
const std::vector<T> vec(42); // create a std::vector of 42 elements
cv::detail::VectorRefT<T> ref(vec); // RO_EXT (since reference is const)
auto &vref = ref.rref();
EXPECT_EQ(vec.data(), vref.data());
EXPECT_EQ(vec.size(), vref.size());
}
TYPED_TEST(VectorRefT, ReadRef_Internal)
{
using T = typename TestFixture::Type;
cv::detail::VectorRefT<T> ref;
ref.reset(); // RW_OWN (reset on empty ref)
auto &vref = ref.rref(); // read access is valid for RW_OWN
EXPECT_EQ(0u, vref.size()); // by default vector is empty
}
TYPED_TEST(VectorRefT, WriteRef_External)
{
using T = typename TestFixture::Type;
std::vector<T> vec(42); // create a std::vector of 42 elements
cv::detail::VectorRefT<T> ref(vec); // RW_EXT (since reference is not const)
auto &vref = ref.wref(); // write access is valid with RW_EXT
EXPECT_EQ(vec.data(), vref.data());
EXPECT_EQ(vec.size(), vref.size());
}
TYPED_TEST(VectorRefT, WriteRef_Internal)
{
using T = typename TestFixture::Type;
cv::detail::VectorRefT<T> ref;
ref.reset(); // RW_OWN (reset on empty ref)
auto &vref = ref.wref(); // write access is valid for RW_OWN
EXPECT_EQ(0u, vref.size()); // empty vector by default
}
TYPED_TEST(VectorRefT, WriteToRO)
{
using T = typename TestFixture::Type;
const std::vector<T> vec(42); // create a std::vector of 42 elements
cv::detail::VectorRefT<T> ref(vec); // RO_EXT (since reference is const)
EXPECT_ANY_THROW(ref.wref());
}
TYPED_TEST(VectorRefT, ReadAfterWrite)
{
using T = typename TestFixture::Type;
std::vector<T> vec; // Initial data holder (empty vector)
cv::detail::VectorRefT<T> writer(vec); // RW_EXT
const auto& ro_ref = vec;
cv::detail::VectorRefT<T> reader(ro_ref); // RO_EXT
EXPECT_EQ(0u, writer.wref().size()); // Check the initial state
EXPECT_EQ(0u, reader.rref().size());
writer.wref().emplace_back(); // Check that write is successful
EXPECT_EQ(1u, writer.wref().size());
EXPECT_EQ(1u, vec.size()); // Check that changes are reflected to the original container
EXPECT_EQ(1u, reader.rref().size()); // Check that changes are reflected to reader's view
EXPECT_EQ(T(), vec.at(0)); // Check the value (must be default-initialized)
EXPECT_EQ(T(), reader.rref().at(0));
EXPECT_EQ(T(), writer.wref().at(0));
}
template<typename T> struct VectorRefU: public ::testing::Test { using Type = T; };
TYPED_TEST_CASE(VectorRefU, VectorRef_Test_Types);
template<class T> struct custom_struct { T a; T b; };
TYPED_TEST(VectorRefU, Reset_Valid)
{
using T = typename TestFixture::Type;
cv::detail::VectorRef ref; // vector ref created empty
EXPECT_NO_THROW(ref.reset<T>()); // 1st reset is OK (initializes)
EXPECT_NO_THROW(ref.reset<T>()); // 2nd reset is also OK (resets)
EXPECT_ANY_THROW(ref.reset<custom_struct<T> >()); // type change is not allowed
}
TYPED_TEST(VectorRefU, Reset_Invalid)
{
using T = typename TestFixture::Type;
std::vector<T> vec(42); // create a std::vector of 42 elements
cv::detail::VectorRef ref(vec); // RO_EXT (since reference is const)
EXPECT_ANY_THROW(ref.reset<T>()); // data-bound vector ref can't be reset
}
TYPED_TEST(VectorRefU, ReadRef_External)
{
using T = typename TestFixture::Type;
const std::vector<T> vec(42); // create a std::vector of 42 elements
cv::detail::VectorRef ref(vec); // RO_EXT (since reference is const)
auto &vref = ref.rref<T>();
EXPECT_EQ(vec.data(), vref.data());
EXPECT_EQ(vec.size(), vref.size());
}
TYPED_TEST(VectorRefU, ReadRef_Internal)
{
using T = typename TestFixture::Type;
cv::detail::VectorRef ref;
ref.reset<T>(); // RW_OWN (reset on empty ref)
auto &vref = ref.rref<T>(); // read access is valid for RW_OWN
EXPECT_EQ(0u, vref.size()); // by default vector is empty
}
TYPED_TEST(VectorRefU, WriteRef_External)
{
using T = typename TestFixture::Type;
std::vector<T> vec(42); // create a std::vector of 42 elements
cv::detail::VectorRef ref(vec); // RW_EXT (since reference is not const)
auto &vref = ref.wref<T>(); // write access is valid with RW_EXT
EXPECT_EQ(vec.data(), vref.data());
EXPECT_EQ(vec.size(), vref.size());
}
TYPED_TEST(VectorRefU, WriteRef_Internal)
{
using T = typename TestFixture::Type;
cv::detail::VectorRef ref;
ref.reset<T>(); // RW_OWN (reset on empty ref)
auto &vref = ref.wref<T>(); // write access is valid for RW_OWN
EXPECT_EQ(0u, vref.size()); // empty vector by default
}
TYPED_TEST(VectorRefU, WriteToRO)
{
using T = typename TestFixture::Type;
const std::vector<T> vec(42); // create a std::vector of 42 elements
cv::detail::VectorRef ref(vec); // RO_EXT (since reference is const)
EXPECT_ANY_THROW(ref.wref<T>());
}
TYPED_TEST(VectorRefU, ReadAfterWrite)
{
using T = typename TestFixture::Type;
std::vector<T> vec; // Initial data holder (empty vector)
cv::detail::VectorRef writer(vec); // RW_EXT
const auto& ro_ref = vec;
cv::detail::VectorRef reader(ro_ref); // RO_EXT
EXPECT_EQ(0u, writer.wref<T>().size()); // Check the initial state
EXPECT_EQ(0u, reader.rref<T>().size());
writer.wref<T>().emplace_back(); // Check that write is successful
EXPECT_EQ(1u, writer.wref<T>().size());
EXPECT_EQ(1u, vec.size()); // Check that changes are reflected to the original container
EXPECT_EQ(1u, reader.rref<T>().size()); // Check that changes are reflected to reader's view
EXPECT_EQ(T(), vec.at(0)); // Check the value (must be default-initialized)
EXPECT_EQ(T(), reader.rref<T>().at(0));
EXPECT_EQ(T(), writer.wref<T>().at(0));
}
TEST(VectorRefU, TypeCheck)
{
cv::detail::VectorRef ref;
ref.reset<int>(); // RW_OWN
EXPECT_ANY_THROW(ref.reset<char>());
EXPECT_ANY_THROW(ref.rref<char>());
EXPECT_ANY_THROW(ref.wref<char>());
}
} // namespace opencv_test

View File

@ -0,0 +1,369 @@
// 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) 2018 - 2020 Intel Corporation
#include "../test_precomp.hpp"
#include <ade/graph.hpp>
#include <ade/typed_graph.hpp>
#include "compiler/transactions.hpp"
namespace opencv_test
{
namespace
{
bool contains(const ade::Graph& graph, const ade::NodeHandle& node)
{
auto nodes = graph.nodes();
return nodes.end() != std::find(nodes.begin(), nodes.end(), node);
}
bool connected(const ade::NodeHandle& src_node, const ade::NodeHandle& dst_node)
{
auto nodes = src_node->outNodes();
return nodes.end() != std::find(nodes.begin(), nodes.end(), dst_node);
}
struct SimpleGraph
{
// ehs[0] ehs[1] ehs[2] ehs[3]
// nhs[0] -- > nhs[1] --> nhs[2] --> nhs[3] --> nhs[4]
enum { node_nums = 5 };
ade::Graph graph;
ade::NodeHandle fused_nh; // For check that fusion node is connected to the
// inputs of the prod and the outputs of the cons
std::array<ade::NodeHandle, node_nums> nhs;
std::array<ade::EdgeHandle, node_nums - 1> ehs;
using Change = ChangeT<>;
Change::List changes;
SimpleGraph()
{
nhs[0] = graph.createNode();
for (int i = 1; i < node_nums; ++i)
{
nhs[i ] = graph.createNode();
ehs[i - 1] = graph.link(nhs[i - 1], nhs[i]);
}
}
void fuse()
{
// nhs[0] --> fused_nh --> nhs[4]
fused_nh = graph.createNode();
changes.enqueue<Change::NodeCreated>(fused_nh);
changes.enqueue<Change::NewLink> (graph, nhs[0], fused_nh);
changes.enqueue<Change::DropLink>(graph, nhs[1], ehs[0]);
changes.enqueue<Change::NewLink> (graph, fused_nh, nhs[4]);
changes.enqueue<Change::DropLink>(graph, nhs[3], ehs[3]);
changes.enqueue<Change::DropLink>(graph, nhs[1], ehs[1]);
changes.enqueue<Change::DropLink>(graph, nhs[2], ehs[2]);
changes.enqueue<Change::DropNode>(nhs[1]);
changes.enqueue<Change::DropNode>(nhs[2]);
changes.enqueue<Change::DropNode>(nhs[3]);
}
void commit() { changes.commit(graph); }
void rollback() { changes.rollback(graph); }
};
struct Transactions: public ::testing::Test, public SimpleGraph {};
} // anonymous namespace
TEST_F(Transactions, NodeCreated_Create)
{
auto new_nh = graph.createNode();
Change::NodeCreated node_created(new_nh);
EXPECT_EQ(6u, static_cast<std::size_t>(graph.nodes().size()));
EXPECT_TRUE(contains(graph, new_nh));
}
TEST_F(Transactions, NodeCreated_RollBack)
{
auto new_nh = graph.createNode();
Change::NodeCreated node_created(new_nh);
node_created.rollback(graph);
EXPECT_EQ(5u, static_cast<std::size_t>(graph.nodes().size()));
EXPECT_FALSE(contains(graph, new_nh));
}
TEST_F(Transactions, NodeCreated_Commit)
{
auto new_nh = graph.createNode();
Change::NodeCreated node_created(new_nh);
node_created.commit(graph);
EXPECT_EQ(6u, static_cast<std::size_t>(graph.nodes().size()));
EXPECT_TRUE(contains(graph, new_nh));
}
TEST_F(Transactions, DropLink_Create)
{
Change::DropLink drop_link(graph, nhs[0], ehs[0]);
EXPECT_FALSE(connected(nhs[0], nhs[1]));
}
TEST_F(Transactions, DropLink_RollBack)
{
Change::DropLink drop_link(graph, nhs[0], ehs[0]);
drop_link.rollback(graph);
EXPECT_TRUE(connected(nhs[0], nhs[1]));
}
TEST_F(Transactions, DropLink_Commit)
{
Change::DropLink drop_link(graph, nhs[0], ehs[0]);
drop_link.commit(graph);
EXPECT_FALSE(connected(nhs[0], nhs[1]));
}
TEST_F(Transactions, NewLink_Create)
{
auto new_nh = graph.createNode();
Change::NewLink new_link(graph, new_nh, nhs[0]);
EXPECT_TRUE(connected(new_nh, nhs[0]));
}
TEST_F(Transactions, NewLink_RollBack)
{
auto new_nh = graph.createNode();
Change::NewLink new_link(graph, new_nh, nhs[0]);
new_link.rollback(graph);
EXPECT_FALSE(connected(new_nh, nhs[0]));
}
TEST_F(Transactions, NewLink_Commit)
{
auto new_nh = graph.createNode();
Change::NewLink new_link(graph, new_nh, nhs[0]);
new_link.commit(graph);
EXPECT_TRUE(connected(new_nh, nhs[0]));
}
TEST_F(Transactions, DropNode_Create)
{
auto new_nh = graph.createNode();
Change::DropNode drop_node(new_nh);
EXPECT_EQ(6u, static_cast<std::size_t>(graph.nodes().size()));
EXPECT_TRUE(contains(graph, new_nh));
}
TEST_F(Transactions, DropNode_RollBack)
{
auto new_nh = graph.createNode();
Change::DropNode drop_node(new_nh);
drop_node.rollback(graph);
EXPECT_EQ(6u, static_cast<std::size_t>(graph.nodes().size()));
EXPECT_TRUE(contains(graph, new_nh));
}
TEST_F(Transactions, DropNode_Commit)
{
auto new_nh = graph.createNode();
Change::DropNode drop_node(new_nh);
drop_node.commit(graph);
EXPECT_EQ(5u, static_cast<std::size_t>(graph.nodes().size()));
EXPECT_FALSE(contains(graph, new_nh));
}
TEST_F(Transactions, Fusion_Commit)
{
fuse();
commit();
EXPECT_EQ(3u, static_cast<std::size_t>(graph.nodes().size()));
EXPECT_TRUE(connected(nhs[0] , fused_nh));
EXPECT_TRUE(connected(fused_nh, nhs[4]));
}
TEST_F(Transactions, Fusion_RollBack)
{
fuse();
rollback();
EXPECT_EQ(static_cast<std::size_t>(node_nums),
static_cast<std::size_t>(graph.nodes().size()));
EXPECT_FALSE(contains(graph, fused_nh));
for (int i = 0; i < static_cast<int>(node_nums) - 1; ++i)
{
EXPECT_TRUE(connected(nhs[i], nhs[i + 1]));
}
}
namespace
{
struct MetaInt {
static const char *name() { return "int_meta"; }
int x;
};
struct MetaStr {
static const char *name() { return "string_meta"; }
std::string s;
};
}
TEST(PreservedMeta, TestMetaCopy_Full)
{
ade::Graph g;
ade::TypedGraph<MetaInt, MetaStr> tg(g);
auto src_nh = tg.createNode();
tg.metadata(src_nh).set(MetaInt{42});
tg.metadata(src_nh).set(MetaStr{"hi"});
auto dst_nh = tg.createNode();
EXPECT_FALSE(tg.metadata(dst_nh).contains<MetaInt>());
EXPECT_FALSE(tg.metadata(dst_nh).contains<MetaStr>());
// Here we specify all the meta types we know about the src node
// Assume Preserved copies its all for us
Preserved<ade::NodeHandle, MetaInt, MetaStr>(g, src_nh).copyTo(g, dst_nh);
ASSERT_TRUE(tg.metadata(dst_nh).contains<MetaInt>());
ASSERT_TRUE(tg.metadata(dst_nh).contains<MetaStr>());
EXPECT_EQ(42, tg.metadata(dst_nh).get<MetaInt>().x);
EXPECT_EQ("hi", tg.metadata(dst_nh).get<MetaStr>().s);
}
TEST(PreservedMeta, TestMetaCopy_Partial_Dst)
{
ade::Graph g;
ade::TypedGraph<MetaInt, MetaStr> tg(g);
auto tmp_nh1 = tg.createNode();
auto tmp_nh2 = tg.createNode();
auto src_eh = tg.link(tmp_nh1, tmp_nh2);
tg.metadata(src_eh).set(MetaInt{42});
tg.metadata(src_eh).set(MetaStr{"hi"});
auto tmp_nh3 = tg.createNode();
auto tmp_nh4 = tg.createNode();
auto dst_eh = tg.link(tmp_nh3, tmp_nh4);
EXPECT_FALSE(tg.metadata(dst_eh).contains<MetaInt>());
EXPECT_FALSE(tg.metadata(dst_eh).contains<MetaStr>());
// Here we specify just a single meta type for the src node
// Assume Preserved copies only this type and nothing else
Preserved<ade::EdgeHandle, MetaStr>(g, src_eh).copyTo(g, dst_eh);
ASSERT_FALSE(tg.metadata(dst_eh).contains<MetaInt>());
ASSERT_TRUE (tg.metadata(dst_eh).contains<MetaStr>());
EXPECT_EQ("hi", tg.metadata(dst_eh).get<MetaStr>().s);
}
TEST(PreservedMeta, TestMetaCopy_Partial_Src)
{
ade::Graph g;
ade::TypedGraph<MetaInt, MetaStr> tg(g);
auto src_nh = tg.createNode();
tg.metadata(src_nh).set(MetaInt{42});
auto dst_nh = tg.createNode();
EXPECT_FALSE(tg.metadata(dst_nh).contains<MetaInt>());
EXPECT_FALSE(tg.metadata(dst_nh).contains<MetaStr>());
// Here we specify all the meta types we know about the src node
// but the src node has just one of them.
// A valid situation, only MetaInt to be copied.
Preserved<ade::NodeHandle, MetaInt, MetaStr>(g, src_nh).copyTo(g, dst_nh);
ASSERT_TRUE (tg.metadata(dst_nh).contains<MetaInt>());
ASSERT_FALSE(tg.metadata(dst_nh).contains<MetaStr>());
EXPECT_EQ(42, tg.metadata(dst_nh).get<MetaInt>().x);
}
TEST(PreservedMeta, TestMetaCopy_Nothing)
{
ade::Graph g;
ade::TypedGraph<MetaInt, MetaStr> tg(g);
auto src_nh = tg.createNode();
auto dst_nh = tg.createNode();
EXPECT_FALSE(tg.metadata(src_nh).contains<MetaInt>());
EXPECT_FALSE(tg.metadata(src_nh).contains<MetaStr>());
EXPECT_FALSE(tg.metadata(dst_nh).contains<MetaInt>());
EXPECT_FALSE(tg.metadata(dst_nh).contains<MetaStr>());
// Here we specify all the meta types we know about the src node
// but the src node has none of those. See how it works now
Preserved<ade::NodeHandle, MetaInt, MetaStr>(g, src_nh).copyTo(g, dst_nh);
ASSERT_FALSE(tg.metadata(dst_nh).contains<MetaInt>());
ASSERT_FALSE(tg.metadata(dst_nh).contains<MetaStr>());
}
TEST(PreservedMeta, DropEdge)
{
ade::Graph g;
ade::TypedGraph<MetaInt, MetaStr> tg(g);
auto nh1 = tg.createNode();
auto nh2 = tg.createNode();
auto eh = tg.link(nh1, nh2);
tg.metadata(eh).set(MetaInt{42});
tg.metadata(eh).set(MetaStr{"hi"});
// Drop an edge using the transaction API
using Change = ChangeT<MetaInt, MetaStr>;
Change::List changes;
changes.enqueue<Change::DropLink>(g, nh1, eh);
EXPECT_EQ(0u, nh1->outNodes().size());
EXPECT_EQ(nullptr, eh);
// Now restore the edge and check if it's meta was restored
changes.rollback(g);
ASSERT_EQ(1u, nh1->outNodes().size());
eh = *nh1->outEdges().begin();
ASSERT_TRUE(tg.metadata(eh).contains<MetaInt>());
ASSERT_TRUE(tg.metadata(eh).contains<MetaStr>());
EXPECT_EQ(42, tg.metadata(eh).get<MetaInt>().x);
EXPECT_EQ("hi", tg.metadata(eh).get<MetaStr>().s);
}
} // opencv_test