sift算法使用


實際項目中一般都直接使用封裝好的sift算法。以前為了用sift,都是用的舊版本:opencv-contib-python=3.4.2.17,現在sift專利過期了,新版的opencv直接可以使用sift算法,opencv-python==4.5.1版本測試可以使用。

sift算法理論部分參考前面文章:sift算法理解

關於sift,opencv中主要有這個幾個函數:

1.1 sift特征點檢測

cv2.SIFT_create()

創建sift對象,官方文檔:https://docs.opencv.org/4.5.3/d7/d60/classcv_1_1SIFT.html

sift = cv2.SIFT_create(nfeatures=0, nOctaveLayers=3, contrastThreshold=0.04, edgeThreshold=10, sigma=1.6)

參數:
	nfeatures: 需要保留的特征點的個數,特征按分數排序(分數取決於局部對比度)
	nOctaveLayers:每一組高斯差分金字塔的層數,sift論文中用的3。高斯金字塔的組數通過圖片分辨率計算得到
	contrastThreshold: 對比度閾值,用於過濾低對比度區域中的特征點。閾值越大,檢測器產生的特征越少。 (sift論文用的0.03,nOctaveLayers若為3, 設置參數為0.09,實際值為:contrastThreshold/nOctaveLayers)
	edgeThreshold:用於過濾掉類似圖片邊界處特征的閾值(邊緣效應產生的特征),注意其含義與contrastThreshold不同,即edgeThreshold越大,檢測器產生的特征越多(過濾掉的特征越少);sift論文中用的10;
	sigma:第一組高斯金字塔高斯核的sigma值,sift論文中用的1.6。 (圖片較模糊,或者光線較暗的圖片,降低這個參數)
	descriptorType:特征描述符的數據類型,支持CV_32F和CV_8U

返回值:sift對象(cv2.Feature2D對象)

cv2.Feature2D.detect()

檢測特征關鍵點,官方文檔:https://docs.opencv.org/4.5.3/d0/d13/classcv_1_1Feature2D.html#a8be0d1c20b08eb867184b8d74c15a677

keypoints = cv2.Feature2D.detect(image, mask)
參數:
	image:需要檢測關鍵點的圖片
	mask:掩膜,為0的區域表示不需要檢測關鍵點,大於0的區域檢測
返回值:
	keypoints:檢測到的關鍵點

cv2.Feature2D.compute()

生成特征關鍵點的描述符,官方文檔:https://docs.opencv.org/4.5.3/d0/d13/classcv_1_1Feature2D.html#a8be0d1c20b08eb867184b8d74c15a677

keypoints, descriptors = cv.Feature2D.compute(image, keypoints)
參數:
	image:需要生成描述子的圖片
	keypoints: 需要生成描述子的關鍵點
返回值:
	keypoints:關鍵點(原始關鍵點中,不能生成描述子的關鍵點會被移除;)
	descriptors:關鍵點對應的描述子
	

cv2.Feature2D.detectAndCompute()

檢測關鍵點,並生成描述符,是上面detect()和compute()的綜合

keypoints, descriptors = cv.Feature2D.detectAndCompute(image, mask)

cv2.drawKetpoints()

繪制檢測到的關鍵點,官方文檔:https://docs.opencv.org/4.5.3/d4/d5d/group__features2d__draw.html#ga5d2bafe8c1c45289bc3403a40fb88920

outImage = cv2.drawKeypoints(image, keypoints, outImage, color, flags)
參數:
	image:檢測關鍵點的原始圖像
	keypoints:檢測到的關鍵點
	outImage:繪制關鍵點后的圖像,其內容取決於falgs的設置
	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:只繪制匹配的關鍵點,單獨的關鍵點不繪制

flags的原始含義如下:

keypoint

https://docs.opencv.org/4.5.3/d2/d29/classcv_1_1KeyPoint.html#aea339bc868102430087b659cd0709c11

上述檢測到的keypoint,在opencv中是一個類對象,其具有如下幾個屬性:

angle: 特征點的方向,值在0-360
class_id: 用於聚類id,沒有進行聚類時為-1
octave: 特征點所在的高斯金差分字塔組
pt: 特征點坐標
response: 特征點響應強度,代表了該點時特征點的程度(特征點分數排序時,會根據特征點強度)
size:特征點領域直徑

descriptor

檢測點對應的descriptor,是一個128維的向量。

sift簡單使用

opencv中sift特征點檢測和繪制,使用代碼和結果如下:(opencv版本:opencv-python==4.5.1.48)

import cv2

img = cv2.imread(r"./lenna.png")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

sift = cv2.SIFT_create(nfeatures=0, nOctaveLayers=3, contrastThreshold=0.04, edgeThreshold=10, sigma=1.6)
keypoints, descriptors = sift.detectAndCompute(img_gray, None)

for keypoint,descriptor in zip(keypoints, descriptors):
    print("keypoint:", keypoint.angle, keypoint.class_id, keypoint.octave, keypoint.pt, keypoint.response, keypoint.size)
    print("descriptor: ", descriptor.shape)

img = cv2.drawKeypoints(image=img_gray, outImage=img, keypoints=keypoints,
                        flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS,
                        color=(51, 163, 236))

cv2.imshow("img_gray", img_gray)
cv2.imshow("new_img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

1.2 sift特征點匹配

通過sift得到圖片特征點后,一般會進行圖片之間的特征點匹配。

1. 匹配方法

opencv中的特征點匹配主要有兩種方法:BFMatcher,FlannBasedMatcher:

BFMatcher

官方文檔:https://docs.opencv.org/4.5.3/d3/da1/classcv_1_1BFMatcher.html

Brute Froce Matcher: 簡稱暴力匹配,意思就是嘗試所有可能匹配,實現最佳匹配。其繼承於類cv2.DescriptorMatcher,

matcher = cv2.BFMatcher(normType=cv2.NORM_L2, crossCheck=False)   # 創建BFMatcher對象

FlannBasedMatcher

官方文檔:https://docs.opencv.org/4.5.3/dc/de2/classcv_1_1FlannBasedMatcher.html

Flann-based descriptor matcher: 最近鄰近似匹配。 是一種近似匹配方法,並不追求完美,因此速度更快。 可以調整FlannBasedMatcher參數改變匹配精度或算法速度。其繼承於類cv2.DescriptorMatcher。

FLANN_INDEX_KDTREE = 0  # 建立FLANN匹配器的參數
indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)  # 配置索引,密度樹的數量為5
searchParams = dict(checks=50)  # 指定遞歸次數
matcher = cv2.FlannBasedMatcher(indexParams, searchParams)  # 建立FlannBasedMatcher對象

indexParams:

algorithm:
    FLANN_INDEX_LINEAR:	線性暴力(brute-force)搜索
    FLANN_INDEX_KDTREE:	隨機kd樹,平行搜索。默認trees=4
    FLANN_INDEX_KMEANS:	層次k均值樹。默認branching=32,iterations=11,centers_init = CENTERS_RANDOM, cb_index =0.2
    FLANN_INDEX_COMPOSITE:	隨機kd樹和層次k均值樹來構建索引。默認trees =4,branching =32,iterations =11,centers_init=CENTERS_RANDOM,cb_index =0.2

searchParams:

SearchParams (checks=32, eps=0, sorted=true)
    checks:	默認32
    eps:	默認為0
    sorted:	默認True

2. 繪制匹配

KMatch

官方文檔:https://docs.opencv.org/4.5.3/d4/de0/classcv_1_1DMatch.html

上述通過匹配方法得到的匹配,在opencv中都用KMatch類表示,其具有幾個屬性如下:

queryIdx:查詢點的索引
trainIdx:被查詢點的索引
distance:查詢點和被查詢點之間的距離

下面代碼中,每個點尋找兩個最近鄰匹配點,即對於kp1中的每個關鍵點,在kp2中尋找兩個和它距離最近的特征點,所以每個關鍵點產生兩組匹配,即兩個KMatch類。kp1相當於索引關鍵點,對應queryIdx; kp2相當於查詢關鍵點,對應trainIdx。

import cv2

img1 = cv2.imread("iphone1.png")
img2 = cv2.imread("iphone2.png")
sift = cv2.SIFT_create()

kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# 采用暴力匹配
matcher = cv2.BFMatcher()
matches = matcher.knnMatch(des1, des2, k=2)  # k=2,表示尋找兩個最近鄰

# 上面每個點尋找兩個最近鄰匹配點,即對於kp1中的每個關鍵點,在kp2中尋找兩個和它距離最近的特征點,所以每個關鍵點產生兩組匹配,即兩個KMatch類
# kp1相當於索引關鍵點,對應queryIdx; kp2相當於查詢關鍵點,對應trainIdx
for m in matches:  # 若尋找三個最近鄰點,則m包括三個KMacth
    print(m[0].queryIdx, m[0].queryIdx, m[0].distance)  # m[0]表示距離最近的那個匹配
    print(m[1].queryIdx, m[1].queryIdx, m[1].distance)  # m[1]表示距離第二近的那個匹配

參考:https://blog.csdn.net/wphkadn/article/details/85805105

drawMatches()

opencv中drawMatches()能繪制匹配特征點

官方文檔:https://docs.opencv.org/4.5.3/d4/d5d/group__features2d__draw.html#ga5d2bafe8c1c45289bc3403a40fb88920

outImg = cv2.drawMatches(img1, keypoints1, img2, keypoints2, matches1to2, outImg, matchColor, singlePointColor, matchesMask, flags)
參數:
	img1:圖像1
	keypoints1:圖像1的特征點
	img2:圖像2
	keypoints1:圖像2的特征點
	matches1to2:圖像1特征點到圖像2特征點的匹配,keypoints1[i]和keypoints2[matches[i]]為匹配點
	outImg: 繪制完的輸出圖像
	matchColor:匹配特征點和其連線的顏色,-1時表示顏色隨機
	singlePointColor:未匹配點的顏色,-1時表示顏色隨機
	matchesMask: mask決定那些匹配點被畫出,若為空,則畫出所有匹配點
	flags: 和上述cv2.drawKeypoints()中flags取值一樣

下面代碼中對sift提取的特征點進行匹配,采用暴力法匹配,並根據knn近鄰法中兩個鄰居的距離,篩選出了部分匹配點,代碼和結果如下:

import cv2
import numpy as np

#自己繪制匹配連線
def drawMatchesKnn_cv2(img1, kp1, img2, kp2, goodMatch):
    h1, w1 = img1.shape[:2]
    h2, w2 = img2.shape[:2]

    vis = np.zeros((max(h1, h2), w1 + w2, 3), np.uint8)
    vis[:h1, :w1] = img1
    vis[:h2, w1:w1 + w2] = img2

    p1 = [kpp.queryIdx for kpp in goodMatch]
    p2 = [kpp.trainIdx for kpp in goodMatch]
    post1 = np.int32([kp1[pp].pt for pp in p1])
    post2 = np.int32([kp2[pp].pt for pp in p2]) + (w1, 0)
    for (x1, y1), (x2, y2) in zip(post1, post2):
        cv2.line(vis, (x1, y1), (x2, y2), (0, 0, 255))
    cv2.imshow("match", vis)


img1 = cv2.imread("iphone1.png")
img2 = cv2.imread("iphone2.png")
sift = cv2.SIFT_create()

kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)

# 采用暴力匹配
matcher = cv2.BFMatcher()
matches = matcher.knnMatch(des1, des2, k=2)  # k=2,表示尋找兩個最近鄰

# 采用最近鄰近似匹配
# FLANN_INDEX_KDTREE = 0  # 建立FLANN匹配器的參數
# indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)  # 配置索引,密度樹的數量為5
# searchParams = dict(checks=50)  # 指定遞歸次數
# matcher = cv2.FlannBasedMatcher(indexParams, searchParams)  # 建立FlannBasedMatcher對象
# matches = matcher.knnMatch(des1, des2, k=2)  # k=2,表示尋找兩個最近鄰

h1, w1 = img1.shape[:2]
h2, w2 = img2.shape[:2]

out_img1 = np.zeros((max(h1, h2), w1 + w2, 3), np.uint8)
out_img1[:h1, :w1] = img1
out_img1[:h2, w1:w1 + w2] = img2
out_img1 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, out_img1)

good_match = []
for m, n in matches:
    if m.distance < 0.5*n.distance:    # 如果第一個鄰近距離比第二個鄰近距離的0.5倍小,則保留
        good_match.append(m)

out_img2 = np.zeros((max(h1, h2), w1 + w2, 3), np.uint8)
out_img2[:h1, :w1] = img1
out_img2[:h2, w1:w1 + w2] = img2
# p1 = [kp1[kpp.queryIdx] for kpp in good_match]  # kp1中挑選處的關鍵點
# p2 = [kp2[kpp.trainIdx] for kpp in good_match]  # kp2中挑選處的關鍵點
out_img2 = cv2.drawMatches(img1, kp1, img2, kp2, good_match, out_img2)
# drawMatchesKnn_cv2(img1, kp1, img2, kp2, good_match)


cv2.imshow("out_img1", out_img1)
cv2.imshow("out_img2", out_img2)
cv2.waitKey(0)
cv2.destroyAllWindows()

參考:https://www.cnblogs.com/wangguchangqing/p/4333873.html

https://blog.csdn.net/claroja/article/details/83411108


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM