1. 程式人生 > >OpenCV運動目標檢測

OpenCV運動目標檢測

       運動目標檢測的常用方法有光流法(Optical Flow),背景減除法(Background Subtraction)和幀間差分法(Frame Difference)。其中背景減除法和幀間差分法適用於攝像頭靜止的情況,而光流法用於攝像頭運動的情況,但計算量比較大。

       下面介紹如何運用openCV中的函式cvUpdateMotionHistory進行運動目標檢測。cvUpdateMotionHistory的功能是更新運動歷史影象,它的原型是:

void cvUpdateMotionHistory(
const CvArr* silhouette,
CvArr* mhi,
double timestamp,
double duration );

各個引數解釋如下,

silhouette,由幀間差分得到的運動輪廓影象。

mhi,motion histoty image的縮寫,表示運動歷史影象。

timestamp,時間標記。

duration,發生過運動的畫素所能保持的最長時間。

作為輸出的運動歷史影象mhi的與作為輸入的運動輪廓影象silhouette的具體關係可以表達為如下三種情況:

(1)mhi(x,y) = timestamp if silhouette(x,y)!=0.即當運動輪廓影象在座標(x,y)處不為零時,那麼將mhi在(x,y)處打上時間標記timestamp.

(2)mhi(x,y) = 0 if silhouette(x, y) = 0 and mhi(x,y) < (timestamp - duration).由於timestamp是在不斷隨著時間增長的,所以發生過運動的畫素點維持的 最長時間為duration.當mhi(x,y)保持的時間超過duration後,若在(x,y)處沒有發生新的運動,就令mhi(x,y)為零。

(3)mhi(x,y) = mhi(x,y) otherwise.只要發生過運動的畫素點維持的時間沒有超過duration,那麼令mhi(x,y)的大小維持發生運動的那個時刻的時間標記。

下面給出基於opencv2.0的程式碼,程式碼參考了opencv安裝目錄中的例程,並做了一些改動,方便其他專案中直接呼叫函式進行運動目標檢測。

#include <cv.h>
#include <highgui.h>
#include <time.h>

void updateMhi(const IplImage* src_image,IplImage* binary_mhi)
{
	//運動畫素維持的最大時間,單位與timestamp的單位一致
	#define MHI_DURATION 1
	//時間標記
	double timestamp = clock()/100.;
	
	//當前幀的上一幀影象
	static IplImage* last_image = NULL;
	static IplImage* color_silh = NULL;
	static IplImage* gray_silh = NULL;
	//運動歷史影象
	static IplImage* mhi = NULL;
	
	if(NULL==last_image && NULL==color_silh && NULL==gray_silh && NULL==mhi)
	{
		CvSize size = cvGetSize(src_image);
		//初始分配記憶體,在該程式中沒有釋放如下影象的記憶體空間
		last_image = cvCreateImage(size,8,3);
		color_silh = cvCreateImage(size,8,3);
		gray_silh = cvCreateImage(size,8,1);
		mhi = cvCreateImage(size,IPL_DEPTH_32F,1);
		//初始情況下令當前幀與上一幀影象相同
		cvCopy(src_image,last_image);
		//置零初始運動歷史影象
		cvZero(binary_mhi);
	}
	else
	{
		//獲得幀差影象color_silh
		cvAbsDiff(src_image,last_image,color_silh);
		cvCopy(src_image,last_image);
		cvCvtColor(color_silh,gray_silh,CV_BGR2GRAY);

		cvThreshold( gray_silh, gray_silh, 30, 255, CV_THRESH_BINARY);
		//更新運動歷史影象
		cvUpdateMotionHistory(gray_silh,mhi,timestamp,MHI_DURATION);
		//將浮點型的運動歷史影象轉換成影象binary_mhi
		cvCvtScale( mhi, binary_mhi, 255./MHI_DURATION,
				  (MHI_DURATION - timestamp)*255./MHI_DURATION );
		//二值化
		cvThreshold( binary_mhi, binary_mhi, 0, 255, CV_THRESH_BINARY );
	}

}

int main()
{
	CvCapture* capture = cvCreateFileCapture("D:\\technology\\CV\\Databases\\vedio\\motion.avi");

	if(capture)
	{
		IplImage* frame = NULL;
		int width = (int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_WIDTH);
		int height = (int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_HEIGHT);

		CvSize frame_size = cvSize(width,height);
		IplImage* binary_mhi = cvCreateImage(frame_size,8,1);
		IplImage* src_image = cvCreateImage(frame_size,8,3);
		while(frame = cvQueryFrame(capture))
		{
			cvCopy(frame,src_image);
			updateMhi(src_image,binary_mhi);

			cvNamedWindow("src");
			cvShowImage("src",frame);

			cvNamedWindow("mhi");
			cvShowImage("mhi",binary_mhi);
			
			if(27==cvWaitKey(33))
				break;
		}
		cvReleaseCapture(&capture);
		cvDestroyWindow("src");
		cvDestroyWindow("mhi");
		cvReleaseImage(&src_image);
		cvReleaseImage(&binary_mhi);
	}


	return 0;
}

程式執行的截圖如下: