1. 程式人生 > >基於OpenCV 影象分割

基於OpenCV 影象分割

一、概述

       從影象中將目標或部分目標分割出來,是我們在進行影象處理的操作,也是有意義的事情。在很多情況下,我們也希望將感興趣的目標區域分割出來,比如將一個人的臉或手分割出來。具體有下面提到的幾種方法。

1、背景減除

        背景減除是在背景模型建立後,將背景模型和當前的影象進行比較,然後減去這些已知的背景資訊,則剩下的目標物大致就是所求的前景目標。背景減除法的缺點是假設所有畫素點是獨立的,這種更完整的空間和時間模型需要更多的記憶體、收集資料樣本更多的以及更多的計算資源。

2、幀差法

       幀差法就是用一幀減去另一幀(也可能是後幾幀),然後將足夠大的差別標為前景。這種方法是最有效的捕捉運動目標的邊緣。可以使用cvAbsDiff(frameTime1, frameTime2, frameForeground)、cvThreshold(frameForeground, frameForeground, 15, 255, CV_THRESH_BINARY)等函式實現。

3、平均背景法

       平均背景法的基本思想是計算每一個畫素的平均值和標準差(或相似的,但計算速度更快的平均差值)作為它的背景模型。平均背景法使用四個OpenCV函式:累積影象cvAcc( )、計算一定時間內的每幀影象之差cvAbsDiff( )、將影象分割成前景區域和背景區域cvInRange( )、將不同的彩色通道影象中合成為一個掩碼影象cvOr( )

4、codebook背景模型

      codebook由一些boxes組成,這些boxes包含很長時間不變的畫素值。如果當前值和歷時值相差比較大,就會產生一個新的box來覆蓋它,同樣慢慢地接近新的值。這種codebook方法能夠解決畫素激烈變化的問題(例如,被風吹的樹的畫素,它可能在很多樹葉的顏色和數之間的藍天顏色之間交替出現)。

     我們為每一個畫素設定一個碼元code_elements,需要有與訓練的影象畫素數目長度一樣的一組碼本。對每一個不同的畫素,呼叫函式update_codebook( )以捕捉背景中相關的變化影象。訓練可以自始至終定期更新,同時函式clear_stale_entries( )用於訓練有移動的前景目標的背景。使用codebook背景分割技術,通常可以有以下步驟:

(1)、使用函式update_codebook( )在幾秒鐘或幾分鐘時間內訓練一個基本的背景模型。

(2)、呼叫函式clear_stale_entries( )清除stale索引。

(3)、調整閾值minMod和maxMod對已知前景達到最好的分割。

(4)、保持一個更高級別的場景模型。

(5)、通過函式background_diff( )使用訓練好的模型將前景從背景中分割出來。

(6)、定期更新學習的背景畫素。

(7)、在一個頻率較慢的情況下,用函式clear_stale_entries( )定期清理stale的codebook索引。

5、分水嶺演算法

     分水嶺演算法在分割影象是比較有效果的一種。該演算法可以將影象中的邊緣轉化成“山脈”,將平均區域轉化為“山谷”,這樣有助於分割目標。分水嶺演算法首先計算灰度影象的梯度,這對山谷或沒有紋理的盆地的形式有效,對有山頭或影象中有主導的山脈的形成有效。然後開始從使用者指定點開始持續灌注盆地直到這些區域連在一起。

二、相關函式

1、線採用函式

int cvInitLineIterator( const CvArr* image, CvPoint pt1, CvPoint pt2, CvLineIterator* line_iterator,

                                  int connectivity=8, int left_to_right=0 );

img

       用以獲取直線的影象。 pt1 線段的第一個端點。 pt2 線段的第二個端點。

line_iterator

      指向直線迭代狀態結構體的指標。

connectivity

       直線的鄰接方式,4鄰接或者8鄰接。

left_to_right

       標誌值,指出掃描直線是從pt1和pt2外面最左邊的點掃描到最右邊的點(left_to_right≠0),還是按照指定的順序,從pt1到pt2(left_to_right=0)。

       函式cvInitLineIterator初始化直線迭代器並返回兩個端點間點的數目。兩個端點都必須在影象內部。在迭代器初始化以後,所有的在連線兩個終點的柵欄線上的點,可以通過訪問CV_NEXT_LINE_POINT點的方式獲得。在線上的這些點使用4-鄰接或者8-鄰接的Bresenham演算法計算得到。

2、計算兩個陣列差的絕對值

void cvAbsDiff( const CvArr* src1, const CvArr* src2, CvArr* dst );
src1
     第一個原陣列
src2
     第二個原陣列
dst
     輸出陣列
     函式 cvAbsDiff 計算兩個陣列差的絕對值 dst(I)c = abs(src1(I)c - src2(I)c).所有陣列必須有相同的資料型別相同的大小(或ROI大小)

3、對陣列元素進行固定閾值操作

void cvThreshold( const CvArr* src, CvArr* dst, double threshold,
                  double max_value, int threshold_type );

src

     原始陣列 (單通道 , 8-bit of 32-bit 浮點數).

dst

     輸出陣列,必須與 src 的型別一致,或者為 8-bit.

threshold

     閾值

max_value

     使用 CV_THRESH_BINARY 和 CV_THRESH_BINARY_INV 的最大值.

threshold_type

     閾值型別 (見討論)

     函式 cvThreshold 對單通道陣列應用固定閾值操作。該函式的典型應用是對灰度影象進行閾值操作得到二值影象。(cvCmpS 也可以達到此目的) 或者是去掉噪聲,例如過濾很小或很大象素值的影象點。本函式支援的對影象取閾值的方法由 threshold_type 確定。

4、將幀疊加到累積器(accumulator)中

void cvAcc( const CvArr* image, CvArr* sum, const CvArr* mask=NULL );
image
     輸入影象, 1- 或 3-通道, 8-位元或32-位元浮點數. (多通道的每一個通道都單獨處理).
sum
    同一個輸入影象通道的累積,32-位元或64-位元浮點數陣列.
mask
    可選的運算 mask. 
   函式 cvAcc 將整個影象 image 或某個選擇區域疊加到 sum 中: sum(x,y)=sum(x,y)+image(x,y) if mask(x,y)!=0

5、檢查陣列元素是否在兩個陣列之間

void cvInRange( const CvArr* src, const CvArr* lower, const CvArr* upper, CvArr* dst );
src
    第一個原陣列
lower
    包括進的下邊界陣列
upper
    不包括進的上邊界線陣列
dst
    輸出陣列必須是 8u 或 8s 型別.
函式 cvInRange 對輸入的陣列作範圍檢查,

對於單通道陣列:
    dst(I)=lower(I)0 <= src(I)0 < upper(I)0
對二通道陣列:
    dst(I)=lower(I)0 <= src(I)0 < upper(I)0 && lower(I)1 <= src(I)1 < upper(I)1
以此類推
    如果 src(I) 在範圍內dst(I)被設定為 0xff (每一位都是 '1')否則置0 。 除了輸出陣列所有陣列必須是相同的型別相同的大小(或ROI大小)。

6、計算兩個陣列每個元素的按位或

void cvOr( const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL );
src1
    第一個原陣列
src2
    第二個原陣列
dst
    輸出陣列.
mask
   操作覆蓋面( 8-bit 單通道陣列); 只有覆蓋面指定的輸出陣列被修改。

函式 cvOr 計算兩個陣列每個元素的按位或: dst(I)=src1(I)|src2(I)。對浮點陣列按位表示操作是很有利的。除覆蓋面,所有陣列都必須有相同的型別,相同的大小(或ROI大小)。

7、疊加輸入影象的平方到累積器中

void cvSquareAcc( const CvArr* image, CvArr* sqsum, const CvArr* mask=NULL );
image
    輸入影象, 1- 或 3-通道, 8-位元或32-位元浮點數 (多通道的每一個通道都單獨處理)
sqsum
   同一個輸入影象通道的累積,32-位元或64-位元浮點數陣列.
mask
   可選的運算 mask.
函式 cvSquareAcc 疊加輸入影象 image 或某個選擇區域的二次方,到累積器 sqsum 中 sqsum(x,y)=sqsum(x,y)+image(x,y)2 if mask(x,y)!=0

8、將兩幅輸入影象的乘積疊加到累積器中

void cvMultiplyAcc( const CvArr* image1, const CvArr* image2, CvArr* acc, const CvArr* mask=NULL );
image1
   第一個輸入影象, 1- or 3-通道, 8-位元 or 32-位元 浮點數 (多通道的每一個通道都單獨處理)
image2
   第二個輸入影象, 與第一個影象的格式一樣
acc
   同一個輸入影象通道的累積,32-位元或64-位元浮點數陣列.
mask
   可選的運算 mask. 
函式 cvMultiplyAcc 疊加兩個輸入影象的乘積到累積器 acc: acc(x,y)=acc(x,y) + image1(x,y)?image2(x,y) if mask(x,y)!=0