📝个人主页🌹:Eternity._
🌹🌹期待您的关注 🌹🌹
❀ 进行不同视角图像的拼接
- 背景描述
- 算法简介
- SIFT算法原理
- 代码原理
- 代码部署
- 核心代码
- 拼接结果
- 其他的图片如何进行拼接?
修改内容:
1、将附件中的“数据集”选项进行了删除;
2、对代码的原理进行更加详细的说明,修改步骤(1)和(2),对表意不明“组件”一词进行了更正,原理描述更加清晰。
本文所涉及的所有资源的获取方式:这里
背景描述
首先来看这样两幅图:
图1 一栋大楼的左半部分
图2 一栋大楼的右半部分
在很多情况下,我们往往会拍摄一些有重叠但是并不完整的图片。
无论对于日常的娱乐需求还是一些专业性的测绘工作,都需要我们基于这些“残缺”的图片来构建出完整的图像。
因此,本人根据先前图像处理的经验,使用一种基于SIFT特征匹配的算法来实现此任务。
算法简介
SIFT(Scale Invariant Feature Transform,尺度不变特征变换匹配算法),是由David G. Lowe在《Object Recognition from Local Scale-Invariant Features》提出的高效区域检测算法,在2004年的《Distinctive Image Features from Scale-Invariant Keypoints》中得以完善。
其主要特点如下:
(1)尺度不变性:SIFT算法可以在不同尺度的图像上检测和描述特征。这使得它对于图像中存在不同尺度的物体或图像的缩放变换具有鲁棒性。
(2)旋转不变性:SIFT算法可以检测和描述在不同旋转角度下的特征。这使得它对于图像中存在旋转变换的物体具有鲁棒性。
(3)对光照变化和视角变化具有一定的鲁棒性。
(4)提取特征:SIFT算法通过在图像中检测局部特征点(例如边缘、角点等)来提取特征。它使用一种称为DoG(Difference of Gaussian)的算法来检测图像中的特征点。
(5)描述特征:对于每个检测到的特征点,SIFT算法计算其周围区域的特征描述符,该描述符是一种对特征点的局部图像区域进行编码的向量。这些描述符对于不同的特征点具有唯一性,可以用于特征匹配和识别。
(6)特征匹配和识别:通过比较不同图像中的特征点的描述符,可以进行特征匹配和识别。SIFT算法使用一种称为RANSAC(Random Sample Consensus)的算法来寻找在多个图像中匹配的特征点,从而可以进行目标跟踪和图像配准等任务。
总而言之,SIFT算法是一种强大的图像特征提取算法,它可以在不同尺度和旋转角度下提取具有尺度不变性和旋转不变性的局部特征,并用于图像匹配、目标跟踪、图像配准等计算机视觉任务。
SIFT算法原理
这一部分简要介绍SIFT算法的执行流程。
- 检测尺度空间极值
图3 DOG算法示意图
-
关键点的精确定位
以上方法检测到的极值点是离散空间的极值点,以下通过拟合三维二次函数来精确确定关键点的位置和尺度,同时去除低对比度的关键点和不稳定的边缘响应点(因为DOG算子会产生较强的边缘响应),以增强匹配稳定性、提高抗噪声能力。 -
方向匹配
通过根据局部图像属性为每个关键点分配一致的方向,可以相对于该方向表示关键点描述符,从而实现图像旋转的不变性。 -
局部图像描述
之前的操作已经为每个关键点分配了图像位置、比例和方向。这些参数强加了一个可重复的局部 2D 坐标系,在其中描述局部图像区域,因此为这些参数提供了不变性。下一步是计算局部图像区域的描述符,该描述符具有高度独特性,对剩余变化(例如照明或 3D 视点的变化)尽可能保持不变
代码原理
为实现SIFT特征检测,主要使用到了以下的两个工具包:OpenCV,numpy。其中OpenCV是一个非常知名且受欢迎的跨平台计算机视觉库,它不仅包含常用的图像读取、显示、颜色变换,还包含一些为人熟知的经典特征检测算法,其中就包括SIFT,所以本文使用OpenCV进行读取和SIFT特征检测。numpy是一个非常优秀的数值计算库,也常用于图像的处理,这里使用numpy主要用于图像的拼接和显示。
代码的具体实现逻辑如下:
(1)首先先读入待拼接的图像,例如下述代码示例中的’hanying1.jpg’和’hanying2.jpg’,然后使用opencv自带的cv2.SIFT_create()创建SIFT对象,用于计算每幅图像的特征点和特征描述符。
(2)然后对各幅图像生成的特征描述符使用Flann算法进行匹配,并筛选出匹配结果较好的特征点用于下述单应性矩阵的计算。
(3)单应矩阵估计:通过至少4对匹配的关键点对,可以使用RANSAC(随机抽样一致性)算法估计出单应矩阵H。RANSAC算法通过随机选择一部分关键点对来估计H,并通过计算其他关键点对与估计的H之间的误差,筛选出符合约束条件的关键点对,并最终得到较好的单应矩阵估计。
(4)图像视点变换:对于每个视点的图像,使用估计得到的单应矩阵H进行变换。对于输出图像的每个像素点,通过逆变换将其映射回到原始视点的坐标系中。具体来说,对于输出图像中的每个像素点(x, y),通过矩阵乘法运算得到变换后的坐标。然后,根据相应的像素值进行插值,得到变换后的像素值。
(5)图像拼接:将变换后的图像按照一定的拼接规则进行拼接,以生成最终的全景图像或多视点切换图像。拼接规则可以根据具体需求来确定,常见的方法包括:重叠区域的像素平均值、像素最大值、图像融合等。
代码部署
需要导入的核心类库为opencv和numpy
import cv2
import numpy as np
核心代码
首先使用cv2.SIFT_create()创建SIFT特征检测器对象,然后就需要将图片输入到特征描述对象中,用于检测相应的特征点和计算相应的特征描述符,具体代码如下:
# 在每个图像中检测特征点和计算描述符
keypoints = []
descriptors = []
for image in images:
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
kp, des = sift.detectAndCompute(gray, None)
keypoints.append(kp)
descriptors.append(des)
# 特征点匹配
matcher = cv2.FlannBasedMatcher()
matches = []
for i in range(len(descriptors)-1):
matches.append(matcher.knnMatch(descriptors[i], descriptors[i+1], k=2))
# 筛选匹配点
good_matches = []
for match in matches:
good = []
for m, n in match:
if m.distance < 0.7 * n.distance:
good.append(m)
good_matches.append(good)
# 计算单应矩阵
homographies = []
for i, match in enumerate(good_matches):
if len(match) >= 4:
src_pts = np.float32([keypoints[i][m.queryIdx].pt for m in match]).reshape(-1, 1, 2)
dst_pts = np.float32([keypoints[i+1][m.trainIdx].pt for m in match]).reshape(-1, 1, 2)
H, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 2.0)
homographies.append(H)
# 拼接图像
result = images[0]
for i in range(len(images)-1):
result = cv2.warpPerspective(result, homographies[i], (result.shape[1]+images[i+1].shape[1], result.shape[0]))
result[0:images[i+1].shape[0], 0:images[i+1].shape[1]] = images[i+1]
拼接结果
图4 最终拼接结果(由于拍摄时间原因,亮度存在一定差异)
其他的图片如何进行拼接?
本博客提供的两张图片为作者自行拍摄,如果读者想要拼接自己拍摄的图片,只需将代码里相应的图片路径和名称更换为自己本地的图片路径和名称即可。如果不愿添加路径信息,仅需将自己的图片放置在代码工程下,修改为图片的名称即可。
例如我重新对如下两幅图进行拼接:
只需将其放入代码工程后,在下图相应位置改为图片名即可。
拼接结果如下图:
编程未来,从这里启航!解锁无限创意,让每一行代码都成为你通往成功的阶梯,帮助更多人欣赏与学习!
更多内容详见:这里