光流optical flow的理解和求法

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;  
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章