1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
5 // Copyright (C) 2020 Intel Corporation
7 #include "gapi_ocv_stateful_kernel_test_utils.hpp"
8 #include <opencv2/gapi/cpu/core.hpp>
9 #include <opencv2/gapi/streaming/cap.hpp>
11 #include <opencv2/core.hpp>
12 #include <opencv2/core/cvstd.hpp>
13 #ifdef HAVE_OPENCV_VIDEO
14 #include <opencv2/video.hpp>
20 struct BackSubStateParams
30 template<> struct CompileArgTag<opencv_test::BackSubStateParams>
32 static const char* tag()
34 return "org.opencv.test..background_substractor_state_params";
42 //TODO: test OT, Background Subtractor, Kalman with 3rd version of API
43 //----------------------------------------------- Simple tests ------------------------------------------------
46 inline void initTestDataPath()
49 static bool initialized = false;
52 // Since G-API has no own test data (yet), it is taken from the common space
53 const char* testDataPath = getenv("OPENCV_TEST_DATA_PATH");
54 GAPI_Assert(testDataPath != nullptr);
56 cvtest::addDataSearchPath(testDataPath);
62 G_TYPED_KERNEL(GCountCalls, <cv::GOpaque<int>(GMat)>, "org.opencv.test.count_calls")
64 static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
67 GAPI_OCV_KERNEL_ST(GOCVCountCalls, GCountCalls, int)
69 static void setup(const cv::GMatDesc &/* in */, std::shared_ptr<int> &state)
71 state.reset(new int{ });
74 static void run(const cv::Mat &/* in */, int &out, int& state)
80 G_TYPED_KERNEL(GIsStateUpToDate, <cv::GOpaque<bool>(GMat)>,
81 "org.opencv.test.is_state_up-to-date")
83 static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
86 GAPI_OCV_KERNEL_ST(GOCVIsStateUpToDate, GIsStateUpToDate, cv::Size)
88 static void setup(const cv::GMatDesc &in, std::shared_ptr<cv::Size> &state)
90 state.reset(new cv::Size(in.size));
93 static void run(const cv::Mat &in , bool &out, cv::Size& state)
95 out = in.size() == state;
99 G_TYPED_KERNEL(GStInvalidResize, <GMat(GMat,Size,double,double,int)>,
100 "org.opencv.test.st_invalid_resize")
102 static GMatDesc outMeta(GMatDesc in, Size, double, double, int) { return in; }
105 GAPI_OCV_KERNEL_ST(GOCVStInvalidResize, GStInvalidResize, int)
107 static void setup(const cv::GMatDesc, cv::Size, double, double, int,
108 std::shared_ptr<int> &/* state */)
111 static void run(const cv::Mat& in, cv::Size sz, double fx, double fy, int interp,
112 cv::Mat &out, int& /* state */)
114 cv::resize(in, out, sz, fx, fy, interp);
118 G_TYPED_KERNEL(GBackSub, <GMat(GMat)>, "org.opencv.test.background_substractor")
120 static GMatDesc outMeta(GMatDesc in) { return in.withType(CV_8U, 1); }
122 #ifdef HAVE_OPENCV_VIDEO
123 GAPI_OCV_KERNEL_ST(GOCVBackSub, GBackSub, cv::BackgroundSubtractor)
125 static void setup(const cv::GMatDesc &/* desc */,
126 std::shared_ptr<BackgroundSubtractor> &state,
127 const cv::GCompileArgs &compileArgs)
129 auto sbParams = cv::gapi::getCompileArg<BackSubStateParams>(compileArgs)
130 .value_or(BackSubStateParams { });
132 if (sbParams.method == "knn")
133 state = createBackgroundSubtractorKNN();
134 else if (sbParams.method == "mog2")
135 state = createBackgroundSubtractorMOG2();
140 static void run(const cv::Mat& in, cv::Mat &out, BackgroundSubtractor& state)
142 state.apply(in, out, -1);
148 TEST(StatefulKernel, StateIsMutableInRuntime)
150 constexpr int expectedCallsCount = 10;
152 cv::Mat dummyIn { 1, 1, CV_8UC1 };
153 int actualCallsCount = 0;
155 // Declaration of G-API expression
157 GOpaque<int> out = GCountCalls::on(in);
158 cv::GComputation comp(cv::GIn(in), cv::GOut(out));
160 const auto pkg = cv::gapi::kernels<GOCVCountCalls>();
162 // Compilation of G-API expression
163 auto callsCounter = comp.compile(cv::descr_of(dummyIn), cv::compile_args(pkg));
165 // Simulating video stream: call GCompiled multiple times
166 for (int i = 0; i < expectedCallsCount; i++)
168 callsCounter(cv::gin(dummyIn), cv::gout(actualCallsCount));
169 EXPECT_EQ(i + 1, actualCallsCount);
172 // End of "video stream"
173 EXPECT_EQ(expectedCallsCount, actualCallsCount);
175 // User asks G-API to prepare for a new stream
176 callsCounter.prepareForNewStream();
177 callsCounter(cv::gin(dummyIn), cv::gout(actualCallsCount));
178 EXPECT_EQ(1, actualCallsCount);
182 TEST(StatefulKernel, StateIsAutoResetForNewStream)
187 cv::GOpaque<bool> out = GIsStateUpToDate::on(in);
188 cv::GComputation c(cv::GIn(in), cv::GOut(out));
190 const auto pkg = cv::gapi::kernels<GOCVIsStateUpToDate>();
192 // Compilation & testing
193 auto ccomp = c.compileStreaming(cv::compile_args(pkg));
195 ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>
196 (findDataFile("cv/video/768x576.avi")));
198 EXPECT_TRUE(ccomp.running());
200 // Process the full video
201 bool isStateUpToDate = false;
202 while (ccomp.pull(cv::gout(isStateUpToDate))) {
203 EXPECT_TRUE(isStateUpToDate);
205 EXPECT_FALSE(ccomp.running());
207 ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>
208 (findDataFile("cv/video/1920x1080.avi")));
210 EXPECT_TRUE(ccomp.running());
212 while (ccomp.pull(cv::gout(isStateUpToDate))) {
213 EXPECT_TRUE(isStateUpToDate);
215 EXPECT_FALSE(ccomp.running());
218 TEST(StatefulKernel, InvalidReallocatingKernel)
221 cv::Mat in_mat(500, 500, CV_8UC1), out_mat;
222 out = GStInvalidResize::on(in, cv::Size(300, 300), 0.0, 0.0, cv::INTER_LINEAR);
224 const auto pkg = cv::gapi::kernels<GOCVStInvalidResize>();
225 cv::GComputation comp(cv::GIn(in), cv::GOut(out));
227 EXPECT_THROW(comp.apply(in_mat, out_mat, cv::compile_args(pkg)), std::logic_error);
230 #ifdef HAVE_OPENCV_VIDEO
233 void compareBackSubResults(const cv::Mat &actual, const cv::Mat &expected,
234 const int diffPercent)
236 GAPI_Assert(actual.size() == expected.size());
237 int allowedNumDiffPixels = actual.size().area() * diffPercent / 100;
240 cv::absdiff(actual, expected, diff);
242 cv::Mat hist(256, 1, CV_32FC1, cv::Scalar(0));
243 const float range[] { 0, 256 };
244 const float *histRange { range };
245 calcHist(&diff, 1, 0, Mat(), hist, 1, &hist.rows, &histRange, true, false);
246 for (int i = 2; i < hist.rows; ++i)
248 hist.at<float>(i) += hist.at<float>(i - 1);
251 int numDiffPixels = static_cast<int>(hist.at<float>(255));
253 EXPECT_GT(allowedNumDiffPixels, numDiffPixels);
255 } // anonymous namespace
257 TEST(StatefulKernel, StateIsInitViaCompArgs)
259 cv::Mat frame(1080, 1920, CV_8UC3),
263 cv::randu(frame, cv::Scalar(0, 0, 0), cv::Scalar(255, 255, 255));
267 cv::GMat out = GBackSub::on(in);
268 cv::GComputation c(cv::GIn(in), cv::GOut(out));
270 const auto pkg = cv::gapi::kernels<GOCVBackSub>();
272 auto gapiBackSub = c.compile(cv::descr_of(frame),
273 cv::compile_args(pkg, BackSubStateParams { "knn" }));
275 gapiBackSub(cv::gin(frame), cv::gout(gapiForeground));
278 auto pOcvBackSub = createBackgroundSubtractorKNN();
279 pOcvBackSub->apply(frame, ocvForeground);
282 // Allowing 1% difference of all pixels between G-API and OpenCV results
283 compareBackSubResults(gapiForeground, ocvForeground, 1);
285 // Additionally, test the case where state is resetted
286 gapiBackSub.prepareForNewStream();
287 gapiBackSub(cv::gin(frame), cv::gout(gapiForeground));
288 pOcvBackSub->apply(frame, ocvForeground);
289 compareBackSubResults(gapiForeground, ocvForeground, 1);
293 #ifdef HAVE_OPENCV_VIDEO
296 void testBackSubInStreaming(cv::GStreamingCompiled gapiBackSub, const int diffPercent)
303 EXPECT_TRUE(gapiBackSub.running());
305 // OpenCV reference substractor
306 auto pOCVBackSub = createBackgroundSubtractorKNN();
308 // Comparison of G-API and OpenCV substractors
309 std::size_t frames = 0u;
310 while (gapiBackSub.pull(cv::gout(frame, gapiForeground))) {
311 pOCVBackSub->apply(frame, ocvForeground, -1);
313 compareBackSubResults(gapiForeground, ocvForeground, diffPercent);
317 EXPECT_LT(0u, frames);
318 EXPECT_FALSE(gapiBackSub.running());
320 } // anonymous namespace
322 TEST(StatefulKernel, StateIsInitViaCompArgsInStreaming)
326 // G-API graph declaration
328 cv::GMat out = GBackSub::on(in);
329 // Preserving 'in' in output to have possibility to compare with OpenCV reference
330 cv::GComputation c(cv::GIn(in), cv::GOut(cv::gapi::copy(in), out));
332 // G-API compilation of graph for streaming mode
333 const auto pkg = cv::gapi::kernels<GOCVBackSub>();
334 auto gapiBackSub = c.compileStreaming(
335 cv::compile_args(pkg, BackSubStateParams { "knn" }));
337 // Testing G-API Background Substractor in streaming mode
338 gapiBackSub.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>
339 (findDataFile("cv/video/768x576.avi")));
340 // Allowing 1% difference of all pixels between G-API and reference OpenCV results
341 testBackSubInStreaming(gapiBackSub, 1);
343 // Additionally, test the case when the new stream happens
344 gapiBackSub.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>
345 (findDataFile("cv/video/1920x1080.avi")));
346 // Allowing 5% difference of all pixels between G-API and reference OpenCV results
347 testBackSubInStreaming(gapiBackSub, 5);
350 //-------------------------------------------------------------------------------------------------------------
353 //------------------------------------------- Typed tests on setup() ------------------------------------------
356 template<typename Tuple>
357 struct SetupStateTypedTest : public ::testing::Test
359 using StateT = typename std::tuple_element<0, Tuple>::type;
360 using SetupT = typename std::tuple_element<1, Tuple>::type;
362 G_TYPED_KERNEL(GReturnState, <cv::GOpaque<StateT>(GMat)>, "org.opencv.test.return_state")
364 static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
367 GAPI_OCV_KERNEL_ST(GOCVReturnState, GReturnState, StateT)
369 static void setup(const cv::GMatDesc &/* in */, std::shared_ptr<StateT> &state)
371 // Don't use input cv::GMatDesc intentionally
372 state.reset(new StateT(SetupT::value()));
375 static void run(const cv::Mat &/* in */, StateT &out, StateT& state)
382 TYPED_TEST_CASE_P(SetupStateTypedTest);
386 TYPED_TEST_P(SetupStateTypedTest, ReturnInitializedState)
388 using StateType = typename TestFixture::StateT;
389 using SetupType = typename TestFixture::SetupT;
391 cv::Mat dummyIn { 1, 1, CV_8UC1 };
392 StateType retState { };
395 auto out = TestFixture::GReturnState::on(in);
396 cv::GComputation comp(cv::GIn(in), cv::GOut(out));
398 const auto pkg = cv::gapi::kernels<typename TestFixture::GOCVReturnState>();
399 comp.apply(cv::gin(dummyIn), cv::gout(retState), cv::compile_args(pkg));
401 EXPECT_EQ(SetupType::value(), retState);
404 REGISTER_TYPED_TEST_CASE_P(SetupStateTypedTest,
405 ReturnInitializedState);
408 DEFINE_INITIALIZER(CharValue, char, 'z');
409 DEFINE_INITIALIZER(IntValue, int, 7);
410 DEFINE_INITIALIZER(FloatValue, float, 42.f);
411 DEFINE_INITIALIZER(UcharPtrValue, uchar*, nullptr);
414 using Std3IntArray = std::array<int, 3>;
416 DEFINE_INITIALIZER(StdArrayValue, Std3IntArray, { 1, 2, 3 });
417 DEFINE_INITIALIZER(UserValue, UserStruct, { 5, 7.f });
419 using TypesToVerify = ::testing::Types<std::tuple<char, CharValue>,
420 std::tuple<int, IntValue>,
421 std::tuple<float, FloatValue>,
422 std::tuple<uchar*, UcharPtrValue>,
423 std::tuple<std::array<int, 3>, StdArrayValue>,
424 std::tuple<UserStruct, UserValue>>;
426 INSTANTIATE_TYPED_TEST_CASE_P(SetupStateTypedInst, SetupStateTypedTest, TypesToVerify);
427 //-------------------------------------------------------------------------------------------------------------