Fix Farneback Optical Flow Algorithm
authorNamgoo Lee <devnglee@gmail.com>
Fri, 11 Jan 2019 04:00:08 +0000 (04:00 +0000)
committerNamgoo Lee <devnglee@gmail.com>
Sun, 13 Jan 2019 15:14:45 +0000 (15:14 +0000)
- Before this PR, following tests failed on some platform.
  CUDA_OptFlow/FarnebackOpticalFlow.Accuracy/19
  CUDA_OptFlow/FarnebackOpticalFlow.Accuracy/23

- The algorithm now recognizes the OPTFLOW_USE_INITIAL_FLOW flag.
  Previously, when the flag was set, it did not use the flow data
  passed as input, instead used some garbage data in memory.

- More strict test limit.

modules/cudaoptflow/src/farneback.cpp
modules/cudaoptflow/test/test_optflow.cpp
modules/video/src/optflowgf.cpp

index 43032b4..69ea437 100644 (file)
@@ -165,10 +165,29 @@ namespace
     {
         const GpuMat frame0 = _frame0.getGpuMat();
         const GpuMat frame1 = _frame1.getGpuMat();
+        GpuMat flow = _flow.getGpuMat();
 
-        BufferPool pool(stream);
-        GpuMat flowx = pool.getBuffer(frame0.size(), CV_32FC1);
-        GpuMat flowy = pool.getBuffer(frame0.size(), CV_32FC1);
+        CV_Assert(frame0.channels() == 1 && frame1.channels() == 1);
+        CV_Assert(frame0.size() == frame1.size());
+
+        GpuMat flowx, flowy;
+
+        // If flag is set, check for integrity; if not set, allocate memory space
+        if (flags_ & OPTFLOW_USE_INITIAL_FLOW)
+        {
+            CV_Assert(flow.size() == frame0.size() && flow.channels() == 2 &&
+                      flow.depth() == CV_32F);
+
+            std::vector<cuda::GpuMat> _flows(2);
+            cuda::split(flow, _flows, stream);
+            flowx = _flows[0];
+            flowy = _flows[1];
+        }
+        else
+        {
+            flowx.create(frame0.size(), CV_32FC1);
+            flowy.create(frame0.size(), CV_32FC1);
+        }
 
         calcImpl(frame0, frame1, flowx, flowy, stream);
 
@@ -291,8 +310,6 @@ namespace
 
     void FarnebackOpticalFlowImpl::calcImpl(const GpuMat &frame0, const GpuMat &frame1, GpuMat &flowx, GpuMat &flowy, Stream &stream)
     {
-        CV_Assert(frame0.channels() == 1 && frame1.channels() == 1);
-        CV_Assert(frame0.size() == frame1.size());
         CV_Assert(polyN_ == 5 || polyN_ == 7);
         CV_Assert(!fastPyramids_ || std::abs(pyrScale_ - 0.5) < 1e-6);
 
@@ -303,8 +320,6 @@ namespace
         Size size = frame0.size();
         GpuMat prevFlowX, prevFlowY, curFlowX, curFlowY;
 
-        flowx.create(size, CV_32F);
-        flowy.create(size, CV_32F);
         GpuMat flowx0 = flowx;
         GpuMat flowy0 = flowy;
 
index c856474..37ffe9e 100644 (file)
@@ -337,7 +337,15 @@ CUDA_TEST_P(FarnebackOpticalFlow, Accuracy)
         frame0, frame1, flow, farn->getPyrScale(), farn->getNumLevels(), farn->getWinSize(),
         farn->getNumIters(), farn->getPolyN(), farn->getPolySigma(), farn->getFlags());
 
-    EXPECT_MAT_SIMILAR(flow, d_flow, 0.1);
+    // Relax test limit when the flag is set
+    if (farn->getFlags() & cv::OPTFLOW_FARNEBACK_GAUSSIAN)
+    {
+        EXPECT_MAT_SIMILAR(flow, d_flow, 2e-2);
+    }
+    else
+    {
+        EXPECT_MAT_SIMILAR(flow, d_flow, 1e-4);
+    }
 }
 
 INSTANTIATE_TEST_CASE_P(CUDA_OptFlow, FarnebackOpticalFlow, testing::Combine(
index 2e6251b..e06dbbf 100644 (file)
@@ -646,8 +646,6 @@ private:
         Size size = frame0.size();
         UMat prevFlowX, prevFlowY, curFlowX, curFlowY;
 
-        flowx.create(size, CV_32F);
-        flowy.create(size, CV_32F);
         UMat flowx0 = flowx;
         UMat flowy0 = flowy;
 
@@ -1075,12 +1073,19 @@ private:
             return false;
 
         std::vector<UMat> flowar;
-        if (!_flow0.empty())
+
+        // If flag is set, check for integrity; if not set, allocate memory space
+        if (flags_ & OPTFLOW_USE_INITIAL_FLOW)
+        {
+            if (_flow0.empty() || _flow0.size() != _prev0.size() || _flow0.channels() != 2 ||
+                _flow0.depth() != CV_32F)
+                return false;
             split(_flow0, flowar);
+        }
         else
         {
-            flowar.push_back(UMat());
-            flowar.push_back(UMat());
+            flowar.push_back(UMat(_prev0.size(), CV_32FC1));
+            flowar.push_back(UMat(_prev0.size(), CV_32FC1));
         }
         if(!this->operator()(_prev0.getUMat(), _next0.getUMat(), flowar[0], flowar[1])){
             return false;
@@ -1112,7 +1117,14 @@ void FarnebackOpticalFlowImpl::calc(InputArray _prev0, InputArray _next0,
 
     CV_Assert( prev0.size() == next0.size() && prev0.channels() == next0.channels() &&
                prev0.channels() == 1 && pyrScale_ < 1 );
-    _flow0.create( prev0.size(), CV_32FC2 );
+
+    // If flag is set, check for integrity; if not set, allocate memory space
+    if( flags_ & OPTFLOW_USE_INITIAL_FLOW )
+        CV_Assert( _flow0.size() == prev0.size() && _flow0.channels() == 2 &&
+                   _flow0.depth() == CV_32F );
+    else
+        _flow0.create( prev0.size(), CV_32FC2 );
+
     Mat flow0 = _flow0.getMat();
 
     for( k = 0, scale = 1; k < levels; k++ )