Farneback光流


    使用稀疏光流跟踪算法,可以实现关键点跟踪。大概流程为:首先在上一帧图像中检测出关键点,然后在下一帧图像中评估关键点位移量。如果下一帧图像以统一的方向偏移,则可以剔除该偏移以达到视频稳像效果。

    关键点检测是稀疏光流跟踪基础,一般情况下,关键点为角点,边缘上的点由于孔径问题难以在系数光流中实现跟踪。使用稠密光流算法可以描述所有点(包括边缘上点)的运动情况,这在某些边缘较多的场景中,稠密光流算法更加便于描述物体运动。

    Faerneback 算法使用多项式拟合平面方式评估图像中各个点的运动情况,其拟合平面方程为:

    当图像发生平移后,平移前后拟合的二次多项式也同样存在一个平移关系:,由于  已知,则可以求解出 

    在一维图像下,可使用下图表示:

    

    上图中,拟合前后两帧图像相同位置附近区间的二次曲线,通过模型  可求解偏移量 d。

    以上跟踪精度取决于拟合函数精度,当拟合区间越小,更细小的变化可以被二次函数表现处理。但过小的拟合区间可能受到噪声影响,故需要选择一个合适的拟合区间。同时,可以使用多次拟合的方式来提升跟踪精度,第一次拟合时,下一帧图像拟合中心与上一帧图像拟合中心保持一致;当通过第一次拟合求解出偏移量后,使用偏移后位置作为下一帧图像拟合中心,可以求解更精确拟合函数,从而得到更精确跟踪精度。

    通过金字塔分层方式可以跟踪到大偏移。首先使用较低分辨率跟踪各点偏移量,再以已经得到偏移量作为拟合中心,使用更高分辨率图像求解更精确偏移量,即可跟踪较大偏移。

    

    在 opencv 中,使用 cv::calcOpticalFlowFarneback() 函数实现基于二次多项式拟合的稠密光流计算,其函数原型如下:

    void cv::calcOpticalFlowFarneback(cv::InputArray preImg, cv::InputArray nextImg, cv::InputOutputArray flow,

                                                          double pyrScale, int levels, int winsize, int iterations, int polyN, double polySigma, int flags);

    preImg, nextImg: 传入图像序列中前一帧与后一帧图像,图像格式均为8位单通道,且尺寸应该保持一致。

    flow: 函数记录各点运动向量,图像格式位32位浮点双通道(CV_32FC2),图像尺寸与 preImg, nextImg 保持一致。

    pyrScale: 表示金字塔各层缩减系数,一般使用 .5。

    levels: 表示金字塔层数。

    winsize: 在光流跟踪前,使用 winsize 窗口大小对图像平滑,避免噪声干扰;可将 flags 设置为 cv::OPTFLOW_FARNEBACK_GAUSSIAN 以使用 Gaussian 平滑。

    iterations: 通过控制各层上迭代拟合次数控制跟踪精度。

    polyN: 控制二次多项式拟合区间大小。

    polySigma: polyN = 5, polySigma = 1.1; polyN = 7, polySigma = 1.5。

    flags: 当使用 cv::OPTFLOW_USE_INITIAL_FLOW 时,flow 被同时当作输入参数,表示初始光流速度场;通常在序列视频中,相邻帧运动方向具有相似性,故可以使用上帧速度场作为初始猜测。

    以下代码计算两帧图像间的速度场:

 

 1 cv::Mat img0 = cv::imread("prev.bmp", cv::IMREAD_GRAYSCALE);
 2 cv::Mat img1 = cv::imread("next.bmp", cv::IMREAD_GRAYSCALE);
 3 cv::Mat flow(img0.size(), CV_32FC2);
 4 cv::calcOpticalFlowFarneback(img0,
 5         img1,
 6         flow,
 7         .5,
 8         3,
 9         5,
10         1,
11         7,
12         1.5,
13         cv::OPTFLOW_FARNEBACK_GAUSSIAN);
14 
15 cv::Mat flow2(flow.size(), CV_8UC1);
16 for (int y = 0; y < flow.rows; ++y)
17 {
18     float* data1 = flow.ptr<float>(y);
19     uchar* data2 = flow2.ptr<uchar>(y);
20     for (int x = 0; x < flow.cols; ++x)
21     {
22         float mx = data1[x * 2];
23         float my = data1[x * 2 + 1];
24         float mv = sqrt(mx * mx + my * my);
25         data2[x] = cv::saturate_cast<uchar>(mv * 10);
26     }
27 }
28 cv::imwrite("Farneback.bmp", flow2);

 

 

    执行效果如下:

        

   

    

 

    参考资料: Learning Opencv 3   Adrian Kaehler & Gary Bradski


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM