随机抽样一致算法(RANdom SAmple Consensus,RANSAC),采用迭代的方式从一组包含离群的被观测数据中估算出数学模型的参数。RANSAC算法被广泛应用在计算机视觉领域和数学领域,例如直线拟合、平面拟合、计算图像或点云间的变换矩阵、计算基础矩阵等方面。
RANSAC算法假设数据中包含正确数据和异常数据(或称为噪声)。正确数据记为局内点(inliers),异常数据记为外点(outliers),也是异常值。同时RANSAC也假设,给定一组正确的数据,存在可以计算出符合这些数据的模型参数的方法。该算法核心思想就是随机性和假设性,随机性是根据正确数据出现概率去随机选取抽样数据,根据大数定律,随机性模拟可以近似得到正确结果。假设性是假设选取出的抽样数据都是正确数据,然后用这些正确数据通过问题满足的模型,去计算其他点,然后对这次结果进行一个评分。
RANSAC算法的输入是一组观测数据(往往含有较大的噪声或无效点),一个用于解释观测数据的参数化模型以及一些可信的参数。假设观测数据中包含局内点和局外点,其中局内点近似的被直线所通过,而局外点远离于直线。简单的最小二乘法不能找适应于局内点的直线,原因是最小二乘法尽量去适应包括局外点在内的所有点,相反,RANSA能得出一个仅仅用局内点计算出模型,并且概率还足够高,但是不能保证结果一定正确。
RANSAC通过反复选择数据中的一组随机子集来达成目标。被选取的子集被假设为局内点,并用下述方法进行验证:①有一个模型适应于假设的局内点,即所有的未知参数都能从假设的局内点计算得出。②用上步得到的模型去测试所有的其它数据,如果某个点适用于估计的模型,认为它也是局内点。③如果有足够多的点被归类为假设的局内点,那么估计的模型就足够合理。④然后,用所有假设的局内点去重新估计模型,因为它仅仅被初始的假设局内点估计过。⑤最后,通过估计局内点与模型的错误率来评估模型。
上述过程被重复执行固定的迭代次数,每次产生的模型要么因为局内点太少而被舍弃,要么因为比现有的模型更好而被选用。
RANSAC算法在消除图像误匹配中是寻找一个最佳单应性矩阵H,RANSAC算法从匹配数据集中随机抽出4个样本并保证这四个样本之间不共线。然后利用这个模型测试所有数据,并计算满足这个模型数据点的个数与投影误差(即代价函数)若此模型为最优模型。
import cv2
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image
import os
from pylab import *
# 加载图像
img_left = cv2.imread('F:\work\CV\ExmCode\ex3\img01.jpg')
img_right = cv2.imread('F:\work\CV\ExmCode\ex3\img02.jpg')
# 将图像转为灰度图
gray_left = cv2.cvtColor(img_left, cv2.COLOR_BGR2GRAY)
gray_right = cv2.cvtColor(img_right, cv2.COLOR_BGR2GRAY)
# 创建SIFT对象
sift = cv2.SIFT_create()
# 在图像中找到关键点和描述符
kp_left, des_left = sift.detectAndCompute(gray_left, None)
kp_right, des_right = sift.detectAndCompute(gray_right, None)
# 创建BFMatcher对象
bf = cv2.BFMatcher()
# 使用KNN匹配
matches = bf.knnMatch(des_left, des_right, k=2)
# 根据Lowe's ratio test筛选匹配点
good_matches = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good_matches.append(m)
# 获取匹配点的坐标
pts_left = np.float32([kp_left[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
pts_right = np.float32([kp_right[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)
# 利用匹配点进行透视变换
M, _ = cv2.findHomography(pts_right, pts_left, cv2.RANSAC, 5.0)
# 对右侧图像进行透视变换
result = cv2.warpPerspective(img_right, M, (img_left.shape[1] + img_right.shape[1], img_left.shape[0]))
# 拼接图像
result[0:img_left.shape[0], 0:img_left.shape[1]] = img_left
# 显示结果
print(result.shape)
figure()
imshow(result)
show()
cv2.waitKey()
cv2.destroyAllWindows()
唯一不同的是在利用cv2.findHomography()
函数计算变换矩阵时,添加了cv2.RANSAC
参数以启用RANSAC算法进行优化
import cv2
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image
import os
from pylab import *
# 读取待拼接的两张图像
img1 = cv2.imread('F:\work\CV\ExmCode\ex3\img01.jpg')
img2 = cv2.imread('F:\work\CV\ExmCode\ex3\img02.jpg')
# SIFT特征点检测器
sift = cv2.SIFT_create()
# 检测关键点并计算描述符
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# 特征点匹配器
matcher = cv2.BFMatcher()
# KNN匹配结果
knn_matches = matcher.knnMatch(des1, des2, k=2)
# 过滤不满足条件的匹配
good_matches = []
for m, n in knn_matches:
if m.distance < 0.5 * n.distance:
good_matches.append(m)
# 获取匹配关键点的坐标
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)
# 利用RANSAC算法优化关键点匹配
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
# 获取第一张图像的高度和宽度
h1, w1 = img1.shape[:2]
# 将第二张图像进行透视变换,得到配准后的图像
aligned_img = cv2.warpPerspective(img2, M, (w1 + w1, h1))
# 将两张图像进行拼接
aligned_img[:h1, :w1] = img1
# 显示拼接结果
print(aligned_img.shape)
figure()
imshow(aligned_img)
show()
总结:这里单对这两张图片的优化前的拼接结果与优化后的拼接结果对比。可以看出,使用RANSAC算法优化关键点匹配后,拼接效果视觉上变差了,这个实验结果受实验数据影响比较大,下图是优化前后的匹配点对比,可见原因是过滤不满足条件的匹配点过多,导致匹配点变得很少。