最近学习了图像拼接的一些知识,在这里记录一下,方便以后的学习, 博客中的代码均基于python,目前只能用于左右拼接
基于sift特征点的图像拼接包括以下几个步骤:
1.sift特征点的提取
2.利用快速最近邻算法进行特征点粗匹配,并利用阈值设置和双向交叉检查方法进行初步的筛选
3.利用RANSAC 算法进行精匹配
4.图像变换,使不同图像映射到同一坐标系下
5.图像融合,融合规则包括取大,最佳缝合线发,Laplace金字塔法

1.sift特征点的提取
sift特征点的提取原理网上有很多的介绍,在这里不再赘述
具体看参考网址:https://blog.csdn.net/lingyunxianhe/article/details/79063547
在python中,已经集成了sift特征点提取的算法

# 创建sift特征提取器
sift = cv2.xfeatures2d.SIFT_create()
# 计算sift特征点的关键点和相应的描述子。注意,img为灰度图像
keypoints, des = sift.detectAndCompute(img, None)

keypoints参数是一个关键字列表,有以下几个属性:
pt: 表示图像中关键点的X坐标和Y坐标
size: 表示特征的直径
angle:表示特征的方向
response:表示关键点的强度。某些特征会通过SIFT来分类,因为它得到的特征比其他特征更好,通过查看response属性可以评估特征强度
octave:表示特征所在金字塔的层级。SIFT算法与人脸检测算法类似,即只通过改变计算参数来依次处理相同的图像。例如算法在每次迭代(octave)时,作为参数的图像尺寸和相邻像素都会发生变化,因此,octave属性表示的是检测到的关键点所在的层级。
class_id: 表示关键点的ID

2.利用快速最近邻算法进行特征点粗匹配,并利用阈值设置和双向交叉检查方法进行初步的筛选

快速最近邻算法特征匹配算法使用快速近似最近邻搜索算法寻找,适合在大量数据中查找匹配图像
另一种常用的匹配算法是暴力匹配法(Brute-force match)会尝试所有可能的匹配,使得它总能够找到最佳匹配同时导致消耗的时间远大于快速最近邻算法,Flann匹配的速度远远快于Blute-Force,大约是10倍,因此只适合于少量图像的匹配
具体实现:
1.快速最近邻算法特征匹配算法实现步骤:利用K-D 树( KDimensional Tree)数据结构来有序存放特征描述向量,再用快速近似 k 最近邻(FLANN)算法找出最近邻点和次近邻点。
2.找出特征点后, 由于匹配的特征点之间不仅要求特征描述向量距离最近,而且应该与其它特征点能够区分开来, 因此采用最近邻点与次近邻点距离比值来限制特征点匹配。 计算最近邻点的距离和次近邻点的距离的比值,若小于阈值则保留,反之,则剔除。
3.双向交叉检查方法:通过单一方向搜索匹配的特征点对易出现图像中单个特征点与另一幅图像多个特征点匹配的问题, 当图像匹配特征点对较少时,这一问题会严重影响空间变换矩阵的估计精度。通过双向交叉检查方法可以解决这一问题

# K-D tree建立索引方式的常量参数
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)  # checks指定索引树要被遍历的次数
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches_1 = flann.knnMatch(des1, des2, k=2)  # 进行匹配搜索,参数k为返回的匹配点对数量
# 把保留的匹配点放入good列表
good1 = []
T = 0.5  # 阈值
# 筛选特征点
for i, (m, n) in enumerate(matches_1):if m.distance < T * n.distance:  # 如果最近邻点的距离和次近邻点的距离比值小于阈值,则保留最近邻点matchesMask[i] = [1, 0]good1.append(m)#  双向交叉检查方法matches_2 = flann.knnMatch(des2, des1, k=2)  # 进行匹配搜索
# 把保留的匹配点放入good2列表
good2 = []
for (m, n) in matches_2:if m.distance < T * n.distance:  # 如果最近邻点的距离和次近邻点的距离比值小于阈值,则保留最近邻点good2.append(m)
match_features = []  # 存放最终的匹配点
for i in good1:for j in good2:if (i.trainIdx == j.queryIdx) & (i.queryIdx == j.trainIdx):match_features.append(i)

matches_1 和 matches_2 属于 DMatch 类,有以下属性:
• DMatch.distance - 描述符之间的距离。越小越好。
• DMatch.trainIdx - 待匹配图像中描述符的索引。
• DMatch.queryIdx - 当前图像中描述符的索引。
• DMatch.imgIdx - 目标图像的索引。

3.利用 RANSAC 算法进行精匹配
RANSAC 算法(RANdom SAmple Consensus(随机抽样一致))
RANSAC算法(RANdom SAmple Consensus(随机抽样一致)) 在SIFT特征筛选中的主要流程是
(1) 从样本集中随机抽选一个RANSAC样本,即4个匹配点对
(2) 根据这4个匹配点对计算变换矩阵M
(3) 根据样本集,变换矩阵M,和误差度量函数计算满足当前变换矩阵的一致集consensus,并返回一致集中元素个数
(4) 根据当前一致集中元素个数判断是否最优(最大)一致集,若是则更新当前最优一致集
(5) 更新当前错误概率p,若p大于允许的最小错误概率则重复(1)至(4)继续迭代,直到当前错误概率p小于最小错误概率

当然,在python中已经集成了这个算法

src_pts = np.float32([kp1[m.queryIdx].pt for m in match_features]).reshape(-1, 1, 2)  # 转换成列表
dst_pts = np.float32([kp2[m.trainIdx].pt for m in match_features]).reshape(-1, 1, 2)
"""findHomography: 计算多个二维点对之间的最优单映射变换矩阵 H(3行x3列) ,使用最小均方误差或者RANSAC方法参考网址: https://blog.csdn.net/fengyeer20120/article/details/87798638
"""
H, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC, 0.1)  # mask中返回的匹配点是否满足最优单映射变换矩阵# 存放精匹配后的特征点
src_result_pts = []
dst_result_pts = []
for i, value in enumerate(mask):if value == 1:src_result_pts.append(src_pts[i])dst_result_pts.append(dst_pts[i])

下面这个函数用于画出精匹配后图像特征点连线图像

def darwMatches(img1, kp1, img2, kp2):# 画出精匹配的特征点# 创建新图像rows1 = img1.shape[0]cols1 = img1.shape[1]rows2 = img2.shape[0]cols2 = img2.shape[1]out = np.zeros((max([rows1, rows2]), cols1 + cols2, 3), dtype='uint8')out[:rows1, :cols1] = img1out[:rows2, cols1:] = img2for i in range(len(kp1)):# x - columns# y - rows(x1, y1) = kp1[i][0](x2, y2) = kp2[i][0]a = np.random.randint(0, 256)b = np.random.randint(0, 256)c = np.random.randint(0, 256)cv2.circle(out, (int(np.round(x1)), int(np.round(y1))), 2, (a, b, c), 1)  # 画圆,cv2.circle()参考官方文档cv2.circle(out, (int(np.round(x2) + cols1), int(np.round(y2))), 2, (a, b, c), 1)# Draw a line in between the two points# thickness = 1# colour bluecv2.line(out, (int(np.round(x1)), int(np.round(y1))), (int(np.round(x2) + cols1), int(np.round(y2))),(a, b, c), 1, shift=0)  # 画线,cv2.line()参考官方文档# Also return the image if you'd like a copyreturn out

4.图像变换
图像变换的作用是使不同图像映射到同一坐标系下

# 获取原图像的高和宽
h1, w1, p1 = img_1.shape
h2, w2, p2 = img_2.shape# h = np.maximum(h1, h2)
# w = np.maximum(w1, w2)from ImageStitching.function import CalCornerscornor = CalCorners(H, img_2)  # 计算四个角坐标,依次为左上角,左下角, 右上角,右下角
# print(cornor)
right_top_x = np.int32(cornor[2, 0])
right_bottom_x = np.int32(cornor[3, 0])
left_top_x = np.int32(cornor[0, 0])
left_bottom_x = np.int32(cornor[1, 0])right_top_y = np.int32(cornor[2, 1])
right_bottom_y = np.int32(cornor[3, 1])
left_top_y = np.int32(cornor[0, 1])
left_bottom_y = np.int32(cornor[1, 1])w_max = np.maximum(right_top_x, right_bottom_x)
w_min = np.minimum(left_bottom_x, left_top_x)
h_max = np.maximum(left_bottom_y, right_bottom_y)
h_min = np.minimum(right_top_y, left_top_y)print('w_max:', w_max)
print('w_min:', w_min)
print('h_max:', h_max)
print('h_min:', h_min)# 坐标转换
if h_min < 0:# 补上图像img_2new = np.zeros((h2 + np.abs(h_min), w2, p1), dtype=np.uint8)img_2new[np.abs(h_min):, :] = img_2if (h1 - h_min) > (h_max - h_min):h = h1 - h_minelse:h = h_max - h_minimageTransform = cv2.warpPerspective(img_2new, H, (w_max, h))  # 坐标转换img_1new = np.zeros((imageTransform.shape[0], w1, p2 ), dtype=np.uint8)  img_1new[np.abs(h_min):(np.abs(h_min) + h1), :w1] = img_1  # 参考图像高度补齐
else:if img_1.shape[0] > h_max:h = img_1.shape[0]else:h = h_maximageTransform = cv2.warpPerspective(img_2, H, (w_max, h))  # 坐标转换img_1new = np.zeros((imageTransform.shape[0], w1, p2 ), dtype=np.uint8)img_1new[:h1, :w1] = img_1
def CalCorners(H, image):# 计算四个坐标点corner = np.zeros((4, 2))  # 存放四个角坐标,依次为左上角,左下角, 右上角,右下角row, col, c = image.shape# 左上角(0, 0, 1)v2 = np.array([0, 0, 1])v1 = np.dot(H, v2)corner[0, 0] = v1[0] / v1[2]corner[0, 1] = v1[1] / v1[2]# 左下角v2[0] = 0v2[1] = rowv1 = np.dot(H, v2)corner[1, 0] = v1[0] / v1[2]corner[1, 1] = v1[1] / v1[2]# 右上角v2[0] = colv2[1] = 0v1 = np.dot(H, v2)corner[2, 0] = v1[0] / v1[2]corner[2, 1] = v1[1] / v1[2]# 右下角v2[0] = colv2[1] = rowv1 = np.dot(H, v2)corner[3, 0] = v1[0] / v1[2]corner[3, 1] = v1[1] / v1[2]return corner

5.图像融合
直接拼接会产生鬼影,色差等问题,因此需要利用一些算法来进行图像融合。
1.取大,即保留重叠区域像素灰度值大的像素点, 其保留了图像中对比度最大的信息

def OptimizeBig(img, imageTransform, cornor):"""优化拼接区域方式:取大,即保留重叠区域像素灰度值大的像素点, 其保留了图像中对比度最大的信息:param img1: 左图:param imageTransform: 变换后的右图:param cornor:四角坐标,依次为左上角,左下角, 右上角,右下角:return:"""# 直接拼接dst = np.zeros(imageTransform.shape, dtype=np.uint8)dst[0:imageTransform.shape[0], 0:imageTransform.shape[1]] = imageTransformdst[0:img.shape[0], 0:img.shape[1]] = img  start = int(np.minimum(cornor[0, 0], cornor[1, 0]))  # 开始区域,即重叠区域的左边界processWidth = int(img.shape[1] - start)  # 重叠区域的宽度rows = dst.shape[0]cols = img.shape[1]# 转换成灰度图img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)imageTransform_gray = cv2.cvtColor(imageTransform, cv2.COLOR_BGR2GRAY)for i in range(rows):for j in range(start, cols):if img_gray[i, j] >= imageTransform_gray[i, j]:dst[i, j][0] = img[i, j][0]dst[i, j][1] = img[i, j][1]dst[i, j][2] = img[i, j][2]else:dst[i, j][0] = imageTransform[i, j][0]dst[i, j][1] = imageTransform[i, j][1]dst[i, j][2] = imageTransform[i, j][2]return dst

2.最佳缝合线法

def bestlinefusion(img, imageTransform, cornor):"""优化拼接区域方式:最佳缝合线法:param img1: 左图:param imageTransform: 变换后的右图:param cornor:四角坐标,依次为左上角,左下角, 右上角,右下角:return:"""start = int(np.minimum(cornor[0, 0], cornor[1, 0]))  # 开始区域,即重叠区域的左边界processWidth = int(img.shape[1] - start)  # 重叠区域的宽度# 掩模dst_width = imageTransform.shape[1]h = imageTransform.shape[0]dst_height = hdst = np.zeros((dst_height, dst_width, 3), dtype=np.uint8)  # 拼接后的图像dst[:, :start] = 255# # 使左图的高度等于右图# if img.shape[0] < h:#     img_new = np.zeros((h, img.shape[1], 3), dtype=np.uint8)#     img_new[:img.shape[0], :] = img# else:#     img_new = img# 转化为灰度图,取出特征区域img_Rect = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)[:, start:]imageTransform_Rext = cv2.cvtColor(imageTransform, cv2.COLOR_BGR2GRAY)[:, start:img.shape[1]]# 计算梯度图img_Laplac = cv2.Laplacian(img_Rect, cv2.CV_32F, ksize=3)imageTransform_Laplac = cv2.Laplacian(imageTransform_Rext, cv2.CV_32F, ksize=3)# 计算差值图像I = (img_Rect - imageTransform_Rext) + (img_Laplac - imageTransform_Laplac)# 寻找最佳缝合线rows = I.shape[0]cols = I.shape[1]strengths = np.zeros((rows, cols))  # 存放每行最小强度值points = np.zeros((rows, cols, 2))  # 存放每行最小强度对应的坐标值(x,y)temp = np.zeros((3, 2))  # 存放下一行三个坐标点value = [0, 0, 0]  # 存放下一行三个坐标点对应的强度值# 第一行for i in range(cols):strengths[0, i] = I[0, i]points[0, i][0] = 0points[0, i][1] = i# 第二行到倒数第二行for i in range(cols):for j in range(1, rows):point = points[j - 1, i]if point[1] == 0:temp[0] = [j, point[1]]else:temp[0] = [j, point[1] - 1]temp[1] = [j, point[1]]if point[1] == (cols - 1):temp[2] = [j, point[1]]else:temp[2] = [j, point[1] + 1]value[0] = I[int(temp[0, 0]), int(temp[0, 1])]value[1] = I[int(temp[1, 0]), int(temp[1, 1])]value[2] = I[int(temp[2, 0]), int(temp[2, 1])]# print('value:', value)strength = np.min(value)flag = value.index(strength)strengths[j, i] = strengthpoints[j, i] = temp[flag]min_strength = np.min(np.sum(strengths, axis=0))  # 最小强度值index = list(np.sum(strengths, axis=0)).index(min_strength)  # 最小强度值对应的索引# print('strengths:', strengths)# print('min_strength:', min_strength)# print('index', index)# print('points', points[:, index])# 重叠区域掩模mask = np.zeros((rows, cols, 3))bestline = points[:, index]for k in range(rows):y = int(bestline[k, 1]) + 1mask[k, :y] = 255dst[:, start:img.shape[1]] = mask# 扩展参考图像大小为掩模图像img_new = np.zeros(dst.shape, dtype=np.uint8)img_new[:, :img.shape[1]] = imgresult = img_new * (dst/255) + imageTransform * (1 - dst/255)result = np.array(result, dtype=np.uint8)

3.Laplace图像金字塔
未完待续。。。

基于sift特征点的图像拼接相关推荐

  1. 基于SIFT特征的全景图像拼接

    主要分为以下几个步骤: (1) 读入两张图片并分别提取SIFT特征 (2) 利用k-d tree和BBF算法进行特征匹配查找 (3) 利用RANSAC算法筛选匹配点并计算变换矩阵 (3) 图像融合 S ...

  2. 基于SIFT特征的图像拼接融合(matlab+vlfeat实现)

    基于SIFT特征的图像拼接融合(matlab+vlfeat实现) piccolo,之前做的东西,简单整理下,不是做图像方向的,写的不好轻喷 主要原理参看SIFT算法详解和SIFT特征匹配算法介绍--寻 ...

  3. matlab人民币识别,Matlab图像处理学习笔记(六):基于sift特征点的人民币识别...

    本文记录如何利用sift特征点进行人民币的识别.本文给出的matlab源码识别了1元与100元人民币的面额,相同思路,可以对各种币值的人民币进行面额.正反面的识别.但由于本程序采用串行,模板数的增多会 ...

  4. 基于SIFT特征的图像配准(附Matlab源代码)

    基于SIFT特征的图像配准(附Matlab源代码) 本文先给出了采用SIFT方法进行图像配准的实验原图以及实验结果,最后附上Matlab源代码. 实验一: 实验一的图像(见图1.1)是本人自己拍摄的, ...

  5. 【OPENCV】图像拼接(python)-基于SIFT特征点和RANSAC方法

    简单的图像拼接为将两幅图像简单的拼接在一起,不考虑图像内容,仅仅是图像几何空间的转移与合成. 复杂的图像拼接也叫作基于特征匹配的图像拼接,考虑图像内容,拼接时消去两幅图像相同的部分,实现拼接合成全景图 ...

  6. 【交通标志识别】基于SIFT特征实现交通标志识别matlab代码

    1 简介 为了适应日益恶化的交通环境,本文提出了一种基于SIFT(Scale Invariant Feature Transform)算法的交通标志识别方法,利用SIFT算法构建仿射不变的特征子空间, ...

  7. 基于SIFT和颜色特征的花卉图像分类

    基于SIFT特征和颜色直方图的花卉图像分类 摘 要 课程实验提取图片的SIFT特征,通过k均值聚类的方法将所有训练图片的特征聚类为800类,以每个类出现的频率构建特征向量(又称为词袋模型),同时,通过 ...

  8. 【计算机视觉】基于FREAK特征描述子的分析、仿真与思考

    这段日子研究了很多的特征点检测与匹配,基本把Harris.Fast.SIFT.SURF.ORB.Censure.Brief.Brisk等各类特征检测子与描述子的论文和原理都大致了解了一遍,并进行了仿真 ...

  9. Ubuntu16.04下基于opencv--实现图像SIFT特征与全景图片的生成

    Ubuntu16.04下基于opencv--实现图像SIFT特征与全景图片的生成 一. 理解和实践SIFT特征提取与匹配 二. 全景图片的生成 三.循环依次读取一个序列图片,进行匹配连线 一. 理解和 ...

最新文章

  1. Android 多媒体综述
  2. 福特数据总监:汽车业的大数据框架如何构建?
  3. Tips-Windows 10【多桌面视窗】操作
  4. centos 7 备份代码,然后回滚
  5. ui项目答辩中学到了什么_我在UI设计9年中学到的12件事
  6. C语言课后习题(39)
  7. mysql的建表语句
  8. 混迹职场,有交换意识的人都是聪明人
  9. iis服务器网站指向ip地址,IIS之Web服务器 一个IP地址对应多个Web站点 多个域名...
  10. VBB 3.8.4 XSS
  11. 量子保密通信技术基本原理
  12. mysql查看列名_MySQL:从查询中获取列名或别名
  13. android- activity,Application,activity渲染xml文件
  14. “当前页面的脚本发生错误”如何解决
  15. 基于torch学汪峰写歌词、聊天机器人、图像着色/生成、看图说话、字幕生成
  16. 云上PDF怎么删除页眉页脚_PDF怎么删除页面?
  17. linux vim命令翻页,详解Vim编辑器翻页控制命令
  18. 软件开发常见英文单词
  19. response.getWriter()
  20. 春节灯笼Html代码实现+点击页面出现文字

热门文章

  1. python基础1-转自金角大王
  2. 西门子S7-1200数据处理指令应用|阶段性题目
  3. 执法机构洋气了:网络黑产市场遭警告
  4. dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchan关于苹果禁用JSPatch(热修复一类),上架审核被拒问题
  5. asp.net html控件上传图片,asp.net fileupload控件上传图片并预览图片
  6. 基于 Paddle2.0 的强化学习新玩法 —— 通关超级马里奥兄弟
  7. Postgres-XL 调优
  8. Centos 8 stream安装snort3(2023年2月3日更新部分问题)
  9. QT再创世纪--(3)ComboBox用法
  10. 红色警戒2+尤里的复仇,带完整音乐+影片,免安装版本