gapi_ocv_stateful_kernel_tests.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  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. #include "gapi_ocv_stateful_kernel_test_utils.hpp"
  7. #include <opencv2/gapi/cpu/core.hpp>
  8. #include <opencv2/gapi/streaming/cap.hpp>
  9. #include <opencv2/core.hpp>
  10. #include <opencv2/core/cvstd.hpp>
  11. #ifdef HAVE_OPENCV_VIDEO
  12. #include <opencv2/video.hpp>
  13. #endif
  14. namespace opencv_test
  15. {
  16. struct BackSubStateParams
  17. {
  18. std::string method;
  19. };
  20. } // namespace opencv_test
  21. namespace cv
  22. {
  23. namespace detail
  24. {
  25. template<> struct CompileArgTag<opencv_test::BackSubStateParams>
  26. {
  27. static const char* tag()
  28. {
  29. return "org.opencv.test.background_substractor_state_params";
  30. }
  31. };
  32. } // namespace detail
  33. } // namespace cv
  34. namespace opencv_test
  35. {
  36. //TODO: test OT, Background Subtractor, Kalman with 3rd version of API
  37. //----------------------------------------------- Simple tests ------------------------------------------------
  38. namespace
  39. {
  40. G_TYPED_KERNEL(GCountCalls, <cv::GOpaque<int>(GMat)>, "org.opencv.test.count_calls")
  41. {
  42. static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
  43. };
  44. GAPI_OCV_KERNEL_ST(GOCVCountCalls, GCountCalls, int)
  45. {
  46. static void setup(const cv::GMatDesc &/* in */, std::shared_ptr<int> &state)
  47. {
  48. state.reset(new int{ });
  49. }
  50. static void run(const cv::Mat &/* in */, int &out, int& state)
  51. {
  52. out = ++state;
  53. }
  54. };
  55. G_TYPED_KERNEL(GIsStateUpToDate, <cv::GOpaque<bool>(GMat)>,
  56. "org.opencv.test.is_state_up-to-date")
  57. {
  58. static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
  59. };
  60. GAPI_OCV_KERNEL_ST(GOCVIsStateUpToDate, GIsStateUpToDate, cv::Size)
  61. {
  62. static void setup(const cv::GMatDesc &in, std::shared_ptr<cv::Size> &state)
  63. {
  64. state.reset(new cv::Size(in.size));
  65. }
  66. static void run(const cv::Mat &in , bool &out, cv::Size& state)
  67. {
  68. out = in.size() == state;
  69. }
  70. };
  71. G_TYPED_KERNEL(GStInvalidResize, <GMat(GMat,Size,double,double,int)>,
  72. "org.opencv.test.st_invalid_resize")
  73. {
  74. static GMatDesc outMeta(GMatDesc in, Size, double, double, int) { return in; }
  75. };
  76. GAPI_OCV_KERNEL_ST(GOCVStInvalidResize, GStInvalidResize, int)
  77. {
  78. static void setup(const cv::GMatDesc, cv::Size, double, double, int,
  79. std::shared_ptr<int> &/* state */)
  80. { }
  81. static void run(const cv::Mat& in, cv::Size sz, double fx, double fy, int interp,
  82. cv::Mat &out, int& /* state */)
  83. {
  84. cv::resize(in, out, sz, fx, fy, interp);
  85. }
  86. };
  87. G_TYPED_KERNEL(GBackSub, <GMat(GMat)>, "org.opencv.test.background_substractor")
  88. {
  89. static GMatDesc outMeta(GMatDesc in) { return in.withType(CV_8U, 1); }
  90. };
  91. #ifdef HAVE_OPENCV_VIDEO
  92. GAPI_OCV_KERNEL_ST(GOCVBackSub, GBackSub, cv::BackgroundSubtractor)
  93. {
  94. static void setup(const cv::GMatDesc &/* desc */,
  95. std::shared_ptr<BackgroundSubtractor> &state,
  96. const cv::GCompileArgs &compileArgs)
  97. {
  98. auto sbParams = cv::gapi::getCompileArg<BackSubStateParams>(compileArgs)
  99. .value_or(BackSubStateParams { });
  100. if (sbParams.method == "knn")
  101. state = createBackgroundSubtractorKNN();
  102. else if (sbParams.method == "mog2")
  103. state = createBackgroundSubtractorMOG2();
  104. GAPI_Assert(state);
  105. }
  106. static void run(const cv::Mat& in, cv::Mat &out, BackgroundSubtractor& state)
  107. {
  108. state.apply(in, out, -1);
  109. }
  110. };
  111. #endif
  112. };
  113. TEST(StatefulKernel, StateIsMutableInRuntime)
  114. {
  115. constexpr int expectedCallsCount = 10;
  116. cv::Mat dummyIn { 1, 1, CV_8UC1 };
  117. int actualCallsCount = 0;
  118. // Declaration of G-API expression
  119. GMat in;
  120. GOpaque<int> out = GCountCalls::on(in);
  121. cv::GComputation comp(cv::GIn(in), cv::GOut(out));
  122. const auto pkg = cv::gapi::kernels<GOCVCountCalls>();
  123. // Compilation of G-API expression
  124. auto callsCounter = comp.compile(cv::descr_of(dummyIn), cv::compile_args(pkg));
  125. // Simulating video stream: call GCompiled multiple times
  126. for (int i = 0; i < expectedCallsCount; i++)
  127. {
  128. callsCounter(cv::gin(dummyIn), cv::gout(actualCallsCount));
  129. EXPECT_EQ(i + 1, actualCallsCount);
  130. }
  131. // End of "video stream"
  132. EXPECT_EQ(expectedCallsCount, actualCallsCount);
  133. // User asks G-API to prepare for a new stream
  134. callsCounter.prepareForNewStream();
  135. callsCounter(cv::gin(dummyIn), cv::gout(actualCallsCount));
  136. EXPECT_EQ(1, actualCallsCount);
  137. }
  138. TEST(StatefulKernel, StateIsAutoResetForNewStream)
  139. {
  140. cv::GMat in;
  141. cv::GOpaque<bool> out = GIsStateUpToDate::on(in);
  142. cv::GComputation c(cv::GIn(in), cv::GOut(out));
  143. const auto pkg = cv::gapi::kernels<GOCVIsStateUpToDate>();
  144. // Compilation & testing
  145. auto ccomp = c.compileStreaming(cv::compile_args(pkg));
  146. auto path = findDataFile("cv/video/768x576.avi");
  147. try {
  148. ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(path));
  149. } catch(...) {
  150. throw SkipTestException("Video file can not be opened");
  151. }
  152. ccomp.start();
  153. EXPECT_TRUE(ccomp.running());
  154. // Process the full video
  155. bool isStateUpToDate = false;
  156. while (ccomp.pull(cv::gout(isStateUpToDate))) {
  157. EXPECT_TRUE(isStateUpToDate);
  158. }
  159. EXPECT_FALSE(ccomp.running());
  160. path = findDataFile("cv/video/1920x1080.avi");
  161. try {
  162. ccomp.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(path));
  163. } catch(...) {
  164. throw SkipTestException("Video file can not be opened");
  165. }
  166. ccomp.start();
  167. EXPECT_TRUE(ccomp.running());
  168. while (ccomp.pull(cv::gout(isStateUpToDate))) {
  169. EXPECT_TRUE(isStateUpToDate);
  170. }
  171. EXPECT_FALSE(ccomp.running());
  172. }
  173. TEST(StatefulKernel, InvalidReallocatingKernel)
  174. {
  175. cv::GMat in, out;
  176. cv::Mat in_mat(500, 500, CV_8UC1), out_mat;
  177. out = GStInvalidResize::on(in, cv::Size(300, 300), 0.0, 0.0, cv::INTER_LINEAR);
  178. const auto pkg = cv::gapi::kernels<GOCVStInvalidResize>();
  179. cv::GComputation comp(cv::GIn(in), cv::GOut(out));
  180. EXPECT_THROW(comp.apply(in_mat, out_mat, cv::compile_args(pkg)), std::logic_error);
  181. }
  182. #ifdef HAVE_OPENCV_VIDEO
  183. namespace
  184. {
  185. void compareBackSubResults(const cv::Mat &actual, const cv::Mat &expected,
  186. const int diffPercent)
  187. {
  188. GAPI_Assert(actual.size() == expected.size());
  189. int allowedNumDiffPixels = actual.size().area() * diffPercent / 100;
  190. cv::Mat diff;
  191. cv::absdiff(actual, expected, diff);
  192. cv::Mat hist(256, 1, CV_32FC1, cv::Scalar(0));
  193. const float range[] { 0, 256 };
  194. const float *histRange { range };
  195. calcHist(&diff, 1, 0, Mat(), hist, 1, &hist.rows, &histRange, true, false);
  196. for (int i = 2; i < hist.rows; ++i)
  197. {
  198. hist.at<float>(i) += hist.at<float>(i - 1);
  199. }
  200. int numDiffPixels = static_cast<int>(hist.at<float>(255));
  201. EXPECT_GT(allowedNumDiffPixels, numDiffPixels);
  202. }
  203. } // anonymous namespace
  204. TEST(StatefulKernel, StateIsInitViaCompArgs)
  205. {
  206. cv::Mat frame(1080, 1920, CV_8UC3),
  207. gapiForeground,
  208. ocvForeground;
  209. cv::randu(frame, cv::Scalar(0, 0, 0), cv::Scalar(255, 255, 255));
  210. // G-API code
  211. cv::GMat in;
  212. cv::GMat out = GBackSub::on(in);
  213. cv::GComputation c(cv::GIn(in), cv::GOut(out));
  214. const auto pkg = cv::gapi::kernels<GOCVBackSub>();
  215. auto gapiBackSub = c.compile(cv::descr_of(frame),
  216. cv::compile_args(pkg, BackSubStateParams { "knn" }));
  217. gapiBackSub(cv::gin(frame), cv::gout(gapiForeground));
  218. // OpenCV code
  219. auto pOcvBackSub = createBackgroundSubtractorKNN();
  220. pOcvBackSub->apply(frame, ocvForeground);
  221. // Comparison
  222. // Allowing 1% difference of all pixels between G-API and OpenCV results
  223. compareBackSubResults(gapiForeground, ocvForeground, 1);
  224. // Additionally, test the case where state is resetted
  225. gapiBackSub.prepareForNewStream();
  226. gapiBackSub(cv::gin(frame), cv::gout(gapiForeground));
  227. pOcvBackSub->apply(frame, ocvForeground);
  228. compareBackSubResults(gapiForeground, ocvForeground, 1);
  229. }
  230. #endif
  231. #ifdef HAVE_OPENCV_VIDEO
  232. namespace
  233. {
  234. void testBackSubInStreaming(cv::GStreamingCompiled gapiBackSub, const int diffPercent)
  235. {
  236. cv::Mat frame,
  237. gapiForeground,
  238. ocvForeground;
  239. gapiBackSub.start();
  240. EXPECT_TRUE(gapiBackSub.running());
  241. // OpenCV reference substractor
  242. auto pOCVBackSub = createBackgroundSubtractorKNN();
  243. // Comparison of G-API and OpenCV substractors
  244. std::size_t frames = 0u;
  245. while (gapiBackSub.pull(cv::gout(frame, gapiForeground))) {
  246. pOCVBackSub->apply(frame, ocvForeground, -1);
  247. compareBackSubResults(gapiForeground, ocvForeground, diffPercent);
  248. frames++;
  249. }
  250. EXPECT_LT(0u, frames);
  251. EXPECT_FALSE(gapiBackSub.running());
  252. }
  253. } // anonymous namespace
  254. TEST(StatefulKernel, StateIsInitViaCompArgsInStreaming)
  255. {
  256. // G-API graph declaration
  257. cv::GMat in;
  258. cv::GMat out = GBackSub::on(in);
  259. // Preserving 'in' in output to have possibility to compare with OpenCV reference
  260. cv::GComputation c(cv::GIn(in), cv::GOut(cv::gapi::copy(in), out));
  261. // G-API compilation of graph for streaming mode
  262. const auto pkg = cv::gapi::kernels<GOCVBackSub>();
  263. auto gapiBackSub = c.compileStreaming(
  264. cv::compile_args(pkg, BackSubStateParams { "knn" }));
  265. // Testing G-API Background Substractor in streaming mode
  266. auto path = findDataFile("cv/video/768x576.avi");
  267. try {
  268. gapiBackSub.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(path));
  269. } catch(...) {
  270. throw SkipTestException("Video file can not be opened");
  271. }
  272. // Allowing 1% difference of all pixels between G-API and reference OpenCV results
  273. testBackSubInStreaming(gapiBackSub, 1);
  274. path = findDataFile("cv/video/1920x1080.avi");
  275. try {
  276. // Additionally, test the case when the new stream happens
  277. gapiBackSub.setSource(gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(path));
  278. } catch(...) {
  279. throw SkipTestException("Video file can not be opened");
  280. }
  281. // Allowing 5% difference of all pixels between G-API and reference OpenCV results
  282. testBackSubInStreaming(gapiBackSub, 5);
  283. }
  284. #endif
  285. //-------------------------------------------------------------------------------------------------------------
  286. //------------------------------------------- Typed tests on setup() ------------------------------------------
  287. namespace
  288. {
  289. template<typename Tuple>
  290. struct SetupStateTypedTest : public ::testing::Test
  291. {
  292. using StateT = typename std::tuple_element<0, Tuple>::type;
  293. using SetupT = typename std::tuple_element<1, Tuple>::type;
  294. G_TYPED_KERNEL(GReturnState, <cv::GOpaque<StateT>(GMat)>, "org.opencv.test.return_state")
  295. {
  296. static GOpaqueDesc outMeta(GMatDesc /* in */) { return empty_gopaque_desc(); }
  297. };
  298. GAPI_OCV_KERNEL_ST(GOCVReturnState, GReturnState, StateT)
  299. {
  300. static void setup(const cv::GMatDesc &/* in */, std::shared_ptr<StateT> &state)
  301. {
  302. // Don't use input cv::GMatDesc intentionally
  303. state.reset(new StateT(SetupT::value()));
  304. }
  305. static void run(const cv::Mat &/* in */, StateT &out, StateT& state)
  306. {
  307. out = state;
  308. }
  309. };
  310. };
  311. TYPED_TEST_CASE_P(SetupStateTypedTest);
  312. } // namespace
  313. TYPED_TEST_P(SetupStateTypedTest, ReturnInitializedState)
  314. {
  315. using StateType = typename TestFixture::StateT;
  316. using SetupType = typename TestFixture::SetupT;
  317. cv::Mat dummyIn { 1, 1, CV_8UC1 };
  318. StateType retState { };
  319. GMat in;
  320. auto out = TestFixture::GReturnState::on(in);
  321. cv::GComputation comp(cv::GIn(in), cv::GOut(out));
  322. const auto pkg = cv::gapi::kernels<typename TestFixture::GOCVReturnState>();
  323. comp.apply(cv::gin(dummyIn), cv::gout(retState), cv::compile_args(pkg));
  324. EXPECT_EQ(SetupType::value(), retState);
  325. }
  326. REGISTER_TYPED_TEST_CASE_P(SetupStateTypedTest,
  327. ReturnInitializedState);
  328. DEFINE_INITIALIZER(CharValue, char, 'z');
  329. DEFINE_INITIALIZER(IntValue, int, 7);
  330. DEFINE_INITIALIZER(FloatValue, float, 42.f);
  331. DEFINE_INITIALIZER(UcharPtrValue, uchar*, nullptr);
  332. namespace
  333. {
  334. using Std3IntArray = std::array<int, 3>;
  335. }
  336. DEFINE_INITIALIZER(StdArrayValue, Std3IntArray, { 1, 2, 3 });
  337. DEFINE_INITIALIZER(UserValue, UserStruct, { 5, 7.f });
  338. using TypesToVerify = ::testing::Types<std::tuple<char, CharValue>,
  339. std::tuple<int, IntValue>,
  340. std::tuple<float, FloatValue>,
  341. std::tuple<uchar*, UcharPtrValue>,
  342. std::tuple<std::array<int, 3>, StdArrayValue>,
  343. std::tuple<UserStruct, UserValue>>;
  344. INSTANTIATE_TYPED_TEST_CASE_P(SetupStateTypedInst, SetupStateTypedTest, TypesToVerify);
  345. //-------------------------------------------------------------------------------------------------------------
  346. } // opencv_test