75ca7989e09697aca4336b15fb1e9f906371c668
[platform/upstream/opencv.git] / modules / gapi / test / cpu / gapi_ocv_stateful_kernel_tests.cpp
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.
4 //
5 // Copyright (C) 2020 Intel Corporation
6
7 #include "gapi_ocv_stateful_kernel_test_utils.hpp"
8 #include <opencv2/gapi/cpu/core.hpp>
9 #include <opencv2/gapi/streaming/cap.hpp>
10
11 #include <opencv2/core.hpp>
12 #include <opencv2/core/cvstd.hpp>
13 #ifdef HAVE_OPENCV_VIDEO
14 #include <opencv2/video.hpp>
15 #endif
16
17
18 namespace opencv_test
19 {
20     struct BackSubStateParams
21     {
22         std::string method;
23     };
24 }
25
26 namespace cv
27 {
28     namespace detail
29     {
30         template<> struct CompileArgTag<opencv_test::BackSubStateParams>
31         {
32             static const char* tag()
33             {
34                 return "org.opencv.test..background_substractor_state_params";
35             }
36         };
37     }
38 }
39
40 namespace opencv_test
41 {
42 //TODO: test OT, Background Subtractor, Kalman with 3rd version of API
43 //----------------------------------------------- Simple tests ------------------------------------------------
44 namespace
45 {
46     inline void initTestDataPath()
47     {
48 #ifndef WINRT
49         static bool initialized = false;
50         if (!initialized)
51         {
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);
55
56             cvtest::addDataSearchPath(testDataPath);
57             initialized = true;
58         }
59 #endif // WINRT
60     }
61
62     G_TYPED_KERNEL(GCountCalls, <cv::GOpaque<int>(GMat)>, "org.opencv.test.count_calls")
63     {
64         static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
65     };
66
67     GAPI_OCV_KERNEL_ST(GOCVCountCalls, GCountCalls, int)
68     {
69         static void setup(const cv::GMatDesc &/* in */, std::shared_ptr<int> &state)
70         {
71             state.reset(new int{  });
72         }
73
74         static void run(const cv::Mat &/* in */, int &out, int& state)
75         {
76             out = ++state;
77         }
78     };
79
80     G_TYPED_KERNEL(GIsStateUpToDate, <cv::GOpaque<bool>(GMat)>,
81                    "org.opencv.test.is_state_up-to-date")
82     {
83         static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
84     };
85
86     GAPI_OCV_KERNEL_ST(GOCVIsStateUpToDate, GIsStateUpToDate, cv::Size)
87     {
88         static void setup(const cv::GMatDesc &in, std::shared_ptr<cv::Size> &state)
89         {
90             state.reset(new cv::Size(in.size));
91         }
92
93         static void run(const cv::Mat &in , bool &out, cv::Size& state)
94         {
95             out = in.size() == state;
96         }
97     };
98
99     G_TYPED_KERNEL(GStInvalidResize, <GMat(GMat,Size,double,double,int)>,
100                    "org.opencv.test.st_invalid_resize")
101     {
102          static GMatDesc outMeta(GMatDesc in, Size, double, double, int) { return in; }
103     };
104
105     GAPI_OCV_KERNEL_ST(GOCVStInvalidResize, GStInvalidResize, int)
106     {
107         static void setup(const cv::GMatDesc, cv::Size, double, double, int,
108                           std::shared_ptr<int> &/* state */)
109         {  }
110
111         static void run(const cv::Mat& in, cv::Size sz, double fx, double fy, int interp,
112                         cv::Mat &out, int& /* state */)
113         {
114             cv::resize(in, out, sz, fx, fy, interp);
115         }
116     };
117
118     G_TYPED_KERNEL(GBackSub, <GMat(GMat)>, "org.opencv.test.background_substractor")
119     {
120          static GMatDesc outMeta(GMatDesc in) { return in.withType(CV_8U, 1); }
121     };
122 #ifdef HAVE_OPENCV_VIDEO
123     GAPI_OCV_KERNEL_ST(GOCVBackSub, GBackSub, cv::BackgroundSubtractor)
124     {
125         static void setup(const cv::GMatDesc &/* desc */,
126                           std::shared_ptr<BackgroundSubtractor> &state,
127                           const cv::GCompileArgs &compileArgs)
128         {
129             auto sbParams = cv::gapi::getCompileArg<BackSubStateParams>(compileArgs)
130                                 .value_or(BackSubStateParams { });
131
132             if (sbParams.method == "knn")
133                 state = createBackgroundSubtractorKNN();
134             else if (sbParams.method == "mog2")
135                 state = createBackgroundSubtractorMOG2();
136
137             GAPI_Assert(state);
138         }
139
140         static void run(const cv::Mat& in, cv::Mat &out, BackgroundSubtractor& state)
141         {
142             state.apply(in, out, -1);
143         }
144     };
145 #endif
146 };
147
148 TEST(StatefulKernel, StateIsMutableInRuntime)
149 {
150     constexpr int expectedCallsCount = 10;
151
152     cv::Mat dummyIn { 1, 1, CV_8UC1 };
153     int actualCallsCount = 0;
154
155     // Declaration of G-API expression
156     GMat in;
157     GOpaque<int> out = GCountCalls::on(in);
158     cv::GComputation comp(cv::GIn(in), cv::GOut(out));
159
160     const auto pkg = cv::gapi::kernels<GOCVCountCalls>();
161
162     // Compilation of G-API expression
163     auto callsCounter = comp.compile(cv::descr_of(dummyIn), cv::compile_args(pkg));
164
165     // Simulating video stream: call GCompiled multiple times
166     for (int i = 0; i < expectedCallsCount; i++)
167     {
168         callsCounter(cv::gin(dummyIn), cv::gout(actualCallsCount));
169         EXPECT_EQ(i + 1, actualCallsCount);
170     }
171
172     // End of "video stream"
173     EXPECT_EQ(expectedCallsCount, actualCallsCount);
174
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);
179
180 }
181
182 TEST(StatefulKernel, StateIsAutoResetForNewStream)
183 {
184     initTestDataPath();
185
186     cv::GMat in;
187     cv::GOpaque<bool> out = GIsStateUpToDate::on(in);
188     cv::GComputation c(cv::GIn(in), cv::GOut(out));
189
190     const auto pkg = cv::gapi::kernels<GOCVIsStateUpToDate>();
191
192     // Compilation & testing
193     auto ccomp = c.compileStreaming(cv::compile_args(pkg));
194
195     ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>
196                                (findDataFile("cv/video/768x576.avi")));
197     ccomp.start();
198     EXPECT_TRUE(ccomp.running());
199
200     // Process the full video
201     bool isStateUpToDate = false;
202     while (ccomp.pull(cv::gout(isStateUpToDate))) {
203         EXPECT_TRUE(isStateUpToDate);
204     }
205     EXPECT_FALSE(ccomp.running());
206
207     ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>
208                                (findDataFile("cv/video/1920x1080.avi")));
209     ccomp.start();
210     EXPECT_TRUE(ccomp.running());
211
212     while (ccomp.pull(cv::gout(isStateUpToDate))) {
213         EXPECT_TRUE(isStateUpToDate);
214     }
215     EXPECT_FALSE(ccomp.running());
216 }
217
218 TEST(StatefulKernel, InvalidReallocatingKernel)
219 {
220     cv::GMat in, out;
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);
223
224     const auto pkg = cv::gapi::kernels<GOCVStInvalidResize>();
225     cv::GComputation comp(cv::GIn(in), cv::GOut(out));
226
227     EXPECT_THROW(comp.apply(in_mat, out_mat, cv::compile_args(pkg)), std::logic_error);
228 }
229
230 #ifdef HAVE_OPENCV_VIDEO
231 namespace
232 {
233     void compareBackSubResults(const cv::Mat &actual, const cv::Mat &expected,
234                                const int diffPercent)
235     {
236         GAPI_Assert(actual.size() == expected.size());
237         int allowedNumDiffPixels = actual.size().area() * diffPercent / 100;
238
239         cv::Mat diff;
240         cv::absdiff(actual, expected, diff);
241
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)
247         {
248             hist.at<float>(i) += hist.at<float>(i - 1);
249         }
250
251         int numDiffPixels = static_cast<int>(hist.at<float>(255));
252
253         EXPECT_GT(allowedNumDiffPixels, numDiffPixels);
254     }
255 } // anonymous namespace
256
257 TEST(StatefulKernel, StateIsInitViaCompArgs)
258 {
259     cv::Mat frame(1080, 1920, CV_8UC3),
260             gapiForeground,
261             ocvForeground;
262
263     cv::randu(frame, cv::Scalar(0, 0, 0), cv::Scalar(255, 255, 255));
264
265     // G-API code
266     cv::GMat in;
267     cv::GMat out = GBackSub::on(in);
268     cv::GComputation c(cv::GIn(in), cv::GOut(out));
269
270     const auto pkg = cv::gapi::kernels<GOCVBackSub>();
271
272     auto gapiBackSub = c.compile(cv::descr_of(frame),
273                                  cv::compile_args(pkg, BackSubStateParams { "knn" }));
274
275     gapiBackSub(cv::gin(frame), cv::gout(gapiForeground));
276
277     // OpenCV code
278     auto pOcvBackSub = createBackgroundSubtractorKNN();
279     pOcvBackSub->apply(frame, ocvForeground);
280
281     // Comparison
282     // Allowing 1% difference of all pixels between G-API and OpenCV results
283     compareBackSubResults(gapiForeground, ocvForeground, 1);
284
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);
290 }
291 #endif
292
293 #ifdef HAVE_OPENCV_VIDEO
294 namespace
295 {
296     void testBackSubInStreaming(cv::GStreamingCompiled gapiBackSub, const int diffPercent)
297     {
298         cv::Mat frame,
299                 gapiForeground,
300                 ocvForeground;
301
302         gapiBackSub.start();
303         EXPECT_TRUE(gapiBackSub.running());
304
305         // OpenCV reference substractor
306         auto pOCVBackSub = createBackgroundSubtractorKNN();
307
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);
312
313             compareBackSubResults(gapiForeground, ocvForeground, diffPercent);
314
315             frames++;
316         }
317         EXPECT_LT(0u, frames);
318         EXPECT_FALSE(gapiBackSub.running());
319     }
320 } // anonymous namespace
321
322 TEST(StatefulKernel, StateIsInitViaCompArgsInStreaming)
323 {
324     initTestDataPath();
325
326     // G-API graph declaration
327     cv::GMat in;
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));
331
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" }));
336
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);
342
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);
348 }
349 #endif
350 //-------------------------------------------------------------------------------------------------------------
351
352
353 //------------------------------------------- Typed tests on setup() ------------------------------------------
354 namespace
355 {
356 template<typename Tuple>
357 struct SetupStateTypedTest : public ::testing::Test
358 {
359     using StateT = typename std::tuple_element<0, Tuple>::type;
360     using SetupT = typename std::tuple_element<1, Tuple>::type;
361
362     G_TYPED_KERNEL(GReturnState, <cv::GOpaque<StateT>(GMat)>, "org.opencv.test.return_state")
363     {
364         static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
365     };
366
367     GAPI_OCV_KERNEL_ST(GOCVReturnState, GReturnState, StateT)
368     {
369         static void setup(const cv::GMatDesc &/* in */, std::shared_ptr<StateT> &state)
370         {
371             // Don't use input cv::GMatDesc intentionally
372             state.reset(new StateT(SetupT::value()));
373         }
374
375         static void run(const cv::Mat &/* in */, StateT &out, StateT& state)
376         {
377             out = state;
378         }
379     };
380 };
381
382 TYPED_TEST_CASE_P(SetupStateTypedTest);
383 } // namespace
384
385
386 TYPED_TEST_P(SetupStateTypedTest, ReturnInitializedState)
387 {
388     using StateType = typename TestFixture::StateT;
389     using SetupType = typename TestFixture::SetupT;
390
391     cv::Mat dummyIn { 1, 1, CV_8UC1 };
392     StateType retState { };
393
394     GMat in;
395     auto out = TestFixture::GReturnState::on(in);
396     cv::GComputation comp(cv::GIn(in), cv::GOut(out));
397
398     const auto pkg = cv::gapi::kernels<typename TestFixture::GOCVReturnState>();
399     comp.apply(cv::gin(dummyIn), cv::gout(retState), cv::compile_args(pkg));
400
401     EXPECT_EQ(SetupType::value(), retState);
402 }
403
404 REGISTER_TYPED_TEST_CASE_P(SetupStateTypedTest,
405                            ReturnInitializedState);
406
407
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);
412 namespace
413 {
414 using Std3IntArray = std::array<int, 3>;
415 }
416 DEFINE_INITIALIZER(StdArrayValue, Std3IntArray, { 1, 2, 3 });
417 DEFINE_INITIALIZER(UserValue, UserStruct, { 5, 7.f });
418
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>>;
425
426 INSTANTIATE_TYPED_TEST_CASE_P(SetupStateTypedInst, SetupStateTypedTest, TypesToVerify);
427 //-------------------------------------------------------------------------------------------------------------
428
429 } // opencv_test