From d50c21e571c3daa255dfb83c8f190996d86cfa34 Mon Sep 17 00:00:00 2001 From: OrestChura Date: Wed, 8 Apr 2020 17:05:43 +0300 Subject: [PATCH] gapi: Full calcOpticalFlowPyrLK implementation (2 overloads) and tests - opencv_gapi module is linked with opencv_video module (optional dependency) - kernels added to a new cv::gapi::video namespace and a brand new files created to provide gapi_video environment - there are 2 different kernels as G-API should provide GMat AND GArray implementation: cv::calcOptFlowPyrLK doesn't calculate pyramids if vector is given so just the cast GMat -> GArray wouldn't represent all the cv:: functionality - tests to check both kernels (based on cv::video tests for cv::calcOpticalFlowPyrLK()) - tests for internal purposes added - vectors comparison in tests implemented - new (and old too) common test structures refactored to avoid code copypasting - "modules/gapi/test/common/gapi_video_tests_common.hpp" created to share some code snippets between perf and acc tests and avoid code copypasting --- modules/gapi/CMakeLists.txt | 9 +- .../gapi/include/opencv2/gapi/cpu/video.hpp | 25 ++ modules/gapi/include/opencv2/gapi/imgproc.hpp | 1 - modules/gapi/include/opencv2/gapi/video.hpp | 135 ++++++++++ .../perf/common/gapi_imgproc_perf_tests.hpp | 3 +- .../perf/common/gapi_video_perf_tests.cpp | 9 + .../perf/common/gapi_video_perf_tests.hpp | 26 ++ .../perf/common/gapi_video_perf_tests_inl.hpp | 90 +++++++ .../perf/cpu/gapi_imgproc_perf_tests_cpu.cpp | 2 +- .../perf/cpu/gapi_video_perf_tests_cpu.cpp | 64 +++++ modules/gapi/perf/perf_precomp.hpp | 3 +- modules/gapi/src/api/kernels_video.cpp | 45 ++++ modules/gapi/src/backends/cpu/gcpuvideo.cpp | 80 ++++++ modules/gapi/src/compiler/gcompiler.cpp | 13 +- .../test/common/gapi_imgproc_tests_inl.hpp | 1 - .../gapi/test/common/gapi_tests_common.hpp | 39 ++- modules/gapi/test/common/gapi_video_tests.cpp | 9 + modules/gapi/test/common/gapi_video_tests.hpp | 24 ++ .../test/common/gapi_video_tests_common.hpp | 249 ++++++++++++++++++ .../gapi/test/common/gapi_video_tests_inl.hpp | 53 ++++ .../gapi/test/cpu/gapi_video_tests_cpu.cpp | 62 +++++ modules/gapi/test/test_precomp.hpp | 5 +- 22 files changed, 921 insertions(+), 26 deletions(-) create mode 100644 modules/gapi/include/opencv2/gapi/cpu/video.hpp create mode 100644 modules/gapi/include/opencv2/gapi/video.hpp create mode 100644 modules/gapi/perf/common/gapi_video_perf_tests.cpp create mode 100644 modules/gapi/perf/common/gapi_video_perf_tests.hpp create mode 100644 modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp create mode 100644 modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp create mode 100644 modules/gapi/src/api/kernels_video.cpp create mode 100644 modules/gapi/src/backends/cpu/gcpuvideo.cpp create mode 100644 modules/gapi/test/common/gapi_video_tests.cpp create mode 100644 modules/gapi/test/common/gapi_video_tests.hpp create mode 100644 modules/gapi/test/common/gapi_video_tests_common.hpp create mode 100644 modules/gapi/test/common/gapi_video_tests_inl.hpp create mode 100644 modules/gapi/test/cpu/gapi_video_tests_cpu.cpp diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 519ebba4aa..ff3cb0a8ac 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -15,7 +15,12 @@ endif() set(the_description "OpenCV G-API Core Module") -ocv_add_module(gapi opencv_imgproc) +ocv_add_module(gapi + REQUIRED + opencv_imgproc + OPTIONAL + opencv_video +) if(MSVC) # Disable obsollete warning C4503 popping up on MSVC <<2017 @@ -55,6 +60,7 @@ set(gapi_srcs src/api/operators.cpp src/api/kernels_core.cpp src/api/kernels_imgproc.cpp + src/api/kernels_video.cpp src/api/render.cpp src/api/render_ocv.cpp src/api/ginfer.cpp @@ -87,6 +93,7 @@ set(gapi_srcs src/backends/cpu/gcpubackend.cpp src/backends/cpu/gcpukernel.cpp src/backends/cpu/gcpuimgproc.cpp + src/backends/cpu/gcpuvideo.cpp src/backends/cpu/gcpucore.cpp # Fluid Backend (also built-in, FIXME:move away) diff --git a/modules/gapi/include/opencv2/gapi/cpu/video.hpp b/modules/gapi/include/opencv2/gapi/cpu/video.hpp new file mode 100644 index 0000000000..d3c1f2e670 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/cpu/video.hpp @@ -0,0 +1,25 @@ +// 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 + +#ifndef OPENCV_GAPI_CPU_VIDEO_API_HPP +#define OPENCV_GAPI_CPU_VIDEO_API_HPP + +#include // GKernelPackage + +namespace cv { +namespace gapi { +namespace video { +namespace cpu { + +GAPI_EXPORTS GKernelPackage kernels(); + +} // namespace cpu +} // namespace video +} // namespace gapi +} // namespace cv + + +#endif // OPENCV_GAPI_CPU_VIDEO_API_HPP diff --git a/modules/gapi/include/opencv2/gapi/imgproc.hpp b/modules/gapi/include/opencv2/gapi/imgproc.hpp index 148539826d..4faf5e1178 100644 --- a/modules/gapi/include/opencv2/gapi/imgproc.hpp +++ b/modules/gapi/include/opencv2/gapi/imgproc.hpp @@ -261,7 +261,6 @@ namespace imgproc { } //namespace imgproc - //! @addtogroup gapi_filters //! @{ /** @brief Applies a separable linear filter to a matrix(image). diff --git a/modules/gapi/include/opencv2/gapi/video.hpp b/modules/gapi/include/opencv2/gapi/video.hpp new file mode 100644 index 0000000000..7602e1d201 --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/video.hpp @@ -0,0 +1,135 @@ +// 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 + +#ifndef OPENCV_GAPI_VIDEO_HPP +#define OPENCV_GAPI_VIDEO_HPP + +#include // std::tuple + +#include + + +/** \defgroup gapi_video G-API Video processing functionality + */ + +namespace cv { namespace gapi { +namespace video +{ + using GOptFlowLKOutput = std::tuple, + cv::GArray, + cv::GArray>; + + G_TYPED_KERNEL(GCalcOptFlowLK, + ,cv::GArray,Size, + int,TermCriteria,int,double)>, + "org.opencv.video.calcOpticalFlowPyrLK") + { + static std::tuple outMeta(GMatDesc,GMatDesc,GArrayDesc, + GArrayDesc,const Size&,int, + const TermCriteria&,int,double) + { + return std::make_tuple(empty_array_desc(), empty_array_desc(), empty_array_desc()); + } + + }; + + G_TYPED_KERNEL(GCalcOptFlowLKForPyr, + ,cv::GArray, + cv::GArray,cv::GArray,Size,int, + TermCriteria,int,double)>, + "org.opencv.video.calcOpticalFlowPyrLKForPyr") + { + static std::tuple outMeta(GArrayDesc,GArrayDesc, + GArrayDesc,GArrayDesc, + const Size&,int, + const TermCriteria&,int,double) + { + return std::make_tuple(empty_array_desc(), empty_array_desc(), empty_array_desc()); + } + }; +} //namespace video + +//! @addtogroup gapi_video +//! @{ +/** @brief Calculates an optical flow for a sparse feature set using the iterative Lucas-Kanade +method with pyramids. + +See @cite Bouguet00 . + +@note Function textual ID is "org.opencv.video.calcOpticalFlowPyrLK" + +@param prevImg first 8-bit input image (GMat) or pyramid (GArray) constructed by +buildOpticalFlowPyramid. +@param nextImg second input image (GMat) or pyramid (GArray) of the same size and the same +type as prevImg. +@param prevPts GArray of 2D points for which the flow needs to be found; point coordinates must be +single-precision floating-point numbers. +@param predPts GArray of 2D points initial for the flow search; make sense only when +OPTFLOW_USE_INITIAL_FLOW flag is passed; in that case the vector must have the same size as in +the input. +@param winSize size of the search window at each pyramid level. +@param maxLevel 0-based maximal pyramid level number; if set to 0, pyramids are not used (single +level), if set to 1, two levels are used, and so on; if pyramids are passed to input then +algorithm will use as many levels as pyramids have but no more than maxLevel. +@param criteria parameter, specifying the termination criteria of the iterative search algorithm +(after the specified maximum number of iterations criteria.maxCount or when the search window +moves by less than criteria.epsilon). +@param flags operation flags: + - **OPTFLOW_USE_INITIAL_FLOW** uses initial estimations, stored in nextPts; if the flag is + not set, then prevPts is copied to nextPts and is considered the initial estimate. + - **OPTFLOW_LK_GET_MIN_EIGENVALS** use minimum eigen values as an error measure (see + minEigThreshold description); if the flag is not set, then L1 distance between patches + around the original and a moved point, divided by number of pixels in a window, is used as a + error measure. +@param minEigThresh the algorithm calculates the minimum eigen value of a 2x2 normal matrix of +optical flow equations (this matrix is called a spatial gradient matrix in @cite Bouguet00), divided +by number of pixels in a window; if this value is less than minEigThreshold, then a corresponding +feature is filtered out and its flow is not processed, so it allows to remove bad points and get a +performance boost. + +@return GArray of 2D points (with single-precision floating-point coordinates) +containing the calculated new positions of input features in the second image. +@return status GArray (of unsigned chars); each element of the vector is set to 1 if +the flow for the corresponding features has been found, otherwise, it is set to 0. +@return GArray of errors (doubles); each element of the vector is set to an error for the +corresponding feature, type of the error measure can be set in flags parameter; if the flow wasn't +found then the error is not defined (use the status parameter to find such cases). + */ +GAPI_EXPORTS std::tuple, GArray, GArray> +calcOpticalFlowPyrLK(const GMat &prevImg, + const GMat &nextImg, + const GArray &prevPts, + const GArray &predPts, + const Size &winSize = Size(21, 21), + int maxLevel = 3, + const TermCriteria &criteria = TermCriteria(TermCriteria::COUNT | + TermCriteria::EPS, + 30, 0.01), + int flags = 0, + double minEigThresh = 1e-4); + +/** +@overload +@note Function textual ID is "org.opencv.video.calcOpticalFlowPyrLKForPyr" +*/ +GAPI_EXPORTS std::tuple, GArray, GArray> +calcOpticalFlowPyrLK(const GArray &prevPyr, + const GArray &nextPyr, + const GArray &prevPts, + const GArray &predPts, + const Size &winSize = Size(21, 21), + int maxLevel = 3, + const TermCriteria &criteria = TermCriteria(TermCriteria::COUNT | + TermCriteria::EPS, + 30, 0.01), + int flags = 0, + double minEigThresh = 1e-4); + +//! @} gapi_video +} //namespace gapi +} //namespace cv + +#endif // OPENCV_GAPI_VIDEO_HPP diff --git a/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp b/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp index 9d3433d19b..659fc53635 100644 --- a/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp +++ b/modules/gapi/perf/common/gapi_imgproc_perf_tests.hpp @@ -49,5 +49,6 @@ class YUV2BGRPerfTest : public TestPerfParams> {}; class BayerGR2RGBPerfTest : public TestPerfParams> {}; class RGB2YUV422PerfTest : public TestPerfParams> {}; -} +} // opencv_test + #endif //OPENCV_GAPI_IMGPROC_PERF_TESTS_HPP diff --git a/modules/gapi/perf/common/gapi_video_perf_tests.cpp b/modules/gapi/perf/common/gapi_video_perf_tests.cpp new file mode 100644 index 0000000000..efa176510e --- /dev/null +++ b/modules/gapi/perf/common/gapi_video_perf_tests.cpp @@ -0,0 +1,9 @@ +// 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 "../perf_precomp.hpp" +#include "gapi_video_perf_tests_inl.hpp" diff --git a/modules/gapi/perf/common/gapi_video_perf_tests.hpp b/modules/gapi/perf/common/gapi_video_perf_tests.hpp new file mode 100644 index 0000000000..240014ce0b --- /dev/null +++ b/modules/gapi/perf/common/gapi_video_perf_tests.hpp @@ -0,0 +1,26 @@ +// 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 + +#ifndef OPENCV_GAPI_VIDEO_PERF_TESTS_HPP +#define OPENCV_GAPI_VIDEO_PERF_TESTS_HPP + +#include "../../test/common/gapi_video_tests_common.hpp" + +namespace opencv_test +{ + +using namespace perf; + +//------------------------------------------------------------------------------ + +class OptFlowLKPerfTest : public TestPerfParams,int, + cv::TermCriteria,cv::GCompileArgs>> {}; +class OptFlowLKForPyrPerfTest : public TestPerfParams,int, + cv::TermCriteria,bool, + cv::GCompileArgs>> {}; +} // opencv_test + +#endif // OPENCV_GAPI_VIDEO_PERF_TESTS_HPP diff --git a/modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp b/modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp new file mode 100644 index 0000000000..109532205b --- /dev/null +++ b/modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp @@ -0,0 +1,90 @@ +// 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 + +#ifndef OPENCV_GAPI_VIDEO_PERF_TESTS_INL_HPP +#define OPENCV_GAPI_VIDEO_PERF_TESTS_INL_HPP + +#include + +#include "gapi_video_perf_tests.hpp" + +namespace opencv_test +{ + + using namespace perf; + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(OptFlowLKPerfTest, TestPerformance) +{ + std::vector outPtsOCV, outPtsGAPI, inPts; + std::vector outStatusOCV, outStatusGAPI; + std::vector outErrOCV, outErrGAPI; + + OptFlowLKTestParams params; + std::tie(params.fileNamePattern, params.channels, + params.pointsNum, params.winSize, params.criteria, + params.compileArgs) = GetParam(); + + OptFlowLKTestOutput outOCV { outPtsOCV, outStatusOCV, outErrOCV }; + OptFlowLKTestOutput outGAPI { outPtsGAPI, outStatusGAPI, outErrGAPI }; + + cv::GComputation c = runOCVnGAPIOptFlowLK(*this, inPts, params, outOCV, outGAPI); + + declare.in(in_mat1, in_mat2, inPts).out(outPtsGAPI, outStatusGAPI, outErrGAPI); + + TEST_CYCLE() + { + c.apply(cv::gin(in_mat1, in_mat2, inPts, std::vector{ }), + cv::gout(outPtsGAPI, outStatusGAPI, outErrGAPI)); + } + + // Comparison ////////////////////////////////////////////////////////////// + compareOutputsOptFlow(outOCV, outGAPI); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +PERF_TEST_P_(OptFlowLKForPyrPerfTest, TestPerformance) +{ + std::vector inPyr1, inPyr2; + std::vector outPtsOCV, outPtsGAPI, inPts; + std::vector outStatusOCV, outStatusGAPI; + std::vector outErrOCV, outErrGAPI; + + bool withDeriv = false; + OptFlowLKTestParams params; + std::tie(params.fileNamePattern, params.channels, + params.pointsNum, params.winSize, params.criteria, + withDeriv, params.compileArgs) = GetParam(); + + OptFlowLKTestInput> in { inPyr1, inPyr2, inPts }; + OptFlowLKTestOutput outOCV { outPtsOCV, outStatusOCV, outErrOCV }; + OptFlowLKTestOutput outGAPI { outPtsGAPI, outStatusGAPI, outErrGAPI }; + + cv::GComputation c = runOCVnGAPIOptFlowLKForPyr(*this, in, params, withDeriv, outOCV, outGAPI); + + declare.in(inPyr1, inPyr2, inPts).out(outPtsGAPI, outStatusGAPI, outErrGAPI); + + TEST_CYCLE() + { + c.apply(cv::gin(inPyr1, inPyr2, inPts, std::vector{ }), + cv::gout(outPtsGAPI, outStatusGAPI, outErrGAPI)); + } + + // Comparison ////////////////////////////////////////////////////////////// + compareOutputsOptFlow(outOCV, outGAPI); + + SANITY_CHECK_NOTHING(); +} + +//------------------------------------------------------------------------------ + +} // opencv_test + +#endif // OPENCV_GAPI_VIDEO_PERF_TESTS_INL_HPP diff --git a/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp b/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp index 3f2270391c..6b2efb70b2 100644 --- a/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp +++ b/modules/gapi/perf/cpu/gapi_imgproc_perf_tests_cpu.cpp @@ -220,4 +220,4 @@ INSTANTIATE_TEST_CASE_P(RGB2YUV422PerfTestCPU, RGB2YUV422PerfTest, Combine(Values(ToleranceColor(1e-3).to_compare_f()), Values(szVGA, sz720p, sz1080p), Values(cv::compile_args(IMGPROC_CPU)))); -} +} // opencv_test diff --git a/modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp b/modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp new file mode 100644 index 0000000000..4b36dace60 --- /dev/null +++ b/modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp @@ -0,0 +1,64 @@ +// 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 "../perf_precomp.hpp" + +#include "../common/gapi_video_perf_tests.hpp" +#include + +namespace +{ +#define VIDEO_CPU cv::gapi::video::cpu::kernels() + +#ifdef HAVE_OPENCV_VIDEO +#define WITH_VIDEO(X) X +#else +#define WITH_VIDEO(X) DISABLED_##X +#endif // HAVE_OPENCV_VIDEO + +#define INSTANTIATE_TEST_CASE_MACRO_P(prefix, test_case_name, generator, ...) \ + INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator, __VA_ARGS__) +} // namespace + + +namespace opencv_test +{ +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKPerfTestCPU), OptFlowLKPerfTest, + Combine(Values("cv/optflow/rock_%01d.bmp", + "cv/optflow/frames/1080p_%02d.png"), + Values(1, 3, 4), + Values(std::make_tuple(9, 9), std::make_tuple(15, 15)), + Values(7, 11), + Values(cv::TermCriteria(cv::TermCriteria::COUNT | + cv::TermCriteria::EPS, + 30, 0.01)), + Values(cv::compile_args(VIDEO_CPU)))); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKForPyrPerfTestCPU), OptFlowLKForPyrPerfTest, + Combine(Values("cv/optflow/rock_%01d.bmp", + "cv/optflow/frames/1080p_%02d.png"), + Values(1, 3, 4), + Values(std::make_tuple(9, 9), std::make_tuple(15, 15)), + Values(7, 11), + Values(cv::TermCriteria(cv::TermCriteria::COUNT | + cv::TermCriteria::EPS, + 30, 0.01)), + Values(true, false), + Values(cv::compile_args(VIDEO_CPU)))); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKInternalPerfTestCPU), + OptFlowLKForPyrPerfTest, + Combine(Values("cv/optflow/rock_%01d.bmp"), + Values(1), + Values(std::make_tuple(10, 10)), + Values(15), + Values(cv::TermCriteria(cv::TermCriteria::COUNT | + cv::TermCriteria::EPS, + 21, 0.05)), + Values(true), + Values(cv::compile_args(VIDEO_CPU)))); +} // opencv_test diff --git a/modules/gapi/perf/perf_precomp.hpp b/modules/gapi/perf/perf_precomp.hpp index bf1953284b..44b9dc7150 100644 --- a/modules/gapi/perf/perf_precomp.hpp +++ b/modules/gapi/perf/perf_precomp.hpp @@ -2,7 +2,7 @@ // 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 +// Copyright (C) 2018-2020 Intel Corporation #ifndef __OPENCV_GAPI_PERF_PRECOMP_HPP__ @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/modules/gapi/src/api/kernels_video.cpp b/modules/gapi/src/api/kernels_video.cpp new file mode 100644 index 0000000000..68e9cdfa64 --- /dev/null +++ b/modules/gapi/src/api/kernels_video.cpp @@ -0,0 +1,45 @@ +// 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 "precomp.hpp" + +#include + +namespace cv { namespace gapi { +using namespace video; + + +GOptFlowLKOutput calcOpticalFlowPyrLK(const GMat &prevImg, + const GMat &nextImg, + const cv::GArray &prevPts, + const cv::GArray &predPts, + const Size &winSize, + int maxLevel, + const TermCriteria &criteria, + int flags, + double minEigThresh) +{ + return GCalcOptFlowLK::on(prevImg, nextImg, prevPts, predPts, winSize, maxLevel, + criteria, flags, minEigThresh); +} + +GOptFlowLKOutput calcOpticalFlowPyrLK(const cv::GArray &prevPyr, + const cv::GArray &nextPyr, + const cv::GArray &prevPts, + const cv::GArray &predPts, + const Size &winSize, + int maxLevel, + const TermCriteria &criteria, + int flags, + double minEigThresh) +{ + return GCalcOptFlowLKForPyr::on(prevPyr, nextPyr, prevPts, predPts, winSize, maxLevel, + criteria, flags, minEigThresh); +} + +} //namespace gapi +} //namespace cv diff --git a/modules/gapi/src/backends/cpu/gcpuvideo.cpp b/modules/gapi/src/backends/cpu/gcpuvideo.cpp new file mode 100644 index 0000000000..829e662860 --- /dev/null +++ b/modules/gapi/src/backends/cpu/gcpuvideo.cpp @@ -0,0 +1,80 @@ +// 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 "precomp.hpp" + +#include +#include +#include + +#ifdef HAVE_OPENCV_VIDEO +#include +#endif // HAVE_OPENCV_VIDEO + +#ifdef HAVE_OPENCV_VIDEO + +GAPI_OCV_KERNEL(GCPUCalcOptFlowLK, cv::gapi::video::GCalcOptFlowLK) +{ + static void run(const cv::Mat &prevImg, + const cv::Mat &nextImg, + const std::vector &prevPts, + const std::vector &predPts, + const cv::Size &winSize, + int maxLevel, + const cv::TermCriteria &criteria, + int flags, + double minEigThresh, + std::vector &outPts, + std::vector &status, + std::vector &err) + { + if (flags & cv::OPTFLOW_USE_INITIAL_FLOW) + outPts = predPts; + cv::calcOpticalFlowPyrLK(prevImg, nextImg, prevPts, outPts, status, err, winSize, maxLevel, + criteria, flags, minEigThresh); + } +}; + +GAPI_OCV_KERNEL(GCPUCalcOptFlowLKForPyr, cv::gapi::video::GCalcOptFlowLKForPyr) +{ + static void run(const std::vector &prevPyr, + const std::vector &nextPyr, + const std::vector &prevPts, + const std::vector &predPts, + const cv::Size &winSize, + int maxLevel, + const cv::TermCriteria &criteria, + int flags, + double minEigThresh, + std::vector &outPts, + std::vector &status, + std::vector &err) + { + if (flags & cv::OPTFLOW_USE_INITIAL_FLOW) + outPts = predPts; + cv::calcOpticalFlowPyrLK(prevPyr, nextPyr, prevPts, outPts, status, err, winSize, maxLevel, + criteria, flags, minEigThresh); + } +}; + +cv::gapi::GKernelPackage cv::gapi::video::cpu::kernels() +{ + static auto pkg = cv::gapi::kernels + < GCPUCalcOptFlowLK + , GCPUCalcOptFlowLKForPyr + >(); + return pkg; +} + +#else + +cv::gapi::GKernelPackage cv::gapi::video::cpu::kernels() +{ + return GKernelPackage(); +} + +#endif // HAVE_OPENCV_VIDEO diff --git a/modules/gapi/src/compiler/gcompiler.cpp b/modules/gapi/src/compiler/gcompiler.cpp index 92db768c5c..5262acd73b 100644 --- a/modules/gapi/src/compiler/gcompiler.cpp +++ b/modules/gapi/src/compiler/gcompiler.cpp @@ -2,7 +2,7 @@ // 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 +// Copyright (C) 2018-2020 Intel Corporation #include "precomp.hpp" @@ -38,8 +38,9 @@ // #if !defined(GAPI_STANDALONE) -#include // Also directly refer to Core -#include // ...and Imgproc kernel implementations +#include // Also directly refer to Core, +#include // ...Imgproc +#include // ...and Video kernel implementations #include // render::ocv::backend() #endif // !defined(GAPI_STANDALONE) // @@ -66,9 +67,9 @@ namespace static auto ocv_pkg = #if !defined(GAPI_STANDALONE) - // FIXME add N-arg version combine - combine(combine(cv::gapi::core::cpu::kernels(), - cv::gapi::imgproc::cpu::kernels()), + combine(cv::gapi::core::cpu::kernels(), + cv::gapi::imgproc::cpu::kernels(), + cv::gapi::video::cpu::kernels(), cv::gapi::render::ocv::kernels()); #else cv::gapi::GKernelPackage(); diff --git a/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp b/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp index ecc7195c41..a7aca789c2 100644 --- a/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp +++ b/modules/gapi/test/common/gapi_imgproc_tests_inl.hpp @@ -784,7 +784,6 @@ TEST_P(RGB2YUV422Test, AccuracyTest) EXPECT_EQ(out_mat_gapi.size(), sz); } } - } // opencv_test #endif //OPENCV_GAPI_IMGPROC_TESTS_INL_HPP diff --git a/modules/gapi/test/common/gapi_tests_common.hpp b/modules/gapi/test/common/gapi_tests_common.hpp index 938a7f77da..a21cae460b 100644 --- a/modules/gapi/test/common/gapi_tests_common.hpp +++ b/modules/gapi/test/common/gapi_tests_common.hpp @@ -196,25 +196,32 @@ public: } } + void initMatsFromImages(int channels, const std::string& pattern, int imgNum) + { + initTestDataPath(); + GAPI_Assert(channels == 1 || channels == 3 || channels == 4); + const int flags = (channels == 1) ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR; + + cv::Mat m1 = cv::imread(findDataFile(cv::format(pattern.c_str(), imgNum)), flags); + cv::Mat m2 = cv::imread(findDataFile(cv::format(pattern.c_str(), imgNum + 1)), flags); + if (channels == 4) + { + cvtColor(m1, in_mat1, cv::COLOR_BGR2BGRA); + cvtColor(m2, in_mat2, cv::COLOR_BGR2BGRA); + } + else + { + std::tie(in_mat1, in_mat2) = std::make_tuple(m1, m2); + } + } + // empty function intended to show that nothing is to be initialized via TestFunctional methods void initNothing(int, cv::Size, int, bool = true) {} }; -template -class TestParams: public TestFunctional, public TestWithParam{}; - template class TestPerfParams: public TestFunctional, public perf::TestBaseWithParam{}; -using compare_f = std::function; - -using compare_scalar_f = std::function; - -template -using compare_vector_f = std::function &a, - const std::vector &b)>; - - // FIXME: re-use MatType. current problem: "special values" interpreted incorrectly (-1 is printed // as 16FC512) struct MatType2 @@ -368,6 +375,14 @@ struct TestWithParamsSpecific : public TestWithParamsBase #define FIXTURE_API(...) <__VA_ARGS__> + +using compare_f = std::function; +using compare_scalar_f = std::function; + +template +using compare_vector_f = std::function &a, + const std::vector &b)>; + template struct CompareF { diff --git a/modules/gapi/test/common/gapi_video_tests.cpp b/modules/gapi/test/common/gapi_video_tests.cpp new file mode 100644 index 0000000000..3501b05e46 --- /dev/null +++ b/modules/gapi/test/common/gapi_video_tests.cpp @@ -0,0 +1,9 @@ +// 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 "gapi_video_tests_inl.hpp" diff --git a/modules/gapi/test/common/gapi_video_tests.hpp b/modules/gapi/test/common/gapi_video_tests.hpp new file mode 100644 index 0000000000..c5dafacb8a --- /dev/null +++ b/modules/gapi/test/common/gapi_video_tests.hpp @@ -0,0 +1,24 @@ +// 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 + +#ifndef OPENCV_GAPI_VIDEO_TESTS_HPP +#define OPENCV_GAPI_VIDEO_TESTS_HPP + +#include "gapi_video_tests_common.hpp" + +namespace opencv_test +{ +GAPI_TEST_FIXTURE_SPEC_PARAMS(OptFlowLKTest, FIXTURE_API(std::string,int,tuple,int, + cv::TermCriteria), + 5, fileNamePattern, channels, pointsNum, winSize, criteria) + +GAPI_TEST_FIXTURE_SPEC_PARAMS(OptFlowLKTestForPyr, FIXTURE_API(std::string,int,tuple,int, + cv::TermCriteria,bool), + 6, fileNamePattern, channels, pointsNum, winSize, criteria,withDeriv) +} // opencv_test + + +#endif // OPENCV_GAPI_VIDEO_TESTS_HPP diff --git a/modules/gapi/test/common/gapi_video_tests_common.hpp b/modules/gapi/test/common/gapi_video_tests_common.hpp new file mode 100644 index 0000000000..c056d50c51 --- /dev/null +++ b/modules/gapi/test/common/gapi_video_tests_common.hpp @@ -0,0 +1,249 @@ +// 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 + +#ifndef OPENCV_GAPI_VIDEO_TESTS_COMMON_HPP +#define OPENCV_GAPI_VIDEO_TESTS_COMMON_HPP + +#include "gapi_tests_common.hpp" +#include "../../include/opencv2/gapi/video.hpp" + +#ifdef HAVE_OPENCV_VIDEO +#include +#endif // HAVE_OPENCV_VIDEO + + + +namespace opencv_test +{ +namespace +{ +inline void initTrackingPointsArray(std::vector& points, int width, int height, + int nPointsX, int nPointsY) +{ + if (nPointsX > width || nPointsY > height) + { + FAIL() << "Specified points number is too big"; + } + + int stepX = width / nPointsX; + int stepY = height / nPointsY; + + + points.clear(); + GAPI_Assert((nPointsX >= 0) && (nPointsY) >= 0); + points.reserve(static_cast(nPointsX * nPointsY)); + + for (int x = stepX / 2; x < width; x += stepX) + { + for (int y = stepY / 2; y < height; y += stepY) + { + Point2f pt(static_cast(x), static_cast(y)); + points.push_back(pt); + } + } +} + +template +struct OptFlowLKTestInput +{ + Type& prevData; + Type& nextData; + std::vector& prevPoints; +}; + +struct OptFlowLKTestOutput +{ + std::vector &nextPoints; + std::vector &statuses; + std::vector &errors; +}; + +struct OptFlowLKTestParams +{ + OptFlowLKTestParams(): fileNamePattern(""), format(1), channels(0), pointsNum{0, 0}, + winSize(0), maxLevel(3), minEigThreshold(1e-4), flags(0) { } + + OptFlowLKTestParams(const std::string& namePat, int chans, + const std::tuple& ptsNum, int winSz, + const cv::TermCriteria& crit, const cv::GCompileArgs& compArgs, + int flgs = 0, int fmt = 1, int maxLvl = 3, double minEigThresh = 1e-4): + + fileNamePattern(namePat), format(fmt), channels(chans), + pointsNum(ptsNum), winSize(winSz), maxLevel(maxLvl), + criteria(crit), minEigThreshold(minEigThresh), compileArgs(compArgs), + flags(flgs) { } + + std::string fileNamePattern = ""; + int format = 1; + int channels = 0; + std::tuple pointsNum = std::make_tuple(0, 0); + int winSize = 0; + int maxLevel = 3; + cv::TermCriteria criteria; + double minEigThreshold = 1e-4; + cv::GCompileArgs compileArgs; + int flags = 0; +}; + +#ifdef HAVE_OPENCV_VIDEO + +template +cv::GComputation runOCVnGAPIOptFlowLK(OptFlowLKTestInput& in, + int width, int height, + const OptFlowLKTestParams& params, + OptFlowLKTestOutput& ocvOut, + OptFlowLKTestOutput& gapiOut) +{ + + int nPointsX = 0, nPointsY = 0; + std::tie(nPointsX, nPointsY) = params.pointsNum; + + initTrackingPointsArray(in.prevPoints, width, height, nPointsX, nPointsY); + + cv::Size winSize(params.winSize, params.winSize); + + // OpenCV code ///////////////////////////////////////////////////////////// + { + cv::calcOpticalFlowPyrLK(in.prevData, in.nextData, in.prevPoints, + ocvOut.nextPoints, ocvOut.statuses, ocvOut.errors, + winSize, params.maxLevel, params.criteria, + params.flags, params.minEigThreshold); + } + + // G-API code ////////////////////////////////////////////////////////////// + { + GType inPrev, inNext; + GArray prevPts, predPts, nextPts; + GArray statuses; + GArray errors; + std::tie(nextPts, statuses, errors) = cv::gapi::calcOpticalFlowPyrLK( + inPrev, inNext, + prevPts, predPts, winSize, + params.maxLevel, params.criteria, + params.flags, params.minEigThreshold); + + cv::GComputation c(cv::GIn(inPrev, inNext, prevPts, predPts), + cv::GOut(nextPts, statuses, errors)); + + c.apply(cv::gin(in.prevData, in.nextData, in.prevPoints, std::vector{ }), + cv::gout(gapiOut.nextPoints, gapiOut.statuses, gapiOut.errors), + std::move(const_cast(params.compileArgs))); + + return c; + } +} + +inline cv::GComputation runOCVnGAPIOptFlowLK(TestFunctional& testInst, + std::vector& inPts, + const OptFlowLKTestParams& params, + OptFlowLKTestOutput& ocvOut, + OptFlowLKTestOutput& gapiOut) +{ + testInst.initMatsFromImages(params.channels, + params.fileNamePattern, + params.format); + + OptFlowLKTestInput in{ testInst.in_mat1, testInst.in_mat2, inPts }; + + return runOCVnGAPIOptFlowLK(in, + testInst.in_mat1.cols, + testInst.in_mat1.rows, + params, + ocvOut, + gapiOut); +} + +inline cv::GComputation runOCVnGAPIOptFlowLKForPyr(TestFunctional& testInst, + OptFlowLKTestInput>& in, + const OptFlowLKTestParams& params, + bool withDeriv, + OptFlowLKTestOutput& ocvOut, + OptFlowLKTestOutput& gapiOut) +{ + testInst.initMatsFromImages(params.channels, + params.fileNamePattern, + params.format); + + cv::Size winSize(params.winSize, params.winSize); + + OptFlowLKTestParams updatedParams(params); + updatedParams.maxLevel = cv::buildOpticalFlowPyramid(testInst.in_mat1, in.prevData, + winSize, params.maxLevel, withDeriv); + updatedParams.maxLevel = cv::buildOpticalFlowPyramid(testInst.in_mat2, in.nextData, + winSize, params.maxLevel, withDeriv); + + + return runOCVnGAPIOptFlowLK>(in, + testInst.in_mat1.cols, + testInst.in_mat1.rows, + updatedParams, + ocvOut, + gapiOut); +} + +#else // !HAVE_OPENCV_VIDEO + +inline cv::GComputation runOCVnGAPIOptFlowLK(TestFunctional&, + std::vector&, + const OptFlowLKTestParams&, + OptFlowLKTestOutput&, + OptFlowLKTestOutput&) +{ + GAPI_Assert(0 && "This function shouldn't be called without opencv_video"); +} + +inline cv::GComputation runOCVnGAPIOptFlowLKForPyr(TestFunctional&, + OptFlowLKTestInput>&, + const OptFlowLKTestParams&, + bool, + OptFlowLKTestOutput&, + OptFlowLKTestOutput&) +{ + GAPI_Assert(0 && "This function shouldn't be called without opencv_video"); +} + +#endif // HAVE_OPENCV_VIDEO + +template +inline bool compareVectorsAbsExactForOptFlow(std::vector outOCV, std::vector outGAPI) +{ + return AbsExactVector().to_compare_f()(outOCV, outGAPI); +} + +inline void compareOutputsOptFlow(const OptFlowLKTestOutput& outOCV, + const OptFlowLKTestOutput& outGAPI) +{ + EXPECT_TRUE(compareVectorsAbsExactForOptFlow(outGAPI.nextPoints, outOCV.nextPoints)); + EXPECT_TRUE(compareVectorsAbsExactForOptFlow(outGAPI.statuses, outOCV.statuses)); + EXPECT_TRUE(compareVectorsAbsExactForOptFlow(outGAPI.errors, outOCV.errors)); +} + + +inline std::ostream& operator<<(std::ostream& os, const cv::TermCriteria& criteria) +{ + os << "{"; + switch (criteria.type) { + case cv::TermCriteria::COUNT: + os << "COUNT; "; + break; + case cv::TermCriteria::EPS: + os << "EPS; "; + break; + case cv::TermCriteria::COUNT | cv::TermCriteria::EPS: + os << "COUNT | EPS; "; + break; + default: + os << "TypeUndifined; "; + break; + }; + + return os << criteria.maxCount << "; " << criteria.epsilon <<"}"; +} +} // namespace +} // namespace opencv_test + + +#endif // OPENCV_GAPI_VIDEO_TESTS_COMMON_HPP diff --git a/modules/gapi/test/common/gapi_video_tests_inl.hpp b/modules/gapi/test/common/gapi_video_tests_inl.hpp new file mode 100644 index 0000000000..a5059a89e5 --- /dev/null +++ b/modules/gapi/test/common/gapi_video_tests_inl.hpp @@ -0,0 +1,53 @@ +// 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 + +#ifndef OPENCV_GAPI_VIDEO_TESTS_INL_HPP +#define OPENCV_GAPI_VIDEO_TESTS_INL_HPP + +#include "gapi_video_tests.hpp" + +namespace opencv_test +{ + +TEST_P(OptFlowLKTest, AccuracyTest) +{ + std::vector outPtsOCV, outPtsGAPI, inPts; + std::vector outStatusOCV, outStatusGAPI; + std::vector outErrOCV, outErrGAPI; + + OptFlowLKTestParams params { fileNamePattern, channels, pointsNum, + winSize, criteria, getCompileArgs() }; + + OptFlowLKTestOutput outOCV { outPtsOCV, outStatusOCV, outErrOCV }; + OptFlowLKTestOutput outGAPI { outPtsGAPI, outStatusGAPI, outErrGAPI }; + + runOCVnGAPIOptFlowLK(*this, inPts, params, outOCV, outGAPI); + + compareOutputsOptFlow(outOCV, outGAPI); +} + +TEST_P(OptFlowLKTestForPyr, AccuracyTest) +{ + std::vector inPyr1, inPyr2; + std::vector outPtsOCV, outPtsGAPI, inPts; + std::vector outStatusOCV, outStatusGAPI; + std::vector outErrOCV, outErrGAPI; + + OptFlowLKTestParams params { fileNamePattern, channels, pointsNum, + winSize, criteria, getCompileArgs() }; + + OptFlowLKTestInput> in { inPyr1, inPyr2, inPts }; + OptFlowLKTestOutput outOCV { outPtsOCV, outStatusOCV, outErrOCV }; + OptFlowLKTestOutput outGAPI { outPtsGAPI, outStatusGAPI, outErrGAPI }; + + runOCVnGAPIOptFlowLKForPyr(*this, in, params, withDeriv, outOCV, outGAPI); + + compareOutputsOptFlow(outOCV, outGAPI); +} + +} // opencv_test + +#endif // OPENCV_GAPI_VIDEO_TESTS_INL_HPP diff --git a/modules/gapi/test/cpu/gapi_video_tests_cpu.cpp b/modules/gapi/test/cpu/gapi_video_tests_cpu.cpp new file mode 100644 index 0000000000..c3e0f7e8e8 --- /dev/null +++ b/modules/gapi/test/cpu/gapi_video_tests_cpu.cpp @@ -0,0 +1,62 @@ +// 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 "../common/gapi_video_tests.hpp" +#include + +namespace +{ +#define VIDEO_CPU [] () { return cv::compile_args(cv::gapi::video::cpu::kernels()); } + +#ifdef HAVE_OPENCV_VIDEO +#define WITH_VIDEO(X) X +#else +#define WITH_VIDEO(X) DISABLED_##X +#endif // HAVE_OPENCV_VIDEO + +#define INSTANTIATE_TEST_CASE_MACRO_P(prefix, test_case_name, generator, ...) \ + INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator, __VA_ARGS__) +} // anonymous namespace + +namespace opencv_test +{ +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKTestCPU), OptFlowLKTest, + Combine(Values(VIDEO_CPU), + Values("cv/optflow/rock_%01d.bmp", + "cv/optflow/frames/1080p_%02d.png"), + Values(1, 3, 4), + Values(std::make_tuple(9, 9), std::make_tuple(15, 15)), + Values(7, 11), + Values(cv::TermCriteria(cv::TermCriteria::COUNT | + cv::TermCriteria::EPS, + 30, 0.01)))); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKTestForPyrCPU), OptFlowLKTestForPyr, + Combine(Values(VIDEO_CPU), + Values("cv/optflow/rock_%01d.bmp", + "cv/optflow/frames/1080p_%02d.png"), + Values(1, 3, 4), + Values(std::make_tuple(9, 9), std::make_tuple(15, 15)), + Values(7, 11), + Values(cv::TermCriteria(cv::TermCriteria::COUNT | + cv::TermCriteria::EPS, + 30, 0.01)), + testing::Bool())); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKInternalTestCPU), OptFlowLKTestForPyr, + Combine(Values(VIDEO_CPU), + Values("cv/optflow/rock_%01d.bmp"), + Values(1), + Values(std::make_tuple(10, 10)), + Values(15), + Values(cv::TermCriteria(cv::TermCriteria::COUNT | + cv::TermCriteria::EPS, + 21, 0.05)), + Values(true))); +} // opencv_test diff --git a/modules/gapi/test/test_precomp.hpp b/modules/gapi/test/test_precomp.hpp index 03f0be24fb..6253acfcb3 100644 --- a/modules/gapi/test/test_precomp.hpp +++ b/modules/gapi/test/test_precomp.hpp @@ -2,7 +2,7 @@ // 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 +// Copyright (C) 2018-2020 Intel Corporation // FIXME: OpenCV header @@ -16,8 +16,9 @@ #include #include -#include #include +#include +#include #include #include #include