1. 程式人生 > >Opencv特徵點檢測

Opencv特徵點檢測

前面描述角點檢測的時候說到,角點其實也是一種影象特徵點,對於一張影象來說,特徵點分為三種形式包括邊緣,焦點和斑點,在OPENCV中,加上角點檢測,總共提供了以下的影象特徵點檢測方法
  1. FAST
  2. SURF
  3. ORB
  4. BRISK
  5. KAZE
  6. AKAZE
  7. MESR
  8. GFTT good feature to tack
  9. Bob斑點
  10. STAR
  11. AGAST

  接下來分別講述這是一種影象特徵檢測演算法,但是首先,需要了解OPENCV的一種資料結構, KeyPoint結構,該結構的標頭檔案定義如下:

class KeyPoint

{

Point2f  pt;    //該影象特徵點的座標

float  size;     //特徵點鄰域直徑

float  angle; //特徵點的方向,值為[零,三百六十),負值表示不使用,有了這個方向,能夠讓特徵點擁有更高的辨識度,否則僅僅座標和直徑有時會誤判特徵點

float  response;//響應程度,代表該點的強壯程度,也就是該點角點程度,用於後期使用和排序

int  octave; //特徵點所在的影象金字塔的組

int  class_id; //用於聚類的id

}

  每個影象特徵點檢測演算法最終的目標之一,而當一張影象的特徵點被檢測出來之後,就可以和另一張影象的特徵點進行匹配,根據相似級別判定兩個影象的相似程度.

  比如我們可以在影象中檢測一張人臉的特徵點,從而來檢索在另一張圖中是否存在相似程度很高的特徵點集,從而確認另一張影象中的人臉以及人臉的位置,等,特徵點檢測演算法在物體檢測,視覺跟蹤,3D重建的時候都有著重要的作用.

一. 影象特徵點檢測的通用介面
  Opencv為了方便使用者使用影象特徵點檢測的相應演算法,將全部的特徵點檢測都封在一個類似的API中,名為Ptr的模板類,也就是說,所有的特徵檢測演算法都實現了相同的藉口,detect 檢測影象特徵點.使用方法類似於
  Ptr<相應的特徵點檢測類名>變數名 = 相應的特徵點檢測類::create()
  變數名->detect(原影象,特徵點向量).
  使用上面描述的演算法,就可以呼叫幾乎全部的影象特徵檢測演算法.但是注意,create函式有多個過載函式,如果為空,每個影象檢測演算法都會使用自己的一套預設的初始值來初始化類,如果想修改引數,那麼create函式呼叫的時候需要根據檢測類的不同,設定不同的初始化變數.
  另外,opencv提供而一個快速顯示影象特徵點的函式,如下
  drawKeyPoints(畫布影象,特徵點向量集,輸出的繪製結果,繪製顏色值,繪製模式)
  一般來說,畫布影象會使用我們檢測特徵點的原影象(一般檢測特徵點都是原影象變換為灰度影象之後進行的檢測,簡單演算法複雜度).
  繪製模式有以下方法可以選擇,是DrawMatchesFlags列舉
  DEFAULT:只繪製特徵點的座標點,顯示在影象上就是一個個小圓點,每個小圓點的圓心座標都是特徵點的座標.
  DRAW_OVER_OUTIMG:函式不建立輸出的影象,而是直接在輸出影象變數空間繪製,要求本身輸出影象變數就是一個初始化好了的,size與type都是已經初始化好的變數
  NOT_DRAW_SINGLE 單點的特徵點不被繪製
  DRAW_RICH_KEYPOINT 繪製特徵點的時候繪製的是一個個帶有方向的圓,這種方法同時顯示影象的座標,size,和方向,是最能顯示特徵的一種繪製方式,但是缺點就是繪製結果太雜亂.

一.   FAST特徵點檢測演算法

FAST演算法是基於角點檢測的影象特徵.

一個特徵點檢測的演算法的第一步是定義什麼是特徵點,FAST演算法定義特徵點是如果某個畫素點和他周圍領域足夠多的畫素點處於不同區域,那麼這個畫素點就是特徵點,對於灰度影象來說,也就是該點的灰度值和其周圍足夠多的畫素點的灰度值不同,那麼這個畫素點就是一個特徵點.

該演算法的詳細計算步驟如下

  1. 從圖片中選取一個座標點,獲取該點的畫素值,接下來判定該點是否為特徵點.
  2. 選取一個以選取點座標為圓心的半徑等於三的Bresenham圓(一個計算圓的軌跡的離散演算法,得到整數級的圓的軌跡點),一般來說,這個圓上有16個點,如下所示

  

黑點座標為(0,0),座標step為1

  1. 現在選取一個閾值,假設為t,關鍵步驟,假設這16個點中,有N個連續的畫素點,他們的亮度值與中心點的畫素值的差大於或者小於t,那麼這個點就是一個特徵點.(n的取值一般取值12或者9,實驗證明9可以取得更好的效果,因為可以獲取更多的特徵點,後面進行處理時,資料樣本額相對多一些).
  2. 加入每個軌跡點都需要遍歷的話,那麼需要的時間比較長,有一種比較簡單的方法可以選擇,那就是僅僅檢查在位置1,9,5和13四個位置的畫素,首先檢測位置1和位置9,如果它們都比閾值暗或比閾值亮,再檢測位置5和位置13, 如果P" role="presentation" style="word-wrap: normal; max-width: none; max-height: none; min-width: 0px; min-height: 0px; float: none;" id="MathJax-Element-8-Frame">中心點是一個角點,那麼上述四個畫素點中至少有3個應該必須都大於Ip+t" role="presentation" style="word-wrap: normal; max-width: none; max-height: none; min-width: 0px; min-height: 0px; float: none;" id="MathJax-Element-9-Frame">中心點亮度值+閾值或者小於Ip&#x2212;t" role="presentation" style="word-wrap: normal; max-width: none; max-height: none; min-width: 0px; min-height: 0px; float: none;" id="MathJax-Element-10-Frame">中心點亮度值-閾值,因為若是一個角點,超過四分之三圓的部分應該滿足判斷條件。如果不滿足,那麼p" role="presentation" style="word-wrap: normal; max-width: none; max-height: none; min-width: 0px; min-height: 0px; float: none;" id="MathJax-Element-11-Frame">中心點不可能是一個角點。對於所有點做上面這一部分初步的檢測後,符合條件的將成為候選的角點,我們再對候選的角點,做完整的測試,即檢測圓上的所有點.
  3. 但是,這種檢測方法會帶來一個問題,就是造成特徵點的聚簇效應,多個特徵點在影象的某一塊重複高頻率的出現,FAST演算法提出了一種非極大值抑制的辦法來消除這種情況,具體辦法如下.
    1. 為每一個檢測到的特徵點計算它的響應大小(score function)VV。這裡VV定義為中心點和它周圍16個畫素點的絕對偏差的和.
    2. 考慮兩個相鄰的特徵點,並比較它們的VV值
    3. VV值較低的點將會被刪除

  以上就是快速特徵點檢測的原理,OPENCV中定義的快速特徵點檢測演算法的檢測API如下

  static Ptr<FastFeatureDetector> create( int threshold=10, bool nonmaxSuppression=true,

                                 int type=FastFeatureDetector::TYPE_9_16 );

  threshold是指比較時邊緣軌跡點和中心點的差值,也就是第三步的閾值t, nonmaxSuppression代表是否使用第五步非極大值抑制,如果發現fast檢測的結果有聚簇情況,那麼可以考慮採用,第三個引數type的取值來自於FastFeatureDetector列舉,有如下取值:

  1. TYPE_5_8 從軌跡中取8個點,當有5個點滿足條件,就是特徵點.
  2. TYPE_7_12 取軌跡12個點,7個滿足條件,就是特徵點.
  3. TYPE_9_16 取軌跡16個點,當9個滿足條件,就是特徵點.

  綜上所述我們可以看出,FAST檢測演算法沒有多尺度的問題,所以計算速度相對較快,但是當圖片中的噪點較多的時候,會產生較多的錯誤特徵點,健壯性並不好,並且, 演算法的效果還依賴於一個閾值t。而且FAST不產生多尺度特徵而且FAST特徵點沒有方向資訊,這樣就會失去旋轉不變性.但是在要求實時性的場合,比如視訊監控的物體識別,是可以使用的.

使用程式碼如下

123456789101112131415161718192021222324252627282930//fastint main(int argc,char* argv[]){Mat srcImage = imread("F:\\opencv\\OpenCVImage\\FeatureDetectSrc1.jpg");Mat srcGrayImage;if (srcImage.channels() == 3){cvtColor(srcImage,srcGrayImage,CV_RGB2GRAY);}else{srcImage.copyTo(srcGrayImage);}vector<KeyPoint>detectKeyPoint;Mat keyPointImage1,keyPointImage2;Ptr<FastFeatureDetector> fast = FastFeatureDetector::create();fast->detect(srcGrayImage,detectKeyPoint);drawKeypoints(srcImage,detectKeyPoint,keyPointImage1,Scalar(0,0,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);drawKeypoints(srcImage,detectKeyPoint,keyPointImage2,Scalar(0,0,255),DrawMatchesFlags::DEFAULT);imshow("src image",srcImage);imshow("keyPoint image1",keyPointImage1);imshow("keyPoint image2",keyPointImage2);imwrite("F:\\opencv\\OpenCVImage\\FeatureDetectSrc1FASTKeyPointImageDefault.jpg",keyPointImage2);waitKey(0);return 0;}