python opencv入门 特征点匹配+单应性查找目标(39)

内容来自OpenCV-Python Tutorials 自己翻译整理

目标:
我们将结合特征点匹配和寻找单应性的方法,使用calib3d模块在复杂的图像当中寻找已知目标。

基础:


这里简单说一下什么事单应变换,如果有说的不对,还请各位看官斧正。

一般来讲,二维的图像变换可以分成这几类

  • 等距变换:简单的说就是对一个图像使用旋转、平移等操作。对应的矩阵也是旋转平移的矩阵
  • 相似变换:把等距变换再加上一个尺度,支持对图像按比例的放大和缩小
  • 仿射变换:仿射变换可以看成是旋转变换+非均匀缩放的复合,这个非均匀不是随意的,也是按照系数来实现的,例如上初中时候学到的斜二测+上个旋转,就是仿射变换。单应变换和仿射变换很像,不过二者自由度不同,而且单应变换使用的是齐次坐标,可以产生透视的效果。可以认为单应变换包含仿射变换,或者粗暴的理解为仿射变换加上“近大远小”的效果。
  • 射影变换:它是建立在齐次坐标系上的,也就是能产生“近大远小”的效果。有八个自由度,与单应变换相当接近了
  • 二维单应变换:把相似变换、仿射变换、射影变换叠加在一起,使得单应变换具有前三个变换的性质。

综上,简答的来理解单应变换,就是相当于你用一个照相机,正对着一个物体照一张照片。然后你再换个角度,换个距离,换个一个高度对着这个物体再拍一张。那么,你可以用单应变换找到这两张图片对应关系。


上节课我们使用了一个查询图像找到了它的一些特征点,我们又使用另外一个训练图像,同样找到了一些特征点,并且找出他们之间的最佳匹配。
简单的说,我们在另外一组图片当中,找到了一个目标图片的部分位置。这个信息足够在训练图片中精确的找到目标图片当中的对象了。

为了达到上面的目的,我们使用一个calib3d的模块,其中有cv2.findHomography()函数。如果我们传入一组两个图像对应的点集,它会自动找到对应透视变换的目标对象。然后我们可以使用cv2.perspectiveTransform() 函数来找到对应目标。参数至少需要四个正确的对应点来找到这个变换。

我们会看到在匹配的过程中,可能出现一些误差,这些误差可能会影响结果。为了解决这个问题,算法使用了RANSAC(随机抽样一致算法)或者LEAST_MEDIAN(最小中位数平方法?),这两个方法可以通过设定参数来决定使用哪一个。所以一个优秀的匹配点会提供一个正确的估计结果,这个结果叫做内点(inliers),剩下的点被称为外点。cv2.findHomography()返回一个蒙版,可以指定内点和外点。

代码:

首先使用SIFT算法找到特征点,然后使用比值测试找到最佳匹配。

import numpy as np
import cv2 
from matplotlib import pyplot as plt

MIN_MATCH_COUNT = 10

img1 = cv2.imread('1.jpg',0)
img2 = cv2.imread('26.jpg',0)

sift = cv2.xfeatures2d.SIFT_create()

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

FLANN_INDEX_KDTREE = 0#kd树
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary

flann = cv2.FlannBasedMatcher(index_params,search_params)

matches = flann.knnMatch(des1,des2,k=2)


# store all the good matches as per Lowe's ratio test.

good = []
for m,n in matches:
    if m.distance < 0.7*n.distance:
        good.append(m)

现在我们要设定一个条件,至少要找到十个匹配才能开始寻找目标(设定MIN_MATCH_COUNT参数)。否则显示信息没有足够的匹配点。

如果找到了足够的匹配点,我们可以在两幅图像中,提取匹配到的关键点的位置信息。将他们当做参数传递给要寻找对应透视变换的算法。一旦我们得到的了这个3×3的变换矩阵,我们就可以使用它来变换查询图片的角点信息来对应到训练图片的角点信息。然后把它们画出来。

if len(good)>MIN_MATCH_COUNT:
    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)

    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
    matchesMask = mask.ravel().tolist()

    h,w = img1.shape
    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,3, cv2.LINE_AA)

else:
    print("Not enough matches are found - %d/%d" % (len(good),MIN_MATCH_COUNT))
    matchesMask = None

最后画出我们的“内点”(如果成功找到目标)或者匹配点(失败)
原图:

python opencv入门 特征点匹配+单应性查找目标(39)_第1张图片
python opencv入门 特征点匹配+单应性查找目标(39)_第2张图片

python opencv入门 特征点匹配+单应性查找目标(39)_第3张图片

import numpy as np
import cv2 
from matplotlib import pyplot as plt


MIN_MATCH_COUNT = 10

img1 = cv2.imread('a.jpg',0)
img2 = cv2.imread('b.jpg',0)

sift = cv2.xfeatures2d.SIFT_create()

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

FLANN_INDEX_KDTREE = 0#kd树
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary

flann = cv2.FlannBasedMatcher(index_params,search_params)

matches = flann.knnMatch(des1,des2,k=2)


# store all the good matches as per Lowe's ratio test.

good = []
for m,n in matches:
    if m.distance < 0.7*n.distance:
        good.append(m)

if len(good)>MIN_MATCH_COUNT:
    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)

    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
    matchesMask = mask.ravel().tolist()

    h,w = img1.shape
    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,3, cv2.LINE_AA)

else:
    print("Not enough matches are found - %d/%d" % (len(good),MIN_MATCH_COUNT))
    matchesMask = None

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

你可能感兴趣的:(python opencv入门 特征点匹配+单应性查找目标(39))