使用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()

作者:沙丁鱼意大利面

物联沃分享整理
物联沃-IOTWORD物联网 » 使用Python-OpenCV进行RANSAC SIFT影像特征匹配

发表回复