您好,登錄后才能下訂單哦!
特征檢測是計(jì)算機(jī)對一張圖像中最為明顯的特征進(jìn)行識別檢測并將其勾畫出來。大多數(shù)特征檢測都會(huì)涉及圖像的角點(diǎn)、邊和斑點(diǎn)的識別、或者是物體的對稱軸。
角點(diǎn)檢測 是由Opencv的cornerHarris函數(shù)實(shí)現(xiàn),其他函數(shù)參數(shù)說明如下:
cv2.cornerHarris(src=gray, blockSize=9, ksize=23, k=0.04) # cornerHarris參數(shù): # src - 數(shù)據(jù)類型為 float32 的輸入圖像。 # blockSize - 角點(diǎn)檢測中要考慮的領(lǐng)域大小。 # ksize - Sobel 求導(dǎo)中使用的窗口大小 # k - Harris 角點(diǎn)檢測方程中的自由參數(shù),取值參數(shù)為 [0,04,0.06].
以國際象棋為例,這是計(jì)算機(jī)視覺最為常見的分析對象,如圖所示:
角點(diǎn)檢測代碼如下:
import cv2 import numpy as np img = cv2.imread('chess_board.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # cornerHarris函數(shù)圖像格式為 float32 ,因此需要將圖像轉(zhuǎn)換 float32 類型 gray = np.float32(gray) # cornerHarris參數(shù): # src - 數(shù)據(jù)類型為 float32 的輸入圖像。 # blockSize - 角點(diǎn)檢測中要考慮的領(lǐng)域大小。 # ksize - Sobel 求導(dǎo)中使用的窗口大小 # k - Harris 角點(diǎn)檢測方程中的自由參數(shù),取值參數(shù)為 [0,04,0.06]. dst = cv2.cornerHarris(src=gray, blockSize=9, ksize=23, k=0.04) # 變量a的閾值為0.01 * dst.max(),如果dst的圖像值大于閾值,那么該圖像的像素點(diǎn)設(shè)為True,否則為False # 將圖片每個(gè)像素點(diǎn)根據(jù)變量a的True和False進(jìn)行賦值處理,賦值處理是將圖像角點(diǎn)勾畫出來 a = dst>0.01 * dst.max() img[a] = [0, 0, 255] # 顯示圖像 while (True): cv2.imshow('corners', img) if cv2.waitKey(120) & 0xff == ord("q"): break cv2.destroyAllWindows()
運(yùn)行代碼,結(jié)果如圖所示:
但有時(shí)候,圖像的像素大小對角點(diǎn)存在一定的影響。比如圖像越小,角點(diǎn)看上去趨向近似一條直線,這樣很容易造成角點(diǎn)的丟失。如果按照上述的檢測方法,會(huì)造成角點(diǎn)檢測結(jié)果不相符,因此引入DoG和SIFT算法進(jìn)行檢測。Opencv的SIFT類是DoG和SIFT算法組合。
DoG是對同一圖像使用不同高斯濾波器所得的結(jié)果。
SIFT是通過一個(gè)特征向量來描述關(guān)鍵點(diǎn)周圍區(qū)域的情況。
我們以下圖為例:
import cv2 # 讀取圖片并灰度處理 imgpath = 'varese.jpg' img = cv2.imread(imgpath) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 創(chuàng)建SIFT對象 sift = cv2.xfeatures2d.SIFT_create() # 將圖片進(jìn)行SURF計(jì)算,并找出角點(diǎn)keypoints,keypoints是檢測關(guān)鍵點(diǎn) # descriptor是描述符,這是圖像一種表示方式,可以比較兩個(gè)圖像的關(guān)鍵點(diǎn)描述符,可作為特征匹配的一種方法。 keypoints, descriptor = sift.detectAndCompute(gray, None) # cv2.drawKeypoints() 函數(shù)主要包含五個(gè)參數(shù): # image: 原始圖片 # keypoints:從原圖中獲得的關(guān)鍵點(diǎn),這也是畫圖時(shí)所用到的數(shù)據(jù) # outputimage:輸出 # color:顏色設(shè)置,通過修改(b,g,r)的值,更改畫筆的顏色,b=藍(lán)色,g=綠色,r=紅色。 # flags:繪圖功能的標(biāo)識設(shè)置,標(biāo)識如下: # cv2.DRAW_MATCHES_FLAGS_DEFAULT 默認(rèn)值 # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS # cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG # cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS img = cv2.drawKeypoints(image=img, outImage=img, keypoints = keypoints, flags=cv2.DRAW_MATCHES_FLAGS_DEFAULT, color = (51, 163, 236)) # 顯示圖片 cv2.imshow('sift_keypoints', img) while (True): if cv2.waitKey(120) & 0xff == ord("q"): break cv2.destroyAllWindows()
運(yùn)行代碼,結(jié)果如圖所示:
除了SIFT算法檢測之外,還有SURF特征檢測算法,比SIFT算法快,并吸收了SIFT算法的思想。SURF采用Hessian算法檢測關(guān)鍵點(diǎn),而SURF是提取特征,這個(gè)與SIFT很像。Opencv的SURF類是Hessian算法和SURF算法組合。我們根據(jù)SIFT的代碼進(jìn)行修改,代碼如下:
import cv2 # 讀取圖片并灰度處理 imgpath = 'varese.jpg' img = cv2.imread(imgpath) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 創(chuàng)建SURF對象,對象參數(shù)float(4000)為閾值,閾值越高,識別的特征越小。 sift = cv2.xfeatures2d.SURF_create(float(4000)) # 將圖片進(jìn)行SURF計(jì)算,并找出角點(diǎn)keypoints,keypoints是檢測關(guān)鍵點(diǎn) # descriptor是描述符,這是圖像一種表示方式,可以比較兩個(gè)圖像的關(guān)鍵點(diǎn)描述符,可作為特征匹配的一種方法。 keypoints, descriptor = sift.detectAndCompute(gray, None) # cv2.drawKeypoints() 函數(shù)主要包含五個(gè)參數(shù): # image: 原始圖片 # keypoints:從原圖中獲得的關(guān)鍵點(diǎn),這也是畫圖時(shí)所用到的數(shù)據(jù) # outputimage:輸出 # color:顏色設(shè)置,通過修改(b,g,r)的值,更改畫筆的顏色,b=藍(lán)色,g=綠色,r=紅色。 # flags:繪圖功能的標(biāo)識設(shè)置,標(biāo)識如下: # cv2.DRAW_MATCHES_FLAGS_DEFAULT 默認(rèn)值 # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS # cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG # cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS img = cv2.drawKeypoints(image=img, outImage=img, keypoints = keypoints, flags=cv2.DRAW_MATCHES_FLAGS_DEFAULT, color = (51, 163, 236)) # 顯示圖片 cv2.imshow('sift_keypoints', img) while (True): if cv2.waitKey(120) & 0xff == ord("q"): break cv2.destroyAllWindows()
上述代碼我們只修改sift = cv2.xfeatures2d.SURF_create(float(4000))
即可實(shí)現(xiàn)SURF特征檢測算法。運(yùn)行結(jié)果如圖所示:
對比SURF和SIFT算法,ORB算法更處于起步階段,在2011年才首次發(fā)布。但比前兩者的速度更快。ORB基于FAST關(guān)鍵點(diǎn)檢測和BRIEF的描述符技術(shù)相結(jié)合,因此我們先了解FAST和BRIEF。
FAST:特征檢測算法。
BRIEF:只是一個(gè)描述符,這是圖像一種表示方式,可以比較兩個(gè)圖像的關(guān)鍵點(diǎn)描述符,可作為特征匹配的一種方法。
暴力匹配:比較兩個(gè)描述符并產(chǎn)生匹配結(jié)果。
在上述的例子中,我們只是將檢測的關(guān)鍵點(diǎn)進(jìn)行勾畫,在這例子中,將使用ORB檢測關(guān)鍵點(diǎn)之外,還將兩圖進(jìn)行匹配,匹配的圖像如下:
實(shí)現(xiàn)方法:首先分別對兩圖進(jìn)行ORB處理,然后將兩圖的關(guān)鍵點(diǎn)進(jìn)行暴力匹配。具體代碼如下:
# ORB算法實(shí)現(xiàn)特征檢測+暴力匹配
import numpy as np import cv2 from matplotlib import pyplot as plt # 讀取圖片內(nèi)容 img1 = cv2.imread('aa.jpg',0) img2 = cv2.imread('bb.png',0) # 使用ORB特征檢測器和描述符,計(jì)算關(guān)鍵點(diǎn)和描述符 orb = cv2.ORB_create() kp1, des1 = orb.detectAndCompute(img1,None) kp2, des2 = orb.detectAndCompute(img2,None) # 暴力匹配BFMatcher,遍歷描述符,確定描述符是否匹配,然后計(jì)算匹配距離并排序 # BFMatcher函數(shù)參數(shù): # normType:NORM_L1, NORM_L2, NORM_HAMMING, NORM_HAMMING2。 # NORM_L1和NORM_L2是SIFT和SURF描述符的優(yōu)先選擇,NORM_HAMMING和NORM_HAMMING2是用于ORB算法 bf = cv2.BFMatcher(normType=cv2.NORM_HAMMING, crossCheck=True) matches = bf.match(des1,des2) matches = sorted(matches, key = lambda x:x.distance) # matches是DMatch對象,具有以下屬性: # DMatch.distance - 描述符之間的距離。 越低越好。 # DMatch.trainIdx - 訓(xùn)練描述符中描述符的索引 # DMatch.queryIdx - 查詢描述符中描述符的索引 # DMatch.imgIdx - 訓(xùn)練圖像的索引。 # 使用plt將兩個(gè)圖像的匹配結(jié)果顯示出來 img3 = cv2.drawMatches(img1=img1,keypoints1=kp1,img2=img2,keypoints2=kp2, matches1to2=matches, outImg=img2, flags=2) plt.imshow(img3),plt.show()
運(yùn)行結(jié)果如圖所示:
# SURF和SIFT算法+暴力匹配
暴力匹配BFMatcher是一種匹配方法,只要提供兩個(gè)關(guān)鍵點(diǎn)即可實(shí)現(xiàn)匹配。若將上述例子改為SURF和SIFT算法,只需修改以下代碼:
將orb = cv2.ORB_create()改為 orb = cv2.xfeatures2d.SURF_create(float(4000)) 將bf = cv2.BFMatcher(normType=cv2.NORM_HAMMING, crossCheck=True)改為 bf = cv2.BFMatcher(normType=cv2.NORM_L1, crossCheck=True)
# 獲取匹配關(guān)鍵點(diǎn)的坐標(biāo)位置
在上述例子中,matches是DMatch對象,DMatch是以列表的形式表示,每個(gè)元素代表兩圖能匹配得上的點(diǎn)。如果想獲取某個(gè)點(diǎn)的坐標(biāo)位置,在上述例子添加以下代碼:
# 由于匹配順序是:matches = bf.match(des1,des2),先des1后des2。 # 因此,kp1的索引由DMatch對象屬性為queryIdx決定,kp2的索引由DMatch對象屬性為trainIdx決定 # 獲取aa.jpg的關(guān)鍵點(diǎn)位置 x,y = kp1[matches[0].queryIdx].pt cv2.rectangle(img1, (int(x),int(y)), (int(x) + 5, int(y) + 5), (0, 255, 0), 2) cv2.imshow('a', img1) # 獲取bb.png的關(guān)鍵點(diǎn)位置 x,y = kp2[matches[0].trainIdx].pt cv2.rectangle(img2, (int(x1),int(y1)), (int(x1) + 5, int(y1) + 5), (0, 255, 0), 2) cv2.imshow('b', img2) # 使用plt將兩個(gè)圖像的第一個(gè)匹配結(jié)果顯示出來 img3 = cv2.drawMatches(img1=img1,keypoints1=kp1,img2=img2,keypoints2=kp2, matches1to2=matches[:1], outImg=img2, flags=2) plt.imshow(img3),plt.show()
運(yùn)行結(jié)果如圖所示:
上述講到的暴力匹配是使用BFMatcher匹配器實(shí)現(xiàn)的,然后由match函數(shù)實(shí)現(xiàn)匹配。接下來講解K-最近鄰匹配(KNN),并在BFMatcher匹配下實(shí)現(xiàn)。在所有機(jī)器學(xué)習(xí)的算法中,KNN可能是最為簡單的算法。針對上述例子,改為KNN匹配,實(shí)現(xiàn)代碼如下:
import numpy as np import cv2 from matplotlib import pyplot as plt # 讀取圖片內(nèi)容 img1 = cv2.imread('aa.jpg',0) img2 = cv2.imread('bb.png',0) # 使用ORB特征檢測器和描述符,計(jì)算關(guān)鍵點(diǎn)和描述符 orb = cv2.ORB_create() kp1, des1 = orb.detectAndCompute(img1,None) kp2, des2 = orb.detectAndCompute(img2,None) # 暴力匹配BFMatcher,遍歷描述符,確定描述符是否匹配,然后計(jì)算匹配距離并排序 # BFMatcher函數(shù)參數(shù): # normType:NORM_L1, NORM_L2, NORM_HAMMING, NORM_HAMMING2。 # NORM_L1和NORM_L2是SIFT和SURF描述符的優(yōu)先選擇,NORM_HAMMING和NORM_HAMMING2是用于ORB算法 bf = cv2.BFMatcher(normType=cv2.NORM_HAMMING, crossCheck=True) # knnMatch 函數(shù)參數(shù)k是返回符合匹配的個(gè)數(shù),暴力匹配match只返回最佳匹配結(jié)果。 matches = bf.knnMatch(des1,des2,k=1) # 使用plt將兩個(gè)圖像的第一個(gè)匹配結(jié)果顯示出來 # 若使用knnMatch進(jìn)行匹配,則需要使用drawMatchesKnn函數(shù)將結(jié)果顯示 img3 = cv2.drawMatchesKnn(img1=img1,keypoints1=kp1,img2=img2,keypoints2=kp2, matches1to2=matches, outImg=img2, flags=2) plt.imshow(img3),plt.show()
最后是介紹FLANN匹配,相對暴力匹配BFMatcher來講,這匹配算法比較準(zhǔn)確、快速和使用方便。FLANN具有一種內(nèi)部機(jī)制,可以根據(jù)數(shù)據(jù)本身選擇最合適的算法來處理數(shù)據(jù)集。值得注意的是,F(xiàn)LANN匹配器只能使用SURF和SIFT算法。
FLANN實(shí)現(xiàn)方式如下:
import numpy as np import cv2 from matplotlib import pyplot as plt queryImage = cv2.imread('aa.jpg',0) trainingImage = cv2.imread('bb.png',0) # 只使用SIFT 或 SURF 檢測角點(diǎn) sift = cv2.xfeatures2d.SIFT_create() # sift = cv2.xfeatures2d.SURF_create(float(4000)) kp1, des1 = sift.detectAndCompute(queryImage,None) kp2, des2 = sift.detectAndCompute(trainingImage,None) # 設(shè)置FLANN匹配器參數(shù) # algorithm設(shè)置可參考https://docs.opencv.org/3.1.0/dc/d8c/namespacecvflann.html indexParams = dict(algorithm=0, trees=5) searchParams = dict(checks=50) # 定義FLANN匹配器 flann = cv2.FlannBasedMatcher(indexParams,searchParams) # 使用 KNN 算法實(shí)現(xiàn)匹配 matches = flann.knnMatch(des1,des2,k=2) # 根據(jù)matches生成相同長度的matchesMask列表,列表元素為[0,0] matchesMask = [[0,0] for i in range(len(matches))] # 去除錯(cuò)誤匹配 for i,(m,n) in enumerate(matches): if m.distance < 0.7*n.distance: matchesMask[i] = [1,0] # 將圖像顯示 # matchColor是兩圖的匹配連接線,連接線與matchesMask相關(guān) # singlePointColor是勾畫關(guān)鍵點(diǎn) drawParams = dict(matchColor = (0,255,0), singlePointColor = (255,0,0), matchesMask = matchesMask, flags = 0) resultImage = cv2.drawMatchesKnn(queryImage,kp1,trainingImage,kp2,matches,None,**drawParams) plt.imshow(resultImage,),plt.show()
運(yùn)行結(jié)果如圖所示:
FLANN的單應(yīng)性匹配,單應(yīng)性是一個(gè)條件,該條件表面當(dāng)兩幅圖像中的一副出像投影畸變時(shí),他們還能匹配。FLANN的單應(yīng)性實(shí)現(xiàn)代碼如下:
import numpy as np import cv2 from matplotlib import pyplot as plt MIN_MATCH_COUNT = 10 img1 = cv2.imread('tattoo_seed.jpg',0) img2 = cv2.imread('hush.jpg',0) # 使用SIFT檢測角點(diǎn) sift = cv2.xfeatures2d.SIFT_create() # 獲取關(guān)鍵點(diǎn)和描述符 kp1, des1 = sift.detectAndCompute(img1,None) kp2, des2 = sift.detectAndCompute(img2,None) # 定義FLANN匹配器 index_params = dict(algorithm = 1, trees = 5) search_params = dict(checks = 50) flann = cv2.FlannBasedMatcher(index_params, search_params) # 使用KNN算法匹配 matches = flann.knnMatch(des1,des2,k=2) # 去除錯(cuò)誤匹配 good = [] for m,n in matches: if m.distance < 0.7*n.distance: good.append(m) # 單應(yīng)性 if len(good)>MIN_MATCH_COUNT: # 改變數(shù)組的表現(xiàn)形式,不改變數(shù)據(jù)內(nèi)容,數(shù)據(jù)內(nèi)容是每個(gè)關(guān)鍵點(diǎn)的坐標(biāo)位置 src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2) dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2) # findHomography 函數(shù)是計(jì)算變換矩陣 # 參數(shù)cv2.RANSAC是使用RANSAC算法尋找一個(gè)最佳單應(yīng)性矩陣H,即返回值M # 返回值:M 為變換矩陣,mask是掩模 M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0) # ravel方法將數(shù)據(jù)降維處理,最后并轉(zhuǎn)換成列表格式 matchesMask = mask.ravel().tolist() # 獲取img1的圖像尺寸 h,w = img1.shape # pts是圖像img1的四個(gè)頂點(diǎn) pts = np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2) # 計(jì)算變換后的四個(gè)頂點(diǎn)坐標(biāo)位置 dst = cv2.perspectiveTransform(pts,M) # 根據(jù)四個(gè)頂點(diǎn)坐標(biāo)位置在img2圖像畫出變換后的邊框 img2 = cv2.polylines(img2,[np.int32(dst)],True,(255,0,0),3, cv2.LINE_AA) else: print("Not enough matches are found - %d/%d") % (len(good),MIN_MATCH_COUNT) matchesMask = None # 顯示匹配結(jié)果 draw_params = dict(matchColor = (0,255,0), # draw matches in green color singlePointColor = None, matchesMask = matchesMask, # draw only inliers flags = 2) img3 = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params) plt.imshow(img3, 'gray'),plt.show()
運(yùn)行結(jié)果如下所示:
單應(yīng)性實(shí)際應(yīng)用
從上述的例子可以看到,單應(yīng)性是在兩圖片匹配的時(shí)候,其中某一圖片發(fā)生變換處理,變換后圖像會(huì)呈現(xiàn)一種立體空間的視覺效果,圖像發(fā)生變換程度稱為變換矩陣。以下例子將圖像中的書本替換成其他書本,例子中所使用圖片如下:
我們根據(jù)圖1和圖2計(jì)算變換矩陣,然后通過變換矩陣將圖3進(jìn)行變換,最后將圖3加入到圖1中,實(shí)現(xiàn)圖片替換。實(shí)現(xiàn)代碼如下:
import numpy as np import cv2 from matplotlib import pyplot as plt img1 = cv2.imread('logo.jpg',0) img2 = cv2.imread('book.jpg',0) # 使用SIFT檢測角點(diǎn) sift = cv2.xfeatures2d.SIFT_create() # 獲取關(guān)鍵點(diǎn)和描述符 kp1, des1 = sift.detectAndCompute(img1,None) kp2, des2 = sift.detectAndCompute(img2,None) # 定義FLANN匹配器 index_params = dict(algorithm = 1, trees = 5) search_params = dict(checks = 50) flann = cv2.FlannBasedMatcher(index_params, search_params) # 使用KNN算法匹配 matches = flann.knnMatch(des1,des2,k=2) # 去除錯(cuò)誤匹配 good = [] for m,n in matches: if m.distance < 0.7*n.distance: good.append(m) # 單應(yīng)性實(shí)際應(yīng)用 # 改變數(shù)組的表現(xiàn)形式,不改變數(shù)據(jù)內(nèi)容,數(shù)據(jù)內(nèi)容是每個(gè)關(guān)鍵點(diǎn)的坐標(biāo)位置 src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2) dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2) # findHomography 函數(shù)是計(jì)算變換矩陣 # 參數(shù)cv2.RANSAC是使用RANSAC算法尋找一個(gè)最佳單應(yīng)性矩陣H,即返回值M # 返回值:M 為變換矩陣,mask是掩模 M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0) # 獲取img1的圖像尺寸 h,w = img1.shape # pts是圖像img1的四個(gè)頂點(diǎn) pts = np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2) # 計(jì)算變換后的四個(gè)頂點(diǎn)坐標(biāo)位置 dst = cv2.perspectiveTransform(pts,M) # 圖片替換 img3 = cv2.imread('aa.png',0) # 降維處理 b = np.int32(dst).reshape(4, 2) x,y = img2.shape # 根據(jù)變換矩陣將圖像img3進(jìn)行變換處理 res = cv2.warpPerspective(img3, M, (y,x)) img_temp = img2.copy() # 將圖像img2的替換區(qū)域進(jìn)行填充處理 cv2.fillConvexPoly(img_temp, b, 0) # 將變換后的img3圖像替換到圖像img2 cv2.imshow('bb',img_temp) res = img_temp + res cv2.imshow('aa',res) plt.imshow(res),plt.show()
運(yùn)行結(jié)果如圖所示:
從結(jié)果可以看到,替換后的圖像周邊出現(xiàn)黑色線條,這是正常的現(xiàn)象。在上圖最左邊的圖bb可以看到,黑色區(qū)域是由圖1和圖2檢測匹配所得的結(jié)果,如果匹配結(jié)果會(huì)存在一定的誤差,這個(gè)誤差是由多個(gè)因素所導(dǎo)致的。
在實(shí)際中,我們根據(jù)一張圖片在眾多的圖片中查找匹配率最高的圖片。如果按照上面的例子,也可以實(shí)現(xiàn),但每次匹配時(shí)都需要重新檢測圖片的特征數(shù)據(jù),這樣會(huì)導(dǎo)致程序運(yùn)行效率。因此,我們可以將圖片的特征數(shù)據(jù)進(jìn)行保存,每次匹配時(shí),只需讀取特征數(shù)據(jù)進(jìn)行匹配即可。我們以下圖為例:
我們根據(jù)圖1在圖2中查找最佳匹配的圖片。首先獲取圖2的全部圖片的特征數(shù)據(jù),將代碼保存在features.py:
import cv2 import numpy as np from os import walk from os.path import join def create_descriptors(folder): files = [] for (dirpath, dirnames, filenames) in walk(folder): files.extend(filenames) for f in files: if '.jpg' in f: save_descriptor(folder, f, cv2.xfeatures2d.SIFT_create()) def save_descriptor(folder, image_path, feature_detector): # 判斷圖片是否為npy格式 if image_path.endswith("npy"): return # 讀取圖片并檢查特征 img = cv2.imread(join(folder,image_path), 0) keypoints, descriptors = feature_detector.detectAndCompute(img, None) # 設(shè)置文件名并將特征數(shù)據(jù)保存到npy文件 descriptor_file = image_path.replace("jpg", "npy") np.save(join(folder, descriptor_file), descriptors) if __name__=='__main__': path = 'E:\\anchors' create_descriptors(path)
運(yùn)行代碼,結(jié)果如圖所示:
我們將圖片的特征數(shù)據(jù)保存在npy文件。下一步是根據(jù)圖1與這些特征數(shù)據(jù)文件進(jìn)行匹配,從而找出最佳匹配的圖片。代碼存在matching.py:
from os.path import join from os import walk import numpy as np import cv2 query = cv2.imread('tattoo_seed.jpg', 0) folder = 'E:\\anchors' descriptors = [] # 獲取特征數(shù)據(jù)文件名 for (dirpath, dirnames, filenames) in walk(folder): for f in filenames: if f.endswith("npy"): descriptors.append(f) print(descriptors) # 使用SIFT算法檢查圖像的關(guān)鍵點(diǎn)和描述符 sift = cv2.xfeatures2d.SIFT_create() query_kp, query_ds = sift.detectAndCompute(query, None) # 創(chuàng)建FLANN匹配器 index_params = dict(algorithm=0, trees=5) search_params = dict(checks=50) flann = cv2.FlannBasedMatcher(index_params, search_params) potential_culprits = {} for d in descriptors: # 將圖像query與特征數(shù)據(jù)文件的數(shù)據(jù)進(jìn)行匹配 matches = flann.knnMatch(query_ds, np.load(join(folder, d)), k=2) # 清除錯(cuò)誤匹配 good = [] for m, n in matches: if m.distance < 0.7 * n.distance: good.append(m) # 輸出每張圖片與目標(biāo)圖片的匹配數(shù)目 print("img is %s ! matching rate is (%d)" % (d, len(good))) potential_culprits[d] = len(good) # 獲取最多匹配數(shù)目的圖片 max_matches = None potential_suspect = None for culprit, matches in potential_culprits.items(): if max_matches == None or matches > max_matches: max_matches = matches potential_suspect = culprit print("potential suspect is %s" % potential_suspect.replace("npy", "").upper())
代碼運(yùn)行后,輸出結(jié)果如圖所示:
從輸出的結(jié)果可以看到,圖1與圖2的hush.jpg最為匹配,如圖所示:
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。