OpenCV 圖像特徵提取:角點特徵、Harris和Shi-Tomas算法、SIFT/SURF算法、Fast和ORB算法

日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度學習實戰(不定時更新)


 

角點特徵

學習目標

  • 理解圖像的特徵
  • 知道圖像的角點

1 圖像的特徵

大多數人都玩過拼圖遊戲。首先拿到完整圖像的碎片,然後把這些碎片以正確的方式排列起來從而重建這幅圖像。如果把拼圖遊戲的原理寫成計算機程序,那計算機就也會玩拼圖遊戲了。

在拼圖時,我們要尋找一些唯一的特徵,這些特徵要適於被跟蹤,容易被比較。我們在一副圖像中搜索這樣的特徵,找到它們,而且也能在其他圖像中找到這些特徵,然後再把它們拼接到一起。我們的這些能力都是天生的。

那這些特徵是什麼呢?我們希望這些特徵也能被計算機理解。

如果我們深入的觀察一些圖像並搜索不同的區域,以下圖爲例:

在圖像的上方給出了六個小圖。找到這些小圖在原始圖像中的位置。你能找到多少正確結果呢?

A 和 B 是平面,而且它們的圖像中很多地方都存在。很難找到這些小圖的準確位置。

C 和 D 也很簡單。它們是建築的邊緣。可以找到它們的近似位置,但是準確位置還是很難找到。這是因爲:沿着邊緣,所有的地方都一樣。所以邊緣是比平面更好的特徵,但是還不夠好。

最後 E 和 F 是建築的一些角點。它們能很容易的被找到。因爲在角點的地方,無論你向哪個方向移動小圖,結果都會有很大的不同。所以可以把它們當 成一個好的特徵。爲了更好的理解這個概念我們再舉個更簡單的例子。

如上圖所示,藍色框中的區域是一個平面很難被找到和跟蹤。無論向哪個方向移動藍色框,都是一樣的。對於黑色框中的區域,它是一個邊緣。如果沿垂直方向移動,它會改變。但是如果沿水平方向移動就不會改變。而紅色框中的角點,無論你向那個方向移動,得到的結果都不同,這說明它是唯一的。 所以,我們說角點是一個好的圖像特徵,也就回答了前面的問題。

角點是圖像很重要的特徵,對圖像圖形的理解和分析有很重要的作用。角點在三維場景重建運動估計,目標跟蹤、目標識別、圖像配準與匹配等計算機視覺領域起着非常重要的作用。在現實世界中,角點對應於物體的拐角,道路的十字路口、丁字路口等

那我們怎樣找到這些角點呢?接下來我們使用 OpenCV 中的各種算法來查找圖像的特徵,並對它們進行描述。

總結

  1. 圖像特徵

    圖像特徵要有區分性,容易被比較。一般認爲角點,斑點等是較好的圖像特徵

    特徵檢測:找到圖像中的特徵

    特徵描述:對特徵及其周圍的區域進行描述


Harris和Shi-Tomas算法

學習目標

  • 理解Harris和Shi-Tomasi算法的原理
  • 能夠利用Harris和Shi-Tomasi進行角點檢測

1 Harris角點檢測

1.1 原理

Harris角點檢測的思想是通過圖像的局部的小窗口觀察圖像,角點的特徵是窗口沿任意方向移動都會導致圖像灰度的明顯變化,如下圖所示:

  • 當R爲大數值的正數時是角點
  • 當R爲大數值的負數時是邊界
  • 當R爲小數是認爲是平坦區域

1.2 實現

在OpenCV中實現Hariis檢測使用的API是:

dst=cv.cornerHarris(src, blockSize, ksize, k)

參數:

  • img:數據類型爲 float32 的輸入圖像。

  • blockSize:角點檢測中要考慮的鄰域大小。

  • ksize:sobel求導使用的核大小

  • k :角點檢測方程中的自由參數,取值參數爲 [0.04,0.06].

示例:

import cv2 as cv
import numpy as np 
import matplotlib.pyplot as plt
# 1 讀取圖像,並轉換成灰度圖像
img = cv.imread('./image/chessboard.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 2 角點檢測
# 2.1 輸入圖像必須是 float32
gray = np.float32(gray)

# 2.2 最後一個參數在 0.04 到 0.05 之間
dst = cv.cornerHarris(gray,2,3,0.04)
# 3 設置閾值,將角點繪製出來,閾值根據圖像進行選擇
img[dst>0.001*dst.max()] = [0,0,255]
# 4 圖像顯示
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title('Harris角點檢測')
plt.xticks([]), plt.yticks([])
plt.show()

結果如下:

Harris角點檢測的優缺點:

優點:

  • 旋轉不變性,橢圓轉過一定角度但是其形狀保持不變(特徵值保持不變)
  • 對於圖像灰度的仿射變化具有部分的不變性,由於僅僅使用了圖像的一介導數,對於圖像灰度平移變化不變;對於圖像灰度尺度變化不變

缺點:

  • 對尺度很敏感,不具備幾何尺度不變性。
  • 提取的角點是像素級的

2 Shi-Tomasi角點檢測

2.1 原理

Shi-Tomasi算法是對Harris角點檢測算法的改進,一般會比Harris算法得到更好的角點。Harris 算法的角點響應函數是將矩陣 M 的行列式值與 M 的跡相減,利用差值判斷是否爲角點。後來Shi 和Tomasi 提出改進的方法是,若矩陣M的兩個特徵值中較小的一個大於閾值,則認爲他是角點,即:

2.2 實現

在OpenCV中實現Shi-Tomasi角點檢測使用API:

corners = cv2.goodFeaturesToTrack ( image, maxcorners, qualityLevel, minDistance )

參數:

  • Image: 輸入灰度圖像
  • maxCorners : 獲取角點數的數目。
  • qualityLevel:該參數指出最低可接受的角點質量水平,在0-1之間。
  • minDistance:角點之間最小的歐式距離,避免得到相鄰特徵點。

返回:

  • Corners: 搜索到的角點,在這裏所有低於質量水平的角點被排除掉,然後把合格的角點按質量排序,然後將質量較好的角點附近(小於最小歐式距離)的角點刪掉,最後找到maxCorners個角點返回。

示例:

import numpy as np 
import cv2 as cv
import matplotlib.pyplot as plt
# 1 讀取圖像
img = cv.imread('./image/tv.jpg') 
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 2 角點檢測
corners = cv.goodFeaturesToTrack(gray,1000,0.01,10)  
# 3 繪製角點
for i in corners:
    x,y = i.ravel()
    cv.circle(img,(x,y),2,(0,0,255),-1)
# 4 圖像展示
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title('shi-tomasi角點檢測')
plt.xticks([]), plt.yticks([])
plt.show()

結果如下:

總結

  1. Harris算法

    思想:通過圖像的局部的小窗口觀察圖像,角點的特徵是窗口沿任意方向移動都會導致圖像灰度的明顯變化。

    API: cv.cornerHarris()

  2. Shi-Tomasi算法

    對Harris算法的改進,能夠更好地檢測角點

    API: cv2.goodFeatureToTrack()


SIFT/SURF算法

學習目標

  • 理解SIFT/SURF算法的原理,
  • 能夠使用SIFT/SURF進行關鍵點的檢測

SIFT/SURF算法

1.1 SIFT原理

前面兩節我們介紹了Harris和Shi-Tomasi角點檢測算法,這兩種算法具有旋轉不變性,但不具有尺度不變性,以下圖爲例,在左側小圖中可以檢測到角點,但是圖像被放大後,在使用同樣的窗口,就檢測不到角點了。

所以,下面我們來介紹一種計算機視覺的算法,尺度不變特徵轉換即SIFT (Scale-invariant feature transform)。它用來偵測與描述影像中的局部性特徵,它在空間尺度中尋找極值點,並提取出其位置、尺度、旋轉不變量,此算法由 David Lowe在1999年所發表,2004年完善總結。應用範圍包含物體辨識、機器人地圖感知與導航、影像縫合、3D模型建立、手勢辨識、影像追蹤和動作比對等領域。

SIFT算法的實質是在不同的尺度空間上查找關鍵點(特徵點),並計算出關鍵點的方向。SIFT所查找到的關鍵點是一些十分突出,不會因光照,仿射變換和噪音等因素而變化的點,如角點、邊緣點、暗區的亮點及亮區的暗點等。

1.1.1 基本流程

Lowe將SIFT算法分解爲如下四步

  1. 尺度空間極值檢測:搜索所有尺度上的圖像位置。通過高斯差分函數來識別潛在的對於尺度和旋轉不變的關鍵點。
  2. 關鍵點定位:在每個候選的位置上,通過一個擬合精細的模型來確定位置和尺度。關鍵點的選擇依據於它們的穩定程度。
  3. 關鍵點方向確定:基於圖像局部的梯度方向,分配給每個關鍵點位置一個或多個方向。所有後面的對圖像數據的操作都相對於關鍵點的方向、尺度和位置進行變換,從而保證了對於這些變換的不變性。
  4. 關鍵點描述:在每個關鍵點周圍的鄰域內,在選定的尺度上測量圖像局部的梯度。這些梯度作爲關鍵點的描述符,它允許比較大的局部形狀的變形或光照變化。

我們就沿着Lowe的步驟,對SIFT算法的實現過程進行介紹:

1.1.2 尺度空間極值檢測

在不同的尺度空間是不能使用相同的窗口檢測極值點,對小的關鍵點使用小的窗口,對大的關鍵點使用大的窗口,爲了達到上述目的,我們使用尺度空間濾波器。

高斯核是唯一可以產生多尺度空間的核函數。-《Scale-space theory: A basic tool for analysing structures at different scales》。

一個圖像的尺度空間L(x,y,σ),定義爲原始圖像I(x,y)與一個可變尺度的2維高斯函數G(x,y,σ)卷積運算 ,即:

在計算高斯函數的離散近似時,在大概3σ距離之外的像素都可以看作不起作用,這些像素的計算也就可以忽略。所以,在實際應用中,只計算(6σ+1)*(6σ+1)的高斯卷積核就可以保證相關像素影響。

下面我們構建圖像的高斯金字塔,它採用高斯函數對圖像進行模糊以及降採樣處理得到的,高斯金字塔構建過程中,首先將圖像擴大一倍,在擴大的圖像的基礎之上構建高斯金字塔,然後對該尺寸下圖像進行高斯模糊,幾幅模糊之後的圖像集合構成了一個Octave,然後對該Octave下選擇一幅圖像進行下采樣,長和寬分別縮短一倍,圖像面積變爲原來四分之一。這幅圖像就是下一個Octave的初始圖像,在初始圖像的基礎上完成屬於這個Octave的高斯模糊處理,以此類推完成整個算法所需要的所有八度構建,這樣這個高斯金字塔就構建出來了,整個流程如下圖所示:

利用LoG(高斯拉普拉斯方法),即圖像的二階導數,可以在不同的尺度下檢測圖像的關鍵點信息,從而確定圖像的特徵點。但LoG的計算量大,效率低。所以我們通過兩個相鄰高斯尺度空間的圖像的相減,得到DoG(高斯差分)來近似LoG。

爲了計算DoG我們構建高斯差分金字塔,該金字塔是在上述的高斯金字塔的基礎上構建而成的,建立過程是:在高斯金字塔中每個Octave中相鄰兩層相減就構成了高斯差分金字塔。如下圖所示:

高斯差分金字塔的第1組第1層是由高斯金字塔的第1組第2層減第1組第1層得到的。以此類推,逐組逐層生成每一個差分圖像,所有差分圖像構成差分金字塔。概括爲DOG金字塔的第o組第l層圖像是有高斯金字塔的第o組第l+1層減第o組第l層得到的。後續Sift特徵點的提取都是在DOG金字塔上進行的

在 DoG 搞定之後,就可以在不同的尺度空間中搜索局部最大值了。對於圖像中的一個像素點而言,它需要與自己周圍的 8 鄰域,以及尺度空間中上下兩層中的相鄰的 18(2x9)個點相比。如果是局部最大值,它就可能是一個關鍵點。基本上來說關鍵點是圖像在相應尺度空間中的最好代表。如下圖所示:

搜索過程從每組的第二層開始,以第二層爲當前層,對第二層的DoG圖像中的每個點取一個3×3的立方體,立方體上下層爲第一層與第三層。這樣,搜索得到的極值點既有位置座標(DoG的圖像座標),又有空間尺度座標(層座標)。當第二層搜索完成後,再以第三層作爲當前層,其過程與第二層的搜索類似。當S=3時,每組裏面要搜索3層,所以在DOG中就有S+2層,在初使構建的金字塔中每組有S+3層。

1.1.3 關鍵點定位

由於DoG對噪聲和邊緣比較敏感,因此在上面高斯差分金字塔中檢測到的局部極值點需經過進一步的檢驗才能精確定位爲特徵點。

使用尺度空間的泰勒級數展開來獲得極值的準確位置, 如果極值點的 灰度值小於閾值(一般爲0.03或0.04)就會被忽略掉。 在 OpenCV 中這種閾值被稱爲 contrastThreshold。

DoG 算法對邊界非常敏感, 所以我們必須要把邊界去除。 Harris 算法除了可以用於角點檢測之外還可以用於檢測邊界。從 Harris 角點檢測的算法中,當一個特徵值遠遠大於另外一個特徵值時檢測到的是邊界。那在DoG算法中欠佳的關鍵點在平行邊緣的方向有較大的主曲率,而在垂直於邊緣的方向有較小的曲率,兩者的比值如果高於某個閾值(在OpenCV中叫做邊界閾值),就認爲該關鍵點爲邊界,將被忽略,一般將該閾值設置爲10。

將低對比度和邊界的關鍵點去除,得到的就是我們感興趣的關鍵點。

1.1.4 關鍵點方向確定

經過上述兩個步驟,圖像的關鍵點就完全找到了,這些關鍵點具有尺度不變性。爲了實現旋轉不變性,還需要爲每個關鍵點分配一個方向角度,也就是根據檢測到的關鍵點所在高斯尺度圖像的鄰域結構中求得一個方向基準。

對於任一關鍵點,我們採集其所在高斯金字塔圖像以r爲半徑的區域內所有像素的梯度特徵(幅值和幅角),半徑r爲:

鄰域像素梯度的計算結果如下圖所示:

完成關鍵點梯度計算後,使用直方圖統計關鍵點鄰域內像素的梯度幅值和方向。具體做法是,將360°分爲36柱,每10°爲一柱,然後在以r爲半徑的區域內,將梯度方向在某一個柱內的像素找出來,然後將他們的幅值相加在一起作爲柱的高度。因爲在r爲半徑的區域內像素的梯度幅值對中心像素的貢獻是不同的,因此還需要對幅值進行加權處理,採用高斯加權,方差爲1.5σ。如下圖所示,爲簡化圖中只畫了8個方向的直方圖。

每個特徵點必須分配一個主方向,還需要一個或多個輔方向,增加輔方向的目的是爲了增強圖像匹配的魯棒性。輔方向的定義是,當一個柱體的高度大於主方向柱體高度的80%時,則該柱體所代表的的方向就是給特徵點的輔方向。

直方圖的峯值,即最高的柱代表的方向是特徵點鄰域範圍內圖像梯度的主方向,但該柱體代表的角度是一個範圍,所以我們還要對離散的直方圖進行插值擬合,以得到更精確的方向角度值。利用拋物線對離散的直方圖進行擬合,如下圖所示:

獲得圖像關鍵點主方向後,每個關鍵點有三個信息(x,y,σ,θ):位置、尺度、方向。由此我們可以確定一個SIFT特徵區域。通常使用一個帶箭頭的圓或直接使用箭頭表示SIFT區域的三個值:中心表示特徵點位置,半徑表示關鍵點尺度,箭頭表示方向。如下圖所示:

1.1.5 關鍵點描述

通過以上步驟,每個關鍵點就被分配了位置,尺度和方向信息。接下來我們爲每個關鍵點建立一個描述符,該描述符既具有可區分性,又具有對某些變量的不變性,如光照,視角等。而且描述符不僅僅包含關鍵點,也包括關鍵點周圍對其有貢獻的的像素點。主要思路就是通過將關鍵點周圍圖像區域分塊,計算塊內的梯度直方圖,生成具有特徵向量,對圖像信息進行抽象。

爲了保證特徵點的旋轉不變性,以特徵點爲中心,將座標軸旋轉爲關鍵點的主方向,如下圖所示:

計算子區域內的像素的梯度,並按照σ=0.5d進行高斯加權,然後插值計算得到每個種子點的八個方向的梯度,插值方法如下圖所示:

每個種子點的梯度都是由覆蓋其的4個子區域插值而得的。如圖中的紅色點,落在第0行和第1行之間,對這兩行都有貢獻。對第0行第3列種子點的貢獻因子爲dr,對第1行第3列的貢獻因子爲1-dr,同理,對鄰近兩列的貢獻因子爲dc和1-dc,對鄰近兩個方向的貢獻因子爲do和1-do。則最終累加在每個方向上的梯度大小爲:

1.1.6 總結

SIFT在圖像的不變特徵提取方面擁有無與倫比的優勢,但並不完美,仍然存在實時性不高,有時特徵點較少,對邊緣光滑的目標無法準確提取特徵點等缺陷,自SIFT算法問世以來,人們就一直對其進行優化和改進,其中最著名的就是SURF算法。

1.2 SURF原理

使用 SIFT 算法進行關鍵點檢測和描述的執行速度比較慢, 需要速度更快的算法。 2006 年 Bay提出了 SURF 算法,是SIFT算法的增強版,它的計算量小,運算速度快,提取的特徵與SIFT幾乎相同,將其與SIFT算法對比如下:

1.3 實現

在OpenCV中利用SIFT檢測關鍵點的流程如下所示:

1.實例化sift

sift = cv.xfeatures2d.SIFT_create()

2.利用sift.detectAndCompute()檢測關鍵點並計算

kp,des = sift.detectAndCompute(gray,None)

參數:

  • gray: 進行關鍵點檢測的圖像,注意是灰度圖像

返回:

  • kp: 關鍵點信息,包括位置,尺度,方向信息
  • des: 關鍵點描述符,每個關鍵點對應128個梯度信息的特徵向量

3.將關鍵點檢測結果繪製在圖像上

cv.drawKeypoints(image, keypoints, outputimage, color, flags)

參數:

  • image: 原始圖像
  • keypoints:關鍵點信息,將其繪製在圖像上
  • outputimage:輸出圖片,可以是原始圖像
  • color:顏色設置,通過修改(b,g,r)的值,更改畫筆的顏色,b=藍色,g=綠色,r=紅色。
  • flags:繪圖功能的標識設置
    1. cv2.DRAW_MATCHES_FLAGS_DEFAULT:創建輸出圖像矩陣,使用現存的輸出圖像繪製匹配對和特徵點,對每一個關鍵點只繪製中間點
    2. cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG:不創建輸出圖像矩陣,而是在輸出圖像上繪製匹配對
    3. cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS:對每一個特徵點繪製帶大小和方向的關鍵點圖形
    4. cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS:單點的特徵點不被繪製

SURF算法的應用與上述流程是一致,這裏就不在贅述。

示例:

利用SIFT算法在中央電視臺的圖片上檢測關鍵點,並將其繪製出來:

import cv2 as cv 
import numpy as np
import matplotlib.pyplot as plt
# 1 讀取圖像
img = cv.imread('./image/tv.jpg')
gray= cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 2 sift關鍵點檢測
# 2.1 實例化sift對象
sift = cv.xfeatures2d.SIFT_create()

# 2.2 關鍵點檢測:kp關鍵點信息包括方向,尺度,位置信息,des是關鍵點的描述符
kp,des=sift.detectAndCompute(gray,None)
# 2.3 在圖像上繪製關鍵點的檢測結果
cv.drawKeypoints(img,kp,img,flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# 3 圖像顯示
plt.figure(figsize=(8,6),dpi=100)
plt.imshow(img[:,:,::-1]),plt.title('sift檢測')
plt.xticks([]), plt.yticks([])
plt.show()

結果:

總結

SIFT原理:

  • 尺度空間極值檢測:構建高斯金字塔,高斯差分金字塔,檢測極值點。

  • 關鍵點定位:去除對比度較小和邊緣對極值點的影響。

  • 關鍵點方向確定:利用梯度直方圖確定關鍵點的方向。

  • 關鍵點描述:對關鍵點周圍圖像區域分塊,計算塊內的梯度直方圖,生成具有特徵向量,對關鍵點信息進行描述。

API:cv.xfeatures2d.SIFT_create()

SURF算法:

對SIFT算法的改進,在尺度空間極值檢測,關鍵點方向確定,關鍵點描述方面都有改進,提高效率


Fast和ORB算法

學習目標

  • 理解Fast算法角點檢測的原理,能夠完成角點檢測
  • 理解ORB算法的原理,能夠完成特徵點檢測

1 Fast算法

1.1 原理

我們前面已經介紹過幾個特徵檢測器,它們的效果都很好,特別是SIFT和SURF算法,但是從實時處理的角度來看,效率還是太低了。爲了解決這個問題,Edward Rosten和Tom Drummond在2006年提出了FAST算法,並在2010年對其進行了修正。

FAST (全稱Features from accelerated segment test)是一種用於角點檢測的算法,該算法的原理是取圖像中檢測點,以該點爲圓心的周圍鄰域內像素點判斷檢測點是否爲角點,通俗的講就是若一個像素周圍有一定數量的像素與該點像素值不同,則認爲其爲角點

1.1.1 FAST算法的基本流程

  1. 由於在檢測特徵點時是需要對圖像中所有的像素點進行檢測,然而圖像中的絕大多數點都不是特徵點,如果對每個像素點都進行上述的檢測過程,那顯然會浪費許多時間,因此採用一種進行非特徵點判別的方法:首先對候選點的周圍每個 90 度的點:1,9,5,13 進行測試(先測試 1 和 19, 如果它們符合閾值要求再測試 5 和 13)。如果 p 是角點,那麼這四個點中至少有 3 個要符合閾值要求,否則直接剔除。對保留下來的點再繼續進行測試(是否有 12 的點符合閾值要求)。

雖然這個檢測器的效率很高,但它有以下幾條缺點:

  • 獲得的候選點比較多
  • 特徵點的選取不是最優的,因爲它的效果取決與要解決的問題和角點的分佈情況。
  • 進行非特徵點判別時大量的點被丟棄
  • 檢測到的很多特徵點都是相鄰的

前 3 個問題可以通過機器學習的方法解決,最後一個問題可以使用非最大值抑制的方法解決。

1.1.2機器學習的角點檢測器

  1. 選擇一組訓練圖片(最好是跟最後應用相關的圖片)

  2. 使用 FAST 算法找出每幅圖像的特徵點,對圖像中的每一個特徵點,將其周圍的 16 個像素存儲構成一個向量P。

1.1.3 非極大值抑制

在篩選出來的候選角點中有很多是緊挨在一起的,需要通過非極大值抑制來消除這種影響。

1.2 實現

OpenCV中的FAST檢測算法是用傳統方法實現的,

1.實例化fast

fast = =cv.FastFeatureDetector_create( threshold, nonmaxSuppression)

參數:

  • threshold:閾值t,有默認值10
  • nonmaxSuppression:是否進行非極大值抑制,默認值True

返回:

  • Fast:創建的FastFeatureDetector對象

2.利用fast.detect檢測關鍵點,沒有對應的關鍵點描述

kp = fast.detect(grayImg, None)

參數:

  • gray: 進行關鍵點檢測的圖像,注意是灰度圖像

返回:

  • kp: 關鍵點信息,包括位置,尺度,方向信息

3.將關鍵點檢測結果繪製在圖像上,與在sift中是一樣的

cv.drawKeypoints(image, keypoints, outputimage, color, flags)

示例:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# 1 讀取圖像
img = cv.imread('./image/tv.jpg')
# 2 Fast角點檢測
# 2.1 創建一個Fast對象,傳入閾值,注意:可以處理彩色空間圖像
fast = cv.FastFeatureDetector_create(threshold=30)

# 2.2 檢測圖像上的關鍵點
kp = fast.detect(img,None)
# 2.3 在圖像上繪製關鍵點
img2 = cv.drawKeypoints(img, kp, None, color=(0,0,255))

# 2.4 輸出默認參數
print( "Threshold: {}".format(fast.getThreshold()) )
print( "nonmaxSuppression:{}".format(fast.getNonmaxSuppression()) )
print( "neighborhood: {}".format(fast.getType()) )
print( "Total Keypoints with nonmaxSuppression: {}".format(len(kp)) )


# 2.5 關閉非極大值抑制
fast.setNonmaxSuppression(0)
kp = fast.detect(img,None)

print( "Total Keypoints without nonmaxSuppression: {}".format(len(kp)) )
# 2.6 繪製爲進行非極大值抑制的結果
img3 = cv.drawKeypoints(img, kp, None, color=(0,0,255))

# 3 繪製圖像
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img2[:,:,::-1])
axes[0].set_title("加入非極大值抑制")
axes[1].imshow(img3[:,:,::-1])
axes[1].set_title("未加入非極大值抑制")
plt.show()

結果:

2 ORB 算法

2.1 原理

SIFT和SURF算法是受專利保護的,在使用他們時我們是要付費的,但是ORB(Oriented Fast and Rotated Brief)不需要,它可以用來對圖像中的關鍵點快速創建特徵向量,並用這些特徵向量來識別圖像中的對象。

2.1.1 ORB算法流程

ORB算法結合了Fast和Brief算法,提出了構造金字塔,爲Fast特徵點添加了方向,從而使得關鍵點具有了尺度不變性和旋轉不變性。具體流程描述如下:

  • 構造尺度金字塔,金字塔共有n層,與SIFT不同的是,每一層僅有一幅圖像。第s層的尺度爲:

  • 在不同的尺度上利用Fast算法檢測特徵點,採用Harris角點響應函數,根據角點的響應值排序,選取前N個特徵點,作爲本尺度的特徵點。

  • 計算特徵點的主方向,計算以特徵點爲圓心半徑爲r的圓形鄰域內的灰度質心位置,將從特徵點位置到質心位置的方向做特徵點的主方向。

計算方法如下:

  • 爲了解決旋轉不變性,將特徵點的鄰域旋轉到主方向上利用Brief算法構建特徵描述符,至此就得到了ORB的特徵描述向量。

2.1.2 BRIEF算法

BRIEF是一種特徵描述子提取算法,並非特徵點的提取算法,一種生成二值化描述子的算法,不提取代價低,匹配只需要使用簡單的漢明距離(Hamming Distance)利用比特之間的異或操作就可以完成。因此,時間代價低,空間代價低,效果還挺好是最大的優點。

算法的步驟介紹如下

  1. 圖像濾波:原始圖像中存在噪聲時,會對結果產生影響,所以需要對圖像進行濾波,去除部分噪聲。

  2. 選取點對:以特徵點爲中心,取S*S的鄰域窗口,在窗口內隨機選取N組點對,一般N=128,256,512,默認是256,關於如何選取隨機點對,提供了五種形式,結果如下圖所示:

    • x,y方向平均分佈採樣

    • x,y均服從Gauss(0,S^2/25)各向同性採樣

    • x服從Gauss(0,S^2/25),y服從Gauss(0,S^2/100)採樣

    • x,y從網格中隨機獲取

    • x一直在(0,0),y從網格中隨機選取

圖中一條線段的兩個端點就是一組點對,其中第二種方法的結果比較好。

3.構建描述符:假設x,y是某個點對的兩個端點,p(x),p(y)是兩點對應的像素值,則有:

對每一個點對都進行上述的二進制賦值,形成BRIEF的關鍵點的描述特徵向量,該向量一般爲 128-512 位的字符串,其中僅包含 1 和 0,如下圖所示:

2.2 實現

在OPenCV中實現ORB算法,使用的是:

1.實例化ORB

orb = cv.xfeatures2d.orb_create(nfeatures)

參數:

  • nfeatures: 特徵點的最大數量

2.利用orb.detectAndCompute()檢測關鍵點並計算

kp,des = orb.detectAndCompute(gray,None)

參數:

  • gray: 進行關鍵點檢測的圖像,注意是灰度圖像

返回:

  • kp: 關鍵點信息,包括位置,尺度,方向信息
  • des: 關鍵點描述符,每個關鍵點BRIEF特徵向量,二進制字符串,

3.將關鍵點檢測結果繪製在圖像上

cv.drawKeypoints(image, keypoints, outputimage, color, flags)

示例:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
# 1 圖像讀取
img = cv.imread('./image/tv.jpg')

# 2 ORB角點檢測
# 2.1 實例化ORB對象
orb = cv.ORB_create(nfeatures=500)
# 2.2 檢測關鍵點,並計算特徵描述符
kp,des = orb.detectAndCompute(img,None)

print(des.shape)

# 3 將關鍵點繪製在圖像上
img2 = cv.drawKeypoints(img, kp, None, color=(0,0,255), flags=0)

# 4. 繪製圖像
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img2[:,:,::-1])
plt.xticks([]), plt.yticks([])
plt.show()

總結

  1. Fast算法

    原理:若一個像素周圍有一定數量的像素與該點像素值不同,則認爲其爲角點

    API: cv.FastFeatureDetector_create()

  2. ORB算法

    原理:是FAST算法和BRIEF算法的結合

    API:cv.ORB_create()


In [1]:

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

Harris

In [2]:

img = cv.imread("./image/chessboard.jpg")

In [3]:

gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)

In [4]:

gray = np.float32(gray)

In [5]:

dst = cv.cornerHarris(gray,2,3,0.04)

In [6]:

img[dst>0.001*dst.max()]=[0,0,255]

In [7]:

plt.figure(figsize=(10,10))
plt.imshow(img[:,:,::-1])

Out[7]:

<matplotlib.image.AxesImage at 0x11cf66350>

shi-Tomas

In [8]:

img = cv.imread("./image/tv.jpg")

In [9]:

gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)

In [10]:

coners = cv.goodFeaturesToTrack(gray,1000,0.01,10)

In [11]:

coners

Out[11]:

array([[[ 39., 363.]],

       [[ 39., 424.]],

       [[216., 431.]],

       ...,

       [[450., 209.]],

       [[331., 198.]],

       [[365., 421.]]], dtype=float32)

In [12]:

for i in coners:
    x,y = i.ravel()
    cv.circle(img,(x,y),2,(0,0,255),-1)

In [13]:

plt.imshow(img[:,:,::-1])

Out[13]:

<matplotlib.image.AxesImage at 0x1209ab990>

SIFT

In [14]:

img = cv.imread('./image/tv.jpg')

In [15]:

gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)

In [16]:

sift = cv.xfeatures2d.SIFT_create()

In [17]:

kp,des = sift.detectAndCompute(gray,None)

In [18]:

cv.drawKeypoints(img,kp,img,flags=cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

Out[18]:

array([[[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]],

       ...,

       [[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [255, 255, 255],
        [255, 255, 255],
        ...,
        [255, 255, 255],
        [255, 255, 255],
        [255, 255, 255]]], dtype=uint8)

In [19]:

plt.imshow(img[:,:,::-1])

Out[19]:

<matplotlib.image.AxesImage at 0x126d3d7d0>

FAST

In [23]:

img = cv.imread("./image/tv.jpg")

In [24]:

fast = cv.FastFeatureDetector_create(threshold=30)

In [25]:

kp = fast.detect(img,None)

In [26]:

img2 = cv.drawKeypoints(img,kp,None,color=(0,0,255))

In [27]:

plt.imshow(img2[:,:,::-1])

Out[27]:

<matplotlib.image.AxesImage at 0x127deff10>

In [28]:

fast.setNonmaxSuppression(0)

In [29]:

kp = fast.detect(img,None)

In [30]:

img3 = cv.drawKeypoints(img,kp,None,color=(0,0,255))

In [31]:

plt.imshow(img3[:,:,::-1])

Out[31]:

<matplotlib.image.AxesImage at 0x127bec850>

ORB

In [37]:

img = cv.imread("./image/tv.jpg")

In [38]:

orb = cv.ORB_create(nfeatures=5000)

In [39]:

kp,des = orb.detectAndCompute(img,None)

In [40]:

des.shape

Out[40]:

(4395, 32)

In [41]:

img2 = cv.drawKeypoints(img,kp,None,flags=0)

In [42]:

plt.imshow(img2[:,:,::-1])

Out[42]:

<matplotlib.image.AxesImage at 0x1284cd450>

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章