概述
本文主要内容来自参考资料《mage Alignment (Feature Based) using OpenCV (C++/Python)》
本文讲述使用opencv中基于特征的的图像对齐方法。这里将会把手机拍摄的表单和与表单的模板对齐。
这里用到的技术被称为基于特征的图像对齐,因为在这种技术中,要在一个图像中检测到一组特征点,并与另一张图像中的特征点相匹配。然后根据这些匹配的特征点计算出一个转换规则,从而将一个图像映射到另一个图像上。
什么是图像对齐或者图像配准
图像对齐(或者图像配准)可以扭曲旋转(其实是仿射变换)一张图使它和另一个图可以很完美的对齐。
下面是一个例子,中间的表在经过图像对齐技术处理之后,可以和左边的模板一样。对齐之后就可以根据模板的格式对用户填写的内容进行分析了。
图像对齐的应用场景
上面说的表单分析就是,先把拍摄照向模板对齐,易于下一步处理。
在一些医学应用中,可能需要把多次拍摄的照片拼接起来。
最有意思的应用应该是合成全景照片。
图像对齐:基础理论
图像对齐技术的核心是一种维数3X3的单应性矩阵(Homography )。参考目录里有篇资料单独讲了它的相关理论。下面是一些简略的介绍。
什么是单应性矩阵
要想使用单应性矩阵把同一个场景的两张照片联系起来,需要两个条件:
1. 两张照片中拍摄了同一个平面
2. 这两幅照片是通过旋转照相机的光学轴来拍摄的。我们在生成全景图的同时就是这么做的。
单应性矩阵就是一个简单的3X3的矩阵:
假设(x1,y1)是第一张图片上的点,(x2,y2)是第二张图片上的同一个物理点,那么使用单应性矩阵可以映射他们:
个人理解:把所像素上的所有图像都这么转换后,两张照片上的同一个物理平面就会呈现相同的角度和大小。
如何找到单应性矩阵
如果我们知道两张图片中4个或者更多对应的点,就可以使用opencv中的findHomography 来得到正两张图的单应性矩阵。
上图就是一个例子。
findHomography内部其实是通过解一组线性方程组来找到单应性矩阵的。
如何自动找到两张图片的对应点
很多机器视觉需要找到图片中一些神奇的具有一定稳定性的点。这些点被称为关键点或者特征点。OpenCV 中实现了好几种关键点的检测器(e.g. SIFT, SURF, and ORB)。由于SIFT和SURF收费,而ORB又快又准又免费,所以这里主要讲ORB。
下图中画圈的就是ORB关键点:
ORB的本质是带方向的FAST特征点和旋转的BRIEF描述子(yll:就是特征矩阵啦)。
一个特征点检测器由两部分组成:
1.定位器:这个模块要找到图片上具有旋转不变性、缩放不变性及仿射不变性的点。定位器找到这些点的x/y坐标。ORB的定位器被称为FAST。这种寻找特征点的算法一听就知道,很快嘛。
2.描述子:上一步只是找到特征点在哪,这一步要获得它们的外观编码来区分彼此。这样特征点就可以使用被称为描述子的一串数字来表示了。理想情况下,不同照片上对应的同一个物理点应该具有相同的描述子。ORB的描述子是一种改进的BRISK描述子.
由于只有知道两张图片特征点对应关系的情况下才能计算两图的单应性矩阵,所以我们需要一个算法来自动匹配两图的特征点。这个算法将会把两图的特征点一一比较。
opencv图像对齐代码
基于特征的图像对齐步骤
下面的代码进行图像对齐的步骤:
- 读取图片到内存。
- 检测特征点:为两张图检测ORB特征点。为了计算单应性矩阵4个就够了,但是一般会检测到成百上千的特征点。可以使用参数MAX_FEATURES 来控制检测的特征点的数量。检测特征点并计算描述子的函数是detectAndCompute。
- 特征匹配:找到两图中匹配的特征点,并按照匹配度排列,保留最匹配的一小部分。然后把匹配的特征点画出来并保存图片到磁盘。我们使用汉明距离来度量两个特征点描述子的相似度。下图中把匹配的特征点用线连起来了。注意,有很多错误匹配的特征点,所以下一步要用一个健壮的算法来计算单应性矩阵。
- 计算单应性矩阵:上一步产生的匹配的特征点不是100%正确的,就算只有20~30%的匹配是正确的也不罕见。findHomography 函数使用一种被称为随机抽样一致算法(Random Sample Consensus )的技术在大量匹配错误的情况下计算单应性矩阵。
- 扭转图片(Warping image):有了精确的单应性矩阵,就可以把一张图片的所有像素映射到另一个图片。warpPerspective 函数用来完成这个功能。
图像对齐的python源码
下面的源码可以正常运行。来自《Image Alignment (Feature Based) using OpenCV (C++/Python)》
from __future__ import print_function
import cv2
import numpy as np
MAX_FEATURES = 500
GOOD_MATCH_PERCENT = 0.15
def alignImages(im1, im2):
# Convert images to grayscale
im1Gray = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY)
im2Gray = cv2.cvtColor(im2, cv2.COLOR_BGR2GRAY)
# Detect ORB features and compute descriptors.
orb = cv2.ORB_create(MAX_FEATURES)
keypoints1, descriptors1 = orb.detectAndCompute(im1Gray, None)
keypoints2, descriptors2 = orb.detectAndCompute(im2Gray, None)
# Match features.
matcher = cv2.DescriptorMatcher_create(cv2.DESCRIPTOR_MATCHER_BRUTEFORCE_HAMMING)
matches = matcher.match(descriptors1, descriptors2, None)
# Sort matches by score
matches.sort(key=lambda x: x.distance, reverse=False)
# Remove not so good matches
numGoodMatches = int(len(matches) * GOOD_MATCH_PERCENT)
matches = matches[:numGoodMatches]
# Draw top matches
imMatches = cv2.drawMatches(im1, keypoints1, im2, keypoints2, matches, None)
cv2.imwrite("matches.jpg", imMatches)
# Extract location of good matches
points1 = np.zeros((len(matches), 2), dtype=np.float32)
points2 = np.zeros((len(matches), 2), dtype=np.float32)
for i, match in enumerate(matches):
points1[i, :] = keypoints1[match.queryIdx].pt
points2[i, :] = keypoints2[match.trainIdx].pt
# Find homography
h, mask = cv2.findHomography(points1, points2, cv2.RANSAC)
# Use homography
height, width, channels = im2.shape
im1Reg = cv2.warpPerspective(im1, h, (width, height))
return im1Reg, h
if __name__ == '__main__':
# Read reference image
refFilename = "form.jpg"
print("Reading reference image : ", refFilename)
imReference = cv2.imread(refFilename, cv2.IMREAD_COLOR)
# Read image to be aligned
imFilename = "scanned-form.jpg"
print("Reading image to align : ", imFilename);
im = cv2.imread(imFilename, cv2.IMREAD_COLOR)
print("Aligning images ...")
# Registered image will be resotred in imReg.
# The estimated homography will be stored in h.
imReg, h = alignImages(im, imReference)
# Write aligned image to disk.
outFilename = "aligned.jpg"
print("Saving aligned image : ", outFilename);
cv2.imwrite(outFilename, imReg)
# Print estimated homography
print("Estimated homography : \n", h)
用来对齐身份证
本人用上面的代码测试了对齐身份证的功能,反面是可以的,因为反面基本是固定不变的。
身份证正面无法对齐,因为每个人的信息不一样,错误的匹配点太多,无法正确计算单应性矩阵。
单应性矩阵的一个应用:虚拟广告牌
请参考参考资料里的《Opencv日常之Homography》和《Homography Examples using OpenCV ( Python / C ++ )》
下面两张图片是原图:
下面这张是合成:
参考资料
Image Alignment (Feature Based) using OpenCV (C++/Python)
Homography Examples using OpenCV ( Python / C ++ )