文章目录

  • 前言
  • 实现方法
  • 实现代码
    • 直接拼接
    • 加权处理
  • 总结

前言

  图像拼接(Image Stitching)是一种利用实景图像组成全景空间的技术,它将多幅图像拼接成一幅大尺度图像或360度全景图,接可以看做是场景重建的一种特殊情况,其中图像仅通过平面单应性进行关联。图像拼接在运动检测和跟踪,增强现实,分辨率增强,视频压缩和图像稳定等机器视觉领域有很大的应用。
  图像拼接的输出是两个输入图像的并集。通常用到四个步骤:
特征提取(Feature Extraction):检测输入图像中的特征点。

图像配准(Image Registration):建立了图像之间的几何对应关系,使它们可以在一个共同的参照系中进行变换、比较和分析。

图像变形(Warping):图像变形是指将其中一幅图像的图像重投影,并将图像放置在更大的画布上。

图像融合(Blending):图像融合是通过改变边界附近的图像灰度级,去除这些缝隙,创建混合图像,从而在图像之间实现平滑过渡。混合模式(Blend modes)用于将两层融合到一起。

实现方法

1、用SIFT提取图像中的特征点,并对每个关键点周围的区域计算特征向量。可以使用比SIFT快的SURF方法,但是我的opencv版本为最新版,不知道是专利的原因还是什么原因用SURF = cv2.xfeatures2D.SURF_create ()实例化的时候会报错,网上说可以退opencv版本,但是我这里没有尝试,就用了sift = cv2.SIFT_create()。
2、在分别提取好了两张图片的关键点和特征向量以后,可以利用它们进行两张图片的匹配。在拼接图片中,可以使用Knn进行匹配,但是使用FLANN快速匹配库更快,图片拼接,需要用到FLANN的单应性匹配。
3、单应性匹配完之后可以获得透视变换H矩阵,用这个的逆矩阵来对第二幅图片进行透视变换,将其转到和第一张图一样的视角,为下一步拼接做准备。
4、透视变化完后就可以直接拼接图片了,将图片通过numpy直接加到透视变化完成的图像的左边,覆盖掉重合的部分,得到拼接图片,但是这样拼接得图片中间会有一条很明显的缝隙,可以通过加权平均法,界线的两侧各取一定的比例来融合缝隙,速度快,但不自然。或者羽化法,或者拉普拉斯金字塔融合,效果最好。在这里用的是加权平均法,可以把第一张图叠在左边,但是对第一张图和它的重叠区做一些加权处理,重叠部分,离左边图近的,左边图的权重就高一些,离右边近的,右边旋转图的权重就高一些,然后两者相加,使得过渡是平滑地,这样看上去效果好一些,速度就比较慢。

实现代码

先给出原图

直接拼接

#导入库
import cv2
import numpy as np
import sys
from PIL import Image
#图像显示函数
def show(name,img):cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()
#读取输入图片
ima = cv2.imread("you.jpg")
imb = cv2.imread("zuo.jpg")
A = ima.copy()
B = imb.copy()
imageA = cv2.resize(A,(0,0),fx=0.2,fy=0.2)
imageB = cv2.resize(B,(0,0),fx=0.2,fy=0.2)
#检测A、B图片的SIFT关键特征点,并计算特征描述子
def detectAndDescribe(image):# 建立SIFT生成器sift = cv2.SIFT_create()# 检测SIFT特征点,并计算描述子(kps, features) = sift.detectAndCompute(image, None)# 将结果转换成NumPy数组kps = np.float32([kp.pt for kp in kps])# 返回特征点集,及对应的描述特征return (kps, features)#检测A、B图片的SIFT关键特征点,并计算特征描述子
kpsA, featuresA = detectAndDescribe(imageA)
kpsB, featuresB = detectAndDescribe(imageB)
# 建立暴力匹配器
bf = cv2.BFMatcher()
# 使用KNN检测来自A、B图的SIFT特征匹配对,K=2
matches = bf.knnMatch(featuresA, featuresB, 2)
good = []
for m in matches:# 当最近距离跟次近距离的比值小于ratio值时,保留此匹配对if len(m) == 2 and m[0].distance < m[1].distance * 0.75:# 存储两个点在featuresA, featuresB中的索引值good.append((m[0].trainIdx, m[0].queryIdx))# 当筛选后的匹配对大于4时,计算视角变换矩阵
if len(good) > 4:# 获取匹配对的点坐标ptsA = np.float32([kpsA[i] for (_, i) in good])ptsB = np.float32([kpsB[i] for (i, _) in good])# 计算视角变换矩阵H, status = cv2.findHomography(ptsA, ptsB, cv2.RANSAC,4.0)# 匹配两张图片的所有特征点,返回匹配结果
M = (matches, H, status)
# 如果返回结果为空,没有匹配成功的特征点,退出程序
if M is None:print("无匹配结果")sys.exit()
# 否则,提取匹配结果
# H是3x3视角变换矩阵
(matches, H, status) = M
# 将图片A进行视角变换,result是变换后图片
result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
# 将图片B传入result图片最左端
result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB
show('res',result)
print(result.shape)

效果:

  可以发现直接拼接虽然可以拼接但是在拼接的地方会有一条很明显的缝隙,不过直接拼接的速度比较快只用了2点多秒。

加权处理

  我们通常使用alpha因子,通常称为alpha通道,它在中心像素处的值为1,在与边界像素线性递减后变为0。当输出拼接图像中至少有两幅重叠图像时,我们将使用如下的alpha值来计算其中一个像素处的颜色:假设两个图像,在输出图像中重叠;每个像素点在图像,其中(R,G,B)是像素的颜色值,我们将在缝合后的输出图像中计算(x, y)的像素值:
代码如下:

import cv2
import numpy as np
from matplotlib import pyplot as plt
import time
def show(name,img):cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()
MIN = 10
FLANN_INDEX_KDTREE = 0
starttime = time.time()
img1 = cv2.imread('zuo.jpg') #query
img2 = cv2.imread('you.jpg') #train
imageA = cv2.resize(img1,(0,0),fx=0.2,fy=0.2)
imageB = cv2.resize(img2,(0,0),fx=0.2,fy=0.2)
surf=cv2.xfeatures2d.SIFT_create()#可以改为SIFT
#sift = cv2.SIFT_create()
kp1,descrip1 = sift.detectAndCompute(imageA,None)
kp2,descrip2 = sift.detectAndCompute(imageB,None)
#创建字典
indexParams = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
searchParams = dict(checks=50)
flann=cv2.FlannBasedMatcher(indexParams,searchParams)
match=flann.knnMatch(descrip1,descrip2,k=2)
good=[]
#过滤特征点
for i,(m,n) in enumerate(match):if(m.distance<0.75*n.distance):good.append(m)# 当筛选后的匹配对大于10时,计算视角变换矩阵
if len(good) > MIN:src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)ano_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)M,mask = cv2.findHomography(src_pts,ano_pts,cv2.RANSAC,5.0)warpImg = cv2.warpPerspective(imageB, np.linalg.inv(M), (imageA.shape[1]+imageB.shape[1], imageB.shape[0]))direct=warpImg.copy()direct[0:imageA.shape[0], 0:imageB.shape[1]] =imageAsimple=time.time()show('res',warpImg)
rows,cols=imageA.shape[:2]
print(rows)
print(cols)
for col in range(0,cols):# 开始重叠的最左端if imageA[:, col].any() and warpImg[:, col].any():left = colprint(left)breakfor col in range(cols-1, 0, -1):#重叠的最右一列if imageA[:, col].any() and warpImg[:, col].any():right = colprint(right)break
#加权处理
res = np.zeros([rows, cols, 3], np.uint8)
for row in range(0, rows):for col in range(0, cols):if not imageA[row, col].any():  # 如果没有原图,用旋转的填充res[row, col] = warpImg[row, col]elif not warpImg[row, col].any():res[row, col] = imageA[row, col]else:srcImgLen = float(abs(col - left))testImgLen = float(abs(col - right))alpha = srcImgLen / (srcImgLen + testImgLen)res[row, col] = np.clip(imageA[row, col] * (1 - alpha) + warpImg[row, col] * alpha, 0, 255)warpImg[0:imageA.shape[0], 0:imageA.shape[1]]=res
show('res',warpImg)
final=time.time()
print(final-starttime)

效果:

  可以发现经过加权处理融合后的图片要比直接拼接效果要好,但是时间用了差不多16秒,而且还是有一条黑缝,目前还没有找到解决的办法,有好方法的友友们可以在评论区留意哟。


总结

  除了加权处理的方法外,还可以尝试用羽化和拉普拉斯金字塔等方法来实现图像拼接,这里给出实现的原理,方便以后尝试。
羽化(原文连接):
 加载原始图像并找到轮廓。

 模糊原始图像并将其保存在不同的变量中。

 创建一个空的蒙版并在其上绘制检测到的轮廓。

 使用 np.where() 方法从要模糊值的蒙版(轮廓)中选择像素,然后替换它。

拉普拉斯金字塔(原文连接);

Opencv实战——图像拼接相关推荐

  1. 重磅升级,52个Python+OpenCV实战项目教你掌握图像处理

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 近期小白学视觉公众号推出了多篇Python+OpenCV实战项目的 ...

  2. OpenCV实战(1)——OpenCV与图像处理基础

    OpenCV实战(1)--OpenCV与图像处理基础 0. 前言 1. OpenCV 基础 1.1 安装 OpenCV 1.2 OpenCV 主要模块 1.3 使用 Qt 进行 OpenCV 开发 2 ...

  3. 再次升级,985博士整理的71个OpenCV实战项目教程开放下载!

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 近期小白学视觉公众号推出了多篇Python+OpenCV实战项目的 ...

  4. 基于OpenCV实战:3步实现图像降噪

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 在本文中,我们将展示如何通过三个简单的步骤来实现降噪.我们将使用机 ...

  5. 基于OpenCV实战:绘制图像轮廓(附代码)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 山区和地形图中海拔高的区域划出的线称为地形轮廓,它们提供了地形的高 ...

  6. 基于OpenCV实战:车牌检测

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 拥有思维导图或流程将引导我们朝着探索和寻找实现目标的正确道路的方向 ...

  7. 基于OpenCV实战的图像处理:色度分割

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 通过HSV色阶使用彩色图像可以分割来分割图像中的对象,但这并不是分 ...

  8. 基于OpenCV实战:提取中心线

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自|AI算法与图像处理 问题 前几天有个人问了我一个问题,问 ...

  9. 基于OpenCV实战:对象跟踪

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 介绍 跟踪对象的基本思想是找到对象的轮廓,基于HSV颜色值. 轮廓 ...

最新文章

  1. php yar swoole 比较,syar:Swoole 实现的 Yar 服务
  2. HTML5音乐播放器(四):播放列表与播放方式
  3. ROS+Turtlebot3+RVIZ 显示机器人历史轨迹方法
  4. 5G NR中的两套绝对频域位置:GSCN和ARFCN
  5. Python学习笔记-day1(while流程控制)
  6. [视频]Google Chrome背后的故事
  7. DSAPI QQ用户相关
  8. C#开发的3D图表控件,适用于winform项目
  9. TC软件详细设计文档(手机群控)
  10. 千千音乐付费音乐爬取--json数据的处理
  11. word文档怎么找回误删的文件_Word文档删除了怎么恢复?Word误删除恢复技巧
  12. FastDb 简单编码运用
  13. mongoose时间自动转化为格林尼治标准时间的解决方案
  14. java 导出压缩文件,IO流非正常关闭引起的“不可预料的压缩文件末端”的解决办法
  15. vba msgbox html 标签,VBA更改MsgBox中的文本颜色
  16. 计算机开机定屏,电脑主板开机画面定屏解决方法
  17. 迅雷BT种子制作方法
  18. 那些困扰你多年的项目管理问题,终于有解决方案了!
  19. sqlserver读取数据库表结构
  20. 基于asp.net823驴友信息平台旅游网站

热门文章

  1. 【协议分析】Web QQ登陆验证分析
  2. IP V4地址最后一位斜杠是什么意思?
  3. Amlogic平台固定摄像头facing办法
  4. AD19遇到不同层短路 字符阻焊错误 问题解决
  5. org.springframework.dao.DataIntegrityViolationException: Error attempting to get column
  6. 会计学原理学习笔记——第二章——账户设置与借贷记账法(2.5会计科目)
  7. 神舟战神笔记本触控板的开启与关闭
  8. ValueError问题的解决
  9. 使用Xshell部署网页
  10. [BOI Mokia]