[171111] Python OpenCV 中 SIFT 特徵點檢測和匹配
原創內容,禁止轉載!
導讀:這是 Python OpenCV 圖像處理 專欄的第11篇文章。上一篇[171110] 基於 Python OpenCV 復現引導濾波(Guided Filter)和基於暗通道先驗單幅圖像去霧(Haze Removal)主要是概述了何大的引導濾波和暗通道區域。本篇將要介紹的是 OpenCV 中 SIFT特徵點檢測和匹配的用法。 關於 Python OpenCV 處理的基礎知識,可以參考這篇文章 [171102] Python3 OpenCV3 圖像處理基礎(Python3 + Numpy + Matplotlib + OpenCV3 + ...)。更多文章,請查看 [171101] Python OpenCV 圖像處理專欄目錄。
今天是個特殊的日子, 2017.11.11 。在這第N個光棍節里,寫下第11篇文章,想想真是有點小激動類。坑已經挖好了,就等著填了,耶
其實不想寫這個了,可是坑已經挖了。我也知道填不好,但填一點是一點吧。。
13年前,也就是2004,D.Lowe 在其論文 《Distinctive Image Features from Scale-Invariant Keypoints》中提出了一種新的特徵點檢測演算法,即SIFT(Scale Invariant Feature Transform)。與前期的關鍵點檢測演算法(如 Harris 角點檢測)相比,SIFT特徵點檢測演算法具有更強的魯棒性,對光照、尺度、旋轉等具有一定的穩定性,用於物體檢測時對部分遮擋也有一定的穩定性。
SIFT 演算法細節在論文中有講,這裡總結一下。
使用SIFT演算法進行物體查找的幾個步驟:
(1) 尺度空間極值檢測(Scale-space Extrema Detection)n也就是在多尺度高斯差分(Difference of Gauss)空間中檢測極值點(3x3x3 區域極值),n作為候選的關鍵點(Potential keypoints)。nn(2) 定位關鍵點(Keypoint Localization),捨棄低對比度關鍵點和高邊緣響應的關鍵點。n確定候選關鍵點後,使用泰勒級數展開來精確定位極值點。捨棄亮度值較低(對比度較低)的極值點。n同時由於DoG空間是差分空間,對邊緣有較高響應的同時也對雜訊敏感。因此使用 2x2 的 nHassian 矩陣計算其特徵值的比率,捨棄比值較大的極值點。nn(3) 方向賦值(Orientation Assignment)n在關鍵點周圍一定區域內計算梯度方向和幅度累計直方圖。使用高斯窗函數(sigma=1.5 x Scale)加權。n取所有 bin 峰值大於最大值 80% 的峰的朝向作為關鍵點的方向。n(也就是說,可能存在同一尺度空間同一位置不同朝向的關鍵點們)。nn(4) 構建關鍵點描述子(Keypoint descriptor)n取關鍵點周圍 16x16 的鄰域,劃分為 16 個 4x4 的子塊,每個子塊構建 8 方向 朝向直方圖,n構成 16 x 8 = 128 維度特徵向量。nn(5) 關鍵點匹配(Keypoint matching)和測試n對場景圖和目標物體圖都提取 SIFT 關鍵點,然後使用 KNN 以計算匹配最匹配的兩個點。n如果到最匹配距離和到次匹配距離之間比值大於一定閾值(如0.9)則說明是雜訊點,捨棄。nn(6) 獲取仿射變換矩陣,幾何變換查找物體n從獲取到的匹配關鍵點對使用隨機抽樣一致性(RANSAC, Random Sample consensus algorithm)n計算放射變換矩陣,然後變換後即可獲取帶查找目標在場景圖中的位置。n
再次強調:
想要了解演算法細節,請自行參考論文原文,網路上的各種課件,維基百科,或者OpenCV 中的源碼實現。
現在,又要當調包俠了,這裡給出使用 Python OpenCV 里的 SIFT 特徵點用於查找物體的經典例子。
使用的原圖像分別是:
box.png
box_in_scene.png
代碼和注釋:
#!/usr/bin/python3n# 2017.11.11 01:44:37 CSTn# 2017.11.12 00:09:14 CSTn"""n使用Sift特徵點檢測和匹配查找場景中特定物體。n"""nnimport cv2nimport numpy as npnMIN_MATCH_COUNT = 4nnimgname1 = "box.png"nimgname2 = "box_in_scene.png"nn## (1) prepare datanimg1 = cv2.imread(imgname1)nimg2 = cv2.imread(imgname2)ngray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)ngray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)nnn## (2) Create SIFT objectnsift = cv2.xfeatures2d.SIFT_create()nn## (3) Create flann matchernmatcher = cv2.FlannBasedMatcher(dict(algorithm = 1, trees = 5), {})nn## (4) Detect keypoints and compute keypointer descriptorsnkpts1, descs1 = sift.detectAndCompute(gray1,None)nkpts2, descs2 = sift.detectAndCompute(gray2,None)nn## (5) knnMatch to get Top2nmatches = matcher.knnMatch(descs1, descs2, 2)n# Sort by their distance.nmatches = sorted(matches, key = lambda x:x[0].distance)nn## (6) Ratio test, to get good matches.ngood = [m1 for (m1, m2) in matches if m1.distance < 0.7 * m2.distance]nncanvas = img2.copy()nn## (7) find homography matrixn## 當有足夠的健壯匹配點對(至少4個)時nif len(good)>MIN_MATCH_COUNT:n ## 從匹配中提取出對應點對n ## (queryIndex for the small object, trainIndex for the scene )n src_pts = np.float32([ kpts1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)n dst_pts = np.float32([ kpts2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)n ## find homography matrix in cv2.RANSAC using good match pointsn M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)n ## 掩模,用作繪製計算單應性矩陣時用到的點對n #matchesMask2 = mask.ravel().tolist()n ## 計算圖1的畸變,也就是在圖2中的對應的位置。n h,w = img1.shape[:2]n pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)n dst = cv2.perspectiveTransform(pts,M)n ## 繪製邊框n cv2.polylines(canvas,[np.int32(dst)],True,(0,255,0),3, cv2.LINE_AA)nelse:n print( "Not enough matches are found - {}/{}".format(len(good),MIN_MATCH_COUNT))nnn## (8) drawMatchesnmatched = cv2.drawMatches(img1,kpts1,canvas,kpts2,good,None)#,**draw_params)nn## (9) Crop the matched region from scenenh,w = img1.shape[:2]npts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0] ]).reshape(-1,1,2)ndst = cv2.perspectiveTransform(pts,M)nperspectiveM = cv2.getPerspectiveTransform(np.float32(dst),pts)nfound = cv2.warpPerspective(img2,perspectiveM,(w,h))nn## (10) save and displayncv2.imwrite("matched.png", matched)ncv2.imwrite("found.png", found)ncv2.imshow("matched", matched);ncv2.imshow("found", found);ncv2.waitKey();cv2.destroyAllWindows()n
匹配結果圖:
目標在場景中的圖:
參考:
- 《Distinctive Image Features from Scale-Invariant Keypoints》
- Scale-invariant feature transform
- Introduction to SIFT (Scale-Invariant Feature Transform)
- https://raw.githubusercontent.com/opencv/opencv_contrib/master/modules/xfeatures2d/src/sift.cpp
完結,撒花!
推薦閱讀:
※如何用 PS 把這種線條手摳出來?
※Byakuren:一個 C 實現的主題色提取庫
※燃燒船分形(Burning Ship fractal)
※拉普拉斯運算元
※GitHub|教你快速使用Tensorflow/Elasticsearch實現全文的圖片搜索(附源代碼)