使用Python-OpenCV进行RANSAC SIFT影像特征匹配
首先读入图片
import cv2
import numpy as np
img1=cv2.imread("./picture1/picture1/1/14x00424.JPG")
img2=cv2.imread("./picture1/picture1/1/14x00425.JPG")
然后识别影像特征点及其特征点描述子
sift = cv2.SIFT_create(1000)
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
构建匹配器,执行KNN匹配。
bf=cv2.BFMatcher()
matches = bf.knnMatch(des1,des2,k=2)
"""
这里有个大坑,当你想这里直接画出所有的匹配时,
这里面的matches返回的是最近距离和次近距离的两个Dmacther元组。
如下图,直接用drawMatches函数时,matches1 to matches2就会是含有两个值的元组,然后出现BUG。
"""
但是在后续粗匹配时,我们又需要最近邻次距离之比做粗匹配筛选错点。如果不需要,建议直接用matches直接做暴力匹配。
但是,为了同时对比未筛选匹配和粗筛之后的匹配,我这里直接在近次邻中提取了最近距离。
# 画出暴力匹配初步匹配后的所有匹配
matches1=[]
for i in matches:
matches1.append(i[0])
img_matches=cv2.drawMatches(img1,kp1,img2,kp2,matches1,None,flags=2)
然后根据最近邻次比做粗筛,画出粗筛之后的匹配。
good_matches=[]
for m,n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
img_good_matches=cv2.drawMatches(img1,kp1,img2,kp2,good_matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
仔细观察依旧存在很多误匹配的点。
这时,我们采用RANSAC算法进行最后的精匹配。
MIN_MATCH_COUNT = 10
if len(good_matches)>MIN_MATCH_COUNT:
src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 4.0)
matchesMask = mask.ravel().tolist()
ransac = []
for i in range(0,len(matchesMask)):
if matchesMask[i]==1:
# print(i)
ransac.append(good_matches[i]) #单独取出内点
print(ransac)
"""
这里的cv2.findHomography()返回值有两个.
第一个是变换所需要的单应矩阵M,
第二个返回的是一个掩膜值mask,这个掩模确定了RANSAC算法筛选之后的内点和外点。
"""
可以发现这个掩膜的值为0或者1,掩膜通常是一个与原始图像大小相同的二值或布尔图像,其中,选定的区域被标记为1(或True),而其余区域被标记为0(或False)。也就是值为1所对应的good_matches是筛选之后的内点。
最后输出精匹配的成果图即可。
完整代码如下:
import cv2
import numpy as np
img1=cv2.imread("./picture1/picture1/1/14x00424.JPG")
img2=cv2.imread("./picture1/picture1/1/14x00425.JPG")
sift = cv2.SIFT_create(1000)
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
bf=cv2.BFMatcher()
matches = bf.knnMatch(des1,des2,k=2)
# 画出暴力匹配初步匹配后的所有匹配
matches1=[] # 最近距离
for i in matches:
matches1.append(i[0])
img_matches=cv2.drawMatches(img1,kp1,img2,kp2,matches1,None,flags=2)
good_matches=[]
for m,n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
img_good_matches=cv2.drawMatches(img1,kp1,img2,kp2,good_matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
MIN_MATCH_COUNT = 10
if len(good_matches)>MIN_MATCH_COUNT:
src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 4.0)
matchesMask = mask.ravel().tolist()
ransac = [] # 筛选出RANSAC之后的内点
for i in range(0,len(matchesMask)):
if matchesMask[i]==1:
# print(i)
ransac.append(good_matches[i])
print(ransac)
# h,w = img1.shape[:2]
# pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]
# ).reshape(-1, 1, 2)
# dst = cv2.perspectiveTransform(pts, M)
# # img2 = cv2.polylines(img2, [np.int32(dst)], True, 255, 5, cv2.LINE_AA)
img_ransac=cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None)
"未经过筛选的匹配"
cv2.namedWindow("matches1",cv2.WINDOW_NORMAL)
cv2.imshow("matches1",img_matches)
cv2.imwrite("img_matches.jpg",img_matches)
"粗筛之后的匹配"
cv2.namedWindow("good_matches",cv2.WINDOW_NORMAL)
cv2.imshow("good_matches",img_good_matches)
cv2.imwrite("./good_matches.jpg",img_good_matches)
"使用RANSAC算法筛选过后的匹配"
ransac_result=cv2.drawMatches(img1,kp1,img2,kp2,ransac,None,flags=2)
cv2.namedWindow("ransac_result",cv2.WINDOW_NORMAL)
cv2.imshow("ransac_result",ransac_result)
cv2.imwrite("./ransac_result.jpg",ransac_result)
cv2.waitKey(0)
cv2.destroyAllWindows()
作者:沙丁鱼意大利面