天天看点

光流optical flow的理解和求法1.什么是光流2.计算代码示例

1.什么是光流

        通俗的讲就是通过一个图片序列,把每张图像中每个像素的运动速度和运动方向找出来就是光流场。那怎么找呢?咱们直观理解肯定是:第t帧的时候A点的位置是(x1, y1),那么我们在第t+1帧的时候再找到A点,假如它的位置是(x2,y2),那么我们就可以确定A点的运动了:(ux, vy) = (x2, y2) - (x1,y1)。那怎么知道第t+1帧的时候A点的位置呢? 这就存在很多的光流计算方法了。

       1981年,Horn和Schunck创造性地将二维速度场与灰度相联系,引入光流约束方程,得到光流计算的基本算法。人们基于不同的理论基础提出各种光流计算方法,算法性能各有不同。Barron等人对多种光流计算技术进行了总结,按照理论基础与数学方法的区别把它们分成四种:基于梯度的方法、基于匹配的方法、基于能量的方法、基于相位的方法。近年来神经动力学方法也颇受学者重视。      

        稀疏光流:只计算某些点集的光流。稠密光流:图像上所有像素点的光流都计算出来。光流场是图片中每个像素都有一个x方向和y方向的位移,所以在上面那些光流计算结束后得到的光流flow是个和原来图像大小相等的双通道图像。

2.计算代码示例

#include <iostream>  
    #include "opencv2/opencv.hpp"  
    using namespace cv;  
    using namespace std;  
    #define UNKNOWN_FLOW_THRESH 1e9  
      
    // Color encoding of flow vectors from:  
    // http://members.shaw.ca/quadibloc/other/colint.htm  
    // This code is modified from:  
    // http://vision.middlebury.edu/flow/data/  
    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(int, char**)  
    {  
        VideoCapture cap;  
        cap.open(0);  
        //cap.open("test_02.wmv");  
      
        if( !cap.isOpened() )  
            return -1;  
      
        Mat prevgray, gray, flow, cflow, frame;  
        namedWindow("flow", 1);  
      
        Mat motion2color;  
      
        for(;;)  
        {  
            double t = (double)cvGetTickCount();  
      
            cap >> frame;  
            cvtColor(frame, gray, CV_BGR2GRAY);  
            imshow("original", frame);  
      
            if( prevgray.data )  
            {  
                calcOpticalFlowFarneback(prevgray, gray, flow, 0.5, 3, 15, 3, 5, 1.2, 0);  
                motionToColor(flow, motion2color);  
                imshow("flow", motion2color);  
            }  
            if(waitKey(10)>=0)  
                break;  
            std::swap(prevgray, gray);  
      
            t = (double)cvGetTickCount() - t;  
            cout << "cost time: " << t / ((double)cvGetTickFrequency()*1000.) << endl;  
        }  
        return 0;  
    }