文章目錄
這是官方教程的第四篇,OpenCV之圖像特徵檢測,繼續學習opencv。
原教程花了很大篇幅介紹圖像的特徵是什麼含義,如何描述他們,並用“拼圖”遊戲來解釋它們。簡單說圖像的特徵就是一些邊邊角角、變化非常明顯的區域,能在圖片中明顯定位的元素,然後用一些數據去描述這些特徵。
Harris角點檢測
Harris角點檢測是Chris Harris和Mike Stephens在1988年提出的。主要用於運動圖像的追蹤。當時的普遍想法是利用邊緣進行追蹤,但是當相機或物體運動時你不知道朝哪個方向,相機的幾何變換也是未知的,所以邊緣匹配很難達到預期的效果。即使是當時最優秀的邊緣檢測算子Canny算子,它的邊緣檢測也依賴於閾值的選取。所以Harris等人放棄了匹配邊緣,轉而尋找一些特殊的點來匹配,這些點是邊緣線段的連接點,它們表徵了圖像的結構,代表了圖像局部的特徵。
Harris角點檢測
- 函數原型:dst = cv.cornerHarris( src, blockSize, ksize, k[, dst[, borderType]] )
- src:輸入圖像,單通道8bit灰度圖像或者float32型的圖像
- dst:角點檢測後圖像,圖像類型爲CV_32FC1,尺寸與原圖一致
- blockSize:鄰域尺寸大小
- ksize:sobel邊緣檢測窗口大小
- k:係數值,通常取值範圍爲0.04 ~ 0.06之間
- borderType:邊沿像素推斷方法,即邊沿類型
import numpy as np
import cv2 as cv
filename = 'chessboard.png'
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv.cornerHarris(gray,2,3,0.04)
#result is dilated for marking the corners, not important
dst = cv.dilate(dst,None)
# Threshold for an optimal value, it may vary depending on the image.
img[dst>0.01*dst.max()]=[0,0,255]
cv.imshow('dst',img)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
Corner with SubPixel Accuracy
Harris角點檢測存在很多缺陷,如角點是像素級別的,速度較慢,準確度不高等
- 函數原型:corners = cv.cornerSubPix( image, corners, winSize, zeroZone, criteria )
- image:輸入圖像
- corners:輸入的初始角點座標集合,通常是Harris角點檢測的結果,輸出時修改爲精確檢測的檢點座標
- winSize:計算亞像素角點時考慮的區域的大小,大小爲NXN; N=(winSize*2+1);For example, if winSize=Size(5,5) , then a 5∗2+1×5∗2+1=11×11 search window is used.
- zeroZone:類似於winSize,搜索窗口中dead region大小的一半,Size(-1,-1)表示忽略,主要用於避免自相關矩陣中可能的奇點(自己翻譯,不明所以)
- criteria:評判規則,表示計算亞像素時停止迭代的標準,可選的值有cv::TermCriteria::MAX_ITER 、cv::TermCriteria::EPS(可以是兩者其一,或兩者均選),前者表示迭代次數達到了最大次數時停止,後者表示角點位置變化的最小值已經達到最小時停止迭代。二者均使用cv::TermCriteria()構造函數進行指定
import numpy as np
import cv2 as cv
filename = 'chessboard2.jpg'
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# find Harris corners
gray = np.float32(gray)
dst = cv.cornerHarris(gray,2,3,0.04)
dst = cv.dilate(dst,None)
ret, dst = cv.threshold(dst,0.01*dst.max(),255,0)
dst = np.uint8(dst)
# find centroids
ret, labels, stats, centroids = cv.connectedComponentsWithStats(dst)
# define the criteria to stop and refine the corners
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria)
# Now draw them
res = np.hstack((centroids,corners))
res = np.int0(res)
img[res[:,1],res[:,0]]=[0,0,255]
img[res[:,3],res[:,2]] = [0,255,0]
cv.imwrite('subpixel5.png',img)
圖中紅色點表示Harris角點檢測的結果,綠色點表示經過SubPixel優化過的角點檢測結果。
Shi-Tomasi角點檢測
Shi-Tomasi 算法是Harris 算法的改進。Harris 算法最原始的定義是將矩陣 M 的行列式值與 M 的跡相減,再將差值同預先給定的閾值進行比較。後來Shi 和Tomasi 提出改進的方法,若兩個特徵值中較小的一個大於最小閾值,則會得到強角點。
- 函數原型:corners = cv.goodFeaturesToTrack( image, maxCorners, qualityLevel, minDistance[, corners[, mask[, blockSize[, useHarrisDetector[, k]]]]] )
- image:8位或32位單通道灰度圖像
- corners:角點向量,保存的是檢測到的角點的座標
- maxCorners:定義可以檢測到的角點的數量的最大值,maxCorners <= 0 表示無限制
- qualityLevel:檢測到的角點的質量等級,角點特徵值小於qualityLevel*最大特徵值的點將被捨棄 For example, if the best corner has the quality measure = 1500, and the qualityLevel=0.01 , then all the corners with the quality measure less than 15 are rejected.
- minDistance:兩個角點間最小間距,以像素爲單位
- mask:感興趣的角點檢測區域,指定角點檢測區域,類型爲CV_8UC1
- blockSize:計算協方差矩陣時的窗口大小
- useHarrisDetector:是否使用Harris角點檢測,爲false,則使用Shi-Tomasi算子
- k:Harris角點檢測算子用的中間參數,一般取經驗值0.04~0.06;useHarrisDetector參數爲false時,該參數不起作用
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('blox.jpg')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
corners = cv.goodFeaturesToTrack(gray,25,0.01,10)
corners = np.int0(corners)
for i in corners:
x,y = i.ravel()
cv.circle(img,(x,y),3,255,-1)
plt.imshow(img),plt.show()
尺度不變特徵變換算法SIFT (Scale-Invariant Feature Transform)
SIFT算法的特點:
- SIFT特徵是圖像的局部特徵,其對旋轉、尺度縮放、亮度變化保持不變性,對視角變化、仿射變換、噪聲也保持一定程度的穩定性;
- 獨特性(Distinctiveness)好,信息量豐富,適用於在海量特徵數據庫中進行快速、準確的匹配;
- 多量性,即使少數的幾個物體也可以產生大量的SIFT特徵向量;
- 高速性,經優化的SIFT匹配算法甚至可以達到實時的要求;
- 可擴展性,可以很方便的與其他形式的特徵向量進行聯合。
SIFT算法在一定程度上可解決目標的自身狀態、場景所處的環境和成像器材的成像特性等因素影響圖像配準/目標識別跟蹤的性能,主要有以下幾點:
- 目標的旋轉、縮放、平移
- 圖像仿射/投影變換
- 光照影響
- 目標遮擋
- 雜物場景
- 噪聲
import numpy as np
import cv2 as cv
img = cv.imread('test.jpg')
gray= cv.cvtColor(img,cv.COLOR_BGR2GRAY)
sift = cv.xfeatures2d.SIFT_create()
kp = sift.detect(gray,None)
img=cv.drawKeypoints(gray,kp,img)
cv.imwrite('sift_keypoints.jpg',img)
- 函數 sift.compute() 用來計算這些關鍵點的描述符。例如: kp; des = sift:compute(gray; kp)。
- 函數 sift.detectAndCompute()一步到位直接找到關鍵點並計算出其描述符。
這裏需要描述一下函數:cv.drawKeypoints
- outImage = cv.drawKeypoints( image, keypoints, outImage[, color[, flags]] )
- image:查找關鍵點的圖像
- keypoints:在圖像中查找到的關鍵點
- outImage:輸出圖像,根據flag的取值,會在輸出圖像畫上flag只是的信息
- color:關鍵點顏色
- flags:在輸出圖像上描繪關鍵的標誌,取值如下
cv2.DRAW_MATCHES_FLAGS_DEFAULT, cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS, cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG, cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS
SURF (Speeded-Up Robust Features)
SIFT 算法可以進行關鍵點檢測和描述,但是這種算法的執行速度比較慢。
與 SIFT 相同 OpenCV 也提供了 SURF 的相關函數。首先我們要初始化一個 SURF 對象,同時設置好可選參數: 64/128 維描述符, Upright/Normal 模式等。
>>> img = cv.imread('fly.png',0)
# Create SURF object. You can specify params here or later.
# Here I set Hessian Threshold to 400
>>> surf = cv.xfeatures2d.SURF_create(400)
# Find keypoints and descriptors directly
>>> kp, des = surf.detectAndCompute(img,None)
>>> len(kp)
699
#在一幅圖像中顯示 699 個關鍵點太多了,我們把它縮減到 50 個再繪製到圖片上。
# Check present Hessian threshold
>>> print( surf.getHessianThreshold() )
400.0
# We set it to some 50000. Remember, it is just for representing in picture.
# In actual cases, it is better to have a value 300-500
>>> surf.setHessianThreshold(50000)
# Again compute keypoints and check its number.
>>> kp, des = surf.detectAndCompute(img,None)
>>> print( len(kp) )
47
>>> img2 = cv.drawKeypoints(img,kp,None,(255,0,0),4)
>>> plt.imshow(img2),plt.show()
# 不檢測關鍵點的方向
# Check upright flag, if it False, set it to True
>>> print( surf.getUpright() )
False
>>> surf.setUpright(True)
# Recompute the feature points and draw it
>>> kp = surf.detect(img,None)
>>> img2 = cv.drawKeypoints(img,kp,None,(255,0,0),4)
>>> plt.imshow(img2),plt.show()
# 關鍵點描述符的大小,如果是 64 維的就改成 128 維
# Find size of descriptor
>>> print( surf.descriptorSize() )
64
# That means flag, "extended" is False.
>>> surf.getExtended()
False
# So we make it to True to get 128-dim descriptors.
>>> surf.extended = True
>>> kp, des = surf.detectAndCompute(img,None)
>>> print( surf.descriptorSize() )
128
>>> print( des.shape )
(47, 128)
FAST Algorithm for Corner Detection
- cv.FAST_FEATURE_DETECTOR_TYPE_5_8,
- cv.FAST_FEATURE_DETECTOR_TYPE_7_12
- cv.FAST_FEATURE_DETECTOR_TYPE_9_16
以上3個選擇是鄰域大小的選擇
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('simple.jpg',0)
# Initiate FAST object with default values
fast = cv.FastFeatureDetector_create()
# find and draw the keypoints
kp = fast.detect(img,None)
img2 = cv.drawKeypoints(img, kp, None, color=(255,0,0))
# Print all default params
print( "Threshold: {}".format(fast.getThreshold()) )
print( "nonmaxSuppression:{}".format(fast.getNonmaxSuppression()) )
print( "neighborhood: {}".format(fast.getType()) )
print( "Total Keypoints with nonmaxSuppression: {}".format(len(kp)) )
cv.imwrite('fast_true.png',img2)
# Disable nonmaxSuppression
fast.setNonmaxSuppression(0)
kp = fast.detect(img,None)
print( "Total Keypoints without nonmaxSuppression: {}".format(len(kp)) )
img3 = cv.drawKeypoints(img, kp, None, color=(255,0,0))
cv.imwrite('fast_false.png',img3)
至此,學習遇到瓶頸了,會有一大段時間不會更新OpenCV的內容。