轉載請注明出處!
!
!
http://blog.csdn.net/zhonghuan1992
光流(optical flow)和openCV中實現
光流的概念:
是Gibson在1950年首先提出來的。
它是空間運動物體在觀察成像平面上的像素運動的瞬時速度。是利用圖像序列中像素在時間域上的變化以及相鄰幀之間的相關性來找到上一幀跟當前幀之間存在的相應關系,從而計算出相鄰幀之間物體的運動信息的一種方法。
一般而言。光流是因為場景中前景目標本身的移動、相機的運動,或者兩者的共同運動所產生的。
當人的眼睛觀察運動物體時,物體的景象在人眼的視網膜上形成一系列連續變化的圖像。這一系列連續變化的信息不斷“流過”視網膜(即圖像平面)。好像一種光的“流”,故稱之為光流(optical flow)。光流表達了圖像的變化,因為它包括了目標運動的信息。因此可被觀察者用來確定目標的運動情況。
看以下的圖。它展示了一個小球在5個連續的幀中的運動。箭頭上的數字代表不同的幀。那個紅色小球的運動構成了光流。
操作:
給你一個圖上的一系列點,在另外一張圖上找到與前面一些列點同樣的點。
或者給你圖I1上的點[ux, uy]T。找到I2上的點[ux + δx, uy + δy]T。最小化ε:
上面增加Wx表示一塊區域,一般跟蹤一個區域的點。
在圖形學應用中。在多張圖上跟蹤點(特征)是一項主要的操作:在一張圖上找到一個對象,觀察對象怎樣移動。
基於特征點的跟蹤算法大致能夠分為兩個步驟:
1)探測當前幀的特征點;
2)通過當前幀和下一幀灰度比較,預計當前幀特征點在下一幀的位置;
3)過濾位置不變的特征點,余下的點就是目標了。
特征點能夠是Harris角點,也能夠是邊緣點等等。
考慮一個像素在第一幀的光強度(這里添加了一個維度時間。前面的時候我們僅僅是處理圖像。所以沒有必要時間。如今須要添加這個維度)。它移動了
的距離到一下幀。用了
時間。
由於像素點是一樣的。光強度也沒有發生變化(事實上這個光強度沒有改變是非常多光流算法的基本如果)。。所以我們能夠說:
然后通過泰勒級數近似展開有:
所以:
上面的等式叫做光流等式,偏導數能夠求出來。但是 u和v是未知的,所以無法解決上的等式。但是有非常多方法能夠解決問題,當中一個叫做Lucas-Kanade方法。
Lucas-Kanade:
有這么一個假定,全部的相鄰像素有相似的行動,Lucas-Kanade方法使用3*3的一塊區域,它假定這9個點有同樣的行動,所以如今的問題變為有9個等式,2個未知量,這個問題當然可以解決。一個好的解決方案是使用最小二乘法。
令n=9,於是便有了9個等式:
當中q1,q2,…,代表像素點, 是偏導,上面的等式能夠寫成以下的形式:A v = b,當中:
然后,得到以下的:
終於算出來的兩個未知數的解是:
上面的解決小而連貫的運動。想想剛剛我們的如果是9個像素點速度一致。由於現實中大而連貫的運動是普遍存在的,我們須要大的窗體來捕獲運動。但是大窗體違背了運動連貫的如果。圖像金字塔能夠解決問題。(圖像金字塔的內容以后本人掌握很多其它的再補充。如今不敢亂發表)。
OpenCV中的實現:
OpenCV提供了對上面介紹的方法的支持。函數名叫做:cv2.calcOpticalFlowPyrLK(),如今讓我們在視頻中跟蹤一些點。
為了決定跟蹤哪些點,使用cv2.goodFeaturesToTrack()。
我們得到第一幀。探測Shi-Tomasi角點,然后我們使用 Lucas-Kanade光流法來跟綜這些點。
#include "opencv2/video/tracking.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> #include <ctype.h> using namespace cv; using namespace std; static void help() { // print a welcome message, and the OpenCV version cout << "\nThis is ademo of Lukas-Kanade optical flow lkdemo(),\n" "Using OpenCVversion "<< CV_VERSION << endl; cout << "\nIt usescamera by default, but you can provide a path to video as an argument.\n"; cout << "\nHot keys:\n" "\tESC - quitthe program\n" "\tr -auto-initialize tracking\n" "\tc - deleteall the points\n" "\tn - switch the\"night\" mode on/off\n" "To add/removea feature point click it\n" << endl; } Point2f point; bool addRemovePt = false; static void onMouse(int event, int x, int y, int /*flags*/, void* /*param*/) { if (event == CV_EVENT_LBUTTONDOWN) { point = Point2f((float)x, (float)y); addRemovePt = true; } } int main(int argc, char** argv) { help(); VideoCapture cap; TermCriteria termcrit(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03); Size subPixWinSize(10, 10), winSize(31, 31); const int MAX_COUNT = 500; bool needToInit = false; bool nightMode = false; /*if (argc == 1 || (argc == 2 && strlen(argv[1])== 1 && isdigit(argv[1][0]))) cap.open(argc == 2 ? argv[1][0] - '0' :0); else if (argc == 2) cap.open(argv[1]);*/ cap.open("G:\\視頻分析入門練習\\視頻分析入門練習 - 附件\\sample.avi"); if (!cap.isOpened()) { cout << "Could notinitialize capturing...\n"; return 0; } namedWindow("LK", 1); setMouseCallback("LK", onMouse, 0); Mat gray, prevGray, image; vector<Point2f> points[2]; for (;;) { Mat frame; cap >> frame; if (frame.empty()) break; frame.copyTo(image); cvtColor(image, gray, COLOR_BGR2GRAY); if (nightMode) image = Scalar::all(0); if (needToInit) { // automaticinitialization goodFeaturesToTrack(gray, points[1],100, 0.01, 10, Mat(), 3, 0, 0.04); cornerSubPix(gray, points[1],subPixWinSize, Size(-1, -1), termcrit); addRemovePt = false; } else if(!points[0].empty()) { vector<uchar> status; vector<float> err; if (prevGray.empty()) gray.copyTo(prevGray); calcOpticalFlowPyrLK(prevGray, gray,points[0], points[1], status, err, winSize, 3, termcrit, 0, 0.001); size_t i, k; for (i = k = 0; i <points[1].size(); i++) { if (addRemovePt) { if (norm(point -points[1][i]) <= 5) { addRemovePt = false; continue; } } if (!status[i]) continue; points[1][k++] = points[1][i]; circle(image, points[1][i], 3, Scalar(0, 255, 0), -1, 8); } points[1].resize(k); } if (addRemovePt&& points[1].size() < (size_t)MAX_COUNT) { vector<Point2f> tmp; tmp.push_back(point); cornerSubPix(gray, tmp, winSize,cvSize(-1, -1), termcrit); points[1].push_back(tmp[0]); addRemovePt = false; } needToInit = false; imshow("LK", image); char c = (char)waitKey(100); if (c == 27) break; switch (c) { case 'r': needToInit = true; break; case 'c': points[0].clear(); points[1].clear(); break; case 'n': nightMode = !nightMode; break; } std::swap(points[1], points[0]); cv::swap(prevGray, gray); } return 0; }
結果:任意取得一些特征點。特征點會隨着車的移動而移動