From 05d5c284f6b9d4fa5f317ea6dae114ff48ec36eb Mon Sep 17 00:00:00 2001 From: OrestChura Date: Tue, 14 Apr 2020 00:53:01 +0300 Subject: [PATCH] gapi: buildOpticalFlowPyramid() interface, CPUkernel and CPUtests implementation - kernel added to a cv::gapi::video namespace - tests to check a kernels (based on cv::video tests for cv::buildOpticalFlowPyramid()) - tests for a combined G-API-pipeline (buildOpticalFlowPyramid() -> calcOpticalFlowPyrLK()) - tests for internal purposes added - custom function for comparison in tests implemented --- .../gapi/include/opencv2/gapi/cpu/gcpukernel.hpp | 6 + modules/gapi/include/opencv2/gapi/video.hpp | 105 +++++++++---- modules/gapi/perf/common/gapi_video_perf_tests.hpp | 5 + .../gapi/perf/common/gapi_video_perf_tests_inl.hpp | 69 +++++++++ .../gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp | 39 +++++ modules/gapi/src/api/kernels_video.cpp | 15 +- modules/gapi/src/backends/cpu/gcpuvideo.cpp | 34 +++- modules/gapi/test/common/gapi_video_tests.hpp | 10 ++ .../gapi/test/common/gapi_video_tests_common.hpp | 171 ++++++++++++++++++++- modules/gapi/test/common/gapi_video_tests_inl.hpp | 40 +++++ modules/gapi/test/cpu/gapi_video_tests_cpu.cpp | 38 +++++ 11 files changed, 489 insertions(+), 43 deletions(-) diff --git a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp index 764e085..98d5485 100644 --- a/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp +++ b/modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp @@ -256,6 +256,12 @@ template struct get_out> return ctx.outVecR(idx); } }; + +//FIXME(dm): GArray/GArray conversion should be done more gracefully in the system +template<> struct get_out >: public get_out > +{ +}; + template struct get_out> { static U& get(GCPUContext &ctx, int idx) diff --git a/modules/gapi/include/opencv2/gapi/video.hpp b/modules/gapi/include/opencv2/gapi/video.hpp index 7602e1d..7f90134 100644 --- a/modules/gapi/include/opencv2/gapi/video.hpp +++ b/modules/gapi/include/opencv2/gapi/video.hpp @@ -18,42 +18,83 @@ 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") +using GBuildPyrOutput = std::tuple, GScalar>; + +using GOptFlowLKOutput = std::tuple, + cv::GArray, + cv::GArray>; + +G_TYPED_KERNEL(GBuildOptFlowPyramid, , + "org.opencv.video.buildOpticalFlowPyramid") +{ + static std::tuple + outMeta(GMatDesc,const Size&,GScalarDesc,bool,int,int,bool) + { + return std::make_tuple(empty_array_desc(), empty_scalar_desc()); + } +}; + +G_TYPED_KERNEL(GCalcOptFlowLK, + ,cv::GArray,Size, + GScalar,TermCriteria,int,double)>, + "org.opencv.video.calcOpticalFlowPyrLK") +{ + static std::tuple outMeta(GMatDesc,GMatDesc,GArrayDesc, + GArrayDesc,const Size&,GScalarDesc, + const TermCriteria&,int,double) { - 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") + return std::make_tuple(empty_array_desc(), empty_array_desc(), empty_array_desc()); + } + +}; + +G_TYPED_KERNEL(GCalcOptFlowLKForPyr, + ,cv::GArray, + cv::GArray,cv::GArray,Size,GScalar, + TermCriteria,int,double)>, + "org.opencv.video.calcOpticalFlowPyrLKForPyr") +{ + static std::tuple outMeta(GArrayDesc,GArrayDesc, + GArrayDesc,GArrayDesc, + const Size&,GScalarDesc, + const TermCriteria&,int,double) { - 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()); - } - }; + return std::make_tuple(empty_array_desc(), empty_array_desc(), empty_array_desc()); + } +}; } //namespace video //! @addtogroup gapi_video //! @{ +/** @brief Constructs the image pyramid which can be passed to calcOpticalFlowPyrLK. + +@note Function textual ID is "org.opencv.video.buildOpticalFlowPyramid" + +@param img 8-bit input image. +@param winSize window size of optical flow algorithm. Must be not less than winSize + argument of calcOpticalFlowPyrLK. It is needed to calculate required + padding for pyramid levels. +@param maxLevel 0-based maximal pyramid level number. +@param withDerivatives set to precompute gradients for the every pyramid level. If pyramid is + constructed without the gradients then calcOpticalFlowPyrLK will calculate + them internally. +@param pyrBorder the border mode for pyramid layers. +@param derivBorder the border mode for gradients. +@param tryReuseInputImage put ROI of input image into the pyramid if possible. You can pass false + to force data copying. + +@return output pyramid. +@return number of levels in constructed pyramid. Can be less than maxLevel. + */ +GAPI_EXPORTS std::tuple, GScalar> +buildOpticalFlowPyramid(const GMat &img, + const Size &winSize, + const GScalar &maxLevel, + bool withDerivatives = true, + int pyrBorder = BORDER_REFLECT_101, + int derivBorder = BORDER_CONSTANT, + bool tryReuseInputImage = true); + /** @brief Calculates an optical flow for a sparse feature set using the iterative Lucas-Kanade method with pyramids. @@ -104,7 +145,7 @@ calcOpticalFlowPyrLK(const GMat &prevImg, const GArray &prevPts, const GArray &predPts, const Size &winSize = Size(21, 21), - int maxLevel = 3, + const GScalar &maxLevel = 3, const TermCriteria &criteria = TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 30, 0.01), @@ -121,7 +162,7 @@ calcOpticalFlowPyrLK(const GArray &prevPyr, const GArray &prevPts, const GArray &predPts, const Size &winSize = Size(21, 21), - int maxLevel = 3, + const GScalar &maxLevel = 3, const TermCriteria &criteria = TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 30, 0.01), diff --git a/modules/gapi/perf/common/gapi_video_perf_tests.hpp b/modules/gapi/perf/common/gapi_video_perf_tests.hpp index 240014c..b59d6b7 100644 --- a/modules/gapi/perf/common/gapi_video_perf_tests.hpp +++ b/modules/gapi/perf/common/gapi_video_perf_tests.hpp @@ -16,11 +16,16 @@ using namespace perf; //------------------------------------------------------------------------------ +class BuildOptFlowPyramidPerfTest : public TestPerfParams> {}; class OptFlowLKPerfTest : public TestPerfParams,int, cv::TermCriteria,cv::GCompileArgs>> {}; class OptFlowLKForPyrPerfTest : public TestPerfParams,int, cv::TermCriteria,bool, cv::GCompileArgs>> {}; +class BuildPyr_CalcOptFlow_PipelinePerfTest : public TestPerfParams> {}; + } // 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 index 1095322..d1c81a9 100644 --- a/modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp +++ b/modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp @@ -18,6 +18,37 @@ namespace opencv_test //------------------------------------------------------------------------------ +PERF_TEST_P_(BuildOptFlowPyramidPerfTest, TestPerformance) +{ + std::vector outPyrOCV, outPyrGAPI; + int outMaxLevelOCV = 0, outMaxLevelGAPI = 0; + Scalar outMaxLevelSc; + + BuildOpticalFlowPyramidTestParams params; + std::tie(params.fileName, params.winSize, + params.maxLevel, params.withDerivatives, + params.pyrBorder, params.derivBorder, + params.tryReuseInputImage, params.compileArgs) = GetParam(); + + BuildOpticalFlowPyramidTestOutput outOCV { outPyrOCV, outMaxLevelOCV }; + BuildOpticalFlowPyramidTestOutput outGAPI { outPyrGAPI, outMaxLevelGAPI }; + + GComputation c = runOCVnGAPIBuildOptFlowPyramid(*this, params, outOCV, outGAPI); + + declare.in(in_mat1).out(outPyrGAPI); + + TEST_CYCLE() + { + c.apply(cv::gin(in_mat1), cv::gout(outPyrGAPI, outMaxLevelSc)); + } + outMaxLevelGAPI = static_cast(outMaxLevelSc[0]); + + // Comparison ////////////////////////////////////////////////////////////// + compareOutputPyramids(outOCV, outGAPI); + + SANITY_CHECK_NOTHING(); +} + PERF_TEST_P_(OptFlowLKPerfTest, TestPerformance) { std::vector outPtsOCV, outPtsGAPI, inPts; @@ -83,6 +114,44 @@ PERF_TEST_P_(OptFlowLKForPyrPerfTest, TestPerformance) SANITY_CHECK_NOTHING(); } +PERF_TEST_P_(BuildPyr_CalcOptFlow_PipelinePerfTest, TestPerformance) +{ + std::vector outPtsOCV, outPtsGAPI, inPts; + std::vector outStatusOCV, outStatusGAPI; + std::vector outErrOCV, outErrGAPI; + + BuildOpticalFlowPyramidTestParams params; + params.pyrBorder = BORDER_DEFAULT; + params.derivBorder = BORDER_DEFAULT; + params.tryReuseInputImage = true; + std::tie(params.fileName, params.winSize, + params.maxLevel, params.withDerivatives, + params.compileArgs) = GetParam(); + + auto customKernel = gapi::kernels(); + auto kernels = gapi::combine(customKernel, + params.compileArgs[0].get()); + params.compileArgs = compile_args(kernels); + + OptFlowLKTestOutput outOCV { outPtsOCV, outStatusOCV, outErrOCV }; + OptFlowLKTestOutput outGAPI { outPtsGAPI, outStatusGAPI, outErrGAPI }; + + cv::GComputation c = runOCVnGAPIOptFlowPipeline(*this, params, outOCV, outGAPI, inPts); + + 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(); +} + //------------------------------------------------------------------------------ } // 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 index 4b36dac..4ad7163 100644 --- a/modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp +++ b/modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp @@ -27,6 +27,29 @@ namespace namespace opencv_test { +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildOptFlowPyramidPerfTestCPU), + BuildOptFlowPyramidPerfTest, + Combine(Values("cv/optflow/rock_1.bmp", + "cv/optflow/frames/1080p_01.png"), + Values(7, 11), + Values(1000), + testing::Bool(), + Values(BORDER_DEFAULT, BORDER_TRANSPARENT), + Values(BORDER_DEFAULT, BORDER_TRANSPARENT), + testing::Bool(), + Values(cv::compile_args(VIDEO_CPU)))); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildOptFlowPyramidInternalPerfTestCPU), + BuildOptFlowPyramidPerfTest, + Combine(Values("cv/optflow/rock_1.bmp"), + Values(15), + Values(3), + Values(true), + Values(BORDER_REFLECT_101), + Values(BORDER_CONSTANT), + Values(true), + Values(cv::compile_args(VIDEO_CPU)))); + INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKPerfTestCPU), OptFlowLKPerfTest, Combine(Values("cv/optflow/rock_%01d.bmp", "cv/optflow/frames/1080p_%02d.png"), @@ -61,4 +84,20 @@ INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKInternalPerfTestCPU), 21, 0.05)), Values(true), Values(cv::compile_args(VIDEO_CPU)))); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildPyr_CalcOptFlow_PipelinePerfTestCPU), + BuildPyr_CalcOptFlow_PipelinePerfTest, + Combine(Values("cv/optflow/frames/1080p_%02d.png"), + Values(7, 11), + Values(1000), + Values(true, false), + Values(cv::compile_args(VIDEO_CPU)))); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildPyr_CalcOptFlow_PipelineInternalTestPerfCPU), + BuildPyr_CalcOptFlow_PipelinePerfTest, + Combine(Values("cv/optflow/rock_%01d.bmp"), + Values(15), + Values(3), + Values(true), + Values(cv::compile_args(VIDEO_CPU)))); } // opencv_test diff --git a/modules/gapi/src/api/kernels_video.cpp b/modules/gapi/src/api/kernels_video.cpp index 68e9cdf..eff6d48 100644 --- a/modules/gapi/src/api/kernels_video.cpp +++ b/modules/gapi/src/api/kernels_video.cpp @@ -12,13 +12,24 @@ namespace cv { namespace gapi { using namespace video; +GBuildPyrOutput buildOpticalFlowPyramid(const GMat &img, + const Size &winSize, + const GScalar &maxLevel, + bool withDerivatives, + int pyrBorder, + int derivBorder, + bool tryReuseInputImage) +{ + return GBuildOptFlowPyramid::on(img, winSize, maxLevel, withDerivatives, pyrBorder, + derivBorder, tryReuseInputImage); +} GOptFlowLKOutput calcOpticalFlowPyrLK(const GMat &prevImg, const GMat &nextImg, const cv::GArray &prevPts, const cv::GArray &predPts, const Size &winSize, - int maxLevel, + const GScalar &maxLevel, const TermCriteria &criteria, int flags, double minEigThresh) @@ -32,7 +43,7 @@ GOptFlowLKOutput calcOpticalFlowPyrLK(const cv::GArray &prevPyr, const cv::GArray &prevPts, const cv::GArray &predPts, const Size &winSize, - int maxLevel, + const GScalar &maxLevel, const TermCriteria &criteria, int flags, double minEigThresh) diff --git a/modules/gapi/src/backends/cpu/gcpuvideo.cpp b/modules/gapi/src/backends/cpu/gcpuvideo.cpp index 829e662..ac8e9e4 100644 --- a/modules/gapi/src/backends/cpu/gcpuvideo.cpp +++ b/modules/gapi/src/backends/cpu/gcpuvideo.cpp @@ -17,6 +17,25 @@ #ifdef HAVE_OPENCV_VIDEO +GAPI_OCV_KERNEL(GCPUBuildOptFlowPyramid, cv::gapi::video::GBuildOptFlowPyramid) +{ + static void run(const cv::Mat &img, + const cv::Size &winSize, + const cv::Scalar &maxLevel, + bool withDerivatives, + int pyrBorder, + int derivBorder, + bool tryReuseInputImage, + std::vector &outPyr, + cv::Scalar &outMaxLevel) + { + outMaxLevel = cv::buildOpticalFlowPyramid(img, outPyr, winSize, + static_cast(maxLevel[0]), + withDerivatives, pyrBorder, + derivBorder, tryReuseInputImage); + } +}; + GAPI_OCV_KERNEL(GCPUCalcOptFlowLK, cv::gapi::video::GCalcOptFlowLK) { static void run(const cv::Mat &prevImg, @@ -24,7 +43,7 @@ GAPI_OCV_KERNEL(GCPUCalcOptFlowLK, cv::gapi::video::GCalcOptFlowLK) const std::vector &prevPts, const std::vector &predPts, const cv::Size &winSize, - int maxLevel, + const cv::Scalar &maxLevel, const cv::TermCriteria &criteria, int flags, double minEigThresh, @@ -34,8 +53,8 @@ GAPI_OCV_KERNEL(GCPUCalcOptFlowLK, cv::gapi::video::GCalcOptFlowLK) { if (flags & cv::OPTFLOW_USE_INITIAL_FLOW) outPts = predPts; - cv::calcOpticalFlowPyrLK(prevImg, nextImg, prevPts, outPts, status, err, winSize, maxLevel, - criteria, flags, minEigThresh); + cv::calcOpticalFlowPyrLK(prevImg, nextImg, prevPts, outPts, status, err, winSize, + static_cast(maxLevel[0]), criteria, flags, minEigThresh); } }; @@ -46,7 +65,7 @@ GAPI_OCV_KERNEL(GCPUCalcOptFlowLKForPyr, cv::gapi::video::GCalcOptFlowLKForPyr) const std::vector &prevPts, const std::vector &predPts, const cv::Size &winSize, - int maxLevel, + const cv::Scalar &maxLevel, const cv::TermCriteria &criteria, int flags, double minEigThresh, @@ -56,15 +75,16 @@ GAPI_OCV_KERNEL(GCPUCalcOptFlowLKForPyr, cv::gapi::video::GCalcOptFlowLKForPyr) { if (flags & cv::OPTFLOW_USE_INITIAL_FLOW) outPts = predPts; - cv::calcOpticalFlowPyrLK(prevPyr, nextPyr, prevPts, outPts, status, err, winSize, maxLevel, - criteria, flags, minEigThresh); + cv::calcOpticalFlowPyrLK(prevPyr, nextPyr, prevPts, outPts, status, err, winSize, + static_cast(maxLevel[0]), criteria, flags, minEigThresh); } }; cv::gapi::GKernelPackage cv::gapi::video::cpu::kernels() { static auto pkg = cv::gapi::kernels - < GCPUCalcOptFlowLK + < GCPUBuildOptFlowPyramid + , GCPUCalcOptFlowLK , GCPUCalcOptFlowLKForPyr >(); return pkg; diff --git a/modules/gapi/test/common/gapi_video_tests.hpp b/modules/gapi/test/common/gapi_video_tests.hpp index c5dafac..df57bf4 100644 --- a/modules/gapi/test/common/gapi_video_tests.hpp +++ b/modules/gapi/test/common/gapi_video_tests.hpp @@ -11,6 +11,11 @@ namespace opencv_test { +GAPI_TEST_FIXTURE_SPEC_PARAMS(BuildOptFlowPyramidTest, + FIXTURE_API(std::string,int,int,bool,int,int,bool), 7, + fileName, winSize, maxLevel, withDerivatives, pyrBorder, + derivBorder, tryReuseInputImage) + GAPI_TEST_FIXTURE_SPEC_PARAMS(OptFlowLKTest, FIXTURE_API(std::string,int,tuple,int, cv::TermCriteria), 5, fileNamePattern, channels, pointsNum, winSize, criteria) @@ -18,6 +23,11 @@ GAPI_TEST_FIXTURE_SPEC_PARAMS(OptFlowLKTest, FIXTURE_API(std::string,int,tuple,int, cv::TermCriteria,bool), 6, fileNamePattern, channels, pointsNum, winSize, criteria,withDeriv) + +GAPI_TEST_FIXTURE_SPEC_PARAMS(BuildPyr_CalcOptFlow_PipelineTest, + FIXTURE_API(std::string,int,int,bool), 4, + fileNamePattern, winSize, maxLevel, withDerivatives) + } // opencv_test diff --git a/modules/gapi/test/common/gapi_video_tests_common.hpp b/modules/gapi/test/common/gapi_video_tests_common.hpp index c056d50..19efef0 100644 --- a/modules/gapi/test/common/gapi_video_tests_common.hpp +++ b/modules/gapi/test/common/gapi_video_tests_common.hpp @@ -20,6 +20,15 @@ namespace opencv_test { namespace { +G_TYPED_KERNEL(GMinScalar, , "custom.MinScalar") { + static GScalarDesc outMeta(GScalarDesc,GScalarDesc) { return empty_scalar_desc(); } +}; +GAPI_OCV_KERNEL(GCPUMinScalar, GMinScalar) { + static void run(const Scalar &sc1, const Scalar &sc2, Scalar &scOut) { + scOut = Scalar(std::min(sc1[0], sc2[0])); + } +}; + inline void initTrackingPointsArray(std::vector& points, int width, int height, int nPointsX, int nPointsY) { @@ -46,6 +55,14 @@ inline void initTrackingPointsArray(std::vector& points, int width, } } +struct BuildOpticalFlowPyramidTestOutput +{ + BuildOpticalFlowPyramidTestOutput(std::vector &pyr, int maxLvl) : + pyramid(pyr), maxLevel(maxLvl) { } + std::vector &pyramid; + int maxLevel = 0; +}; + template struct OptFlowLKTestInput { @@ -61,6 +78,31 @@ struct OptFlowLKTestOutput std::vector &errors; }; +struct BuildOpticalFlowPyramidTestParams +{ + BuildOpticalFlowPyramidTestParams(): fileName(""), winSize(-1), maxLevel(-1), + withDerivatives(false), pyrBorder(-1), + derivBorder(-1), tryReuseInputImage(false) { } + + BuildOpticalFlowPyramidTestParams(const std::string& name, int winSz, int maxLvl, + bool withDeriv, int pBorder, int dBorder, + bool tryReuse, const GCompileArgs& compArgs): + + fileName(name), winSize(winSz), maxLevel(maxLvl), + withDerivatives(withDeriv), pyrBorder(pBorder), + derivBorder(dBorder), tryReuseInputImage(tryReuse), + compileArgs(compArgs) { } + + std::string fileName = ""; + int winSize = -1; + int maxLevel = -1; + bool withDerivatives = false; + int pyrBorder = -1; + int derivBorder = -1; + bool tryReuseInputImage = false; + cv::GCompileArgs compileArgs; +}; + struct OptFlowLKTestParams { OptFlowLKTestParams(): fileNamePattern(""), format(1), channels(0), pointsNum{0, 0}, @@ -90,6 +132,42 @@ struct OptFlowLKTestParams #ifdef HAVE_OPENCV_VIDEO +inline GComputation runOCVnGAPIBuildOptFlowPyramid(TestFunctional& testInst, + const BuildOpticalFlowPyramidTestParams& params, + BuildOpticalFlowPyramidTestOutput& outOCV, + BuildOpticalFlowPyramidTestOutput& outGAPI) +{ + testInst.initMatFromImage(CV_8UC1, params.fileName); + + // OpenCV code ///////////////////////////////////////////////////////////// + { + outOCV.maxLevel = cv::buildOpticalFlowPyramid(testInst.in_mat1, outOCV.pyramid, + Size(params.winSize, params.winSize), + params.maxLevel, params.withDerivatives, + params.pyrBorder, params.derivBorder, + params.tryReuseInputImage); + } + + // G-API code ////////////////////////////////////////////////////////////// + GMat in; + GArray out; + GScalar outMaxLevel; + std::tie(out, outMaxLevel) = + cv::gapi::buildOpticalFlowPyramid(in, Size(params.winSize, params.winSize), + params.maxLevel, params.withDerivatives, + params.pyrBorder, params.derivBorder, + params.tryReuseInputImage); + + GComputation c(GIn(in), GOut(out, outMaxLevel)); + + Scalar outMaxLevelSc; + c.apply(gin(testInst.in_mat1), gout(outGAPI.pyramid, outMaxLevelSc), + std::move(const_cast(params.compileArgs))); + outGAPI.maxLevel = static_cast(outMaxLevelSc[0]); + + return c; +} + template cv::GComputation runOCVnGAPIOptFlowLK(OptFlowLKTestInput& in, int width, int height, @@ -184,8 +262,77 @@ inline cv::GComputation runOCVnGAPIOptFlowLKForPyr(TestFunctional& testInst, gapiOut); } +inline GComputation runOCVnGAPIOptFlowPipeline(TestFunctional& testInst, + const BuildOpticalFlowPyramidTestParams& params, + OptFlowLKTestOutput& outOCV, + OptFlowLKTestOutput& outGAPI, + std::vector& prevPoints) +{ + testInst.initMatsFromImages(3, params.fileName, 1); + + initTrackingPointsArray(prevPoints, testInst.in_mat1.cols, testInst.in_mat1.rows, 15, 15); + + Size winSize = Size(params.winSize, params.winSize); + + // OpenCV code ///////////////////////////////////////////////////////////// + { + std::vector pyr1, pyr2; + int maxLevel1 = cv::buildOpticalFlowPyramid(testInst.in_mat1, pyr1, winSize, + params.maxLevel, params.withDerivatives, + params.pyrBorder, params.derivBorder, + params.tryReuseInputImage); + int maxLevel2 = cv::buildOpticalFlowPyramid(testInst.in_mat2, pyr2, winSize, + params.maxLevel, params.withDerivatives, + params.pyrBorder, params.derivBorder, + params.tryReuseInputImage); + cv::calcOpticalFlowPyrLK(pyr1, pyr2, prevPoints, + outOCV.nextPoints, outOCV.statuses, outOCV.errors, + winSize, std::min(maxLevel1, maxLevel2)); + } + + // G-API code ////////////////////////////////////////////////////////////// + GMat in1, in2; + GArray gpyr1, gpyr2; + GScalar gmaxLevel1, gmaxLevel2; + GArray gprevPts, gpredPts, gnextPts; + GArray gstatuses; + GArray gerrors; + + std::tie(gpyr1, gmaxLevel1) = cv::gapi::buildOpticalFlowPyramid( + in1, winSize, params.maxLevel, + params.withDerivatives, params.pyrBorder, + params.derivBorder, params.tryReuseInputImage); + + std::tie(gpyr2, gmaxLevel2) = cv::gapi::buildOpticalFlowPyramid( + in2, winSize, params.maxLevel, + params.withDerivatives, params.pyrBorder, + params.derivBorder, params.tryReuseInputImage); + + GScalar gmaxLevel = GMinScalar::on(gmaxLevel1, gmaxLevel2); + + std::tie(gnextPts, gstatuses, gerrors) = cv::gapi::calcOpticalFlowPyrLK( + gpyr1, gpyr2, gprevPts, gpredPts, winSize, + gmaxLevel); + + cv::GComputation c(GIn(in1, in2, gprevPts, gpredPts), cv::GOut(gnextPts, gstatuses, gerrors)); + + c.apply(cv::gin(testInst.in_mat1, testInst.in_mat2, prevPoints, std::vector{ }), + cv::gout(outGAPI.nextPoints, outGAPI.statuses, outGAPI.errors), + std::move(const_cast(params.compileArgs))); + + return c; +} + #else // !HAVE_OPENCV_VIDEO +inline cv::GComputation runOCVnGAPIBuildOptFlowPyramid(TestFunctional&, + const BuildOpticalFlowPyramidTestParams&, + BuildOpticalFlowPyramidTestOutput&, + BuildOpticalFlowPyramidTestOutput&) +{ + GAPI_Assert(0 && "This function shouldn't be called without opencv_video"); +} + inline cv::GComputation runOCVnGAPIOptFlowLK(TestFunctional&, std::vector&, const OptFlowLKTestParams&, @@ -205,8 +352,29 @@ inline cv::GComputation runOCVnGAPIOptFlowLKForPyr(TestFunctional&, GAPI_Assert(0 && "This function shouldn't be called without opencv_video"); } +inline GComputation runOCVnGAPIOptFlowPipeline(TestFunctional&, + const BuildOpticalFlowPyramidTestParams&, + OptFlowLKTestOutput&, + OptFlowLKTestOutput&, + std::vector&) +{ + GAPI_Assert(0 && "This function shouldn't be called without opencv_video"); +} + #endif // HAVE_OPENCV_VIDEO +inline void compareOutputPyramids(const BuildOpticalFlowPyramidTestOutput& outOCV, + const BuildOpticalFlowPyramidTestOutput& outGAPI) +{ + GAPI_Assert(outGAPI.maxLevel == outOCV.maxLevel); + GAPI_Assert(outOCV.maxLevel >= 0); + size_t maxLevel = static_cast(outOCV.maxLevel); + for (size_t i = 0; i <= maxLevel; i++) + { + EXPECT_TRUE(AbsExact().to_compare_f()(outOCV.pyramid[i], outGAPI.pyramid[i])); + } +} + template inline bool compareVectorsAbsExactForOptFlow(std::vector outOCV, std::vector outGAPI) { @@ -221,7 +389,6 @@ inline void compareOutputsOptFlow(const OptFlowLKTestOutput& outOCV, EXPECT_TRUE(compareVectorsAbsExactForOptFlow(outGAPI.errors, outOCV.errors)); } - inline std::ostream& operator<<(std::ostream& os, const cv::TermCriteria& criteria) { os << "{"; @@ -236,7 +403,7 @@ inline std::ostream& operator<<(std::ostream& os, const cv::TermCriteria& criter os << "COUNT | EPS; "; break; default: - os << "TypeUndifined; "; + os << "TypeUndefined; "; break; }; diff --git a/modules/gapi/test/common/gapi_video_tests_inl.hpp b/modules/gapi/test/common/gapi_video_tests_inl.hpp index a5059a8..965c06a 100644 --- a/modules/gapi/test/common/gapi_video_tests_inl.hpp +++ b/modules/gapi/test/common/gapi_video_tests_inl.hpp @@ -12,6 +12,23 @@ namespace opencv_test { +TEST_P(BuildOptFlowPyramidTest, AccuracyTest) +{ + std::vector outPyrOCV, outPyrGAPI; + int outMaxLevelOCV = 0, outMaxLevelGAPI = 0; + + BuildOpticalFlowPyramidTestParams params { fileName, winSize, maxLevel, + withDerivatives, pyrBorder, derivBorder, + tryReuseInputImage, getCompileArgs() }; + + BuildOpticalFlowPyramidTestOutput outOCV { outPyrOCV, outMaxLevelOCV }; + BuildOpticalFlowPyramidTestOutput outGAPI { outPyrGAPI, outMaxLevelGAPI }; + + runOCVnGAPIBuildOptFlowPyramid(*this, params, outOCV, outGAPI); + + compareOutputPyramids(outOCV, outGAPI); +} + TEST_P(OptFlowLKTest, AccuracyTest) { std::vector outPtsOCV, outPtsGAPI, inPts; @@ -48,6 +65,29 @@ TEST_P(OptFlowLKTestForPyr, AccuracyTest) compareOutputsOptFlow(outOCV, outGAPI); } +TEST_P(BuildPyr_CalcOptFlow_PipelineTest, AccuracyTest) +{ + std::vector outPtsOCV, outPtsGAPI, inPts; + std::vector outStatusOCV, outStatusGAPI; + std::vector outErrOCV, outErrGAPI; + + BuildOpticalFlowPyramidTestParams params { fileNamePattern, winSize, maxLevel, + withDerivatives, BORDER_DEFAULT, BORDER_DEFAULT, + true, getCompileArgs() }; + + auto customKernel = gapi::kernels(); + auto kernels = gapi::combine(customKernel, + params.compileArgs[0].get()); + params.compileArgs = compile_args(kernels); + + OptFlowLKTestOutput outOCV { outPtsOCV, outStatusOCV, outErrOCV }; + OptFlowLKTestOutput outGAPI { outPtsGAPI, outStatusGAPI, outErrGAPI }; + + runOCVnGAPIOptFlowPipeline(*this, params, outOCV, outGAPI, inPts); + + 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 index c3e0f7e..c4659ad 100644 --- a/modules/gapi/test/cpu/gapi_video_tests_cpu.cpp +++ b/modules/gapi/test/cpu/gapi_video_tests_cpu.cpp @@ -26,6 +26,28 @@ namespace namespace opencv_test { +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildOptFlowPyramidTestCPU), BuildOptFlowPyramidTest, + Combine(Values(VIDEO_CPU), + Values("cv/optflow/rock_1.bmp", + "cv/optflow/frames/1080p_01.png"), + Values(7, 11), + Values(1000), + testing::Bool(), + Values(BORDER_DEFAULT, BORDER_TRANSPARENT), + Values(BORDER_DEFAULT, BORDER_TRANSPARENT), + testing::Bool())); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildOptFlowPyramidInternalTestCPU), + BuildOptFlowPyramidTest, + Combine(Values(VIDEO_CPU), + Values("cv/optflow/rock_1.bmp"), + Values(15), + Values(3), + Values(true), + Values(BORDER_REFLECT_101), + Values(BORDER_CONSTANT), + Values(true))); + INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKTestCPU), OptFlowLKTest, Combine(Values(VIDEO_CPU), Values("cv/optflow/rock_%01d.bmp", @@ -59,4 +81,20 @@ INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(OptFlowLKInternalTestCPU), OptFlowLKTes cv::TermCriteria::EPS, 21, 0.05)), Values(true))); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildPyr_CalcOptFlow_PipelineTestCPU), + BuildPyr_CalcOptFlow_PipelineTest, + Combine(Values(VIDEO_CPU), + Values("cv/optflow/frames/1080p_%02d.png"), + Values(7, 11), + Values(1000), + testing::Bool())); + +INSTANTIATE_TEST_CASE_MACRO_P(WITH_VIDEO(BuildPyr_CalcOptFlow_PipelineInternalTestCPU), + BuildPyr_CalcOptFlow_PipelineTest, + Combine(Values(VIDEO_CPU), + Values("cv/optflow/rock_%01d.bmp"), + Values(15), + Values(3), + Values(true))); } // opencv_test -- 2.7.4