1. 程式人生 > >基於MeanShift的Camshift演算法原理詳解(opencv實現,有原始碼)

基於MeanShift的Camshift演算法原理詳解(opencv實現,有原始碼)

基於MeanShiftCamshift演算法原理詳解(整理)

第一篇MeanShift原理和實現

1  MeanShift原理

如下圖所示:矩形視窗中的紅色點代表特徵資料點,矩形中的圓圈代表選取視窗meanshift演算法的目的是找到含有最多特徵的視窗區域,即使圓心與概率密度函式的區域性極值點重合,亦即使圓心與特徵資料點最密集的地方中心儘量重合到一塊。演算法實現是通過向特徵資料點密度函式上升梯度方向逐步迭代偏移至上升梯度值近似為零(到達最密集的地方)。


即在不改變選取區域性視窗的情況下,通過給視窗一個向著特徵資料點更密集的方向一個偏移向量,然後將偏移後的選取視窗作為當前選取視窗,根據選取視窗資料特徵點密集情況給出一個一個向著特徵資料點更密集的方向一個偏移向量

……迭代偏移過程中,直到偏移向量的模值近似為零即可。

我們來分析一下上述過程:當選取視窗(圓形)由遠靠近最密集點,再遠離最密集點的過程中,圓形視窗中特徵資料點的數量變化:理想情況下應是選取視窗包含的特徵資料點越來越多,再到越來越少。(這個過程對應meanshift演算法的基本形式,沒有新增核函式時)

2 為什麼要用概率密度函式的上升梯度呢?

概率密度函式可以表示大小不變的選取視窗中特徵資料點的密度,我們要找的是概率密度最大的選取視窗,這樣,我們只要使下一個選取視窗的概率密度函式值減去當前選取視窗的概率密度函式值大於0,就可以越來越靠近取視窗的概率密度函式的最大值,當下一個選取視窗與當前選取視窗非常接近時,就可以用概率密度梯度表示兩視窗概率密度函式對應值的差,也就是說只要使選取視窗向概率密度函式梯度上升的方向偏移就可以在上升梯度值近似為零時取得概率密度函式的近似最大值。所以使用概率,是因為

特徵資料點本身是概率事件或說是標定與標準特徵匹配程度的特徵資料

上面的分析的前提是預設選取視窗中的各個特徵資料點的權值是相等的,但實際中各個特徵資料點對最終判定是否為目標的影響是不一樣的,這可以通過加權值函式(即meanshift擴充套件方法中的核函式)來實現。

3 新增的權值函式應滿足什麼條件呢?

對某一選取視窗的概率密度函式值=各個特徵資料點的總和相當量除以視窗面積的相當量。

當選取視窗為全域性視窗時,目標一定出現,即此範圍內權值函式的各個值的總和歸一化後一定為1

為了便於比較和求概率密度函式的梯度,加權函式亦即核函式在自變數取值範圍內的積分為1.

所以核函式應滿足的條件:自變數範圍內積分,值為

1

4 meanshift方法的適用範圍及其優缺點


meanshift方法適合概率密度函式有極值且在某一區域性區域內唯一,即選擇的特徵資料點能夠較為明顯的判定目標,亦即顯著特徵點。顯然此方法,meanshift的基本形式不適合等概率特徵點(即特徵點是均勻分佈)的情況。

1)       meanshift演算法,受初始值的影響很大,和經驗相關。

2)       演算法收斂的速度和程度很大程度上與選取的視窗有關,選取恰當的視窗非常重要。

3)       視窗選取的是否恰當很大程度上取決於目標(特徵資料點的分佈狀況),這就是說此方法在處理一類目標時,還是很有效的,最起碼跟蹤同一目標,在視窗經過恰當的線性變換後,跟蹤效果應該還是不錯的。即概率密度函式的極值在自變數區域壓縮或擴大的過程中,極值仍存在,仍可跟蹤到目標。

4)       meanshift演算法若用於圖割,則適用於:

已經建立標準的特徵資料點集,且通過恰當的概率密度函式和核函式可以唯一的確定目標時,可以將目標從批量圖片中分割,挑選出來,但是取出的結果顯示為選取視窗中所有的內容,也就是說可能會有目標物之外的影象或缺失部分目標物。若選取視窗為目標物的輪廓,那將非常不錯,也就意味著視窗模板要更新或目標輪廓是不變的(實際中不變幾乎是不可能的),更新以為誤差與誤差的積累,也就是說進行批量分割時,效果是有限的。

5  meanshift 程式設計實現(opencv

具體程式設計實現的時候meanshift演算法思想其實很簡單——利用概率密度的梯度爬升來尋找區域性最優。它要做的就是輸入一個在影象的範圍,然後一直迭代(朝著重心迭代)直到滿足你的要求為止。

例如在opencv中,實現過程:輸入一張影象(imgProb),再輸入一個開始迭代的方框(windowIn)和一個迭代條件(criteria),輸出的是迭代完成的位置(comp )。

這是函式原型:

    int cvMeanShift( const void* imgProb,CvRect windowIn,

                     CvTermCriteria criteria, CvConnectedComp* comp )

但是當它用於跟蹤時,這張輸入的影象就必須是反向投影圖了。反向投影圖實際上是一張概率密度圖。經過反向投影時的輸入是一個目標影象的直方圖(也可以認為是目標影象),還一個輸入是當前影象就是你要跟蹤的全圖,輸出大小與全圖一樣大,它上畫素點表徵著一種概率,就是全圖上這個點是目標影象一部分的概率。如果這個點越亮,就說明這個點屬於物體的概率越大。

1)半自動跟蹤思路:

輸入視訊,用畫筆圈出要跟蹤的目標,然後對物體跟蹤。(用過opencv的都知道,這其實是camshiftdemo的工作過程)

第一步:選中物體,記錄你輸入的方框和物體。

第二步:求出視訊中有關物體的反向投影圖。

第三步:根據反向投影圖和輸入的方框進行meanshift迭代,由於它是向重心移動,即向反向投影圖中概率大的地方移動,所以始終會移動到目標上。

第四步:然後下一幀影象時用上一幀輸出的方框來迭代即可。

2)全自動跟蹤思路:

輸入視訊,對運動物體進行跟蹤。

第一步:運用運動檢測演算法將運動的物體與背景分割開來。

第二步:提取運動物體的輪廓,並從原圖中獲取運動影象的資訊。

第三步:對這個資訊進行反向投影,獲取反向投影圖。

第四步:根據反向投影圖和物體的輪廓(也就是輸入的方框)進行meanshift迭代,由於它是向重心移動,即向反向投影圖中概率大的地方移動,所以始終會移動到物體上。

第五步:然後下一幀影象時用上一幀輸出的方框來迭代即可。

半自動和全自動的區別就是多了物體和背景的分離,特徵提取部分

附:後面的目標的跟蹤例子是半自動的,即需要人手工選定一個目標。我正在努力嘗試全自動的目標跟蹤,希望可以和大家能在這方面與大家交流。微笑


總結:用meanshift進行跟蹤最重要的一點是輸入影象的把握,也就是要讓它的迭代能越來越迭代到目標上。這種影象也不一定就是反向投影圖,只要是一幅反映當前影象中每個畫素點含有目標概率圖就可以了,其實反向投影圖就是這樣的一幅圖而已。

6 程式碼和效果圖

程式碼參見請在此處下載點選開啟連結

效果圖分析:下圖均為跟蹤人面板膚色區域的效果,第一張圖為只有人臉出現的時候,檢測出人臉;第二張圖為當人體其他部位出現時,跟蹤的還是人臉部分,對比下面兩個圖可以發現,meanshift不具備自適應性,當和人臉膚色相近的身體部位出現時,不能自動調整檢測區域(矩形框)的大小)。

補充:在調整過程中,發現:

1)當人臉移除時,檢測失敗,即使沒有移除,在人臉運動過程中,若是人臉在視屏中所佔的區域面積很小時,也會跟蹤失敗。

2)迭代速度體現不明顯,事實上速度是和選取視窗有關的。當人臉移除時,跟蹤區域切換到非膚色區域(即跟蹤丟失),反應速度會稍微變慢。


第二篇CamShift的改進原理和實現(在meanshift基礎上)

1 CamShift原理

CamShiftContinuously Apative Mean-Shift)演算法,是一種運動跟蹤演算法。它主要通過視訊影象中運動物體的顏色資訊來達到跟蹤的目的。

camshift利用目標的顏色直方圖模型將影象轉換為顏色概率分佈圖,初始化一個搜尋窗的大小和位置,並根據上一幀得到的結果自適應調整搜尋視窗的位置和大小,從而定位出當前影象中目標的中心位置。該演算法分為三個部分:

1) Back Projection計算

2) Mean Shift演算法

3) CamShift演算法

具體實現是:

(反向投影:目標的顏色直方圖模型將影象轉換為顏色概率分佈圖)

2 演算法步驟

21 色彩投影圖(反向投影

(1)RGB顏色空間對光照亮度變化較為敏感,為了減少此變化對跟蹤效果的影響,首先將影象從RGB空間轉換到HSV空間。

(2)然後對其中的H分量作直方圖,在直方圖中代表了不同H分量值出現的概率或者畫素個數,就是說可以查找出H分量大小為h的概率或者畫素個數,即得到了顏色概率查詢表

(3)將影象中每個畫素的值用其顏色出現的概率對替換,就得到了顏色概率分佈圖。這個過程就叫反向投影,顏色概率分佈圖是一個灰度影象

22  meanshift

meanshift演算法是一種密度函式梯度估計的非引數方法,通過迭代尋優找到概率分佈的極值來定位目標。演算法過程為:

(1)在顏色概率分佈圖中選取搜尋窗W

(2)計算零階距:

計算一階距:

計算搜尋窗的質心

(3)調整搜尋窗大小:寬度為;長度為1.2s

(4)移動搜尋窗的中心到質心,如果移動距離大於預設的固定閾值,則重複2)3)4),直到搜尋窗的中心與質心間的移動距離小於預設的固定閾值,或者迴圈運算的次數達到某一最大值,停止計算。

演算法過程也可以描述如下:

(1) 選擇搜尋視窗

  a.視窗的初始位置
  b.
視窗的型別(均勻、多項式、指數或者高斯型別)
  c.
視窗的形狀(對稱的或歪斜的,可能旋轉的,圓形或矩形)
  d.
視窗的大小(超出寬口大小被截去)

(2) 計算視窗(可能是帶權重的)的重心

(3) 將視窗的中心設定在計算出的重心處。移動搜尋窗的中心到質心,如果移動距離大於預設的固定閾值,則重複2)3)4),直到搜尋窗的中心與質心間的移動距離小於預設的固定閾值,或者迴圈運算的次數達到某一最大值,停止計算。

(4) 返回(2),直到視窗的位置不再變化(通常會變化,直至最後迭代收斂)

23  camshift

meanshift演算法擴充套件到連續影象序列,就是camshift演算法。它將視訊的所有幀做meanshift運算,並將上一幀的結果,即搜尋窗的大小和中心,作為下一幀meanshift演算法搜尋窗的初始值。如此迭代下去,就可以實現對目標的跟蹤。演算法過程為:

(1) 初始化搜尋窗

(2) 計算搜尋窗的顏色概率分佈(反向投影)

(3) 執行meanshift演算法,獲得搜尋窗新的大小和位置。

(4) 在下一幀視訊影象中用(3)中的值重新初始化搜尋窗的大小和位置,再跳轉到(2)繼續進行。

       camshift能有效解決目標變形和遮擋的問題,對系統資源要求不高時間複雜度低,在簡單背景下能夠取得良好的跟蹤效果。但當背景較為複雜,或者有許多與目標顏色相似畫素干擾的情況下,會導致跟蹤失敗。因為它單純的考慮顏色直方圖,忽略了目標的空間分佈特性,所以這種情況下需加入對跟蹤目標的預測演算法

3 opencv實現

31 Back Projection計算步驟:

1)計算被跟蹤目標的色彩直方圖。在各種色彩空間中,只有HSI空間(或與HSI類似的色彩空間)中的H分量可以表示顏色資訊。所以在具體的計算過程中,首先將其他的色彩空間的值轉化到HSI空間,然後會其中的H分量做1D直方圖計算。

2)根據獲得的色彩直方圖將原始影象轉化成色彩概率分佈圖像,這個過程就被稱作“Back Projection”。在OpenCV中的直方圖函式中,包含BackProjection的函式,函式原型是:

void cvCalcBackProject(IplImage** img,CvArr** backproject, const CvHistogram* hist);

傳遞給這個函式的引數有三個:

1. IplImage** img:存放原始影象,輸入。

2. CvArr** backproject:存放BackProjection結果,輸出。

3. CvHistogram* hist:存放直方圖,輸入

3)下面就給出計算Back ProjectionOpenCV程式碼。

(1)準備一張只包含被跟蹤目標的圖片,將色彩空間轉化到HSI空間,獲得其中的H分量:

IplImage*target=cvLoadImage("target.bmp",-1);  //裝載圖片

IplImage* target_hsv=cvCreateImage(cvGetSize(target), IPL_DEPTH_8U, 3 );

IplImage* target_hue=cvCreateImage(cvGetSize(target), IPL_DEPTH_8U, 3 );

cvCvtColor(target,target_hsv,CV_BGR2HSV);      //轉化到HSV空間

cvSplit( target_hsv, target_hue, NULL,NULL, NULL );    //獲得H分量

(2)計算H分量的直方圖,即1D直方圖:

IplImage* h_plane=cvCreateImage(cvGetSize(target_hsv),IPL_DEPTH_8U,1 );

int hist_size[]={255};         //H分量的值量化到[0,255]

float* ranges[]={ {0,360}};    //H分量的取值範圍是[0,360)

CvHistogram* hist=cvCreateHist(1,hist_size, ranges, 1);

cvCalcHist(&target_hue, hist, 0, NULL);

在這裡需要考慮H分量的取值範圍的問題,H分量的取值範圍是[0,360),這個取值範圍的值不能用一個byte來表示,為了能用一個byte表示,需要將H值做適當的量化處理,在這裡我們將H分量的範圍量化到[0,255]

(3)計算Back Projection

IplImage* rawImage;

//get from video frame,unsigned byte,onechannel

IplImage*result=cvCreateImage(cvGetSize(rawImage),IPL_DEPTH_8U,1);

cvCalcBackProject(&rawImage,result,hist);

(4)result即為我們需要的。

32 Mean Shift演算法的質心計算:

(1)計算區域內0階矩

for(int i=0;i< height;i++)

for(int j=0;j< width;j++)

M00+=I(i,j)

(2)區域內1階矩:

for(int i=0;i< height;i++)

for(int j=0;j< width;j++)

{

M10+=i*I(i,j);

M01+=j*I(i,j);

}

(3)Mass Center為:Xc=M10/M00; Yc=M01/M00

OpenCV中,提供MeanShift演算法的函式,函式的原型是:

int cvMeanShift(IplImage* imgprob,CvRectwindowIn, CvTermCriteria criteria,CvConnectedComp* out);

需要的引數為:

(1).IplImage* imgprob2D概率分佈圖像,傳入;

(2).CvRect windowIn:初始的視窗,傳入;

(3).CvTermCriteria criteria:停止迭代的標準,傳入;

(4).CvConnectedComp* out:查詢結果,傳出。

注:構造CvTermCriteria變數需要三個引數,一個是型別,另一個是迭代的最大次數,最後一個表示特定的閾值。例如可以這樣構造 criteria

criteria=cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,10,0.1)

33  CamShift演算法的具體步驟分5步:

Step 1:將整個影象設為搜尋區域。

Step 2:初始話Search Window的大小和位置。

Step 3:計算Search Window內的彩色概率分佈,此區域的大小比Search Window要稍微大一點。

Step 4:執行MeanShift。獲得Search Window新的位置和大小。

Step 5:在下一幀視訊影象中,用Step 3獲得的值初始化Search Window的位置和大小。跳轉到Step 3繼續執行。

OpenCV中,有實現CamShift演算法的函式,此函式的原型是:

cvCamShift(IplImage* imgprob, CvRectwindowIn,CvTermCriteria criteria,CvConnectedComp* out, CvBox2D* box=0);

其中:

imgprob:色彩概率分佈圖像。

windowInSearch Window的初始值。

Criteria:用來判斷搜尋是否停止的一個標準。

out:儲存運算結果,包括新的Search Window的位置和麵積。

box:包含被跟蹤物體的最小矩形。

總結如下:

Camshift演算法的過程由下面步驟組成:

1)確定初始目標及其區域;

2)計算出目標的色度(Hue)分量的直方圖;

3)利用直方圖計算輸入影象的反向投影圖(後面做進一步的解釋);

(4)利用MeanShift演算法在反向投影圖中迭代收索,直到其收斂或達到最大迭代次數。並儲存零次矩

5)從第(4)步中獲得收索視窗的中心位置和計算出新的視窗大小,以此為引數,進入到下一幀的目標跟蹤。(即跳轉到第(2)步);

幾點說明:

1. 在輸入影象進行反向投影圖之前在HSV空間內做了一個閥值處理,用以濾掉一些噪聲

2. 反向投影圖則是概率分佈圖,在反向投影圖中某一畫素點的值指的是這個點符合目標的概率分佈的概率是多少,或者直接說其為目標影象畫素點的畫素點是多少。計算方法為:根據畫素點的畫素值查目標的直方圖,其對應畫素值的概率是多少就作為該點在反向投影圖中的值。

3.Camshit演算法到底是怎樣自適應調整視窗的大小的。擴大:Canshift演算法在計算視窗大小前,在MeanShift算出的視窗的四個方向上增大了TOLERANCE,即高和寬都增大了2TOLERANCE(此值自己調整設定),這才有可能使得視窗能夠變大。縮小:在擴大的視窗內重新計算0階矩,1階矩和2階矩,利用矩的值重新計算高和寬。因此Camshif演算法相當於在MeanShift的結果上,再做了一個調整,從而使得跟蹤的視窗大小能夠隨目標的大小變化

優點:演算法的效率比較高,如果能利用多少特徵做出來的統計直方圖,我估計實驗效果會更好。

缺點:(1)只利用顏色統計做的跟蹤,在背景有相似顏色時,會出現跟蹤錯誤的情況。(2)不能做多目標跟蹤。(3)由於它只在初始位置(而不是從每個畫素點)開始迭代,所以有可能在初始位置錯了後,收斂的位置還是原位置(即跟丟了後,可能會找不回來)。

說明:有關於視窗大小調整,是根據直方圖來迭代求解,在camshift函式中會有體現,可以檢視其具體演算法部分如下:

程式碼 


Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->CV_IMPL int
cvCamShift( const void* imgProb, CvRect windowIn,
            CvTermCriteria criteria,
            CvConnectedComp* _comp,
            CvBox2D* box )
{
    const int TOLERANCE = 10;
    CvMoments moments;
    double m00 = 0, m10, m01, mu20, mu11, mu02, inv_m00;
    double a, b, c, xc, yc;
    double rotate_a, rotate_c;
    double theta = 0, square;
    double cs, sn;
    double length = 0, width = 0;
    int itersUsed = 0;
    CvConnectedComp comp;
    CvMat  cur_win, stub, *mat = (CvMat*)imgProb;


    CV_FUNCNAME( "cvCamShift" );


    comp.rect = windowIn;//初始化comp


    __BEGIN__;


    CV_CALL( mat = cvGetMat( mat, &stub ));


    CV_CALL( itersUsed = cvMeanShift( mat, windowIn, criteria, &comp ));//呼叫meanshift計算質心
    windowIn = comp.rect;//獲得新的視窗的位置


    //為了容錯,視窗的四邊都增大了TOLERANCE
    windowIn.x -= TOLERANCE;
    if( windowIn.x < 0 )
        windowIn.x = 0;


    windowIn.y -= TOLERANCE;
    if( windowIn.y < 0 )
        windowIn.y = 0;


    windowIn.width += 2 * TOLERANCE;
    if( windowIn.x + windowIn.width > mat->width )
        windowIn.width = mat->width - windowIn.x;


    windowIn.height += 2 * TOLERANCE;
    if( windowIn.y + windowIn.height > mat->height )
        windowIn.height = mat->height - windowIn.y;


    CV_CALL( cvGetSubRect( mat, &cur_win, windowIn ));//獲得指向子視窗的資料指標


    /* Calculating moments in new center mass */
    CV_CALL( cvMoments( &cur_win, &moments ));//重新計算視窗內的各種矩


    m00 = moments.m00;
    m10 = moments.m10;
    m01 = moments.m01;
    mu11 = moments.mu11;
    mu20 = moments.mu20;
    mu02 = moments.mu02;


    if( fabs(m00) < DBL_EPSILON )
        EXIT;


    inv_m00 = 1. / m00;
    xc = cvRound( m10 * inv_m00 + windowIn.x );//新的中心座標
    yc = cvRound( m01 * inv_m00 + windowIn.y );
    a = mu20 * inv_m00;
    b = mu11 * inv_m00;
    c = mu02 * inv_m00;


    /* Calculating width & height */
    square = sqrt( 4 * b * b + (a - c) * (a - c) );


    /* Calculating orientation */
    theta = atan2( 2 * b, a - c + square );


    /* Calculating width & length of figure */
    cs = cos( theta );
    sn = sin( theta );


    rotate_a = cs * cs * mu20 + 2 * cs * sn * mu11 + sn * sn * mu02;
    rotate_c = sn * sn * mu20 - 2 * cs * sn * mu11 + cs * cs * mu02;
    length = sqrt( rotate_a * inv_m00 ) * 4;//長與寬的計算
    width = sqrt( rotate_c * inv_m00 ) * 4;


    /* In case, when tetta is 0 or 1.57... the Length & Width may be exchanged */
    if( length < width )
    {
        double t;
        
        CV_SWAP( length, width, t );
        CV_SWAP( cs, sn, t );
        theta = CV_PI*0.5 - theta;
    }


    /* Saving results */
    //由於有寬和高的重新計算,使得能自動調整視窗大小
    if( _comp || box )
    {
        int t0, t1;
        int _xc = cvRound( xc );//取整
        int _yc = cvRound( yc );


        t0 = cvRound( fabs( length * cs ));
        t1 = cvRound( fabs( width * sn ));


        t0 = MAX( t0, t1 ) + 2;//寬的重新計算
        comp.rect.width = MIN( t0, (mat->width - _xc) * 2 );//保證寬不超出範圍


        t0 = cvRound( fabs( length * sn ));
        t1 = cvRound( fabs( width * cs ));


        t0 = MAX( t0, t1 ) + 2;//高的重新計算
        comp.rect.height = MIN( t0, (mat->height - _yc) * 2 );//保證高不超出範圍
        comp.rect.x = MAX( 0, _xc - comp.rect.width / 2 );
        comp.rect.y = MAX( 0, _yc - comp.rect.height / 2 );


        comp.rect.width = MIN( mat->width - comp.rect.x, comp.rect.width );
        comp.rect.height = MIN( mat->height - comp.rect.y, comp.rect.height );
        comp.area = (float) m00;
    }


    __END__;


    if( _comp )
        *_comp = comp;
    
    if( box )
    {
        box->size.height = (float)length;
        box->size.width = (float)width;
        box->angle = (float)(theta*180./CV_PI);
        box->center = cvPoint2D32f( comp.rect.x + comp.rect.width*0.5f,
                                    comp.rect.y + comp.rect.height*0.5f);
    }


    return itersUsed;
}

opencv總程式碼參考可在此處下載點選開啟連結

說明:跟蹤人面板區域部分,當只露出人臉時,檢測出人臉範圍;露出面板時所有都檢測出來。

這說明Camshift比效果好,驗證了上面的說法——Camshift演算法相當於在MeanShift的結果上,再做了一個調整,從而使得跟蹤的視窗大小能夠隨目標的大小變化

執行效果圖:省去

(由於攝像頭顯示了本人影象,所以就不往這裡貼上了,大家可以自行執行,效果還是很不錯的,微笑

第三篇Camshift應用於人臉檢測

OpenCV的人臉追蹤演算法

Camshift(連續自適應的Meanshift演算法)由以下四個步驟組成:

1 建立一個顏色直方圖表示人臉特徵;

2 對視訊影象中每一幀的每一個畫素進行計算人臉存在的可能性

3 在每個視訊幀中移動人臉矩形框的位置;

4 計算人臉的大小和角度。

下面是每一步驟的詳細工作:

建立一個表示人臉的Camshift直方圖,它是以顏色值的直方圖(或柱狀圖)來進行跟蹤。圖1是由裝有OpenCVCamshiftdemo程式執行得到的兩個直方圖例項。每一個顏色條的高度表明瞭在一幅影象中有多少個畫素點屬於色度值。色度在HSV(色度、飽和度、值)顏色模型中用於描述一個畫素的顏色。

 Camshift演算法用於描述人臉的顏色直方圖

頂部的直方圖代表了一幅影象區域,藍色的色調是最常見的,稍微薰衣草色調是下一個最常見的。底部直方圖中最右邊的值表示最常見的色度範圍,這個色度經常但不總是紅色。

計算人臉概率

在初始跟蹤時計算人臉概率的直方圖只建立一次,在視訊接下來的每一幀的處理中,該直方圖被用於將人臉概率值分配給幀的每一個畫素點。

人臉概率聽起來非常複雜,計算也複雜,而實際上並非如此。現在我們給出它是如何工作的。圖2表明直方圖顏色條的疊加。很明顯在該影象區域中疊加後最右邊的顏色條大約佔45%的畫素。這意味著一個畫素的隨機選擇落在最右邊容器裡的概率是45%,那是這一畫素的“人臉概率”。同樣的邏輯表明,下一個直方圖容器所代表的人臉概率在20%左右,因為它大約佔了總高度的20%

新的視訊幀到達時每一個畫素點的色度值被檢測,通過人臉直方圖給該畫素點賦人臉概率值。這個過程在OpenCV被稱為直方圖背景投影目前在OpenCV有一個內建方法實現了它,那就是cvCalcBackProject()函式

3表示在通過Camshift演算法跟蹤我的臉時計算一個視訊幀影象中可能存在人臉的影象。黑色畫素概率值最低白色畫素概率最高,灰色的畫素介於兩者之間。

在每個視訊幀中移動人臉矩形框的位置

Camshift“改變”它的人臉位置判斷,在可能存在人臉的影象中始終保持集中在高亮畫素區域。它通過原先位置並計算一個矩形框中的人臉概率值來得出新位置,OpenCVcvCamShift函式的功能是增強這些步驟來轉變到一個新的位置。

這種和地球引力中心相一致的轉換矩形的過程是基於Mean shift演算法實現的,Mean shift演算法由Comaniciu Dorin提出。事實上,Camshift演算法就是“連續自適應的Mean shift演算法”。

四、計算人臉大小和角度

Camshift演算法被稱為“持續自適應”,而不僅僅是“Mean shift(均值漂移)”,是因為它在每次計算人臉存在的矩形框的下一個位置時能夠自動調整大小和角度。它選擇的縮放值和初始方向是最符合face-probability的在新的矩形框位置的畫素點。

程式碼還沒有實現,抱歉,不能讓大家看到效果圖,若實現,願共同交流,一起學習。