openCV光流法追蹤運動物體
email:chentravelling@163.com
一、光流簡單介紹
摘自:zouxy09
光流的概念是Gibson在1950年首先提出來的。它是空間運動物體在觀察成像平面上的像素運動的瞬時速度,是利用圖像序列中像素在時間域上的變化以及相鄰幀之間的相關性來找到上一幀跟當前幀之間存在的相應關系。從而計算出相鄰幀之間物體的運動信息的一種方法。一般而言,光流是因為場景中前景目標本身的移動、相機的運動,或者兩者的共同運動所產生的。
研究光流場的目的就是為了從圖片序列中近似得到不能直接得到的運動場。運動場,事實上就是物體在三維真實世界中的運動。光流場,是運動場在二維圖像平面上(人的眼睛或者攝像頭)的投影。
那通俗的講就是通過一個圖片序列,把每張圖像中每一個像素的運動速度和運動方向找出來就是光流場。
那怎么找呢?咱們直觀理解肯定是:第t幀的時候A點的位置是(x1, y1),那么我們在第t+1幀的時候再找到A點,假如它的位置是(x2,y2),那么我們就能夠確定A點的運動了:(ux, vy) = (x2, y2) - (x1,y1)。
那怎么知道第t+1幀的時候A點的位置呢? 這就存在非常多的光流計算方法了。
1981年,Horn和Schunck創造性地將二維速度場與灰度相聯系,引入光流約束方程,得到光流計算的基本算法。人們基於不同的理論基礎提出各種光流計算方法,算法性能各有不同。Barron等人對多種光流計算技術進行了總結。依照理論基礎與數學方法的差別把它們分成四種:基於梯度的方法、基於匹配的方法、基於能量的方法、基於相位的方法。
近年來神經動力學方法也頗受學者重視。
OpenCV中實現了不少的光流算法。
1)calcOpticalFlowPyrLK
通過金字塔Lucas-Kanade 光流方法計算某些點集的光流(稀疏光流)。理解的話,能夠參考這篇論文:”Pyramidal Implementation of the Lucas Kanade Feature TrackerDescription of the algorithm”
2)calcOpticalFlowFarneback
用Gunnar Farneback 的算法計算稠密光流(即圖像上全部像素點的光流都計算出來)。
它的相關論文是:"Two-Frame Motion Estimation Based on PolynomialExpansion"
3)CalcOpticalFlowBM
通過塊匹配的方法來計算光流。
4)CalcOpticalFlowHS
用Horn-Schunck 的算法計算稠密光流。
相關論文好像是這篇:”Determining Optical Flow”
5)calcOpticalFlowSF
這一個是2012年歐洲視覺會議的一篇文章的實現:"SimpleFlow: A Non-iterative, Sublinear Optical FlowAlgorithm",project站點是:http://graphics.berkeley.edu/papers/Tao-SAN-2012-05/ 在OpenCV新版本號中有引入。
稠密光流須要使用某種插值方法在比較easy跟蹤的像素之間進行插值以解決那些運動不明白的像素。所以它的計算開銷是相當大的。而對於稀疏光流來說,在他計算時須要在被跟蹤之前指定一組點(easy跟蹤的點。比如角點),因此在使用LK方法之前我們須要配合使用cvGoodFeatureToTrack()來尋找角點,然后利用金字塔LK光流算法,對運動進行跟蹤。
二、代碼
#include <opencv2\opencv.hpp> #include <iostream> using namespace std; using namespace cv; const int MAX_CORNERS = 100; int main() { IplImage* preImage = cvLoadImage("image133.pgm",CV_LOAD_IMAGE_GRAYSCALE); IplImage* curImage = cvLoadImage("image134.pgm",CV_LOAD_IMAGE_GRAYSCALE); IplImage* curImage_ = cvLoadImage("image134.pgm"); CvPoint2D32f* preFeatures = new CvPoint2D32f[ MAX_CORNERS ];//前一幀中特征點坐標(通過cvGoodFeaturesToTrack獲得) CvPoint2D32f* curFeatures = new CvPoint2D32f[ MAX_CORNERS ];//在當前幀中的特征點坐標(通過光流法獲得) double qlevel; //特征檢測的指標 double minDist;//特征點之間最小容忍距離 vector<uchar> status; //特征點被成功跟蹤的標志 vector<float> err; //跟蹤時的特征點小區域誤差和 CvSize img_sz = cvGetSize(preImage); IplImage* eig_image = cvCreateImage( img_sz, IPL_DEPTH_32F, 1 );//緩沖區 IplImage* tmp_image = cvCreateImage( img_sz, IPL_DEPTH_32F, 1 ); int num = MAX_CORNERS; cvGoodFeaturesToTrack(//獲取特征點 preImage, eig_image, tmp_image, preFeatures, &num, 0.01, 5.0, 0, 3, 0, 0.04 ); CvSize pyr_sz = cvSize( curImage->width+8, curImage->height/3 ); IplImage* pyrA = cvCreateImage( pyr_sz, IPL_DEPTH_32F, 1 ); IplImage* pyrB = cvCreateImage( pyr_sz, IPL_DEPTH_32F, 1 ); char features_found[ MAX_CORNERS ]; float feature_errors[ MAX_CORNERS ]; cvCalcOpticalFlowPyrLK(//計算光流 preImage, curImage, pyrA, pyrB, preFeatures, curFeatures, MAX_CORNERS, cvSize(10,10), 5, features_found, feature_errors, cvTermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, .3 ), 0 ); for(int i=0;i<MAX_CORNERS;i++)//畫線 { cvLine( curImage_, Point(preFeatures[i].x,preFeatures[i].y), Point(curFeatures[i].x,curFeatures[i].y), CV_RGB(255,0,0), 4, CV_AA, 0); } cvShowImage("outPutImage",curImage_); //cvSaveImage("outPutImage.pgm",curImage); waitKey(0); return 0; }
終於結果: