OpenCv_光流法運動目標檢測
以下內容摘自一篇碩士論文《視訊序列中運動目標檢測與跟蹤演算法的研究》:
1950年Gibson首先提出了光流的概念,光流(optical flow)法是空間運動物體在觀測成像面上的畫素運動的瞬時速度。物體在運動的時候,它在影象上對應點的亮度模式也在做相應的運動,這種影象亮度模式的表觀運動就是光流。光流的研究就是利用影象序列中畫素的強度資料的時域變化和相關性來確定各自畫素位置的“運動”。光流表達了影象的變化,因此可被觀察者用來確定目標的運動情況。一般情況下,光流由相機運動、場景中目標運動或兩者的共同運動產生。
光流場是由光流引申出來的,它指的是景物中可見畫素點的三維速度向量在成像表面投影形成的二維瞬時速度場。空間中的運動場轉移到影象上就表示為光流場,光流場反映了影象上每一點的灰度變化趨勢。光流場包含了被觀察物體的運動資訊以及有關景物豐富的三維結構的資訊,它是如今計算機視覺及有關研究領域中的一個重要組成部分。
光流法檢測運動目標,其基本思想是賦予影象中的每一個畫素點一個速度向量,從而形成了該影象的運動場。影象上的點和三維物體上的點在某一特定的運動時刻是一一對應的,根據各畫素點的速度向量特徵對影象進行動態的分析。若影象中不存在運動目標,那麼光流向量在整個影象區域則是連續變化的,而當物體和影象背景中存在相對運動時,運動物體所形成的速度向量則必然不同於鄰域背景的速度向量,從而將運動物體的位置檢測出來。
光流不能由運動影象的區域性資訊來唯一的確定,例如,亮度等值線上的點或者亮度比較均勻的區域都無法唯一的確定其點的運動對應性,但是運動是可以進行觀察得到。由此說明運動場和光流不一定是唯一對應的,即光流不一定是由物體運動產生的,反之如果物體發生了運動也不一定就能產生光流。但是一般情況下,表觀運動和物體真實運動之間的差異是可以忽略的,可以用光流場代替運動場來分析影象中的運動目標及其相關的運動引數。
可以證明動能場不僅僅是分塊連續的,並且其間斷點恰好為物體的邊緣。如此,我們便可以利用影象每一幀的運動場,在動能變化矩陣中提取極值點,便可以得到運動物體的邊緣。可將動能大致相同的點歸於同一物體,進而對影象序列進行分割,從而檢測出多個運動目標。這樣,我們就將運動目標檢測問題,藉助光流場轉換為靜態影象的區域分割問題。
演算法步驟如下:
(1)令i=1,獲得第i幀影象I(x,i);
(2)獲得第i+1幀影象I(x,i+1);
(3)對影象去噪,得到去燥後圖像I ‘(x, i)和I ‘(x, i+1);
(4)利用I ‘(x, i)和I ‘(x, i+1)計算得到光流場;
(5)計算得到區域性動能場K(i);
(6)利用邊緣檢測演算法(如基於小波的方法)區域性動能場並分割影象得到不同的運動單元也理解為一個運動單元);
(7)由於目標一般較背景小,故提取出體積較運動單元作為檢測目標;
(8)計算其質心作為目標位置;
(9)置i=i+1,重複(2)~(8),直至檢測結束。
基於光流場分析的運動目標檢測方法,不僅包含了被觀察物體的運動資訊,而且攜帶了三維結構的豐富資訊,因此它不僅可以用於運動目標檢測,還可以直接應用於運動目標跟蹤,能夠很精確的計算出運動目標的速度,同時在攝像機存在運動的情況下也能夠檢測出運動目標。而在實際的應用中,由於存在多光源、遮擋性、噪聲和透明性等多方面的原因,光流場基本方程中的灰度守恆這個假設條件是得不到滿足的,因此不能求解出正確的光流場,同時由於其採用的是迭代的求解計算方法,故需要的計算時間比較長,從而無法滿足實時的要求,並且該方法受噪聲的影響較大,因而該方法多適用於目標運動速度不大,影象噪聲比較小的情況。
cvCalcOpticalFlowPyrLK 函式在使用時,首先要確定特徵點,也就是目標舊的位置。
本程式通過使用cvGoodFeaturesToTrack 函式選擇角點作為特徵點。
本程式只是一個簡單的運動檢測,在具體應用過程中,可以根據自己的需要修正
#include <cv.h>
#include <highgui.h>
int main (int argc, char **argv)
{
CvCapture* capture = 0;
capture = cvCaptureFromCAM( CV_CAP_ANY );
int i;
int corner_count = 1000;
CvTermCriteria criteria;
criteria = cvTermCriteria (CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 64, 0.01);
IplImage *src_img1;
IplImage *src_img2;
IplImage *dst_img;
IplImage *pre_img;
IplImage *eig_img;
IplImage *temp_img;
IplImage *prev_pyramid;
IplImage *curr_pyramid;
CvPoint2D32f *corners1;
CvPoint2D32f *corners2;
corners1 = (CvPoint2D32f *) cvAlloc (corner_count * sizeof (CvPoint2D32f));
corners2 = (CvPoint2D32f *) cvAlloc (corner_count * sizeof (CvPoint2D32f));
cvNamedWindow ("Image", 1);
char *status;
status = (char *) cvAlloc (corner_count);
while (1)
{
pre_img = cvQueryFrame(capture);
CvSize img_sz = cvGetSize(pre_img);
src_img1 = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvCvtColor(pre_img, src_img1, CV_RGB2GRAY);
dst_img = cvQueryFrame(capture);
src_img2 = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvCvtColor(dst_img, src_img2, CV_RGB2GRAY);
eig_img = cvCreateImage (img_sz, IPL_DEPTH_32F, 1);
temp_img = cvCreateImage (img_sz, IPL_DEPTH_32F, 1);
prev_pyramid = cvCreateImage (cvSize (src_img1->width + 8, src_img1->height / 3), IPL_DEPTH_8U, 1);
curr_pyramid = cvCreateImage (cvSize (src_img1->width + 8, src_img1->height / 3), IPL_DEPTH_8U, 1);
cvGoodFeaturesToTrack (src_img1, eig_img, temp_img, corners1, &corner_count, 0.001, 5, NULL);
cvCalcOpticalFlowPyrLK (src_img1, src_img2, prev_pyramid, curr_pyramid,
corners1, corners2, corner_count, cvSize (10, 10), 4, status, NULL, criteria, 0);
for (i = 0; i < corner_count; i++)
{
if (status[i])
cvLine (dst_img, cvPointFrom32f (corners1[i]), cvPointFrom32f (corners2[i]), CV_RGB (255, 0, 0), 1, CV_AA, 0);
}
cvShowImage ("Image", dst_img);
cvWaitKey (1);
cvReleaseImage (&src_img1);
cvReleaseImage (&src_img2);
cvReleaseImage (&eig_img);
cvReleaseImage (&temp_img);
cvReleaseImage (&prev_pyramid);
cvReleaseImage (&curr_pyramid);
}
cvDestroyWindow ("Image");
cvReleaseImage (&dst_img);
cvReleaseImage(&pre_img);
return 0;
}
轉自:http://blog.csdn.net/zht9961020/article/details/7032061
cvCalcOpticalFlowPyrLK 需要確定特徵點。
本程式,通過幀差獲得運動的點作為特徵點。
本程式原本的目的是計算運動點的速度,通過修正可以進行運動跟蹤。
``` c++
<div class="se-preview-section-delimiter"></div>
#include <cv.h>
<div class="se-preview-section-delimiter"></div>
#include <highgui.h>
<div class="se-preview-section-delimiter"></div>
#include <iostream>
using namespace std;
int const MAX_CORNERS = 1000;
int main (int argc, char **argv)
{
CvCapture* capture = 0;
capture = cvCaptureFromCAM( CV_CAP_ANY ); //get frame
IplImage *src_img1; //the previous frame (gray)
IplImage *src_img2; //the current frame(gray)
IplImge *dst_img; //the result
IplImage *cur_img;
IplImage *pre_img;
CvPoint2D32f * move_old_point = new CvPoint2D32f[ MAX_CORNERS];
CvPoint2D32f * move_new_point = new CvPoint2D32f[ MAX_CORNERS];
char *features_found = new char[MAX_CORNERS];
float *features_error = new float[MAX_CORNERS];
CvTermCriteria criteria;
criteria = cvTermCriteria (CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 64, 0.01);
while(1)
{
int i,j;
int dx, dy;
int p = 0;
int rows, cols;
int countn = MAX_CORNERS;
pre_img = cvQueryFrame(capture);
CvSize img_sz = cvGetSize(pre_img);
src_img1 = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvCvtColor(pre_img, src_img1, CV_RGB2GRAY);
cur_img = cvQueryFrame(capture);
src_img2 = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvCvtColor(cur_img, src_img2, CV_RGB2GRAY);
dst_img = (IplImage *)cvClone(cur_img);
IplImage *move_img = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvZero(move_img);
//cvAbsDiff(src_img1, src_img2,move_img);
cols = src_img1->width;
rows = src_img1->height;
for (i = 0; i <cols; i++)
{
for (j = 0; j<rows; j++)
{
double a = abs(cvGet2D(src_img1, j, i).val[0]-cvGet2D(src_img2, j, i).val[0]);
CvScalar b = cvScalar(a, 0, 0,0);
cvSet2D(move_img, j, i,b);
if (a>40)
{
if (p<MAX_CORNERS-1)
{
int d = ++p;
move_old_point[d].x = i;
move_old_point[d].y = j;
}
}
}
}
cvNamedWindow("moving object", 1);
cvShowImage("moving object", move_img);
CvSize Pyrsize = cvSize(src_img1->width +8, src_img1->height/3);
IplImage * pyrA = cvCreateImage(Pyrsize, IPL_DEPTH_32F, 1); //pyrA是需要尋找的點,不是沒有初始化的
IplImage * pyrB = cvCreateImage(Pyrsize, IPL_DEPTH_32F, 1);
cvCalcOpticalFlowPyrLK(src_img1,
src_img2,
pyrA,
pyrB,
move_old_point,
move_new_point,
countn,
cvSize(10, 10),
3,
features_found,
features_error,
criteria,
0
);
for (i = 0; i < countn; i++)
{
int x1 = (int)move_new_point[i].x;
int x2 = (int)move_old_point[i].x;
int y1 = (int)move_new_point[i].y;
int y2 = (int)move_old_point[i].y;
dx =(int) abs(x1 - x2) ;
dy = (int)abs(y1 - y2);
if (dx >= 5&& dy >= 5)
{
cvLine (dst_img, cvPoint(x2, y2),cvPoint(x2+5, y2+5) , CV_RGB (255, 0, 0), 1, CV_AA, 0);
}
}
cvNamedWindow ("ImagePyrLK", 1);
cvShowImage ("ImagePyrLK", dst_img);
cvWaitKey (1);
cvReleaseImage (&dst_img);
cvReleaseImage(&pyrA);
cvReleaseImage(&pyrB);
cvReleaseImage(&move_img);
}
cvDestroyWindow("moving object");
cvDestroyWindow ("ImagePyrLK");
cvReleaseImage (&src_img1);
cvReleaseImage (&src_img2);
cvReleaseImage (&pre_img);
cvReleaseImage (&cur_img);
return 0;
}
其它參考文獻:
include
using namespace std;
int const MAX_CORNERS = 1000;
int main (int argc, char **argv)
{
CvCapture* capture = 0;
capture = cvCaptureFromCAM( CV_CAP_ANY ); //get frame
IplImage *src_img1; //the previous frame (gray)
IplImage *src_img2; //the current frame(gray)
IplImge *dst_img; //the result
IplImage *cur_img;
IplImage *pre_img;
CvPoint2D32f * move_old_point = new CvPoint2D32f[ MAX_CORNERS];
CvPoint2D32f * move_new_point = new CvPoint2D32f[ MAX_CORNERS];
char *features_found = new char[MAX_CORNERS];
float *features_error = new float[MAX_CORNERS];
CvTermCriteria criteria;
criteria = cvTermCriteria (CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 64, 0.01);
while(1)
{
int i,j;
int dx, dy;
int p = 0;
int rows, cols;
int countn = MAX_CORNERS;
pre_img = cvQueryFrame(capture);
CvSize img_sz = cvGetSize(pre_img);
src_img1 = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvCvtColor(pre_img, src_img1, CV_RGB2GRAY);
cur_img = cvQueryFrame(capture);
src_img2 = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvCvtColor(cur_img, src_img2, CV_RGB2GRAY);
dst_img = (IplImage *)cvClone(cur_img);
IplImage *move_img = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvZero(move_img);
//cvAbsDiff(src_img1, src_img2,move_img);
cols = src_img1->width;
rows = src_img1->height;
for (i = 0; i <cols; i++)
{
for (j = 0; j<rows; j++)
{
double a = abs(cvGet2D(src_img1, j, i).val[0]-cvGet2D(src_img2, j, i).val[0]);
CvScalar b = cvScalar(a, 0, 0,0);
cvSet2D(move_img, j, i,b);
if (a>40)
{
if (p<MAX_CORNERS-1)
{
int d = ++p;
move_old_point[d].x = i;
move_old_point[d].y = j;
}
}
}
}
cvNamedWindow("moving object", 1);
cvShowImage("moving object", move_img);
CvSize Pyrsize = cvSize(src_img1->width +8, src_img1->height/3);
IplImage * pyrA = cvCreateImage(Pyrsize, IPL_DEPTH_32F, 1); //pyrA是需要尋找的點,不是沒有初始化的
IplImage * pyrB = cvCreateImage(Pyrsize, IPL_DEPTH_32F, 1);
cvCalcOpticalFlowPyrLK(src_img1,
src_img2,
pyrA,
pyrB,
move_old_point,
move_new_point,
countn,
cvSize(10, 10),
3,
features_found,
features_error,
criteria,
0
);
for (i = 0; i < countn; i++)
{
int x1 = (int)move_new_point[i].x;
int x2 = (int)move_old_point[i].x;
int y1 = (int)move_new_point[i].y;
int y2 = (int)move_old_point[i].y;
dx =(int) abs(x1 - x2) ;
dy = (int)abs(y1 - y2);
if (dx >= 5&& dy >= 5)
{
cvLine (dst_img, cvPoint(x2, y2),cvPoint(x2+5, y2+5) , CV_RGB (255, 0, 0), 1, CV_AA, 0);
}
}
cvNamedWindow ("ImagePyrLK", 1);
cvShowImage ("ImagePyrLK", dst_img);
cvWaitKey (1);
cvReleaseImage (&dst_img);
cvReleaseImage(&pyrA);
cvReleaseImage(&pyrB);
cvReleaseImage(&move_img);
}
cvDestroyWindow("moving object");
cvDestroyWindow ("ImagePyrLK");
cvReleaseImage (&src_img1);
cvReleaseImage (&src_img2);
cvReleaseImage (&pre_img);
cvReleaseImage (&cur_img);
return 0;
}
“`
其它參考文獻: