您好,登錄后才能下訂單哦!
Opencv學(xué)習(xí)之二幀差法運(yùn)動(dòng)目標(biāo)檢測(cè)與輪廓提取 ,供大家參考,具體內(nèi)容如下
代碼是從網(wǎng)上摘抄學(xué)習(xí)的,加了好多注釋,感覺就像邊看書邊做筆記一樣,給人以滿足的享受。Let's do this!
#include "highgui.h" #include "cv.h" #include "stdio.h" #include <time.h> #include <math.h> #include <string.h> const double MHI_DURATION=0.1;//運(yùn)動(dòng)跟蹤的最大持續(xù)時(shí)間0.1s const double MAX_TIME_DELTA=0.5//最大時(shí)間增量0.5s const double MIN_TIME_DELTA=0.05;//最小時(shí)間增量0.05s const int N=3; const int CONTOUR_MAX_AERA=16; /*做幀差時(shí)要用到的圖像緩沖*/ IplImage **buf=0; int last=0; /*臨時(shí)圖像*/ IplImage* mhi=0;//運(yùn)動(dòng)歷史圖像mhi CvConnectedComp* cur_comp,mincomp; /*typedef struct CvConnectedComp { double area; //區(qū)域的面積 CvScalar value; //區(qū)域顏色的平均值 CvRect rect; //是一個(gè)區(qū)域的外接矩形 CvSeq * contour; //指向另一個(gè)序列的指針 };*/ /*定義一個(gè)內(nèi)存存儲(chǔ)器*/ CvMemStorage* storage; /*二維坐標(biāo)系下的點(diǎn),類型為整型,通常以0點(diǎn)為原點(diǎn),有x、y坐標(biāo)*/ CvPoint pt[4]; /*當(dāng)前畫面索引*/ int nCurFrameIndex=0; /*定義用來更新運(yùn)動(dòng)歷史圖像的函數(shù)*/ /*img-輸入視頻幀;dst-檢測(cè)結(jié)果*/ void update(IplImage *img,IplImage *dst,int diff_threshold) { /*獲得當(dāng)前時(shí)間,單位是秒*/ double timestamp=clock()/100; /*獲得輸入視頻幀的尺寸,用存到size中*/ CvSize size=cvSize(img->width,img->height); /*做幀差要用到的中間變量*/ int i,idx1,idx2; /*當(dāng)前幀與上一幀做幀差之后,得到的圖像數(shù)據(jù)存儲(chǔ)在nimg中*/ IplImage* nimg; /*這步暫時(shí)沒看懂- -!*/ IplImage* pyr=cvCreateImage(cvSize((size.width&-2)/2,(size.height&-2)/2),8,1); /*定義一個(gè)內(nèi)存存儲(chǔ)器*/ CvMemStorage* stor; /*創(chuàng)建一個(gè)可增長(zhǎng)的序列seq*/ CvSeq* seq; /*先進(jìn)行數(shù)據(jù)的初始化*/ /*如果歷史圖像為空,或者歷史圖像尺寸與輸入的當(dāng)前幀尺寸不吻合(這意味著打開了新的視頻?)*/ if(!mhi||mhi->width!=size.width||mhi->height!=size.height) { /*如果buf還未初始化,則為buf分配內(nèi)存*/ if(buf==0) { /*N=3*/ buf=(IplImage**)malloc(N*sizeof(buf[0])); /*將指針s所指向的某一塊內(nèi)存中的每個(gè)字節(jié)的內(nèi)容全部設(shè)置為ch指定的ASCII值,塊的大小由第三個(gè)參數(shù)指定:memset(void *s,char ch,unsigned n)。此處作用相當(dāng)于將buf內(nèi)的元素全部置零*/ memset(buf,0,N*sizeof(buf[0])); } /*若buf已經(jīng)初始化了,也將buf置零*/ for(i=0;i<N;i++) { cvReleaseImage(&buf[i]); buf[i]=cvCreateImage(size,IPL_DEPTH_8U,1); cvZero(buf[i]); } /*重新初始化運(yùn)動(dòng)歷史圖像mhi*/ cvReleaseImage(&mhi); mhi=cvCreateImage(size,IPL_DEPTH_32F,1); cvZero(mhi); } /*將當(dāng)前要處理的幀轉(zhuǎn)化為灰度圖,放到buf的最后一幀*/ cvCvtColor(img,buf[last],CV_BGR2GRAY); /*這三部是為了做幀差,讓buf[idx1]永遠(yuǎn)保存的是上一幀,buf[idx2]保存當(dāng)前幀*/ idx1=last; idx2=(last+1)%N; last=idx2; /*做幀差,函數(shù) cvAbsDiff 計(jì)算兩個(gè)數(shù)組差的絕對(duì)值*/ nimg=buf[idx2]; cvAbsDiff(buf[idx1],buf[idx2],nimg); /*幀差之后,將得到的圖像二值化*/ cvThreshold(nimg,nimg,50,255,CV_THRESH_BINARY); /*去掉超時(shí)的影像以更新運(yùn)動(dòng)歷史圖像*/ cvUpdateMotionHistory(nimg,mhi,timestamp,MHI_DURATION); cvConvert(mhi,dst); /*中值濾波,消除小的噪聲 函數(shù)cvPyrDown使用Gaussian金字塔分解對(duì)輸入圖像向下采樣,去除噪聲,圖像是原圖像的四分之一 函數(shù)cvDialate做膨脹操作,去除目標(biāo)的不連續(xù)空洞 函數(shù)cvPyrUp使用Gaussian金字塔分解對(duì)輸入圖像向上采樣,恢復(fù)圖像,圖象是原圖像的四倍*/ cvSmooth(dst,dst,CV_MEDIAN,3,0,0,0); cvPyrDown(dst,pyr,CV_GAUSSIAN_5x5); cvDilate(pyr,pyr,0,1); cvPyrUp(pyr,dst,CV_GAUSSIAN_5x5); /*創(chuàng)建輪廓*/ stor=cvCreateMemStorage(0); seq=cvCreateSeq(CV_SEQ_ELTYPE_POINT,//從預(yù)定義的序列類型中選擇一合適的類型 sizeof(CvSeq),//此參數(shù)表示序列頭部的大?。槐仨毚笥诨虻扔趕izeof(CvSeq) /*第三個(gè)參數(shù)是元素的大小,以字節(jié)計(jì)。這個(gè)大小必須與序列類型(由seq_flags指定)相一致,例如,對(duì)于一個(gè)點(diǎn)的序列,元素類型 CV_SEQ_ELTYPE_POINT應(yīng)當(dāng)被指定,參數(shù)elem_size必須等同于sizeof(CvPoint)。 */ sizeof(CvPoint), stor);//指向前面定義的內(nèi)存存儲(chǔ)器的指針 /*找到所有輪廓*/ cvFindContours(dst,//源二值圖像 stor,//返回輪廓的容器 &seq,//輸出參數(shù),第一個(gè)外接輪廓的地址。 sizeof(CvContour), CV_RETR_EXTERNAL,//mode:EXTERNAL——只查找最外的輪廓 CV_CHAIN_APPROX_NONE,//輪廓近似的方法,具體見百度百科- - cvPoint(0,0)); /*直接用CONTOUR中的矩形來畫輪廓*/ /*遍歷seq序列*/ for(;seq;seq=seq->h_next) { /*直接使用輪廓的矩形,調(diào)取rect會(huì)得到與x、y軸平行的矩形,并非最小矩形*/ CvRect r=((CvContour*)cont)->rect;//將序列類型轉(zhuǎn)換成輪廓類型的指針? /*矩形的面積小于輪廓面積的話,舍棄;矩形面積也不能過小*/ if((r.height*r.width>CONTOUR_MAX_AERA)&&(r.height*r.width>2560)) { /*cvRectangle函數(shù)通過對(duì)角線兩個(gè)頂點(diǎn),繪制矩形*/ cvRectangle(img,//圖像 cvPoint(r.x,r.y),//一個(gè)頂點(diǎn) cvPoint(r.x + r.width, r.y + r.height),//另一個(gè)頂點(diǎn) CV_RGB(255,0,0),//線條顏色 1,//線條粗細(xì)程度 CV_AA,//線條類型 0); //坐標(biāo)點(diǎn)的小數(shù)點(diǎn)位數(shù) } } /*函數(shù)調(diào)用完畢,釋放內(nèi)存*/ cvReleaseMemStorage(&stor); cvReleaseImage(&pyr); } /處理視頻,主函數(shù)/ int main(int argc,char**argv) { IplImage *motion=0; CvCapture *capture=0; /*讀取視頻幀*/ capture=cvCaptureFromFile("D:\\視頻\\01.mp4"); if(capture) { cvNamedWindow("Motion",1); for(;;) { IplImage *image; /*使用cvGrabFrame函數(shù)抓取幀*/ if(!cvGrabFrame(capture)) break; /*使用cvRetrieveFrame函數(shù)取回被cvGrabFrame抓取的幀*/ image=cvRetrieveFrame(capture); if(image) { /*如果motion并未初始化,說明這是第一幀。我們將motion初始化*/ if(!motion) { motion=cvCreateImage(cvSize(image->width,image->height),8,1); cvZero(motion); /*需要保證內(nèi)存存儲(chǔ)的順序和取出的幀相同*/ motion->origin=image->origin; } } /*若取出了新的一幀,而且motion不為空,則更新畫面*/ update(image,motion,10); /*顯示處理過的圖像*/ cvShowImage("Motion",image); /*10ms內(nèi)檢測(cè)到用戶按了任意鍵,均退出*/ if(cvWaitKey(10)>=0) break; } /*當(dāng)上面這個(gè)for循環(huán)執(zhí)行結(jié)束時(shí),說明視頻已經(jīng)處理完成或者用戶停止處理視頻了*/ cvReleaseCapture(&capture); cvDestroyWindow("Motion"); } return 0; }
經(jīng)過測(cè)試,這個(gè)程序能夠成功檢測(cè)并用紅色方框圈出移動(dòng)的車輛和行人。
待改進(jìn)的地方有:
①視頻處理速度慢,導(dǎo)致視頻處理速度只有視頻正常播放速度的二分之一。
②對(duì)于行人的檢測(cè),畫出的紅色方框不穩(wěn)定,不是將整個(gè)行人框出,經(jīng)常會(huì)分別框出一個(gè)人的幾個(gè)不同部位orz。
③當(dāng)兩個(gè)物體稍有重疊時(shí),會(huì)將重疊物體當(dāng)作一個(gè)物體圈出。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。