运动分析和对象跟踪
[-]
Cv運動分析與對象跟蹤
目錄[隱藏]
|
背景統計量的累積
[編輯]Acc
將幀疊加到累積器(accumulator)中
void cvAcc( const CvArr* image, CvArr* sum, const CvArr* mask=NULL ); image函數 cvAcc 將整個圖像 image 或某個選擇區域疊加到 sum 中:
sum(x,y)=sum(x,y)+image(x,y) if mask(x,y)!=0 [編輯]SquareAcc
疊加輸入圖像的平方到累積器中
void cvSquareAcc( const CvArr* image, CvArr* sqsum, const CvArr* mask=NULL ); image函數 cvSquareAcc 疊加輸入圖像 image 或某個選擇區域的二次方,到累積器 sqsum 中
sqsum(x,y)=sqsum(x,y)+image(x,y)2 if mask(x,y)!=0 [編輯]MultiplyAcc
將兩幅輸入圖像的乘積疊加到累積器中
void cvMultiplyAcc( const CvArr* image1, const CvArr* image2, CvArr* acc, const CvArr* mask=NULL ); image1函數 cvMultiplyAcc 疊加兩個輸入圖像的乘積到累積器 acc:
acc(x,y)=acc(x,y) + image1(x,y)?image2(x,y) if mask(x,y)!=0 [編輯]RunningAvg
更新 running average 滑動平均( Hunnish: 不知道 running average 如何翻譯才恰當)
void cvRunningAvg( const CvArr* image, CvArr* acc, double alpha, const CvArr* mask=NULL ); image函數 cvRunningAvg 計算輸入圖像 image 的加權和,以及累積器 acc 使得 acc 成為幀序列的一個 running average:
acc(x,y)=(1-α)?acc(x,y) + α?image(x,y) if mask(x,y)!=0其中 α (alpha) 調節更新速率 (累積器以多快的速率忘掉前面的幀).
[編輯]運動模板
[編輯]UpdateMotionHistory
去掉影像(silhouette) 以更新運動歷史圖像
void cvUpdateMotionHistory( const CvArr* silhouette, CvArr* mhi,double timestamp, double duration ); silhouette函數 cvUpdateMotionHistory 用下面方式更新運動歷史圖像:
mhi(x,y)=timestamp if silhouette(x,y)!=00 if silhouette(x,y)=0 and mhi(x,y)<timestamp-durationmhi(x,y) otherwise也就是,MHI(motion history image) 中在運動發生的象素點被設置為當前時間戳,而運動發生較久的象素點被清除。
[編輯]CalcMotionGradient
計算運動歷史圖像的梯度方向
void cvCalcMotionGradient( const CvArr* mhi, CvArr* mask, CvArr* orientation,double delta1, double delta2, int aperture_size=3 ); mhi函數 cvCalcMotionGradient 計算 MHI 的差分 Dx 和 Dy ,然后計算梯度方向如下式:
其中都要考慮 Dx(x,y)' 和 Dy(x,y)' 的符號 (如 cvCartToPolar 類似). 然后填充 mask 以表示哪些方向是正確的(見 delta1 和delta2 的描述).
[編輯]CalcGlobalOrientation
計算某些選擇區域的全局運動方向
double cvCalcGlobalOrientation( const CvArr* orientation, const CvArr* mask, const CvArr* mhi,double timestamp, double duration ); orientation函數 cvCalcGlobalOrientation 在選擇的區域內計算整個運動方向,并且返回 0° 到 360° 之間的角度值。首先函數創建運動直方圖,尋找基本方向做為直方圖最大值的坐標。然后函數計算與基本方向的相對偏移量,做為所有方向向量的加權和:運行越近,權重越大。得到的角度是基本方向和偏移量的循環和。
[編輯]SegmentMotion
將整個運動分割為獨立的運動部分
CvSeq* cvSegmentMotion( const CvArr* mhi, CvArr* seg_mask, CvMemStorage* storage,double timestamp, double seg_thresh ); mhi函數 cvSegmentMotion 尋找所有的運動分割,并且在seg_mask 用不同的單獨數字(1,2,...)標識它們。它也返回一個具有 CvConnectedComp 結構的序列,其中每個結構對應一個運動部件。在這之后,每個運動部件的運動方向就可以被函數 cvCalcGlobalOrientation 利用提取的特定部件的掩模(mask)計算出來(使用 cvCmp)
[編輯]對象跟蹤
[編輯]MeanShift
在反向投影圖中發現目標中心
int cvMeanShift( const CvArr* prob_image, CvRect window,CvTermCriteria criteria, CvConnectedComp* comp ); prob_image函數 cvMeanShift 在給定反向投影和初始搜索窗口位置的情況下,用迭代方法尋找目標中心。當搜索窗口中心的移動小于某個給定值時或者函數已經達到最大迭代次數時停止迭代。 函數返回迭代次數。
[編輯]CamShift
發現目標中心,尺寸和方向
int cvCamShift( const CvArr* prob_image, CvRect window, CvTermCriteria criteria,CvConnectedComp* comp, CvBox2D* box=NULL ); prob_image函數 cvCamShift 實現了 CAMSHIFT 目標跟蹤算法([Bradski98]). 首先它調用函數 cvMeanShift 尋找目標中心,然后計算目標尺寸和方向。最后返回函數 cvMeanShift 中的迭代次數。
CvCamShiftTracker 類在 cv.hpp 中被聲明,函數實現了彩色目標的跟蹤。
[編輯]SnakeImage
改變輪廓位置使得它的能量最小
void cvSnakeImage( const IplImage* image, CvPoint* points, int length,float* alpha, float* beta, float* gamma, int coeff_usage,CvSize win, CvTermCriteria criteria, int calc_gradient=1 ); image- CV_VALUE 表示每個 alpha, beta, gamma 都是指向為所有點所用的一個單獨數值;
- CV_ARRAY 表示每個 alpha, beta, gamma 是一個指向系數數組的指針,snake 上面各點的系數都不相同。因此,各個系數數組必須與輪廓具有同樣的大小。所有數組必須與輪廓具有同樣大小
函數 cvSnakeImage 更新 snake 是為了最小化 snake 的整個能量,其中能量是依賴于輪廓形狀的內部能量(輪廓越光滑,內部能量越小)以及依賴于能量場的外部能量之和,外部能量通常在哪些局部能量極值點中達到最小值(這些局部能量極值點與圖像梯度表示的圖像邊緣相對應)。
參數 criteria.epsilon 用來定義必須從迭代中除掉以保證迭代正常運行的點的最少數目。
如果在迭代中去掉的點數目小于 criteria.epsilon 或者函數達到了最大的迭代次數 criteria.max_iter ,則終止函數。
[編輯]光流
[編輯]CalcOpticalFlowHS
計算兩幅圖像的光流
void cvCalcOpticalFlowHS( const CvArr* prev, const CvArr* curr, int use_previous,CvArr* velx, CvArr* vely, double lambda,CvTermCriteria criteria ); prev函數 cvCalcOpticalFlowHS 為輸入圖像的每一個象素計算光流,使用 Horn & Schunck 算法 [Horn81].
[編輯]CalcOpticalFlowLK
計算兩幅圖像的光流
void cvCalcOpticalFlowLK( const CvArr* prev, const CvArr* curr, CvSize win_size,CvArr* velx, CvArr* vely ); prev函數 cvCalcOpticalFlowLK 為輸入圖像的每一個象素計算光流,使用 Lucas & Kanade 算法 [Lucas81].
[編輯]CalcOpticalFlowBM
用塊匹配方法計算兩幅圖像的光流
void cvCalcOpticalFlowBM( const CvArr* prev, const CvArr* curr, CvSize block_size,CvSize shift_size, CvSize max_range, int use_previous,CvArr* velx, CvArr* vely ); prev函數 cvCalcOpticalFlowBM 為重疊塊 block_size.width×block_size.height 中的每一個象素計算光流,因此其速度域小于整個圖像的速度域。對每一個在圖像 prev 中的塊,函數試圖在 curr 中某些原始塊或其偏移 (velx(x0,y0),vely(x0,y0)) 塊的鄰域里尋找類似的塊,如同在前一個函數調用中所計算的類似(如果 use_previous=1)
[編輯]CalcOpticalFlowPyrLK
計算一個稀疏特征集的光流,使用金字塔中的迭代 Lucas-Kanade 方法
void cvCalcOpticalFlowPyrLK( const CvArr* prev, const CvArr* curr, CvArr* prev_pyr, CvArr* curr_pyr,const CvPoint2D32f* prev_features, CvPoint2D32f* curr_features,int count, CvSize win_size, int level, char* status,float* track_error, CvTermCriteria criteria, int flags ); prev- CV_LKFLOW_PYR_A_READY , 在調用之前,第一幀的金字塔已經準備好
- CV_LKFLOW_PYR_B_READY , 在調用之前,第二幀的金字塔已經準備好
- CV_LKFLOW_INITIAL_GUESSES , 在調用之前,數組 B 包含特征的初始坐標 (Hunnish: 在本節中沒有出現數組 B,不知是指的哪一個)
函數 cvCalcOpticalFlowPyrLK 實現了金字塔中 Lucas-Kanade 光流計算的稀疏迭代版本 ([Bouguet00])。 它根據給出的前一幀特征點坐標計算當前視頻幀上的特征點坐標。 函數尋找具有子象素精度的坐標值。
兩個參數 prev_pyr 和 curr_pyr 都遵循下列規則: 如果圖像指針為 0, 函數在內部為其分配緩存空間,計算金字塔,然后再處理過后釋放緩存。 否則,函數計算金字塔且存儲它到緩存中,除非設置標識 CV_LKFLOW_PYR_A[B]_READY 。 圖像應該足夠大以便能夠容納 Gaussian 金字塔數據。 調用函數以后,金字塔被計算而且相應圖像的標識可以被設置,為下一次調用準備就緒 (比如:對除了第一個圖像的所有圖像序列,標識 CV_LKFLOW_PYR_A_READY 被設置).
[編輯]預估器
[編輯]CvKalman
Kalman 濾波器狀態
typedef struct CvKalman {int MP; /* 測量向量維數 */int DP; /* 狀態向量維數 */int CP; /* 控制向量維數 *//* 向后兼容字段 */ #if 1float* PosterState; /* =state_pre->data.fl */float* PriorState; /* =state_post->data.fl */float* DynamMatr; /* =transition_matrix->data.fl */float* MeasurementMatr; /* =measurement_matrix->data.fl */float* MNCovariance; /* =measurement_noise_cov->data.fl */float* PNCovariance; /* =process_noise_cov->data.fl */float* KalmGainMatr; /* =gain->data.fl */float* PriorErrorCovariance;/* =error_cov_pre->data.fl */float* PosterErrorCovariance;/* =error_cov_post->data.fl */float* Temp1; /* temp1->data.fl */float* Temp2; /* temp2->data.fl */ #endifCvMat* state_pre; /* 預測狀態 (x'(k)): x(k)=A*x(k-1)+B*u(k) */CvMat* state_post; /* 矯正狀態 (x(k)):x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) */CvMat* transition_matrix; /* 狀態傳遞矩陣 state transition matrix (A) */CvMat* control_matrix; /* 控制矩陣 control matrix (B)(如果沒有控制,則不使用它)*/CvMat* measurement_matrix; /* 測量矩陣 measurement matrix (H) */CvMat* process_noise_cov; /* 過程噪聲協方差矩陣process noise covariance matrix (Q) */CvMat* measurement_noise_cov; /* 測量噪聲協方差矩陣measurement noise covariance matrix (R) */CvMat* error_cov_pre; /* 先驗誤差計協方差矩陣priori error estimate covariance matrix (P'(k)):P'(k)=A*P(k-1)*At + Q)*/CvMat* gain; /* Kalman 增益矩陣 gain matrix (K(k)):K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R)*/CvMat* error_cov_post; /* 后驗錯誤估計協方差矩陣posteriori error estimate covariance matrix (P(k)):P(k)=(I-K(k)*H)*P'(k) */CvMat* temp1; /* 臨時矩陣 temporary matrices */CvMat* temp2;CvMat* temp3;CvMat* temp4;CvMat* temp5; } CvKalman;結構 CvKalman 用來保存 Kalman 濾波器狀態。它由函數 cvCreateKalman 創建,由函數f cvKalmanPredict 和 cvKalmanCorrect 更新,由 cvReleaseKalman 釋放. 通常該結構是為標準 Kalman 所使用的 (符號和公式都借自非常優秀的 Kalman 教程 [Welch95]):
其中:
對標準 Kalman 濾波器,所有矩陣: A, B, H, Q 和 R 都是通過 cvCreateKalman 在分配結構 CvKalman 時初始化一次。但是,同樣的結構和函數,通過在當前系統狀態鄰域中線性化擴展 Kalman 濾波器方程,可以用來模擬擴展 Kalman 濾波器,在這種情況下, A, B, H (也許還有 Q 和 R) 在每一步中都被更新。
[編輯]CreateKalman
分配 Kalman 濾波器結構
CvKalman* cvCreateKalman( int dynam_params, int measure_params, int control_params=0 ); dynam_params函數 cvCreateKalman 分配 CvKalman 以及它的所有矩陣和初始參數
[編輯]ReleaseKalman
釋放 Kalman 濾波器結構
void cvReleaseKalman( CvKalman** kalman ); kalman函數 cvReleaseKalman 釋放結構 CvKalman 和里面所有矩陣
[編輯]KalmanPredict
估計后來的模型狀態
const CvMat* cvKalmanPredict( CvKalman* kalman, const CvMat* control=NULL ); #define cvKalmanUpdateByTime cvKalmanPredict kalman函數 cvKalmanPredict 根據當前狀態估計后來的隨機模型狀態,并存儲于 kalman->state_pre:
其中
函數返回估計得到的狀態值
[編輯]KalmanCorrect
調節模型狀態
const CvMat* cvKalmanCorrect( CvKalman* kalman, const CvMat* measurement ); #define cvKalmanUpdateByMeasurement cvKalmanCorrect kalman函數 cvKalmanCorrect 在給定的模型狀態的測量基礎上,調節隨機模型狀態:
其中
函數存儲調節狀態到 kalman->state_post 中并且輸出時返回它。
例子. 使用 Kalman 濾波器跟蹤一個旋轉的點
#include "cv.h" #include "highgui.h" #include <math.h>int main(int argc, char** argv) {/* A matrix data */const float A[] = { 1, 1, 0, 1 };IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 );CvKalman* kalman = cvCreateKalman( 2, 1, 0 );/* state is (phi, delta_phi) - angle and angle increment */CvMat* state = cvCreateMat( 2, 1, CV_32FC1 );CvMat* process_noise = cvCreateMat( 2, 1, CV_32FC1 );/* only phi (angle) is measured */CvMat* measurement = cvCreateMat( 1, 1, CV_32FC1 );CvRandState rng;int code = -1;cvRandInit( &rng, 0, 1, -1, CV_RAND_UNI );cvZero( measurement );cvNamedWindow( "Kalman", 1 );for(;;){cvRandSetRange( &rng, 0, 0.1, 0 );rng.disttype = CV_RAND_NORMAL;cvRand( &rng, state );memcpy( kalman->transition_matrix->data.fl, A, sizeof(A));cvSetIdentity( kalman->measurement_matrix, cvRealScalar(1) );//初始化帶尺度的單位矩陣 cvSetIdentity( kalman->process_noise_cov, cvRealScalar(1e-5) );cvSetIdentity( kalman->measurement_noise_cov, cvRealScalar(1e-1) );cvSetIdentity( kalman->error_cov_post, cvRealScalar(1));/* choose random initial state */cvRand( &rng, kalman->state_post );rng.disttype = CV_RAND_NORMAL;for(;;){#define calc_point(angle) \cvPoint( cvRound(img->width/2 + img->width/3*cos(angle)), \cvRound(img->height/2 - img->width/3*sin(angle)))float state_angle = state->data.fl[0];CvPoint state_pt = calc_point(state_angle);/* predict point position */const CvMat* prediction = cvKalmanPredict( kalman, 0 );float predict_angle = prediction->data.fl[0];CvPoint predict_pt = calc_point(predict_angle);float measurement_angle;CvPoint measurement_pt;cvRandSetRange( &rng, 0, sqrt(kalman->measurement_noise_cov->data.fl[0]), 0 );cvRand( &rng, measurement );/* generate measurement */cvMatMulAdd( kalman->measurement_matrix, state, measurement, measurement );measurement_angle = measurement->data.fl[0];measurement_pt = calc_point(measurement_angle);/* plot points */#define draw_cross( center, color, d ) \cvLine( img, cvPoint( center.x - d, center.y - d ), \cvPoint( center.x + d, center.y + d ), color, 1, 0 ); \cvLine( img, cvPoint( center.x + d, center.y - d ), \cvPoint( center.x - d, center.y + d ), color, 1, 0 )cvZero( img );draw_cross( state_pt, CV_RGB(255,255,255), 3 );draw_cross( measurement_pt, CV_RGB(255,0,0), 3 );draw_cross( predict_pt, CV_RGB(0,255,0), 3 );cvLine( img, state_pt, predict_pt, CV_RGB(255,255,0), 3, 0 );/* adjust Kalman filter state */cvKalmanCorrect( kalman, measurement );cvRandSetRange( &rng, 0, sqrt(kalman->process_noise_cov->data.fl[0]), 0 );cvRand( &rng, process_noise );cvMatMulAdd( kalman->transition_matrix, state, process_noise, state );cvShowImage( "Kalman", img );code = cvWaitKey( 100 );if( code > 0 ) /* break current simulation by pressing a key */break;}if( code == 27 ) /* exit by ESCAPE */break;}return 0; } [編輯]CvConDensation
ConDensaation 狀態
typedef struct CvConDensation {int MP; // 測量向量的維數: Dimension of measurement vectorint DP; // 狀態向量的維數: Dimension of state vectorfloat* DynamMatr; // 線性動態系統矩陣:Matrix of the linear Dynamics systemfloat* State; // 狀態向量: Vector of Stateint SamplesNum; // 粒子數: Number of the Samplesfloat** flSamples; // 粒子向量數組: array of the Sample Vectorsfloat** flNewSamples; // 粒子向量臨時數組: temporary array of the Sample Vectorsfloat* flConfidence; // 每個粒子的置信度(譯者注:也就是粒子的權值):Confidence for each Samplefloat* flCumulative; // 權值的累計: Cumulative confidencefloat* Temp; // 臨時向量:Temporary vectorfloat* RandomSample; // 用來更新粒子集的隨機向量: RandomVector to update sample setCvRandState* RandS; // 產生隨機向量的結構數組: Array of structures to generate random vectors } CvConDensation;結構 CvConDensation中條件概率密度傳播(譯者注:粒子濾波的一種特例)(Con-Dens-Ation: 單詞 CONditional DENSity propagATION 的縮寫)跟蹤器的狀態。該算法描述可參考http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/ISARD1/condensation.html
[編輯]CreateConDensation
分配 ConDensation 濾波器結構
CvConDensation* cvCreateConDensation( int dynam_params, int measure_params, int sample_count ); dynam_params函數 cvCreateConDensation 創建結構 CvConDensation 并且返回結構指針。
[編輯]ReleaseConDensation
釋放 ConDensation 濾波器結構
void cvReleaseConDensation( CvConDensation** condens ); condens函數 cvReleaseConDensation 釋放結構 CvConDensation (見cvConDensation) 并且清空所有事先被開辟的內存空間。
[編輯]ConDensInitSampleSet
初始化 ConDensation 算法中的粒子集
void cvConDensInitSampleSet( CvConDensation* condens, CvMat* lower_bound, CvMat* upper_bound ); condens函數 cvConDensInitSampleSet 在指定區間內填充結構 CvConDensation 中的樣例數組。
[編輯]ConDensUpdateByTime
估計下個模型狀態
void cvConDensUpdateByTime( CvConDensation* condens ); condens函數 cvConDensUpdateByTime 從當前狀態估計下一個隨機模型狀態。
取自"http://www.opencv.org.cn/index.php/Cv%E8%BF%90%E5%8A%A8%E5%88%86%E6%9E%90%E4%B8%8E%E5%AF%B9%E8%B1%A總結
- 上一篇: KNN分类器
- 下一篇: 有关cvUpdateMotionHish