得到特徵點後,我們可以進行特徵點匹配,找出兩張圖片中相同的特徵點。也就是計算特徵點 descriptor 之間的相似度,找出最相似的特徵點。
例如,計算兩個向量之間的相似度,可以用 L2 距離 (Euclidean distance):
def compute_distance(x1, x2):
return np.linalg.norm(x1 - x2)
或是 consine 相似度,也就是兩個向量的夾角:
def compute_similarity(x1, x2):
x1 = x1 / np.linalg.norm(x1)
x2 = x2 / np.linalg.norm(x2)
return np.dot(x1, x2)
計算匹配度最簡單的方式就暴力計算,使用兩層迴圈,對於每一個特徵點 descriptor,計算它和另一張圖片中所有特徵點 descriptor 的相似度,找出距離最短(相似度最高的):
for i, x1 in enumerate(descriptors1):
matched_index = -1
min_distance = np.inf
for j, x2 in enumerate(descriptors2):
similarity = compute_similarity(x1, x2)
if similarity > min_distance:
min_distance = similarity
matched_index = j
用這種暴力方法雖然比較沒有效率,但是可以很直接看到所有特徵點之間的相似度,例如以下是第一張圖中一個特徵點和第二張圖中多個特徵點的相似度,紅色代表越相似,藍色代表越不相似:
用 opencv 實作特徵點匹配也很簡單,只需使用 cv2.BFMatcher
,並且可以指定想要使用的相似度計算方式,常見的是使用 cv2.NORM_HAMMING
,速度上較快:
def extract_feature_and_match(image1, image2):
gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
keypoints1, descriptors1 = detcet_features(gray1, "orb")
keypoints2, descriptors2 = detcet_features(gray2, "orb")
print(f"Detected {len(keypoints1)} keypoints in frame 1")
print(f"Detected {len(keypoints2)} keypoints in frame 2")
image1 = cv2.drawKeypoints(image1, keypoints1, None)
image2 = cv2.drawKeypoints(image2, keypoints2, None)
# Match the descriptors
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(descriptors1, descriptors2)
print(f"Found {len(matches)} matches")
out_image = cv2.drawMatches(image1, keypoints1, image2, keypoints2, matches, None)
cv2.imwrite("matches.png", out_image)
可以利用 cv2.drawMatches
畫出匹配的特徵點,就會得到以下視覺化的結果:
當然這當中會有很多錯誤匹配,尤其是一些並沒有在兩張圖中都出現的內容,因此我們之後需要透過一些方式過濾掉這些錯誤的匹配。