1 基本概念
Homegraphy概念
考虑 同一个平面(比如书皮)的两张图片,红点表示同一个物理坐标点在两张图片上的各自位置。在 CV 术语中,我们称之为对应点。
Homography 就是将一张图像上的点映射到另一张图像上对应点的3x3变换矩阵。
对于图中的一对儿对应点,位于图一的 和 位于图二的 , H 把二者映射关系建立起来:
对于所有的对应点,只要它们都位于同一个物理平面上,上述 Homography 就是成立的。换句话说,就是可以把图一中书皮上的所有点都映射到图二的书皮上,也就是看起来,图一中的书皮和图二中的书皮对齐了!
那么对于不在此平面上的点呢?这时再应用 Homography 就无法再对齐到对应点了。比如 上图中的 桌面,地面,橱柜面。对于这种图像中有多个平面的情况,我们就需要针对每一个平面使用其对应的Homography了。
如何计算 Homography?
对于 H 矩阵,一般设 , 所以 H 有 8 个未知参数。至少需要8 个等式才能求解。而一组对应点可以提供 2 个等式,所以,至少需要 4 组对应点(任意三点不共线)来求得 H 。 如果有更多组对应点,效果更佳。
OpenCV 可以鲁棒地计算出一个最好地拟合所有对应点的 Homography。通常,图像间的这些对应点通过 SIFT 或者 SURF 这样算法进行自动特征提取和匹配。当然,对于简单的demo,手动选取对应点就足够了。
2 cv2.findHomegraphy()寻找匹配上的关键点的变换
作用:寻找单应性矩阵
原型:
retv,mask=cv2.findHomography(srcPoints,dstPoints[,method[,ransacReprojThreshold]])
参数:
- retv为返回的转换矩阵。
- mask为返回的查询图像在训练图像中的最佳匹配结果掩模。
- srcPoints, dstPoints为查询图像匹配结果的坐标。
- method为用于计算透视转换矩阵的方法。
- ransacReprojThreshold为可允许的最大重投影误差。
3 cv2.perspectiveTransform() 映射点
作用:
原型:dst=cv2.perspectiveTransform(src,m)
参数:
- src为输入的2通道或3通道浮点类型的数组。
- m是大小为3X3或4X4的浮点类型的转换矩阵,如使用cv2.findHomography()函数返回的转换矩阵。
dst为输出结果数组,大小和类型与src相同。
4 示例
import cv2
import numpy as np
import matplotlib.pyplot as plt# 【1、读入图像】
img1 = cv2.imread('C:/Users/xxx/Downloads/lena.jpg')
# 【1.1 从原图中扣出图像作为匹配图像】
img2 = np.zeros(img1.shape, dtype="uint8") # 生成image大小的全黑图
img2[250:300,400:600,:]=img1[250:300,400:600,:]
# 【2、灰度处理】
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# 【3、检测关键点】
orb=cv2.ORB_create()#创建ORB检测器
kp1, des1 = orb.detectAndCompute(gray1,None) #des1是描述子
kp2, des2 = orb.detectAndCompute(gray2,None) #des2是描述子
# 【4、匹配】
bf=cv2.BFMatcher_create(cv2.NORM_HAMMING,crossCheck=True)#创建匹配器
ms=bf.match(des1,des2)#执行特征匹配
ms=sorted(ms,key=lambda x:x.distance)#按距离排序
matchesMask=None
if len(ms)>10:#在有足够数量的匹配结果后,才计算查询在训练图像中的位置
#计算查询图像匹配结果的坐标
querypts=np.float32([kp1[m.queryIdx].pt for m in ms]).reshape(-1,1,2)
#计算训练图像匹配结果的坐标
trainpts = np.float32([kp1[m.queryIdx].pt for m in ms]).reshape(-1, 1, 2)
#执行查询图像和训练图像的透视变换
retv,mask=cv2.findHomography(querypts,trainpts,cv2.RANSAC)
#计算最佳匹配结果的掩模,用于绘制匹配结果
matchesMask=mask.ravel().tolist()
h,w,c=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,retv)
#用白色矩形在训练图像中绘制出查询图像的范围
img2=cv2.polylines(img2,[np.int32(dst)],True,(255,255,255),5)
img3=cv2.cv2.drawMatches(img1,kp1,img2,kp2,ms,None,
matchColor=(0,255,0),
singlePointColor=None,
matchesMask=matchesMask,
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
plt.figure(figsize=(19.2,14.4))
plt.imshow(img3)
plt.axis('off')
plt.show()