mirror of
https://github.com/opencv/opencv.git
synced 2024-11-30 22:40:17 +08:00
ca8bb8d053
- desync() is a new (and for now, the only one) intrinsic which splits the graph execution into asynchronous parts when running in Streaming mode; - desync() makes no effect when compiling in Traditional mode; - Added tests on desync() working in various scenarios; - Extended GStreamingExecutor to support desync(); also extended GStreamingCompiled() with a new version of pull() returning a vector of optional values; - Fixed various issues with storing the type information & proper construction callbacks for GArray<> and GOpaque; - Introduced a new infer(Roi,GMat) overload with a sample; - Introduced an internal API for Islands to control fusion procedure (to fuse or not to fuse); - Introduced handleStopStream() callback for island executables; - Added GCompileArgs to metadata of the graph (required for other features).
1509 lines
47 KiB
C++
1509 lines
47 KiB
C++
// 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-2020 Intel Corporation
|
|
|
|
|
|
#include "../test_precomp.hpp"
|
|
|
|
#include <thread> // sleep_for (Delay)
|
|
|
|
#include <opencv2/gapi/cpu/core.hpp>
|
|
#include <opencv2/gapi/cpu/imgproc.hpp>
|
|
|
|
#include <opencv2/gapi/fluid/core.hpp>
|
|
#include <opencv2/gapi/fluid/imgproc.hpp>
|
|
#include <opencv2/gapi/fluid/gfluidkernel.hpp>
|
|
|
|
#include <opencv2/gapi/ocl/core.hpp>
|
|
#include <opencv2/gapi/ocl/imgproc.hpp>
|
|
|
|
#include <opencv2/gapi/streaming/cap.hpp>
|
|
#include <opencv2/gapi/streaming/desync.hpp>
|
|
|
|
namespace opencv_test
|
|
{
|
|
namespace
|
|
{
|
|
void initTestDataPath()
|
|
{
|
|
#ifndef WINRT
|
|
static bool initialized = false;
|
|
if (!initialized)
|
|
{
|
|
// Since G-API has no own test data (yet), it is taken from the common space
|
|
const char* testDataPath = getenv("OPENCV_TEST_DATA_PATH");
|
|
GAPI_Assert(testDataPath != nullptr);
|
|
|
|
cvtest::addDataSearchPath(testDataPath);
|
|
initialized = true;
|
|
}
|
|
#endif // WINRT
|
|
}
|
|
|
|
enum class KernelPackage: int
|
|
{
|
|
OCV,
|
|
OCV_FLUID,
|
|
OCL,
|
|
OCL_FLUID,
|
|
};
|
|
std::ostream& operator<< (std::ostream &os, const KernelPackage &e)
|
|
{
|
|
switch (e)
|
|
{
|
|
#define _C(X) case KernelPackage::X: os << #X; break
|
|
_C(OCV);
|
|
_C(OCV_FLUID);
|
|
_C(OCL);
|
|
_C(OCL_FLUID);
|
|
#undef _C
|
|
default: GAPI_Assert(false);
|
|
}
|
|
return os;
|
|
}
|
|
|
|
struct GAPI_Streaming: public ::testing::TestWithParam<KernelPackage> {
|
|
GAPI_Streaming() { initTestDataPath(); }
|
|
|
|
cv::gapi::GKernelPackage getKernelPackage()
|
|
{
|
|
using namespace cv::gapi;
|
|
switch (GetParam())
|
|
{
|
|
case KernelPackage::OCV:
|
|
return cv::gapi::combine(core::cpu::kernels(),
|
|
imgproc::cpu::kernels());
|
|
break;
|
|
|
|
case KernelPackage::OCV_FLUID:
|
|
return cv::gapi::combine(core::cpu::kernels(),
|
|
imgproc::cpu::kernels(),
|
|
core::fluid::kernels());
|
|
break;
|
|
|
|
// FIXME: OpenCL backend seem to work fine with Streaming
|
|
// however the results are not very bit exact with CPU
|
|
// It may be a problem but may be just implementation innacuracy.
|
|
// Need to customize the comparison function in tests where OpenCL
|
|
// is involved.
|
|
case KernelPackage::OCL:
|
|
return cv::gapi::combine(core::ocl::kernels(),
|
|
imgproc::ocl::kernels());
|
|
break;
|
|
|
|
case KernelPackage::OCL_FLUID:
|
|
return cv::gapi::combine(core::ocl::kernels(),
|
|
imgproc::ocl::kernels(),
|
|
core::fluid::kernels());
|
|
break;
|
|
}
|
|
throw std::logic_error("Unknown package");
|
|
}
|
|
};
|
|
|
|
G_API_OP(Delay, <cv::GMat(cv::GMat, int)>, "org.opencv.test.delay") {
|
|
static cv::GMatDesc outMeta(const cv::GMatDesc &in, int) { return in; }
|
|
};
|
|
GAPI_OCV_KERNEL(OCVDelay, Delay) {
|
|
static void run(const cv::Mat &in, int ms, cv::Mat &out) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds{ms});
|
|
in.copyTo(out);
|
|
}
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
TEST_P(GAPI_Streaming, SmokeTest_ConstInput_GMat)
|
|
{
|
|
// This graph models the following use-case:
|
|
// Canny here is used as some "feature detector"
|
|
//
|
|
// Island/device layout may be different given the contents
|
|
// of the passed kernel package.
|
|
//
|
|
// The expectation is that we get as much islands in the
|
|
// graph as backends the GKernelPackage contains.
|
|
//
|
|
// [Capture] --> Crop --> Resize --> Canny --> [out]
|
|
|
|
const auto crop_rc = cv::Rect(13, 75, 377, 269);
|
|
const auto resample_sz = cv::Size(224, 224);
|
|
const auto thr_lo = 64.;
|
|
const auto thr_hi = 192.;
|
|
|
|
cv::GMat in;
|
|
auto roi = cv::gapi::crop(in, crop_rc);
|
|
auto res = cv::gapi::resize(roi, resample_sz);
|
|
auto out = cv::gapi::Canny(res, thr_lo, thr_hi);
|
|
cv::GComputation c(in, out);
|
|
|
|
// Input data
|
|
cv::Mat in_mat = cv::imread(findDataFile("cv/edgefilter/kodim23.png"));
|
|
cv::Mat out_mat_gapi;
|
|
|
|
// OpenCV reference image
|
|
cv::Mat out_mat_ocv;
|
|
{
|
|
cv::Mat tmp;
|
|
cv::resize(in_mat(crop_rc), tmp, resample_sz);
|
|
cv::Canny(tmp, out_mat_ocv, thr_lo, thr_hi);
|
|
}
|
|
|
|
// Compilation & testing
|
|
auto ccomp = c.compileStreaming(cv::descr_of(in_mat),
|
|
cv::compile_args(cv::gapi::use_only{getKernelPackage()}));
|
|
EXPECT_TRUE(ccomp);
|
|
EXPECT_FALSE(ccomp.running());
|
|
|
|
ccomp.setSource(cv::gin(in_mat));
|
|
|
|
ccomp.start();
|
|
EXPECT_TRUE(ccomp.running());
|
|
|
|
// Fetch the result 15 times
|
|
for (int i = 0; i < 15; i++) {
|
|
// With constant inputs, the stream is endless so
|
|
// the blocking pull() should never return `false`.
|
|
EXPECT_TRUE(ccomp.pull(cv::gout(out_mat_gapi)));
|
|
EXPECT_EQ(0, cvtest::norm(out_mat_gapi, out_mat_ocv, NORM_INF));
|
|
}
|
|
|
|
EXPECT_TRUE(ccomp.running());
|
|
ccomp.stop();
|
|
|
|
EXPECT_FALSE(ccomp.running());
|
|
}
|
|
|
|
TEST_P(GAPI_Streaming, SmokeTest_VideoInput_GMat)
|
|
{
|
|
const auto crop_rc = cv::Rect(13, 75, 377, 269);
|
|
const auto resample_sz = cv::Size(224, 224);
|
|
const auto thr_lo = 64.;
|
|
const auto thr_hi = 192.;
|
|
|
|
cv::GMat in;
|
|
auto roi = cv::gapi::crop(in, crop_rc);
|
|
auto res = cv::gapi::resize(roi, resample_sz);
|
|
auto out = cv::gapi::Canny(res, thr_lo, thr_hi);
|
|
cv::GComputation c(cv::GIn(in), cv::GOut(cv::gapi::copy(in), out));
|
|
|
|
// OpenCV reference image code
|
|
auto opencv_ref = [&](const cv::Mat &in_mat, cv::Mat &out_mat) {
|
|
cv::Mat tmp;
|
|
cv::resize(in_mat(crop_rc), tmp, resample_sz);
|
|
cv::Canny(tmp, out_mat, thr_lo, thr_hi);
|
|
};
|
|
|
|
// Compilation & testing
|
|
auto ccomp = c.compileStreaming(cv::GMatDesc{CV_8U,3,cv::Size{768,576}},
|
|
cv::compile_args(cv::gapi::use_only{getKernelPackage()}));
|
|
EXPECT_TRUE(ccomp);
|
|
EXPECT_FALSE(ccomp.running());
|
|
|
|
ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi")));
|
|
|
|
ccomp.start();
|
|
EXPECT_TRUE(ccomp.running());
|
|
|
|
// Process the full video
|
|
cv::Mat in_mat_gapi, out_mat_gapi;
|
|
|
|
std::size_t frames = 0u;
|
|
while (ccomp.pull(cv::gout(in_mat_gapi, out_mat_gapi))) {
|
|
frames++;
|
|
cv::Mat out_mat_ocv;
|
|
opencv_ref(in_mat_gapi, out_mat_ocv);
|
|
EXPECT_EQ(0, cvtest::norm(out_mat_gapi, out_mat_ocv, NORM_INF));
|
|
}
|
|
EXPECT_LT(0u, frames);
|
|
EXPECT_FALSE(ccomp.running());
|
|
|
|
// Stop can be called at any time (even if the pipeline is not running)
|
|
ccomp.stop();
|
|
|
|
EXPECT_FALSE(ccomp.running());
|
|
}
|
|
|
|
TEST_P(GAPI_Streaming, Regression_CompileTimeScalar)
|
|
{
|
|
// There was a bug with compile-time GScalars. Compile-time
|
|
// GScalars generate their own DATA nodes at GModel/GIslandModel
|
|
// level, resulting in an extra link at the GIslandModel level, so
|
|
// GStreamingExecutor automatically assigned an input queue to
|
|
// such edges. Since there were no in-graph producer for that
|
|
// data, no data were pushed to such queue what lead to a
|
|
// deadlock.
|
|
|
|
cv::GMat in;
|
|
cv::GMat tmp = cv::gapi::copy(in);
|
|
for (int i = 0; i < 3; i++) {
|
|
tmp = tmp & cv::gapi::blur(in, cv::Size(3,3));
|
|
}
|
|
cv::GComputation c(cv::GIn(in), cv::GOut(tmp, tmp + 1));
|
|
|
|
auto ccomp = c.compileStreaming(cv::GMatDesc{CV_8U,3,cv::Size{768,512}},
|
|
cv::compile_args(cv::gapi::use_only{getKernelPackage()}));
|
|
|
|
cv::Mat in_mat = cv::imread(findDataFile("cv/edgefilter/kodim23.png"));
|
|
cv::Mat out_mat1, out_mat2;
|
|
|
|
// Fetch the result 15 times
|
|
ccomp.setSource(cv::gin(in_mat));
|
|
ccomp.start();
|
|
for (int i = 0; i < 15; i++) {
|
|
EXPECT_TRUE(ccomp.pull(cv::gout(out_mat1, out_mat2)));
|
|
}
|
|
|
|
ccomp.stop();
|
|
}
|
|
|
|
TEST_P(GAPI_Streaming, SmokeTest_StartRestart)
|
|
{
|
|
cv::GMat in;
|
|
auto res = cv::gapi::resize(in, cv::Size{300,200});
|
|
auto out = cv::gapi::Canny(res, 95, 220);
|
|
cv::GComputation c(cv::GIn(in), cv::GOut(cv::gapi::copy(in), out));
|
|
|
|
auto ccomp = c.compileStreaming(cv::GMatDesc{CV_8U,3,cv::Size{768,576}},
|
|
cv::compile_args(cv::gapi::use_only{getKernelPackage()}));
|
|
EXPECT_TRUE(ccomp);
|
|
EXPECT_FALSE(ccomp.running());
|
|
|
|
// Run 1
|
|
std::size_t num_frames1 = 0u;
|
|
ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi")));
|
|
ccomp.start();
|
|
EXPECT_TRUE(ccomp.running());
|
|
|
|
cv::Mat out1, out2;
|
|
while (ccomp.pull(cv::gout(out1, out2))) num_frames1++;
|
|
|
|
EXPECT_FALSE(ccomp.running());
|
|
|
|
// Run 2
|
|
std::size_t num_frames2 = 0u;
|
|
ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi")));
|
|
ccomp.start();
|
|
EXPECT_TRUE(ccomp.running());
|
|
while (ccomp.pull(cv::gout(out1, out2))) num_frames2++;
|
|
|
|
EXPECT_FALSE(ccomp.running());
|
|
|
|
EXPECT_LT(0u, num_frames1);
|
|
EXPECT_LT(0u, num_frames2);
|
|
EXPECT_EQ(num_frames1, num_frames2);
|
|
}
|
|
|
|
TEST_P(GAPI_Streaming, SmokeTest_VideoConstSource_NoHang)
|
|
{
|
|
// A video source is a finite one, while const source is not.
|
|
// Check that pipeline completes when a video source completes.
|
|
auto refc = cv::GComputation([](){
|
|
cv::GMat in;
|
|
return cv::GComputation(in, cv::gapi::copy(in));
|
|
}).compileStreaming(cv::GMatDesc{CV_8U,3,cv::Size{768,576}},
|
|
cv::compile_args(cv::gapi::use_only{getKernelPackage()}));
|
|
|
|
refc.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi")));
|
|
refc.start();
|
|
std::size_t ref_frames = 0u;
|
|
cv::Mat tmp;
|
|
while (refc.pull(cv::gout(tmp))) ref_frames++;
|
|
EXPECT_EQ(100u, ref_frames);
|
|
|
|
cv::GMat in;
|
|
cv::GMat in2;
|
|
cv::GMat roi = cv::gapi::crop(in2, cv::Rect{1,1,256,256});
|
|
cv::GMat blr = cv::gapi::blur(roi, cv::Size(3,3));
|
|
cv::GMat out = blr - in;
|
|
auto testc = cv::GComputation(cv::GIn(in, in2), cv::GOut(out))
|
|
.compileStreaming(cv::GMatDesc{CV_8U,3,cv::Size{256,256}},
|
|
cv::GMatDesc{CV_8U,3,cv::Size{768,576}},
|
|
cv::compile_args(cv::gapi::use_only{getKernelPackage()}));
|
|
|
|
cv::Mat in_const = cv::Mat::eye(cv::Size(256,256), CV_8UC3);
|
|
testc.setSource(cv::gin(in_const,
|
|
gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi"))));
|
|
testc.start();
|
|
std::size_t test_frames = 0u;
|
|
while (testc.pull(cv::gout(tmp))) test_frames++;
|
|
|
|
EXPECT_EQ(ref_frames, test_frames);
|
|
}
|
|
|
|
TEST_P(GAPI_Streaming, SmokeTest_AutoMeta)
|
|
{
|
|
cv::GMat in;
|
|
cv::GMat in2;
|
|
cv::GMat roi = cv::gapi::crop(in2, cv::Rect{1,1,256,256});
|
|
cv::GMat blr = cv::gapi::blur(roi, cv::Size(3,3));
|
|
cv::GMat out = blr - in;
|
|
|
|
auto testc = cv::GComputation(cv::GIn(in, in2), cv::GOut(out))
|
|
.compileStreaming(cv::compile_args(cv::gapi::use_only{getKernelPackage()}));
|
|
|
|
cv::Mat in_const = cv::Mat::eye(cv::Size(256,256), CV_8UC3);
|
|
cv::Mat tmp;
|
|
|
|
// Test with one video source
|
|
auto in_src = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi"));
|
|
testc.setSource(cv::gin(in_const, in_src));
|
|
testc.start();
|
|
|
|
std::size_t test_frames = 0u;
|
|
while (testc.pull(cv::gout(tmp))) test_frames++;
|
|
EXPECT_EQ(100u, test_frames);
|
|
|
|
// Now test with another one
|
|
in_src = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/1920x1080.avi"));
|
|
testc.setSource(cv::gin(in_const, in_src));
|
|
testc.start();
|
|
|
|
test_frames = 0u;
|
|
while (testc.pull(cv::gout(tmp))) test_frames++;
|
|
EXPECT_EQ(165u, test_frames);
|
|
}
|
|
|
|
|
|
TEST_P(GAPI_Streaming, SmokeTest_AutoMeta_2xConstMat)
|
|
{
|
|
cv::GMat in;
|
|
cv::GMat in2;
|
|
cv::GMat roi = cv::gapi::crop(in2, cv::Rect{1,1,256,256});
|
|
cv::GMat blr = cv::gapi::blur(roi, cv::Size(3,3));
|
|
cv::GMat out = blr - in;
|
|
|
|
auto testc = cv::GComputation(cv::GIn(in, in2), cv::GOut(out))
|
|
.compileStreaming(cv::compile_args(cv::gapi::use_only{getKernelPackage()}));
|
|
|
|
cv::Mat in_const = cv::Mat::eye(cv::Size(256,256), CV_8UC3);
|
|
cv::Mat tmp;
|
|
|
|
// Test with first image
|
|
auto in_src = cv::imread(findDataFile("cv/edgefilter/statue.png"));
|
|
testc.setSource(cv::gin(in_const, in_src));
|
|
testc.start();
|
|
|
|
ASSERT_TRUE(testc.pull(cv::gout(tmp)));
|
|
|
|
testc.stop();
|
|
|
|
// Now test with second image
|
|
in_src = cv::imread(findDataFile("cv/edgefilter/kodim23.png"));
|
|
testc.setSource(cv::gin(in_const, in_src));
|
|
testc.start();
|
|
|
|
ASSERT_TRUE(testc.pull(cv::gout(tmp)));
|
|
|
|
testc.stop();
|
|
}
|
|
|
|
TEST_P(GAPI_Streaming, SmokeTest_AutoMeta_VideoScalar)
|
|
{
|
|
cv::GMat in_m;
|
|
cv::GScalar in_s;
|
|
cv::GMat out_m = in_m * in_s;
|
|
|
|
auto testc = cv::GComputation(cv::GIn(in_m, in_s), cv::GOut(out_m))
|
|
.compileStreaming(cv::compile_args(cv::gapi::use_only{getKernelPackage()}));
|
|
|
|
cv::Mat tmp;
|
|
// Test with one video source and scalar
|
|
auto in_src = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi"));
|
|
testc.setSource(cv::gin(in_src, cv::Scalar{1.25}));
|
|
testc.start();
|
|
|
|
std::size_t test_frames = 0u;
|
|
while (testc.pull(cv::gout(tmp))) test_frames++;
|
|
EXPECT_EQ(100u, test_frames);
|
|
|
|
// Now test with another one video source and scalar
|
|
in_src = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/1920x1080.avi"));
|
|
testc.setSource(cv::gin(in_src, cv::Scalar{0.75}));
|
|
testc.start();
|
|
|
|
test_frames = 0u;
|
|
while (testc.pull(cv::gout(tmp))) test_frames++;
|
|
EXPECT_EQ(165u, test_frames);
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(TestStreaming, GAPI_Streaming,
|
|
Values( KernelPackage::OCV
|
|
//, KernelPackage::OCL // FIXME: Fails bit-exactness check, maybe relax it?
|
|
, KernelPackage::OCV_FLUID
|
|
//, KernelPackage::OCL // FIXME: Fails bit-exactness check, maybe relax it?
|
|
));
|
|
|
|
namespace TypesTest
|
|
{
|
|
G_API_OP(SumV, <cv::GArray<int>(cv::GMat)>, "test.gapi.sumv") {
|
|
static cv::GArrayDesc outMeta(const cv::GMatDesc &) {
|
|
return cv::empty_array_desc();
|
|
}
|
|
};
|
|
G_API_OP(AddV, <cv::GMat(cv::GMat,cv::GArray<int>)>, "test.gapi.addv") {
|
|
static cv::GMatDesc outMeta(const cv::GMatDesc &in, const cv::GArrayDesc &) {
|
|
return in;
|
|
}
|
|
};
|
|
|
|
GAPI_OCV_KERNEL(OCVSumV, SumV) {
|
|
static void run(const cv::Mat &in, std::vector<int> &out) {
|
|
CV_Assert(in.depth() == CV_8U);
|
|
const auto length = in.cols * in.channels();
|
|
out.resize(length);
|
|
|
|
const uchar *ptr = in.ptr(0);
|
|
for (int c = 0; c < length; c++) {
|
|
out[c] = ptr[c];
|
|
}
|
|
for (int r = 1; r < in.rows; r++) {
|
|
ptr = in.ptr(r);
|
|
for (int c = 0; c < length; c++) {
|
|
out[c] += ptr[c];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
GAPI_OCV_KERNEL(OCVAddV, AddV) {
|
|
static void run(const cv::Mat &in, const std::vector<int> &inv, cv::Mat &out) {
|
|
CV_Assert(in.depth() == CV_8U);
|
|
const auto length = in.cols * in.channels();
|
|
CV_Assert(length == static_cast<int>(inv.size()));
|
|
|
|
for (int r = 0; r < in.rows; r++) {
|
|
const uchar *in_ptr = in.ptr(r);
|
|
uchar *out_ptr = out.ptr(r);
|
|
|
|
for (int c = 0; c < length; c++) {
|
|
out_ptr[c] = cv::saturate_cast<uchar>(in_ptr[c] + inv[c]);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
GAPI_FLUID_KERNEL(FluidAddV, AddV, false) {
|
|
static const int Window = 1;
|
|
|
|
static void run(const cv::gapi::fluid::View &in,
|
|
const std::vector<int> &inv,
|
|
cv::gapi::fluid::Buffer &out) {
|
|
const uchar *in_ptr = in.InLineB(0);
|
|
uchar *out_ptr = out.OutLineB(0);
|
|
|
|
const auto length = in.meta().size.width * in.meta().chan;
|
|
CV_Assert(length == static_cast<int>(inv.size()));
|
|
|
|
for (int c = 0; c < length; c++) {
|
|
out_ptr[c] = cv::saturate_cast<uchar>(in_ptr[c] + inv[c]);
|
|
}
|
|
}
|
|
};
|
|
} // namespace TypesTest
|
|
|
|
TEST_P(GAPI_Streaming, SmokeTest_AutoMeta_VideoArray)
|
|
{
|
|
cv::GMat in_m;
|
|
cv::GArray<int> in_v;
|
|
cv::GMat out_m = TypesTest::AddV::on(in_m, in_v) - in_m;
|
|
|
|
// Run pipeline
|
|
auto testc = cv::GComputation(cv::GIn(in_m, in_v), cv::GOut(out_m))
|
|
.compileStreaming(cv::compile_args(cv::gapi::kernels<TypesTest::OCVAddV>()));
|
|
|
|
cv::Mat tmp;
|
|
// Test with one video source and vector
|
|
auto in_src = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi"));
|
|
std::vector<int> first_in_vec(768*3, 1);
|
|
testc.setSource(cv::gin(in_src, first_in_vec));
|
|
testc.start();
|
|
|
|
std::size_t test_frames = 0u;
|
|
while (testc.pull(cv::gout(tmp))) test_frames++;
|
|
EXPECT_EQ(100u, test_frames);
|
|
|
|
// Now test with another one
|
|
in_src = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/1920x1080.avi"));
|
|
std::vector<int> second_in_vec(1920*3, 1);
|
|
testc.setSource(cv::gin(in_src, second_in_vec));
|
|
testc.start();
|
|
|
|
test_frames = 0u;
|
|
while (testc.pull(cv::gout(tmp))) test_frames++;
|
|
EXPECT_EQ(165u, test_frames);
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Types, InputScalar)
|
|
{
|
|
// This test verifies if Streaming works with Scalar data @ input.
|
|
|
|
cv::GMat in_m;
|
|
cv::GScalar in_s;
|
|
cv::GMat out_m = in_m * in_s;
|
|
cv::GComputation c(cv::GIn(in_m, in_s), cv::GOut(out_m));
|
|
|
|
// Input data
|
|
cv::Mat in_mat = cv::Mat::eye(256, 256, CV_8UC1);
|
|
cv::Scalar in_scl = 32;
|
|
|
|
// Run pipeline
|
|
auto sc = c.compileStreaming(cv::descr_of(in_mat), cv::descr_of(in_scl));
|
|
sc.setSource(cv::gin(in_mat, in_scl));
|
|
sc.start();
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
cv::Mat out;
|
|
EXPECT_TRUE(sc.pull(cv::gout(out)));
|
|
EXPECT_EQ(0., cv::norm(out, in_mat.mul(in_scl), cv::NORM_INF));
|
|
}
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Types, InputVector)
|
|
{
|
|
// This test verifies if Streaming works with Vector data @ input.
|
|
|
|
cv::GMat in_m;
|
|
cv::GArray<int> in_v;
|
|
cv::GMat out_m = TypesTest::AddV::on(in_m, in_v) - in_m;
|
|
cv::GComputation c(cv::GIn(in_m, in_v), cv::GOut(out_m));
|
|
|
|
// Input data
|
|
cv::Mat in_mat = cv::Mat::eye(256, 256, CV_8UC1);
|
|
std::vector<int> in_vec;
|
|
TypesTest::OCVSumV::run(in_mat, in_vec);
|
|
EXPECT_EQ(std::vector<int>(256,1), in_vec); // self-sanity-check
|
|
|
|
auto opencv_ref = [&](const cv::Mat &in, const std::vector<int> &inv, cv::Mat &out) {
|
|
cv::Mat tmp = in_mat.clone(); // allocate the same amount of memory as graph does
|
|
TypesTest::OCVAddV::run(in, inv, tmp);
|
|
out = tmp - in;
|
|
};
|
|
|
|
// Run pipeline
|
|
auto sc = c.compileStreaming(cv::descr_of(in_mat),
|
|
cv::descr_of(in_vec),
|
|
cv::compile_args(cv::gapi::kernels<TypesTest::OCVAddV>()));
|
|
sc.setSource(cv::gin(in_mat, in_vec));
|
|
sc.start();
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
cv::Mat out_mat;
|
|
EXPECT_TRUE(sc.pull(cv::gout(out_mat)));
|
|
|
|
cv::Mat ref_mat;
|
|
opencv_ref(in_mat, in_vec, ref_mat);
|
|
EXPECT_EQ(0., cv::norm(ref_mat, out_mat, cv::NORM_INF));
|
|
}
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Types, XChangeScalar)
|
|
{
|
|
// This test verifies if Streaming works when pipeline steps
|
|
// (islands) exchange Scalar data.
|
|
|
|
initTestDataPath();
|
|
|
|
cv::GMat in;
|
|
cv::GScalar m = cv::gapi::mean(in);
|
|
cv::GMat tmp = cv::gapi::convertTo(in, CV_32F) - m;
|
|
cv::GMat out = cv::gapi::blur(tmp, cv::Size(3,3));
|
|
cv::GComputation c(cv::GIn(in), cv::GOut(cv::gapi::copy(in),
|
|
cv::gapi::convertTo(out, CV_8U)));
|
|
|
|
auto ocv_ref = [](const cv::Mat &in_mat, cv::Mat &out_mat) {
|
|
cv::Scalar ocv_m = cv::mean(in_mat);
|
|
cv::Mat ocv_tmp;
|
|
in_mat.convertTo(ocv_tmp, CV_32F);
|
|
ocv_tmp -= ocv_m;
|
|
cv::blur(ocv_tmp, ocv_tmp, cv::Size(3,3));
|
|
ocv_tmp.convertTo(out_mat, CV_8U);
|
|
};
|
|
|
|
// Here we want mean & convertTo run on OCV
|
|
// and subC & blur3x3 on Fluid.
|
|
// FIXME: With the current API it looks quite awful:
|
|
auto ocv_kernels = cv::gapi::core::cpu::kernels(); // convertTo
|
|
ocv_kernels.remove<cv::gapi::core::GSubC>();
|
|
|
|
auto fluid_kernels = cv::gapi::combine(cv::gapi::core::fluid::kernels(), // subC
|
|
cv::gapi::imgproc::fluid::kernels()); // box3x3
|
|
fluid_kernels.remove<cv::gapi::core::GConvertTo>();
|
|
fluid_kernels.remove<cv::gapi::core::GMean>();
|
|
|
|
// FIXME: Now
|
|
// - fluid kernels take over ocv kernels (including Copy, SubC, & Box3x3)
|
|
// - selected kernels (which were removed from the fluid package) remain in OCV
|
|
// (ConvertTo + some others)
|
|
// FIXME: This is completely awful. User should easily pick up specific kernels
|
|
// to an empty kernel package to craft his own but not do it via exclusion.
|
|
// Need to expose kernel declarations to public headers to enable kernels<..>()
|
|
// on user side.
|
|
auto kernels = cv::gapi::combine(ocv_kernels, fluid_kernels);
|
|
|
|
// Compile streaming pipeline
|
|
auto sc = c.compileStreaming(cv::GMatDesc{CV_8U,3,cv::Size{768,576}},
|
|
cv::compile_args(cv::gapi::use_only{kernels}));
|
|
sc.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi")));
|
|
sc.start();
|
|
|
|
cv::Mat in_frame;
|
|
cv::Mat out_mat_gapi;
|
|
cv::Mat out_mat_ref;
|
|
|
|
std::size_t num_frames = 0u;
|
|
while (sc.pull(cv::gout(in_frame, out_mat_gapi))) {
|
|
num_frames++;
|
|
ocv_ref(in_frame, out_mat_ref);
|
|
EXPECT_EQ(0., cv::norm(out_mat_gapi, out_mat_ref, cv::NORM_INF));
|
|
}
|
|
EXPECT_LT(0u, num_frames);
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Types, XChangeVector)
|
|
{
|
|
// This test verifies if Streaming works when pipeline steps
|
|
// (islands) exchange Vector data.
|
|
|
|
initTestDataPath();
|
|
|
|
cv::GMat in1, in2;
|
|
cv::GMat in = cv::gapi::crop(in1, cv::Rect{0,0,576,576});
|
|
cv::GScalar m = cv::gapi::mean(in);
|
|
cv::GArray<int> s = TypesTest::SumV::on(in2); // (in2 = eye, so s = [1,0,0,1,..])
|
|
cv::GMat out = TypesTest::AddV::on(in - m, s);
|
|
|
|
cv::GComputation c(cv::GIn(in1, in2), cv::GOut(cv::gapi::copy(in), out));
|
|
|
|
auto ocv_ref = [](const cv::Mat &in_mat1, const cv::Mat &in_mat2, cv::Mat &out_mat) {
|
|
cv::Mat in_roi = in_mat1(cv::Rect{0,0,576,576});
|
|
cv::Scalar ocv_m = cv::mean(in_roi);
|
|
std::vector<int> ocv_v;
|
|
TypesTest::OCVSumV::run(in_mat2, ocv_v);
|
|
|
|
out_mat.create(cv::Size(576,576), CV_8UC3);
|
|
cv::Mat in_tmp = in_roi - ocv_m;
|
|
TypesTest::OCVAddV::run(in_tmp, ocv_v, out_mat);
|
|
};
|
|
|
|
// Let crop/mean/sumV be calculated via OCV,
|
|
// and AddV/subC be calculated via Fluid
|
|
auto ocv_kernels = cv::gapi::core::cpu::kernels();
|
|
ocv_kernels.remove<cv::gapi::core::GSubC>();
|
|
ocv_kernels.include<TypesTest::OCVSumV>();
|
|
|
|
auto fluid_kernels = cv::gapi::core::fluid::kernels();
|
|
fluid_kernels.include<TypesTest::FluidAddV>();
|
|
|
|
// Here OCV takes precedense over Fluid, with SubC & SumV remaining
|
|
// in Fluid.
|
|
auto kernels = cv::gapi::combine(fluid_kernels, ocv_kernels);
|
|
|
|
// Compile streaming pipeline
|
|
cv::Mat in_eye = cv::Mat::eye(cv::Size(576, 576), CV_8UC3);
|
|
auto sc = c.compileStreaming(cv::GMatDesc{CV_8U,3,cv::Size{768,576}},
|
|
cv::GMatDesc{CV_8U,3,cv::Size{576,576}},
|
|
cv::compile_args(cv::gapi::use_only{kernels}));
|
|
sc.setSource(cv::gin(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi")),
|
|
in_eye));
|
|
sc.start();
|
|
|
|
cv::Mat in_frame;
|
|
cv::Mat out_mat_gapi;
|
|
cv::Mat out_mat_ref;
|
|
|
|
std::size_t num_frames = 0u;
|
|
while (sc.pull(cv::gout(in_frame, out_mat_gapi))) {
|
|
num_frames++;
|
|
ocv_ref(in_frame, in_eye, out_mat_ref);
|
|
EXPECT_EQ(0., cv::norm(out_mat_gapi, out_mat_ref, cv::NORM_INF));
|
|
}
|
|
EXPECT_LT(0u, num_frames);
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Types, OutputScalar)
|
|
{
|
|
// This test verifies if Streaming works when pipeline
|
|
// produces scalar data only
|
|
|
|
initTestDataPath();
|
|
|
|
cv::GMat in;
|
|
cv::GScalar out = cv::gapi::mean(in);
|
|
auto sc = cv::GComputation(cv::GIn(in), cv::GOut(out))
|
|
.compileStreaming(cv::GMatDesc{CV_8U,3,cv::Size{768,576}});
|
|
|
|
const auto video_path = findDataFile("cv/video/768x576.avi");
|
|
sc.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(video_path));
|
|
sc.start();
|
|
|
|
cv::VideoCapture cap;
|
|
cap.open(video_path);
|
|
if (!cap.isOpened())
|
|
throw SkipTestException("Video file can not be opened");
|
|
|
|
cv::Mat tmp;
|
|
cv::Scalar out_scl;
|
|
std::size_t num_frames = 0u;
|
|
while (sc.pull(cv::gout(out_scl)))
|
|
{
|
|
num_frames++;
|
|
cap >> tmp;
|
|
cv::Scalar out_ref = cv::mean(tmp);
|
|
EXPECT_EQ(out_ref, out_scl);
|
|
}
|
|
EXPECT_LT(0u, num_frames);
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Types, OutputVector)
|
|
{
|
|
// This test verifies if Streaming works when pipeline
|
|
// produces vector data only
|
|
|
|
initTestDataPath();
|
|
auto pkg = cv::gapi::kernels<TypesTest::OCVSumV>();
|
|
|
|
cv::GMat in1, in2;
|
|
cv::GMat roi = cv::gapi::crop(in2, cv::Rect(3,3,256,256));
|
|
cv::GArray<int> out = TypesTest::SumV::on(cv::gapi::mul(roi, in1));
|
|
auto sc = cv::GComputation(cv::GIn(in1, in2), cv::GOut(out))
|
|
.compileStreaming(cv::GMatDesc{CV_8U,3,cv::Size{256,256}},
|
|
cv::GMatDesc{CV_8U,3,cv::Size{768,576}},
|
|
cv::compile_args(pkg));
|
|
|
|
auto ocv_ref = [](const cv::Mat &ocv_in1,
|
|
const cv::Mat &ocv_in2,
|
|
std::vector<int> &ocv_out) {
|
|
auto ocv_roi = ocv_in2(cv::Rect{3,3,256,256});
|
|
TypesTest::OCVSumV::run(ocv_roi.mul(ocv_in1), ocv_out);
|
|
};
|
|
|
|
cv::Mat in_eye = cv::Mat::eye(cv::Size(256, 256), CV_8UC3);
|
|
const auto video_path = findDataFile("cv/video/768x576.avi");
|
|
sc.setSource(cv::gin(in_eye, gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(video_path)));
|
|
sc.start();
|
|
|
|
cv::VideoCapture cap;
|
|
cap.open(video_path);
|
|
if (!cap.isOpened())
|
|
throw SkipTestException("Video file can not be opened");
|
|
|
|
cv::Mat tmp;
|
|
std::vector<int> ref_vec;
|
|
std::vector<int> out_vec;
|
|
std::size_t num_frames = 0u;
|
|
while (sc.pull(cv::gout(out_vec)))
|
|
{
|
|
num_frames++;
|
|
cap >> tmp;
|
|
ref_vec.clear();
|
|
ocv_ref(in_eye, tmp, ref_vec);
|
|
EXPECT_EQ(ref_vec, out_vec);
|
|
}
|
|
EXPECT_LT(0u, num_frames);
|
|
}
|
|
|
|
G_API_OP(DimsChans,
|
|
<std::tuple<cv::GArray<int>, cv::GOpaque<int>>(cv::GMat)>,
|
|
"test.streaming.dims_chans") {
|
|
static std::tuple<cv::GArrayDesc, cv::GOpaqueDesc> outMeta(const cv::GMatDesc &) {
|
|
return std::make_tuple(cv::empty_array_desc(),
|
|
cv::empty_gopaque_desc());
|
|
}
|
|
};
|
|
|
|
GAPI_OCV_KERNEL(OCVDimsChans, DimsChans) {
|
|
static void run(const cv::Mat &in, std::vector<int> &ov, int &oi) {
|
|
ov = {in.cols, in.rows};
|
|
oi = in.channels();
|
|
}
|
|
};
|
|
|
|
struct GAPI_Streaming_TemplateTypes: ::testing::Test {
|
|
// There was a problem in GStreamingExecutor
|
|
// when outputs were formally not used by the graph
|
|
// but still should be in place as operation need
|
|
// to produce them, and host data type constructors
|
|
// were missing for GArray and GOpaque in this case.
|
|
// This test tests exactly this.
|
|
|
|
GAPI_Streaming_TemplateTypes() {
|
|
// Prepare everything for the test:
|
|
// Graph itself
|
|
blur = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
cv::GMat blur_d = cv::gapi::streaming::desync(blur);
|
|
std::tie(vec, opq) = DimsChans::on(blur_d);
|
|
|
|
// Kernel package
|
|
pkg = cv::gapi::kernels<OCVDimsChans>();
|
|
|
|
// Input mat
|
|
in_mat = cv::Mat::eye(cv::Size(320,240), CV_8UC3);
|
|
}
|
|
|
|
cv::GMat in;
|
|
cv::GMat blur;
|
|
cv::GArray<int> vec;
|
|
cv::GOpaque<int> opq;
|
|
cv::gapi::GKernelPackage pkg;
|
|
cv::Mat in_mat;
|
|
};
|
|
|
|
TEST_F(GAPI_Streaming_TemplateTypes, UnusedVectorIsOK)
|
|
{
|
|
// Declare graph without listing vec as output
|
|
auto sc = cv::GComputation(cv::GIn(in), cv::GOut(blur, opq))
|
|
.compileStreaming(cv::compile_args(pkg));
|
|
sc.setSource(cv::gin(in_mat));
|
|
sc.start();
|
|
|
|
cv::optional<cv::Mat> out_mat;
|
|
cv::optional<int> out_int;
|
|
|
|
int counter = 0;
|
|
while (sc.pull(cv::gout(out_mat, out_int))) {
|
|
if (counter++ == 10) {
|
|
// Stop the test after 10 iterations
|
|
sc.stop();
|
|
break;
|
|
}
|
|
GAPI_Assert(out_mat || out_int);
|
|
if (out_int) {
|
|
EXPECT_EQ( 3, out_int.value());
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_TemplateTypes, UnusedOpaqueIsOK)
|
|
{
|
|
// Declare graph without listing opq as output
|
|
auto sc = cv::GComputation(cv::GIn(in), cv::GOut(blur, vec))
|
|
.compileStreaming(cv::compile_args(pkg));
|
|
sc.setSource(cv::gin(in_mat));
|
|
sc.start();
|
|
|
|
cv::optional<cv::Mat> out_mat;
|
|
cv::optional<std::vector<int> > out_vec;
|
|
|
|
int counter = 0;
|
|
while (sc.pull(cv::gout(out_mat, out_vec))) {
|
|
if (counter++ == 10) {
|
|
// Stop the test after 10 iterations
|
|
sc.stop();
|
|
break;
|
|
}
|
|
GAPI_Assert(out_mat || out_vec);
|
|
if (out_vec) {
|
|
EXPECT_EQ(320, out_vec.value()[0]);
|
|
EXPECT_EQ(240, out_vec.value()[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct GAPI_Streaming_Unit: public ::testing::Test {
|
|
cv::Mat m;
|
|
|
|
cv::GComputation cc;
|
|
cv::GStreamingCompiled sc;
|
|
|
|
cv::GCompiled ref;
|
|
|
|
GAPI_Streaming_Unit()
|
|
: m(cv::Mat::ones(224,224,CV_8UC3))
|
|
, cc([]{
|
|
cv::GMat a, b;
|
|
cv::GMat c = a + b*2;
|
|
return cv::GComputation(cv::GIn(a, b), cv::GOut(c));
|
|
})
|
|
{
|
|
initTestDataPath();
|
|
|
|
const auto a_desc = cv::descr_of(m);
|
|
const auto b_desc = cv::descr_of(m);
|
|
sc = cc.compileStreaming(a_desc, b_desc);
|
|
ref = cc.compile(a_desc, b_desc);
|
|
}
|
|
};
|
|
|
|
// FIXME: (GAPI_Streaming_Types, InputOpaque) test is missing here!
|
|
// FIXME: (GAPI_Streaming_Types, XChangeOpaque) test is missing here!
|
|
// FIXME: (GAPI_Streaming_Types, OutputOpaque) test is missing here!
|
|
|
|
TEST_F(GAPI_Streaming_Unit, TestTwoVideoSourcesFail)
|
|
{
|
|
const auto c_ptr = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi"));
|
|
auto c_desc = cv::GMatDesc{CV_8U,3,{768,576}};
|
|
auto m_desc = cv::descr_of(m);
|
|
|
|
sc = cc.compileStreaming(c_desc, m_desc);
|
|
EXPECT_NO_THROW(sc.setSource(cv::gin(c_ptr, m)));
|
|
|
|
sc = cc.compileStreaming(m_desc, c_desc);
|
|
EXPECT_NO_THROW(sc.setSource(cv::gin(m, c_ptr)));
|
|
|
|
sc = cc.compileStreaming(c_desc, c_desc);
|
|
EXPECT_ANY_THROW(sc.setSource(cv::gin(c_ptr, c_ptr)));
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, TestStartWithoutnSetSource)
|
|
{
|
|
EXPECT_ANY_THROW(sc.start());
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, TestStopWithoutStart1)
|
|
{
|
|
// It is ok!
|
|
EXPECT_NO_THROW(sc.stop());
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, TestStopWithoutStart2)
|
|
{
|
|
// It should be ok as well
|
|
sc.setSource(cv::gin(m, m));
|
|
EXPECT_NO_THROW(sc.stop());
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, StopStartStop)
|
|
{
|
|
cv::Mat out;
|
|
EXPECT_NO_THROW(sc.stop());
|
|
EXPECT_NO_THROW(sc.setSource(cv::gin(m, m)));
|
|
EXPECT_NO_THROW(sc.start());
|
|
|
|
std::size_t i = 0u;
|
|
while (i++ < 10u) {EXPECT_TRUE(sc.pull(cv::gout(out)));};
|
|
|
|
EXPECT_NO_THROW(sc.stop());
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, ImplicitStop)
|
|
{
|
|
EXPECT_NO_THROW(sc.setSource(cv::gin(m, m)));
|
|
EXPECT_NO_THROW(sc.start());
|
|
// No explicit stop here - pipeline stops successfully at the test exit
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, StartStopStart_NoSetSource)
|
|
{
|
|
EXPECT_NO_THROW(sc.setSource(cv::gin(m, m)));
|
|
EXPECT_NO_THROW(sc.start());
|
|
EXPECT_NO_THROW(sc.stop());
|
|
EXPECT_ANY_THROW(sc.start()); // Should fail since setSource was not called
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, StartStopStress_Const)
|
|
{
|
|
// Runs 100 times with no deadlock - assumed stable (robust) enough
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
sc.stop();
|
|
sc.setSource(cv::gin(m, m));
|
|
sc.start();
|
|
cv::Mat out;
|
|
for (int j = 0; j < 5; j++) EXPECT_TRUE(sc.pull(cv::gout(out)));
|
|
}
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, StartStopStress_Video)
|
|
{
|
|
// Runs 100 times with no deadlock - assumed stable (robust) enough
|
|
sc = cc.compileStreaming(cv::GMatDesc{CV_8U,3,cv::Size{768,576}},
|
|
cv::GMatDesc{CV_8U,3,cv::Size{768,576}});
|
|
m = cv::Mat::eye(cv::Size{768,576}, CV_8UC3);
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
auto src = cv::gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(findDataFile("cv/video/768x576.avi"));
|
|
sc.stop();
|
|
sc.setSource(cv::gin(src, m));
|
|
sc.start();
|
|
cv::Mat out;
|
|
for (int j = 0; j < 5; j++) EXPECT_TRUE(sc.pull(cv::gout(out)));
|
|
}
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, PullNoStart)
|
|
{
|
|
sc.setSource(cv::gin(m, m));
|
|
|
|
cv::Mat out;
|
|
EXPECT_ANY_THROW(sc.pull(cv::gout(out)));
|
|
}
|
|
|
|
|
|
TEST_F(GAPI_Streaming_Unit, SetSource_Multi_BeforeStart)
|
|
{
|
|
cv::Mat eye = cv::Mat::eye (224, 224, CV_8UC3);
|
|
cv::Mat zrs = cv::Mat::zeros(224, 224, CV_8UC3);
|
|
|
|
// Call setSource two times, data specified last time
|
|
// should be actually processed.
|
|
sc.setSource(cv::gin(zrs, zrs));
|
|
sc.setSource(cv::gin(eye, eye));
|
|
|
|
// Run the pipeline, acquire result once
|
|
sc.start();
|
|
cv::Mat out, out_ref;
|
|
EXPECT_TRUE(sc.pull(cv::gout(out)));
|
|
sc.stop();
|
|
|
|
// Pipeline should process `eye` mat, not `zrs`
|
|
ref(cv::gin(eye, eye), cv::gout(out_ref));
|
|
EXPECT_EQ(0., cv::norm(out, out_ref, cv::NORM_INF));
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, SetSource_During_Execution)
|
|
{
|
|
cv::Mat zrs = cv::Mat::zeros(224, 224, CV_8UC3);
|
|
|
|
sc.setSource(cv::gin(m, m));
|
|
sc.start();
|
|
EXPECT_ANY_THROW(sc.setSource(cv::gin(zrs, zrs)));
|
|
EXPECT_ANY_THROW(sc.setSource(cv::gin(zrs, zrs)));
|
|
EXPECT_ANY_THROW(sc.setSource(cv::gin(zrs, zrs)));
|
|
sc.stop();
|
|
}
|
|
|
|
TEST_F(GAPI_Streaming_Unit, SetSource_After_Completion)
|
|
{
|
|
sc.setSource(cv::gin(m, m));
|
|
|
|
// Test pipeline with `m` input
|
|
sc.start();
|
|
cv::Mat out, out_ref;
|
|
EXPECT_TRUE(sc.pull(cv::gout(out)));
|
|
sc.stop();
|
|
|
|
// Test against ref
|
|
ref(cv::gin(m, m), cv::gout(out_ref));
|
|
EXPECT_EQ(0., cv::norm(out, out_ref, cv::NORM_INF));
|
|
|
|
// Now set another source
|
|
cv::Mat eye = cv::Mat::eye(224, 224, CV_8UC3);
|
|
sc.setSource(cv::gin(eye, m));
|
|
sc.start();
|
|
EXPECT_TRUE(sc.pull(cv::gout(out)));
|
|
sc.stop();
|
|
|
|
// Test against new ref
|
|
ref(cv::gin(eye, m), cv::gout(out_ref));
|
|
EXPECT_EQ(0., cv::norm(out, out_ref, cv::NORM_INF));
|
|
}
|
|
|
|
// NB: Check pull overload for python
|
|
TEST(Streaming, Python_Pull_Overload)
|
|
{
|
|
cv::GMat in;
|
|
auto out = cv::gapi::copy(in);
|
|
cv::GComputation c(in, out);
|
|
|
|
cv::Size sz(3,3);
|
|
cv::Mat in_mat(sz, CV_8UC3);
|
|
cv::randu(in_mat, cv::Scalar::all(0), cv::Scalar(255));
|
|
|
|
auto ccomp = c.compileStreaming(cv::descr_of(in_mat));
|
|
|
|
EXPECT_TRUE(ccomp);
|
|
EXPECT_FALSE(ccomp.running());
|
|
|
|
ccomp.setSource(cv::gin(in_mat));
|
|
|
|
ccomp.start();
|
|
EXPECT_TRUE(ccomp.running());
|
|
|
|
bool has_output;
|
|
cv::GRunArgs outputs;
|
|
std::tie(has_output, outputs) = ccomp.pull();
|
|
|
|
EXPECT_TRUE(has_output);
|
|
EXPECT_EQ(1u, outputs.size());
|
|
|
|
auto out_mat = cv::util::get<cv::Mat>(outputs[0]);
|
|
EXPECT_EQ(0., cv::norm(in_mat, out_mat, cv::NORM_INF));
|
|
|
|
ccomp.stop();
|
|
EXPECT_FALSE(ccomp.running());
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, SmokeTest_Regular)
|
|
{
|
|
cv::GMat in;
|
|
cv::GMat tmp1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
cv::GMat out1 = cv::gapi::Canny(tmp1, 32, 128, 3);
|
|
|
|
// FIXME: Unary desync should not require tie!
|
|
cv::GMat tmp2 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out2 = tmp2 / cv::gapi::Sobel(tmp2, CV_8U, 1, 1);;
|
|
|
|
cv::Mat test_in = cv::Mat::eye(cv::Size(32,32), CV_8UC3);
|
|
cv::Mat test_out1, test_out2;
|
|
cv::GComputation(cv::GIn(in), cv::GOut(out1, out2))
|
|
.apply(cv::gin(test_in), cv::gout(test_out1, test_out2));
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, SmokeTest_Streaming)
|
|
{
|
|
initTestDataPath();
|
|
|
|
cv::GMat in;
|
|
cv::GMat tmp1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
cv::GMat out1 = cv::gapi::Canny(tmp1, 32, 128, 3);
|
|
|
|
cv::GMat tmp2 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out2 = Delay::on(tmp2,10) / cv::gapi::Sobel(tmp2, CV_8U, 1, 1);
|
|
|
|
auto sc = cv::GComputation(cv::GIn(in), cv::GOut(out1, out2))
|
|
.compileStreaming(cv::compile_args(cv::gapi::kernels<OCVDelay>()));
|
|
auto sc_file = findDataFile("cv/video/768x576.avi");
|
|
auto sc_src = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(sc_file);
|
|
sc.setSource(cv::gin(sc_src));
|
|
sc.start();
|
|
|
|
std::size_t out1_hits = 0u;
|
|
std::size_t out2_hits = 0u;
|
|
cv::optional<cv::Mat> test_out1, test_out2;
|
|
while (sc.pull(cv::gout(test_out1, test_out2))) {
|
|
GAPI_Assert(test_out1 || test_out2);
|
|
if (test_out1) out1_hits++;
|
|
if (test_out2) out2_hits++;
|
|
}
|
|
EXPECT_EQ(100u, out1_hits); // out1 must be available for all frames
|
|
EXPECT_LE(out2_hits, out1_hits); // out2 must appear less times than out1
|
|
std::cout << "Got " << out1_hits << " out1's and " << out2_hits << " out2's" << std::endl;
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, SmokeTest_Streaming_TwoParts)
|
|
{
|
|
initTestDataPath();
|
|
|
|
cv::GMat in;
|
|
cv::GMat tmp1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
cv::GMat out1 = cv::gapi::Canny(tmp1, 32, 128, 3);
|
|
|
|
// Desynchronized path 1
|
|
cv::GMat tmp2 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out2 = tmp2 / cv::gapi::Sobel(tmp2, CV_8U, 1, 1);
|
|
|
|
// Desynchronized path 2
|
|
cv::GMat tmp3 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out3 = 0.5*tmp3 + 0.5*cv::gapi::medianBlur(tmp3, 7);
|
|
|
|
// The code should compile and execute well (desynchronized parts don't cross)
|
|
auto sc = cv::GComputation(cv::GIn(in), cv::GOut(out1, out2, out3))
|
|
.compileStreaming();
|
|
auto sc_file = findDataFile("cv/video/768x576.avi");
|
|
auto sc_src = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(sc_file);
|
|
sc.setSource(cv::gin(sc_src));
|
|
sc.start();
|
|
|
|
std::size_t test_frames = 0u;
|
|
cv::optional<cv::Mat> test_out1, test_out2, test_out3;
|
|
while (sc.pull(cv::gout(test_out1, test_out2, test_out3))) {
|
|
GAPI_Assert(test_out1 || test_out2 || test_out3);
|
|
if (test_out1) {
|
|
// count frames only for synchronized output
|
|
test_frames++;
|
|
}
|
|
}
|
|
EXPECT_EQ(100u, test_frames);
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, Negative_NestedDesync_Tier0)
|
|
{
|
|
cv::GMat in;
|
|
cv::GMat tmp1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
// Desynchronized path 1
|
|
cv::GMat tmp2 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out1 = cv::gapi::medianBlur(tmp2, 3);
|
|
|
|
// Desynchronized path 2, nested from 1 (directly from desync)
|
|
cv::GMat tmp3 = cv::gapi::streaming::desync(tmp2);
|
|
cv::GMat out2 = 0.5*tmp3;
|
|
|
|
// This shouldn't compile
|
|
EXPECT_ANY_THROW(cv::GComputation(cv::GIn(in), cv::GOut(out1, out2))
|
|
.compileStreaming());
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, Negative_NestedDesync_Tier1)
|
|
{
|
|
cv::GMat in;
|
|
cv::GMat tmp1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
// Desynchronized path 1
|
|
cv::GMat tmp2 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out1 = cv::gapi::medianBlur(tmp2, 3);
|
|
|
|
// Desynchronized path 2, nested from 1 (indirectly from desync)
|
|
cv::GMat tmp3 = cv::gapi::streaming::desync(out1);
|
|
cv::GMat out2 = 0.5*tmp3;
|
|
|
|
// This shouldn't compile
|
|
EXPECT_ANY_THROW(cv::GComputation(cv::GIn(in), cv::GOut(out1, out2))
|
|
.compileStreaming());
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, Negative_CrossMainPart_Tier0)
|
|
{
|
|
cv::GMat in;
|
|
cv::GMat tmp1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
// Desynchronized path: depends on both tmp1 and tmp2
|
|
cv::GMat tmp2 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out1 = 0.5*tmp1 + 0.5*tmp2;
|
|
|
|
// This shouldn't compile
|
|
EXPECT_ANY_THROW(cv::GComputation(in, out1).compileStreaming());
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, Negative_CrossMainPart_Tier1)
|
|
{
|
|
cv::GMat in;
|
|
cv::GMat tmp1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
// Desynchronized path: depends on both tmp1 and tmp2
|
|
cv::GMat tmp2 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out1 = 0.5*tmp1 + 0.5*cv::gapi::medianBlur(tmp2, 3);
|
|
|
|
// This shouldn't compile
|
|
EXPECT_ANY_THROW(cv::GComputation(in, out1).compileStreaming());
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, Negative_CrossOtherDesync_Tier0)
|
|
{
|
|
cv::GMat in;
|
|
cv::GMat tmp1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
// Desynchronized path 1
|
|
cv::GMat tmp2 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out1 = 0.5*tmp2;
|
|
|
|
// Desynchronized path 2 (depends on 1)
|
|
cv::GMat tmp3 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out2 = 0.5*tmp3 + tmp2;
|
|
|
|
// This shouldn't compile
|
|
EXPECT_ANY_THROW(cv::GComputation(cv::GIn(in), cv::GOut(out1, out2))
|
|
.compileStreaming());
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, Negative_CrossOtherDesync_Tier1)
|
|
{
|
|
cv::GMat in;
|
|
cv::GMat tmp1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
// Desynchronized path 1
|
|
cv::GMat tmp2 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out1 = 0.5*tmp2;
|
|
|
|
// Desynchronized path 2 (depends on 1)
|
|
cv::GMat tmp3 = cv::gapi::streaming::desync(tmp1);
|
|
cv::GMat out2 = 0.5*cv::gapi::medianBlur(tmp3,3) + 1.0*tmp2;
|
|
|
|
// This shouldn't compile
|
|
EXPECT_ANY_THROW(cv::GComputation(cv::GIn(in), cv::GOut(out1, out2))
|
|
.compileStreaming());
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, Negative_SynchronizedPull)
|
|
{
|
|
initTestDataPath();
|
|
|
|
cv::GMat in;
|
|
cv::GMat out1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
cv::GMat tmp1 = cv::gapi::streaming::desync(out1);
|
|
cv::GMat out2 = 0.5*tmp1;
|
|
|
|
auto sc = cv::GComputation(cv::GIn(in), cv::GOut(out1, out2))
|
|
.compileStreaming();
|
|
|
|
auto sc_file = findDataFile("cv/video/768x576.avi");
|
|
auto sc_src = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(sc_file);
|
|
sc.setSource(cv::gin(sc_src));
|
|
sc.start();
|
|
|
|
cv::Mat o1, o2;
|
|
EXPECT_ANY_THROW(sc.pull(cv::gout(o1, o2)));
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, UseSpecialPull)
|
|
{
|
|
initTestDataPath();
|
|
|
|
cv::GMat in;
|
|
cv::GMat out1 = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
cv::GMat tmp1 = cv::gapi::streaming::desync(out1);
|
|
cv::GMat out2 = 0.5*tmp1;
|
|
|
|
auto sc = cv::GComputation(cv::GIn(in), cv::GOut(out1, out2))
|
|
.compileStreaming();
|
|
|
|
auto sc_file = findDataFile("cv/video/768x576.avi");
|
|
auto sc_src = gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(sc_file);
|
|
sc.setSource(cv::gin(sc_src));
|
|
sc.start();
|
|
|
|
cv::optional<cv::Mat> o1, o2;
|
|
std::size_t num_frames = 0u;
|
|
|
|
while (sc.pull(cv::gout(o1, o2))) {
|
|
if (o1) num_frames++;
|
|
}
|
|
EXPECT_EQ(100u, num_frames);
|
|
}
|
|
|
|
G_API_OP(ProduceVector, <cv::GArray<int>(cv::GMat)>, "test.desync.vector") {
|
|
static cv::GArrayDesc outMeta(const cv::GMatDesc &) {
|
|
return cv::empty_array_desc();
|
|
}
|
|
};
|
|
|
|
G_API_OP(ProduceOpaque, <cv::GOpaque<int>(cv::GMat)>, "test.desync.opaque") {
|
|
static cv::GOpaqueDesc outMeta(const cv::GMatDesc &) {
|
|
return cv::empty_gopaque_desc();
|
|
}
|
|
};
|
|
|
|
GAPI_OCV_KERNEL(OCVVector, ProduceVector) {
|
|
static void run(const cv::Mat& in, std::vector<int> &out) {
|
|
out = {in.cols, in.rows};
|
|
}
|
|
};
|
|
|
|
GAPI_OCV_KERNEL(OCVOpaque, ProduceOpaque) {
|
|
static void run(const cv::Mat &in, int &v) {
|
|
v = in.channels();
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
cv::GStreamingCompiled desyncTestObject() {
|
|
cv::GMat in;
|
|
cv::GMat blur = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
cv::GMat blur_d = cv::gapi::copy(cv::gapi::streaming::desync(blur));
|
|
cv::GMat d1 = Delay::on(blur_d, 10);
|
|
cv::GMat d2 = Delay::on(blur_d, 30);
|
|
|
|
cv::GArray<int> vec = ProduceVector::on(d1);
|
|
cv::GOpaque<int> opq = ProduceOpaque::on(d2);
|
|
|
|
auto pkg = cv::gapi::kernels<OCVDelay, OCVVector, OCVOpaque>();
|
|
return cv::GComputation(cv::GIn(in), cv::GOut(blur, vec, opq))
|
|
.compileStreaming(cv::compile_args(pkg));
|
|
}
|
|
} // anonymous namespace
|
|
|
|
TEST(GAPI_Streaming_Desync, MultipleDesyncOutputs_1) {
|
|
auto sc = desyncTestObject();
|
|
const cv::Mat in_mat = cv::Mat::eye(cv::Size(320,240), CV_8UC3);
|
|
|
|
sc.setSource(cv::gin(in_mat));
|
|
sc.start();
|
|
|
|
cv::optional<cv::Mat> out_mat;
|
|
cv::optional<std::vector<int> > out_vec;
|
|
cv::optional<int> out_int;
|
|
|
|
int counter = 0;
|
|
while (sc.pull(cv::gout(out_mat, out_vec, out_int))) {
|
|
if (counter++ == 1000) {
|
|
// Stop the test after 1000 iterations
|
|
sc.stop();
|
|
break;
|
|
}
|
|
GAPI_Assert(out_mat || out_vec || out_int);
|
|
|
|
// out_vec and out_int are on the same desynchronized path
|
|
// they MUST arrive together. If one is available, the other
|
|
// also must be available.
|
|
if (out_vec) { ASSERT_TRUE(out_int.has_value()); }
|
|
if (out_int) { ASSERT_TRUE(out_vec.has_value()); }
|
|
|
|
if (out_vec || out_int) {
|
|
EXPECT_EQ(320, out_vec.value()[0]);
|
|
EXPECT_EQ(240, out_vec.value()[1]);
|
|
EXPECT_EQ( 3, out_int.value());
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, StartStop_Stress) {
|
|
auto sc = desyncTestObject();
|
|
const cv::Mat in_mat = cv::Mat::eye(cv::Size(320,240), CV_8UC3);
|
|
|
|
cv::optional<cv::Mat> out_mat;
|
|
cv::optional<std::vector<int> > out_vec;
|
|
cv::optional<int> out_int;
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
sc.setSource(cv::gin(in_mat));
|
|
sc.start();
|
|
int counter = 0;
|
|
while (counter++ < 100) {
|
|
sc.pull(cv::gout(out_mat, out_vec, out_int));
|
|
GAPI_Assert(out_mat || out_vec || out_int);
|
|
if (out_vec) { ASSERT_TRUE(out_int.has_value()); }
|
|
if (out_int) { ASSERT_TRUE(out_vec.has_value()); }
|
|
}
|
|
sc.stop();
|
|
}
|
|
}
|
|
|
|
GAPI_FLUID_KERNEL(FluidCopy, cv::gapi::core::GCopy, false) {
|
|
static const int Window = 1;
|
|
|
|
static void run(const cv::gapi::fluid::View &in,
|
|
cv::gapi::fluid::Buffer &out) {
|
|
const uint8_t *in_ptr = in.InLineB(0);
|
|
uint8_t *out_ptr = out.OutLineB(0);
|
|
|
|
const auto in_type = CV_MAKETYPE(in.meta().depth, in.meta().chan);
|
|
const auto out_type = CV_MAKETYPE(out.meta().depth, out.meta().chan);
|
|
GAPI_Assert(in_type == out_type);
|
|
std::copy_n(in_ptr, in.length()*CV_ELEM_SIZE(in_type), out_ptr);
|
|
}
|
|
};
|
|
|
|
|
|
TEST(GAPI_Streaming_Desync, DesyncObjectConsumedByTwoIslandsViaSeparateDesync) {
|
|
// See comment in the implementation of cv::gapi::streaming::desync (.cpp)
|
|
cv::GMat in;
|
|
cv::GMat tmp = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
cv::GMat tmp1 = cv::gapi::streaming::desync(tmp);
|
|
cv::GMat out1 = cv::gapi::copy(tmp1); // ran via Fluid backend
|
|
|
|
cv::GMat tmp2 = cv::gapi::streaming::desync(tmp);
|
|
cv::GMat out2 = tmp2 * 0.5; // ran via OCV backend
|
|
|
|
auto c = cv::GComputation(cv::GIn(in), cv::GOut(out1, out2));
|
|
auto p = cv::gapi::kernels<FluidCopy>();
|
|
|
|
EXPECT_NO_THROW(c.compileStreaming(cv::compile_args(p)));
|
|
}
|
|
|
|
TEST(GAPI_Streaming_Desync, DesyncObjectConsumedByTwoIslandsViaSameDesync) {
|
|
// See comment in the implementation of cv::gapi::streaming::desync (.cpp)
|
|
cv::GMat in;
|
|
cv::GMat tmp = cv::gapi::boxFilter(in, -1, cv::Size(3,3));
|
|
|
|
cv::GMat tmp1 = cv::gapi::streaming::desync(tmp);
|
|
cv::GMat out1 = cv::gapi::copy(tmp1); // ran via Fluid backend
|
|
cv::GMat out2 = out1 - 0.5*tmp1; // ran via OCV backend
|
|
|
|
auto c = cv::GComputation(cv::GIn(in), cv::GOut(out1, out2));
|
|
auto p = cv::gapi::kernels<FluidCopy>();
|
|
|
|
EXPECT_NO_THROW(c.compileStreaming(cv::compile_args(p)));
|
|
}
|
|
|
|
} // namespace opencv_test
|