gapi: buildOpticalFlowPyramid() interface, CPUkernel and CPUtests implementation
authorOrestChura <orest.chura@intel.com>
Mon, 13 Apr 2020 21:53:01 +0000 (00:53 +0300)
committerOrestChura <orest.chura@intel.com>
Tue, 21 Apr 2020 11:36:42 +0000 (14:36 +0300)
     - 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

modules/gapi/include/opencv2/gapi/cpu/gcpukernel.hpp
modules/gapi/include/opencv2/gapi/video.hpp
modules/gapi/perf/common/gapi_video_perf_tests.hpp
modules/gapi/perf/common/gapi_video_perf_tests_inl.hpp
modules/gapi/perf/cpu/gapi_video_perf_tests_cpu.cpp
modules/gapi/src/api/kernels_video.cpp
modules/gapi/src/backends/cpu/gcpuvideo.cpp
modules/gapi/test/common/gapi_video_tests.hpp
modules/gapi/test/common/gapi_video_tests_common.hpp
modules/gapi/test/common/gapi_video_tests_inl.hpp
modules/gapi/test/cpu/gapi_video_tests_cpu.cpp

index 764e085..98d5485 100644 (file)
@@ -256,6 +256,12 @@ template<typename U> struct get_out<cv::GArray<U>>
         return ctx.outVecR<U>(idx);
     }
 };
+
+//FIXME(dm): GArray<Mat>/GArray<GMat> conversion should be done more gracefully in the system
+template<> struct get_out<cv::GArray<cv::GMat> >: public get_out<cv::GArray<cv::Mat> >
+{
+};
+
 template<typename U> struct get_out<cv::GOpaque<U>>
 {
     static U& get(GCPUContext &ctx, int idx)
index 7602e1d..7f90134 100644 (file)
 namespace cv { namespace gapi {
 namespace  video
 {
-    using GOptFlowLKOutput = std::tuple<cv::GArray<cv::Point2f>,
-                                        cv::GArray<uchar>,
-                                        cv::GArray<float>>;
-
-    G_TYPED_KERNEL(GCalcOptFlowLK,
-                   <GOptFlowLKOutput(GMat,GMat,cv::GArray<cv::Point2f>,cv::GArray<cv::Point2f>,Size,
-                                     int,TermCriteria,int,double)>,
-                   "org.opencv.video.calcOpticalFlowPyrLK")
+using GBuildPyrOutput  = std::tuple<GArray<GMat>, GScalar>;
+
+using GOptFlowLKOutput = std::tuple<cv::GArray<cv::Point2f>,
+                                    cv::GArray<uchar>,
+                                    cv::GArray<float>>;
+
+G_TYPED_KERNEL(GBuildOptFlowPyramid, <GBuildPyrOutput(GMat,Size,GScalar,bool,int,int,bool)>,
+               "org.opencv.video.buildOpticalFlowPyramid")
+{
+    static std::tuple<GArrayDesc,GScalarDesc>
+            outMeta(GMatDesc,const Size&,GScalarDesc,bool,int,int,bool)
+    {
+        return std::make_tuple(empty_array_desc(), empty_scalar_desc());
+    }
+};
+
+G_TYPED_KERNEL(GCalcOptFlowLK,
+               <GOptFlowLKOutput(GMat,GMat,cv::GArray<cv::Point2f>,cv::GArray<cv::Point2f>,Size,
+                                 GScalar,TermCriteria,int,double)>,
+               "org.opencv.video.calcOpticalFlowPyrLK")
+{
+    static std::tuple<GArrayDesc,GArrayDesc,GArrayDesc> outMeta(GMatDesc,GMatDesc,GArrayDesc,
+                                                                GArrayDesc,const Size&,GScalarDesc,
+                                                                const TermCriteria&,int,double)
     {
-        static std::tuple<GArrayDesc,GArrayDesc,GArrayDesc> 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,
-                   <GOptFlowLKOutput(cv::GArray<cv::GMat>,cv::GArray<cv::GMat>,
-                                     cv::GArray<cv::Point2f>,cv::GArray<cv::Point2f>,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,
+               <GOptFlowLKOutput(cv::GArray<cv::GMat>,cv::GArray<cv::GMat>,
+                                 cv::GArray<cv::Point2f>,cv::GArray<cv::Point2f>,Size,GScalar,
+                                 TermCriteria,int,double)>,
+               "org.opencv.video.calcOpticalFlowPyrLKForPyr")
+{
+    static std::tuple<GArrayDesc,GArrayDesc,GArrayDesc> outMeta(GArrayDesc,GArrayDesc,
+                                                                GArrayDesc,GArrayDesc,
+                                                                const Size&,GScalarDesc,
+                                                                const TermCriteria&,int,double)
     {
-        static std::tuple<GArrayDesc,GArrayDesc,GArrayDesc> 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<GArray<GMat>, 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<Point2f> &prevPts,
                      const GArray<Point2f> &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<GMat>    &prevPyr,
                      const GArray<Point2f> &prevPts,
                      const GArray<Point2f> &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),
index 240014c..b59d6b7 100644 (file)
@@ -16,11 +16,16 @@ using namespace perf;
 
 //------------------------------------------------------------------------------
 
+class BuildOptFlowPyramidPerfTest : public TestPerfParams<tuple<std::string,int,int,bool,int,int,
+                                                                bool,GCompileArgs>> {};
 class OptFlowLKPerfTest : public TestPerfParams<tuple<std::string,int,tuple<int,int>,int,
                                                       cv::TermCriteria,cv::GCompileArgs>> {};
 class OptFlowLKForPyrPerfTest : public TestPerfParams<tuple<std::string,int,tuple<int,int>,int,
                                                             cv::TermCriteria,bool,
                                                             cv::GCompileArgs>> {};
+class BuildPyr_CalcOptFlow_PipelinePerfTest : public TestPerfParams<tuple<std::string,int,int,bool,
+                                                                          cv::GCompileArgs>> {};
+
 } // opencv_test
 
 #endif // OPENCV_GAPI_VIDEO_PERF_TESTS_HPP
index 1095322..d1c81a9 100644 (file)
@@ -18,6 +18,37 @@ namespace opencv_test
 
 //------------------------------------------------------------------------------
 
+PERF_TEST_P_(BuildOptFlowPyramidPerfTest, TestPerformance)
+{
+    std::vector<Mat> 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<int>(outMaxLevelSc[0]);
+
+    // Comparison //////////////////////////////////////////////////////////////
+    compareOutputPyramids(outOCV, outGAPI);
+
+    SANITY_CHECK_NOTHING();
+}
+
 PERF_TEST_P_(OptFlowLKPerfTest, TestPerformance)
 {
     std::vector<cv::Point2f> outPtsOCV,    outPtsGAPI,    inPts;
@@ -83,6 +114,44 @@ PERF_TEST_P_(OptFlowLKForPyrPerfTest, TestPerformance)
     SANITY_CHECK_NOTHING();
 }
 
+PERF_TEST_P_(BuildPyr_CalcOptFlow_PipelinePerfTest, TestPerformance)
+{
+    std::vector<Point2f> outPtsOCV,    outPtsGAPI,    inPts;
+    std::vector<uchar>   outStatusOCV, outStatusGAPI;
+    std::vector<float>   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<GCPUMinScalar>();
+    auto kernels       = gapi::combine(customKernel,
+                                       params.compileArgs[0].get<gapi::GKernelPackage>());
+    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::Point2f>{ }),
+                cv::gout(outPtsGAPI, outStatusGAPI, outErrGAPI));
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    compareOutputsOptFlow(outOCV, outGAPI);
+
+    SANITY_CHECK_NOTHING();
+}
+
 //------------------------------------------------------------------------------
 
 } // opencv_test
index 4b36dac..4ad7163 100644 (file)
@@ -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
index 68e9cdf..eff6d48 100644 (file)
 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<cv::Point2f> &prevPts,
                                       const cv::GArray<cv::Point2f> &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<cv::GMat>    &prevPyr,
                                       const cv::GArray<cv::Point2f> &prevPts,
                                       const cv::GArray<cv::Point2f> &predPts,
                                       const Size                    &winSize,
-                                            int                      maxLevel,
+                                      const GScalar                 &maxLevel,
                                       const TermCriteria            &criteria,
                                             int                      flags,
                                             double                   minEigThresh)
index 829e662..ac8e9e4 100644 (file)
 
 #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<cv::Mat> &outPyr,
+                          cv::Scalar           &outMaxLevel)
+    {
+        outMaxLevel = cv::buildOpticalFlowPyramid(img, outPyr, winSize,
+                                                  static_cast<int>(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<cv::Point2f> &prevPts,
                     const std::vector<cv::Point2f> &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<int>(maxLevel[0]), criteria, flags, minEigThresh);
     }
 };
 
@@ -46,7 +65,7 @@ GAPI_OCV_KERNEL(GCPUCalcOptFlowLKForPyr, cv::gapi::video::GCalcOptFlowLKForPyr)
                     const std::vector<cv::Point2f> &prevPts,
                     const std::vector<cv::Point2f> &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<int>(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;
index c5dafac..df57bf4 100644 (file)
 
 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,int>,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<i
 GAPI_TEST_FIXTURE_SPEC_PARAMS(OptFlowLKTestForPyr, FIXTURE_API(std::string,int,tuple<int,int>,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
 
 
index c056d50..19efef0 100644 (file)
@@ -20,6 +20,15 @@ namespace opencv_test
 {
 namespace
 {
+G_TYPED_KERNEL(GMinScalar, <GScalar(GScalar,GScalar)>, "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<cv::Point2f>& points, int width, int height,
                                     int nPointsX, int nPointsY)
 {
@@ -46,6 +55,14 @@ inline void initTrackingPointsArray(std::vector<cv::Point2f>& points, int width,
     }
 }
 
+struct BuildOpticalFlowPyramidTestOutput
+{
+    BuildOpticalFlowPyramidTestOutput(std::vector<Mat> &pyr, int maxLvl) :
+                                      pyramid(pyr), maxLevel(maxLvl) { }
+    std::vector<Mat> &pyramid;
+    int               maxLevel = 0;
+};
+
 template<typename Type>
 struct OptFlowLKTestInput
 {
@@ -61,6 +78,31 @@ struct OptFlowLKTestOutput
     std::vector<float>       &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<GMat> 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<GCompileArgs&>(params.compileArgs)));
+    outGAPI.maxLevel = static_cast<int>(outMaxLevelSc[0]);
+
+    return c;
+}
+
 template<typename GType, typename Type>
 cv::GComputation runOCVnGAPIOptFlowLK(OptFlowLKTestInput<Type>& 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<Point2f>& 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<Mat> 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<GMat>        gpyr1,      gpyr2;
+    GScalar             gmaxLevel1, gmaxLevel2;
+    GArray<cv::Point2f> gprevPts, gpredPts, gnextPts;
+    GArray<uchar>       gstatuses;
+    GArray<float>       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::Point2f>{ }),
+            cv::gout(outGAPI.nextPoints, outGAPI.statuses, outGAPI.errors),
+            std::move(const_cast<cv::GCompileArgs&>(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<cv::Point2f>&,
                                              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<Point2f>&)
+{
+    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<size_t>(outOCV.maxLevel);
+    for (size_t i = 0; i <= maxLevel; i++)
+    {
+        EXPECT_TRUE(AbsExact().to_compare_f()(outOCV.pyramid[i], outGAPI.pyramid[i]));
+    }
+}
+
 template <typename Elem>
 inline bool compareVectorsAbsExactForOptFlow(std::vector<Elem> outOCV, std::vector<Elem> 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;
     };
 
index a5059a8..965c06a 100644 (file)
 namespace opencv_test
 {
 
+TEST_P(BuildOptFlowPyramidTest, AccuracyTest)
+{
+    std::vector<Mat> 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<cv::Point2f> outPtsOCV,    outPtsGAPI,    inPts;
@@ -48,6 +65,29 @@ TEST_P(OptFlowLKTestForPyr, AccuracyTest)
     compareOutputsOptFlow(outOCV, outGAPI);
 }
 
+TEST_P(BuildPyr_CalcOptFlow_PipelineTest, AccuracyTest)
+{
+    std::vector<Point2f> outPtsOCV,    outPtsGAPI,    inPts;
+    std::vector<uchar>   outStatusOCV, outStatusGAPI;
+    std::vector<float>   outErrOCV,    outErrGAPI;
+
+    BuildOpticalFlowPyramidTestParams params { fileNamePattern, winSize, maxLevel,
+                                              withDerivatives, BORDER_DEFAULT, BORDER_DEFAULT,
+                                              true, getCompileArgs() };
+
+    auto customKernel  = gapi::kernels<GCPUMinScalar>();
+    auto kernels       = gapi::combine(customKernel,
+                                       params.compileArgs[0].get<gapi::GKernelPackage>());
+    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
index c3e0f7e..c4659ad 100644 (file)
@@ -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