Advertisement

目标检测光流法(三):opencv下光流Farneback法

阅读量:

在上一节中提到过的calcOpticalFlowPyrLK光流算法,在可以看出它本质上是一种基于稀疏特征求取的光流方法。即我们需要首先定位并确定这些(关键)特征点的位置,并对其进行相应的处理。本节将介绍一种全局性的密集光流计算方法,请注意这里的"每个"具体指代的是每个像素或区域执行相应的计算操作。

以详细的方式介绍参数,并对 参见opencv手册 进行参考

涉及大量控制变量的一类问题,在实际操作中往往需要花费一些时间来查看各个控制项的具体表现情况;部分参数对整体效果的影响相对较小;因此可以选择依据其建议设置的那些参数;共有10个关键的控制变量需要进行配置;按照以下步骤进行配置

prevImg: 第一个输入图
nextImg: 第二个输入图
Flow: 输出相应的光流矩阵;该矩阵尺寸与原始图像一致;然而每个元素并非单一数值;而是由两个数值构成;分别代表该点在x轴及y轴上的运动强度及变化方向
因此,在视觉呈现上实现这一目标确实需要较为复杂的处理逻辑
那么上述提到的第一张及第二张图片与其之间又存在何关联呢?
答案是:第一张图片记录了初始场景状态;第二张图片则反映了时间推移后的变化情况;而所生成的光流场则精确描绘了各区域从第一状态向第二状态转变时的具体运动轨迹及其速度信息

这里写图片描述

pyrScale :一个构造图像金字塔的参数,一般就认为是0.5最好了,也就是将图像缩小一半。那么为什么要构造金字塔呢?这应该是与算法本身的设计有关,其实很多地方在检测特征的时候都会涉及到图像的金字塔,设想下如果有个特征点在原始尺寸与其缩小的尺寸下都是特征点的话,那么这个特征点就很有效了吧。
Levels :依然是与金字塔有关参数,常设值1.
Winsize :相当于一个均值滤波的作用,窗口大小决定了其噪声的抑制能力什么的。
Iterations :在每层金字塔上的迭代次数。
polyN :点与附近领域点之间的联系作用,一般为5,7等等即可。
polySigma :像素点的一个平滑水平,一般1-1.5即可。
Flags :一个标记,决定计算方法。

该方法对结果的影响程度可以通过以下方式探索:具体可以尝试调整相关参数并观察输出效果。
该方法可应用于分析上述两幅图像之间的光流场计算过程。
此外,在这一过程中我们还能够提取出相应的Flow场数据。
这些参数直接影响最终提取出的Flow场数据特性

复制代码
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/video/video.hpp>
    #include <opencv2/core/core.hpp>
    #include <opencv2/imgproc/types_c.h>
    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    using namespace cv;
    #define UNKNOWN_FLOW_THRESH 1e9  
    
    void makecolorwheel(vector<Scalar> &colorwheel)  
    {  
    int RY = 15;  
    int YG = 6;  
    int GC = 4;  
    int CB = 11;  
    int BM = 13;  
    int MR = 6;  
    
    int i;  
    
    for (i = 0; i < RY; i++) colorwheel.push_back(Scalar(255,       255*i/RY,     0));  
    for (i = 0; i < YG; i++) colorwheel.push_back(Scalar(255-255*i/YG, 255,       0));  
    for (i = 0; i < GC; i++) colorwheel.push_back(Scalar(0,         255,      255*i/GC));  
    for (i = 0; i < CB; i++) colorwheel.push_back(Scalar(0,         255-255*i/CB, 255));  
    for (i = 0; i < BM; i++) colorwheel.push_back(Scalar(255*i/BM,      0,        255));  
    for (i = 0; i < MR; i++) colorwheel.push_back(Scalar(255,       0,        255-255*i/MR));  
    }  
    
    void motionToColor(Mat flow, Mat &color)  
    {  
    if (color.empty())  
        color.create(flow.rows, flow.cols, CV_8UC3);  
    
    static vector<Scalar> colorwheel; //Scalar r,g,b  
    if (colorwheel.empty())  
        makecolorwheel(colorwheel);  
    
    // determine motion range:  
    float maxrad = -1;  
    
    // Find max flow to normalize fx and fy  
    for (int i= 0; i < flow.rows; ++i)   
    {  
        for (int j = 0; j < flow.cols; ++j)   
        {  
            Vec2f flow_at_point = flow.at<Vec2f>(i, j);  
            float fx = flow_at_point[0];  
            float fy = flow_at_point[1];  
            if ((fabs(fx) >  UNKNOWN_FLOW_THRESH) || (fabs(fy) >  UNKNOWN_FLOW_THRESH))  
                continue;  
            float rad = sqrt(fx * fx + fy * fy);  
            maxrad = maxrad > rad ? maxrad : rad;  
        }  
    }  
    
    for (int i= 0; i < flow.rows; ++i)   
    {  
        for (int j = 0; j < flow.cols; ++j)   
        {  
            uchar *data = color.data + color.step[0] * i + color.step[1] * j;  
            Vec2f flow_at_point = flow.at<Vec2f>(i, j);  
    
            float fx = flow_at_point[0] / maxrad;  
            float fy = flow_at_point[1] / maxrad;  
            if ((fabs(fx) >  UNKNOWN_FLOW_THRESH) || (fabs(fy) >  UNKNOWN_FLOW_THRESH))  
            {  
                data[0] = data[1] = data[2] = 0;  
                continue;  
            }  
            float rad = sqrt(fx * fx + fy * fy);  
    
            float angle = atan2(-fy, -fx) / CV_PI;  
            float fk = (angle + 1.0) / 2.0 * (colorwheel.size()-1);  
            int k0 = (int)fk;  
            int k1 = (k0 + 1) % colorwheel.size();  
            float f = fk - k0;  
            //f = 0; // uncomment to see original color wheel  
    
            for (int b = 0; b < 3; b++)   
            {  
                float col0 = colorwheel[k0][b] / 255.0;  
                float col1 = colorwheel[k1][b] / 255.0;  
                float col = (1 - f) * col0 + f * col1;  
                if (rad <= 1)  
                    col = 1 - rad * (1 - col); // increase saturation with radius  
                else  
                    col *= .75; // out of range  
                data[2 - b] = (int)(255.0 * col);  
            }  
        }  
    }  
    }  
    
    
    int main()
    {
    Mat I1;
    Mat I2;
    Mat flow;
    //读取两个图像---相邻帧
    I1 = imread("I1.jpg",0);//读取为灰度图像
    I2 = imread("I2.jpg",0);
    calcOpticalFlowFarneback(I1,I2,flow,0.5,3,20,3,5,1.2,0);
    //cout<<I1.size()<<flow.size()<<flow.at<Vec2f>(10,10)<<endl;
    //flow = abs(flow);
    Mat motion2color;  
    motionToColor(flow, motion2color);  
    imshow("flow",motion2color);
    waitKey(0);
    return 0;
    }

可以看到程序的主要功能是如何实现将计算得到的光流flow进行可视化展示。已知该数据结构为矩阵形式,在其中每个元素包含两个数值的情况下进行处理较为繁琐。而实现这一过程所使用的展示函数主要参考相关博客资源

光流Optical Flow介绍与OpenCV实现

该博客里面的文章内容也是值得一读的。
具体而言,在上一篇中展示了
其中原始图像如上文所示

这里写图片描述

颜色越深表示该部分存在的运动变化越大。

早期版本(如3.0之前的)的OpenCV中还提供了多个光流计算函数(包括块匹配BM法、HS法、LK法等)。每种方法通常都源自一篇相关文献。到了3.0版本时,默认不再包含这些经典的方法如块匹配和HS算法。因此,如果希望继续使用这些方法,则需要回溯到之前的OpenCV版本。

全部评论 (0)

还没有任何评论哟~