在..\opencv\doc\vidsurv文件夾中有三個doc文件,
Blob_Tracking_Modules、Blob_Tracking_Tests、TestSeq,其中Blob_Tracking_Modules必須需要詳讀的。
“FG/BG Detection” module performsforeground/background segmentation for each pixel.
“Blob Entering Detection” module uses theresult (FG/BG mask) of “FG/BG Detection” module to detect new blob objectentered to a scene on each frame.
“Blob Tracking” module initialized by“Blob Entering Detection” results and tracks each new entered blob.
“Trajectory Generation” module performs asaving function. It collects all blobs positions and save each whole blobtrajectory to hard disk when it finished (for example tracking is lost).
“Trajectory PostProcessing” moduleperforms a blob trajectory smoothing function. This module is optional and cannot be included in specific pipeline.
OpenCV所提供的這個運動物體跟蹤框架只是一個基本的框架,開發者可以根據自己的實際需要對其中的一些模塊進行自定義擴展,以滿足實際應用中的具體要求。
1、 前景檢測模塊CvFGDetector:它的輸入數據為當前幀圖像,輸出結果數據為當前幀圖像的前景圖像(mask)。前景圖像是一個和輸入的視頻幀具有同樣大小的二值圖像,即如果當前幀中的像素點被判斷為運動前景,則前景掩碼中相應位置的像素點值為1,否則,相應的像素點值為0。
開發者需要繼承CvFGDetector類,並實現其中的純虛函數。其中在函數Virtual void process(IplImage *pImg){}中寫入自己開發的運動目標檢測算法。而函數Virtual IplImage *GetMask()是得到前景檢測的結果圖像,並負責傳遞到后續的模塊中。而函數Virtual void Release()負責一些動態分配內存的釋放。
2、 新團塊檢測模塊CvBlobDetector:該模塊的作用是檢測進入監控范圍的新目標的位置和大小。模塊的輸入是當前幀的前景圖像(前景檢測模塊的結果)和已經檢測並標定的團塊,輸出的結果是新檢測到的團塊。
開發者可以將虛類實例化,然后將自己的新團塊檢測算法寫入到相應的函數中。
新團塊檢測模塊的處理流程為:首先從前景圖像中檢測出所有團塊,然后將較小的團塊(可能是由噪聲引起的)和與已經被跟蹤團塊有重疊的團塊丟棄,並對剩余的團塊按照大小順序排列,只保留其中幾個比較大的團塊(默認為10)。最后利用特定規則篩選,篩選不合標准的團塊,將真正的新團塊保存到團塊列表中。
3、 團塊跟蹤模塊CvBlobTracker:該模塊的作用就是在前面兩個模塊(前景檢測模塊、新團塊檢測模塊)對運動目標檢測的基礎上,實現對運動目標的跟蹤。此模塊的輸入為當前幀的前景圖像和團塊列表以及當前幀圖像,輸出結果是當前視頻幀中所有運動目標的信息,以團塊表示(ID,pos,size)。使用新團塊檢測模塊的結果初始化該模塊,並跟蹤新進入的團塊。
開發者根據自己的算法開發相應的跟蹤系統時,可以繼承該類,然后用自己的算法實現函數Virtural void process(IplImage *pImg,IplImage *pImgFG=NULL)。此虛類中還定義了許多其他的輔助處理函數接口,例如跟蹤索引或ID返回指定團塊指針的函數,根據索引或ID為指定團塊設置參數函數等。
團塊跟蹤模塊的處理流程為:首先從前景圖像提取所有團塊,並計算團塊的質心、寬度和高度;然后對每一個已被跟蹤的軌跡,利用卡爾曼濾波器預測該軌跡在當前幀的團塊的位置和大小;最后對每個跟蹤的軌跡進行處理,尋找離上一幀里的團塊最近的當前幀的團塊,將此團塊添加到跟蹤軌跡。
4、軌跡生成模塊CvBlobTrackGen:該模塊的作用是生成運動目標的運動軌跡,然后將軌跡導出到指定數據庫或文件中(如.txt、.csv文件)。該模塊的輸入是代表當前處理視頻幀中各個運動目標的團塊,輸出結果是存儲在指定位置下的軌跡文件。該模塊主要是保存操作,它收集所有團塊的位置,並在每條軌跡結束時(例如跟蹤丟失時或者物體離開場景時)將其保存到硬盤上,同時也可以為每個團塊計算一些特征並保存。
5、軌跡后處理模塊CvBlobTrackPostProc:該模塊的作用是在前一個模塊所產生的團塊軌跡上做一些處理,例如采用Kalman濾波或平滑濾波處理等。此模塊是可選的,可以不包含在處理流程中。它的輸入是當前處理圖像的所有團塊,輸出結果是處理后所處理圖像的團塊列表。
6、軌跡分析模塊CvBlobTrackAnalysis:當某個目標跟蹤結束后,會產生一個軌跡,CvBlobTrackAnalysis的子類用於對軌跡進行數據分析;
7、跟蹤流程模塊CvBlobTracterAuto:為了方便開發者開發自己的系統,同時也為了保證系統的模塊化設計,OpenCV設計了此虛類描述整個跟蹤流程,這個代表整個跟蹤流程的虛類將各個模塊相互聯系起來成為一個有機的整體。本模塊將前面提到的五個模塊連接起來,形成一個完整的處理流程。
此類中的函數Process負責調用其它各個子模塊,首先對背景圖像進行更新並檢測前景,將獲取的前景圖像保存於成員變量m_pFG中。獲取前景圖像后,便依次調用團塊跟蹤模塊(注意:而不是新團塊檢測模塊,這樣做的主要目的是先執行跟蹤可將當前幀的跟蹤結果傳入新團塊檢測模塊,以提供新團塊檢測的准確度。如果團塊跟蹤在后,則新團塊跟蹤模塊只能與上一幀的團塊列表進行比較,新團塊檢測的准確度將會有所降低),軌跡后處理模塊,團塊檢測模塊,軌跡生成模塊,軌跡分析模塊。
如果想將自己實現的算法加入到以上模塊中也很方便,如將背景差分算法加入到前景檢測模塊中,只要繼承CvFGDetector類,然后主要在Process函數中實現自己的算法就可以了。
原始代碼中,用紅色標注運動目標的表示跟蹤不穩定,綠色則表示穩定跟蹤。
軌跡生成模塊中默認有兩種方法實現數據的保存,其中一種是”RawTracks”方法,每行存放一個運動目標數據,單位為像素,依次為運動目標出現的起始幀,運動目標中心x坐標,運動目標中心y坐標,運動目標寬度,運動目標高度,運動目標中心x坐標,運動目標中心y坐標,運動目標寬度,運動目標高度,……。
參考文獻:
1、http://www.doc88.com/p-896576154875.html
2、http://blog.csdn.net/wk119911/article/details/7664478
3、http://www.opencv.org.cn/forum/viewtopic.php?t=11128
1.在空工程中加入opencv中blobtrack.cpp編譯遇到的錯誤
fatal error C1010: unexpected end of file while looking for precompiled header directive
Error executing cl.exe.
blobtrack.obj - 1 error(s), 0 warning(s)
預編譯頭文件(precompiled header)
解決方法:
1、如果發生錯誤的文件是由其他的C代碼文件添加進入當前工程而引起的,則Alt+F7進入當前工程的 Settings,選擇C/C++選項卡,從Category組合框中選中Precompiled Headers,選擇Not Using Precompiled headers。確定。
2、在文件開頭添加:
#include "stdafx.h"
對預編譯頭文件說明如下:
所謂頭文件預編譯,就是把一個工程(Project)中使用的一些MFC標准頭文件(如Windows.H、Afxwin.H)預先編譯,以后該工程編譯時,不再編譯這部分頭文件,僅僅使用預編譯的結果。這樣可以加快編譯速度,節省時間。
預編譯頭文件通過編譯stdafx.cpp生成,以工程名命名,由於預編譯的頭文件的后綴是“pch”,所以編譯結果文件是projectname.pch。
編譯器通過一個頭文件stdafx.h來使用預編譯頭文件。stdafx.h這個頭文件名是可以在project的編譯設置里指定的。編譯器認為,所有在指令#include "stdafx.h"前的代碼都是預編譯的,它跳過#include "stdafx. h"指令,使用projectname.pch編譯這條指令之后的所有因此,所有的CPP實現文件第一條語句都是:#include "stdafx.h"。
------------------------------------
unexpected end of file while looking for precompiled header directive Error executing cl.exe.我在文件頭上添加了:
#include "stdafx.h" 還是不能運行,請多指教!
Alt+F7進入當前工程的Settings,選擇C/C++選項卡,從Category組合框中選中Precompiled Headers,選擇Not Using Precompiled headers。確定。
如果發生錯誤的文件原本是該工程中的,則檢查該文件頭部有沒有#i nclude "stdafx.h"語句,沒有的話添加。
如果還不行,也有可能是定義的類或結構體等最后忘了加分號,注意一下
------------------------------------------
問題:我在編譯程序中老出現“fatal error C1010: unexpected end of file while looking for precompiled header directive”這一句,但我查看了程序並沒有錯,請問這是怎么一回事?
A回答:
肯定是一個新添加的類的.cpp文件開頭沒包含stdafx.h,在該文件最前面加上即可。
BOBO的意見:
有時可以使用右鍵點擊項目工程中的該cpp文件,選擇setting,在c/c++欄,選擇PreCompiled headers,然后設置第一選項,選擇不使用預編譯頭,解決這個問題。
---------------------------------------
編譯出現問題:unexpected end of file while looking for precompiled header directive
解決方案:
需要在文件頭上添加一句:
#include "stdafx.h"
這個文件定義了源程序為C++格式。
否則文件需要保存為.C格式
--------------------------------
錯誤:fatal error C1083: Cannot open precompiled header file: 'Debug\test4.pch': No such file or directory
解決方案:
或rebuild all,或重新編譯一下stdafx.cpp就OK了,呵呵,又長知識了。
--------------------
代碼。
----------------總結------
如果工程很大,頭文件很多,而有幾個頭文件又是經常要用的,那么
1。把這些頭文件全部寫到一個頭文件里面去,比如寫到preh.h
2。寫一個preh.c,里面只一句話:#include "preh.h"
3。對於preh.c,在project setting里面設置creat precompiled headers,對於其他.c文件,設置use precompiled header file
關於blobtracker的一些解析
由 secondwang » 2010-08-05 10:43
virtual CvBlob* AddBlob(CvBlob* pB, IplImage* pImg, IplImage* pImgFG = NULL )
//這個函數的主要作用是在m_BlobList中加入新的blob,該結構包含了新創建的預測器和跟蹤器
//pPredictor 采用卡爾曼線性濾波器來預測blob的位置
//pResolver 采用meanshift或者/和 particle filter粒子濾波器來進行跟蹤
virtual void Process(IplImage* pImg, IplImage* pImgFG = NULL)
//這個函數先通過輪廓找到contour blob
//然后對前一幀的blob進行預測位置
//再將預測到的新位置與新產生的contour blob進行距離相關處理,
//如果是一個contour blob對應多個預測值,則認為有碰撞
//如果是多個contour blob對應一個預測數值,這里程序未做處理,個人認為應該有個分離處理
//接着對上述信息綜合處理,m_BlobList列表中某個blob如果有碰撞,則采用meanshift 或者/和
// particle filter進行跟蹤的結果(位置信息,預測器,跟蹤器)更新該blob
//如果沒碰撞,如果是一個contour blob對應一個m_BlobList列表中的某個blob,則用contour blob更新
//該blob,如果多個contour blob對應一個m_BlobList列表中的某個blob,則找最佳的contour blob
//更新該blob。最佳blob有兩中方式去判斷,第一是根據meanshift算法中直方圖最小距離法Bhattacharyya
//獲得一個可信度,最大可信度的最匹配;第二采用空間上最小距離法,距離最小最匹配
//這里需要說明的是在處理碰撞情況下pResolver調用的process,由於上層的默認采用meanshift+Partical filter
//進行跟蹤,所以運算量有時候非常大
//最終結果是用跟蹤法更新了m_BlobList表中blob的位置
//新contour blob與以前多個blob的預測位置有交集則認為有碰撞
if(m_BlobList.GetBlobNum()>0 && m_BlobListNew.GetBlobNum()>0)
}/* check intersection */
//
//這里如果沒有找到相關物體,則認為被隱藏,
//程序未做處理也
//可以判斷是否到圖像邊界,如果在圖像邊界則認為消失,如果不在圖像邊界,則認為被遮擋,
//可以考慮繼續跟蹤,直到預測計數達到某個數值的時候認為該物體消失
//判斷依據很簡單:pFLast是否為NULL
}/* check next new blob */
if(pBT->Collision)
{/* tracking in collision */
//多個blob對應一個contour blob
if(pBT->pResolver)
{
//這個函數太耗資源了,恐怖哦,又有粒子跟蹤又有meanshift,肯定很慢
//pBT->pResolver->Process(&(pBT->BlobPredict),pImg, pImgFG)[0];
pB[0] = pBT->BlobPredict;//pBT->pResolver->Process(&(pBT->BlobPredict),pImg, pImgFG)[0];
}
}/* tracking in collision */
/這里還有一種情況是 no contour blob對應的blob,此時pBT->pBlobHyp->GetBlobNum()應該為0
//所以CvBlob NewCC = pBT->BlobPredict;就是預測值
/* one blob several CC */
CvBlob* pBBest = NULL;
int CvBlobDetectorCC::DetectNewBlob(IplImage* /*pImg*/, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList)
/這個函數首先通過輪廓獲得contour blob
//然后篩掉與其關聯的已經存在的blob,該blob應該是通過預測,跟蹤后的blob
//然后將剩下的blob與存在的track進行關聯操作,如果符合關聯條件則計數加1,
//這里的track類似於預備blob的歷史軌跡表,他們可能成為新blob的,需要符合計數達到SEQ_SIZE
//軌跡符合線形擬合
//如果多個contour blob對應一個track則復制這個contour
//如果多個track對應一個contour blob,則通過計數和線形擬合進行最后判斷
/* cvDetectNewBlobs return 1 and fill blob pNewBlob by blob parameters if new blob is detected */
/SEQ_SIZE應該是控制是否符合新blob條件的計數上限,
//比如一個contour blob在一個track里連續SEQ_SIZE次符合新blob條件,則判斷是否該contour blob是一個新的blob,
//符合新blob條件有三個條件:
//1,物體高寬分別大於門限
//2,前后幀質心X,Y方向距離與以前blob列表中的所有blob分別都大於兩個物體寬度和的平均高度和平均
//3,對連續SEQ_SIZE個物體的質心進行最小二乘法的線性擬合看是否符合一定運動軌跡規律
//前面的blob都是coutour blob,通過找輪廓方法獲得的blob,
//coutour blob通過篩選確定是否為新物體
//m_TrackSeq用於存放可能為新blob的contour blob,每一個DefSeq
//都保存了符合條件contour blob的歷史軌跡 當track計數達到了SEQ_SIZE的時候
//就與已有的 blob進行比對,看是否與已有的blob關聯,關聯條件主要是看兩個blob的質心X,Y方向的距離是否
//大於兩個blob寬度和的平均高度和平均,個人 認為這里多余了,因為前面已經檢測過了
//還有就是判斷物體是否在邊界附近,如果在邊界附近也可以不用處理跟蹤了
//當所有的都符合,則用最小二乘法來最終確定track中SEQ_SIZE個歷史blob的軌跡是否合理
{/* shift each track */
int j;
for(j=0;j<m_TrackNum;++j)
{
int i;
DefSeq* pTrack = m_TrackSeq+j;
for(i=SEQ_SIZE-1;i>0;--i)pTrack->pBlobs[i]=pTrack->pBlobs[i-1];
pTrack->pBlobs[0] = NULL;
if(pTrack->size == SEQ_SIZE)
pTrack->size--;
}
}/* shift each track */
else if((m_TrackNum+NewTrackNum)<SEQ_NUM)
{ /* duplicate existed track */
//這里表示前面已有contour blob符合要求,這樣
//兩個contour blob分開成兩個track,類似一個物體分離出兩個物體情況,
m_TrackSeq[m_TrackNum+NewTrackNum] = pTrack[0];//pTrack[0]前面已經pTrack->size++;這里直接賦值過去就可以了
//如果contour blob沒有和任何的track關聯則生成新的track
if(AsignedTrack==0 && (m_TrackNum+NewTrackNum)<SEQ_NUM )
{/* init new track */
//這里采用了最小二乘法進行線性擬合
//y=a*x+b,這里x是從0~N-1的數值,y分別表示運動軌跡的坐標值x,y,將他們分別進行線性擬合
//根據最小二乘法的線性擬合公式
//a=((x*y)'-x'*y')/((x*x)'-x'*x');
//b=y'-a*x';
//這里'是表示平均的意思
//首先有 1^2 + 2^2 + …… + n^2 = n*(n+1)*(2n+1)/6
//有 1 + 2 + …… + n = n*(n+1)/2;
//考慮x,是從0開始則,
//0 + 1^2 + 2^2 + …… + (n-1)^2=n*(n+1)*(2n+1)/6 -n*n=n*(n-1)*(2n-1)/6;
//0 + 1 + 2 + …… + n-1 = n*(n-1)/2;
//公式推導過程
// a = (jsum/N-(N-1)/2*sum/N)/((N*(N+1)*(2*N+1)/6 -N*N)/N-(N-1)/2*(N-1)/2)
// = (jsum-(N-1)/2*sum)/((N-1)*(2N-1)/6-(N-1)*(N-1)/4)/N
// = 6*(2*jsum-(N-1)*sum)/((N-1)*(N+1)*N)
// = 6*(2*jsum-(N-1)*sum)/((N*N-1)*N)
// b = sum/N-a*(N-1)/2;
// = sum/N-3*(2*jsum-(N-1)*sum)/(N*(N+1));
// = ((N+1)*sum-6*jsum-3*(N-1)*sum)/(N*(N+1));
// = ((6*N-3)*sum-6*jsum)/(N*(N+1));
//與程序公式符合
a[0] = 6*((1-N)*sum[0]+2*jsum[0])/(N*(N*N-1));
b[0] = -2*((1-2*N)*sum[0]+3*jsum[0])/(N*(N+1));
a[1] = 6*((1-N)*sum[1]+2*jsum[1])/(N*(N*N-1));
b[1] = -2*((1-2*N)*sum[1]+3*jsum[1])/(N*(N+1));
for(j=0;j<N;++j)
{
Error +=
pow(a[0]*j+b[0]-pBL[j]->x,2)+
pow(a[1]*j+b[1]-pBL[j]->y,2);
}
//a[0],a[1]是斜率,如果斜率過大,表示速度快,這里過濾了快速運動
//
Error = sqrt(Error/N);
if( Error > S.width*0.01 ||
fabs(a[0])>S.width*0.1 ||
fabs(a[1])>S.height*0.1)
{
Good = 0;
}