Python+OpenCV实现图像的全景拼接


Posted in Python onMarch 05, 2020

本文实例为大家分享了Python+OpenCV实现图像的全景拼接的具体代码,供大家参考,具体内容如下

环境:python3.5.2 + openCV3.4

1.算法目的

将两张相同场景的场景图片进行全景拼接。

2.算法步骤

本算法基本步骤有以下几步:

步骤1:将图形先进行桶形矫正

没有进行桶形变换的图片效果可能会像以下这样:

Python+OpenCV实现图像的全景拼接

图片越多拼接可能就会越夸张。

Python+OpenCV实现图像的全景拼接

本算法是将图片进行桶形矫正。目的就是来缩减透视变换(Homography)之后图片产生的变形,从而使拼接图片变得畸形。

步骤2:特征点匹配

本算法使用的sift算法匹配,它具有旋转不变性和缩放不变性,具体原理在之后会补上一篇关于sift算法的文章,这里就不做详细介绍。

在匹配特征点的过程中,透视矩阵选取了4对特征点计算,公式为

Python+OpenCV实现图像的全景拼接

点的齐次坐标依赖于其尺度定义,因此矩阵H也仅依赖尺度定义,所以,单应性矩阵具有8个独立的自由度。

如果在选取的不正确的特征点,那么透视矩阵就可能计算错误,所以为了提高结果的鲁棒性,就要去除这些错误的特征点,而RANSAC方法就是用来删除这些错误的特征点。

**RANSAC:**用来找到正确模型来拟合带有噪声数据的迭代方法。基本思想:数据中包含正确的点和噪声点,合理的模型应该能够在描述正确数据点的同时摈弃噪声点。

RANSAC方法随机获取4对不同的特征匹配坐标,计算出透视矩阵H1,再将第二张图的特征匹配点经过这个矩阵H1映射到第一张图的坐标空间里,通过计算来验证这个H1矩阵是否满足绝大部分的特征点。
通过迭代多次,以满足最多特征匹配点的特征矩阵H作为结果。

这样正常情况就可以去除错误的特征点了,除非匹配错误的特征点比正确的还多。

下图是我在嘉庚图书馆旁拍摄的照片的特征点匹配。

Python+OpenCV实现图像的全景拼接

步骤3:利用得到的变换矩阵进行图片的拼接。

可以看出基本做到了无缝拼接。只是在色差上还是看得出衔接的部分存在。

Python+OpenCV实现图像的全景拼接

实现结果

我在宿舍里又多照了几组照片来实验:
室内宿舍场景的特征点匹配:

Python+OpenCV实现图像的全景拼接

拼接结果:

Python+OpenCV实现图像的全景拼接

在室内的效果根据结果来看效果也还可以。

我测试了宿舍里景深落差较大的两张图片:

特征点匹配:

Python+OpenCV实现图像的全景拼接

虽然距离较远,但是还是可以粗略的匹配到特征点。

拼接结果:

Python+OpenCV实现图像的全景拼接

从结果上来看可以看得出来,两张图片依然可以正确而粗略地拼接再一起,可以看得出是同一个区域。只是由于特征点不够,在细节上景深落差较大的还是没办法完美地拼接。

import numpy as np
import cv2 as cv
import imutils

class Stitcher:
 def __init__(self):
 self.isv3 = imutils.is_cv3()

 def stitch(self,imgs, ratio = 0.75, reprojThresh = 4.0, showMatches = False):
 print('A')
 (img2, img1) = imgs
 #获取关键点和描述符
 (kp1, des1) = self.detectAndDescribe(img1)
 (kp2, des2) = self.detectAndDescribe(img2)
 print(len(kp1),len(des1))
 print(len(kp2), len(des2))
 R = self.matchKeyPoints(kp1, kp2, des1, des2, ratio, reprojThresh)

 #如果没有足够的最佳匹配点,M为None
 if R is None:
 return None
 (good, M, mask) = R
 print(M)
 #对img1透视变换,M是ROI区域矩阵, 变换后的大小是(img1.w+img2.w, img1.h)
 result = cv.warpPerspective(img1, M, (img1.shape[1] + img2.shape[1], img1.shape[0]))
 #将img2的值赋给结果图像
 result[0:img2.shape[0], 0:img2.shape[1]] = img2

 #是否需要显示ROI区域
 if showMatches:
 vis = self.drawMatches1(img1, img2, kp1, kp2, good, mask)
 return (result, vis)

 return result


 def detectAndDescribe(self,img):
 print('B')
 gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

 #检查我们使用的是否是penCV3.x
 if self.isv3:
 sift = cv.xfeatures2d.SIFT_create()
 (kps, des) = sift.detectAndCompute(img, None)
 else:
 sift = cv.FastFeatureDetector_create('SIFT')
 kps = sift.detect(gray)
 des = sift.compute(gray, kps)

 kps = np.float32([kp.pt for kp in kps]) # **********************************
 #返回关键点和描述符
 return (kps, des)

 def matchKeyPoints(self,kp1, kp2, des1, des2, ratio, reprojThresh):
 print('C')
 #初始化BF,因为使用的是SIFT ,所以使用默认参数
 matcher = cv.DescriptorMatcher_create('BruteForce')
 # bf = cv.BFMatcher()
 # matches = bf.knnMatch(des1, des2, k=2)
 matches = matcher.knnMatch(des1, des2, 2) #***********************************

 #获取理想匹配
 good = []
 for m in matches:
 if len(m) == 2 and m[0].distance < ratio * m[1].distance:
 good.append((m[0].trainIdx, m[0].queryIdx))

 print(len(good))
 #最少要有四个点才能做透视变换
 if len(good) > 4:
 #获取关键点的坐标
 # src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
 # dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
 src_pts = np.float32([kp1[i] for (_, i) in good])
 dst_pts = np.float32([kp2[i] for (i, _) in good])

 #通过两个图像的关键点计算变换矩阵
 (M, mask) = cv.findHomography(src_pts, dst_pts, cv.RANSAC, reprojThresh)

 #返回最佳匹配点、变换矩阵和掩模
 return (good, M, mask)
 #如果不满足最少四个 就返回None
 return None

 def drawMatches(img1, img2, kp1, kp2, matches, mask, M):
 # 获得原图像的高和宽
 h, w = img1.shape[:2]
 # 使用得到的变换矩阵对原图像的四个角进行变换,获得目标图像上对应的坐标
 pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)
 dst = cv.perspectiveTransform(pts, M)
 matchesMask = mask.ravel().tolist()

 draw_params = dict(matchColor = (0, 255, 0),
  singlePointColor = None,
  matchesMask = matchesMask,
  flags = 2)
 img = cv.drawMatches(img1, kp1, img2, kp2, matches, None, **draw_params)

 return img

 def drawMatches1(self,img1, img2, kp1, kp2, metches,mask):
 print('D')
 (hA,wA) = img1.shape[:2]
 (hB,wB) = img2.shape[:2]
 vis = np.zeros((max(hA,hB), wA+wB, 3), dtype='uint8')
 vis[0:hA, 0:wA] = img1
 vis[0:hB, wA:] = img2
 for ((trainIdx, queryIdx),s) in zip(metches, mask):
 if s == 1:
 ptA = (int(kp1[queryIdx][0]), int(kp1[queryIdx][1]))
 ptB = (int(kp2[trainIdx][0])+wA, int(kp2[trainIdx][1]))
 cv.line(vis, ptA, ptB, (0, 255, 0), 1)

 return vis

# def show():
# img1 = cv.imread('image/sedona_left_01.png')
# img2 = cv.imread('image/sedona_right_01.png')
# img1 = imutils.resize(img1, width=400)
# img2 = imutils.resize(img2, width=400)
#
# stitcher = cv.Stitcher()
# (result, vis) = stitcher.stitch([img1, img2])
# # (result, vis) = stitch([img1,img2], showMatches=True)
#
# cv.imshow('image A', img1)
# cv.imshow('image B', img2)
# cv.imshow('keyPoint Matches', vis)
# cv.imshow('Result', result)
#
# cv.waitKey(0)
# cv.destroyAllWindows()
# show()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python 随机生成中文验证码的实例代码
Mar 20 Python
跨平台python异步回调机制实现和使用方法
Nov 26 Python
python脚本爬取字体文件的实现方法
Apr 29 Python
Python cookbook(字符串与文本)在字符串的开头或结尾处进行文本匹配操作
Apr 20 Python
python 分离文件名和路径以及分离文件名和后缀的方法
Oct 21 Python
python使用pymongo操作mongo的完整步骤
Apr 13 Python
Django连接数据库并实现读写分离过程解析
Nov 13 Python
Pandas+Matplotlib 箱式图异常值分析示例
Dec 09 Python
浅谈Python中的继承
Jun 19 Python
python如何对链表操作
Oct 10 Python
python使用matplotlib的savefig保存时图片保存不完整的问题
Jan 08 Python
python之django路由和视图案例教程
Jul 26 Python
Python对象的属性访问过程详解
Mar 05 #Python
Python安装OpenCV的示例代码
Mar 05 #Python
opencv python在视屏上截图功能的实现
Mar 05 #Python
谈谈Python:为什么类中的私有属性可以在外部赋值并访问
Mar 05 #Python
python如何将两张图片生成为全景图片
Mar 05 #Python
Python 定义只读属性的实现方式
Mar 05 #Python
Pycharm中import torch报错的快速解决方法
Mar 05 #Python
You might like
PHP 高手之路(二)
2006/10/09 PHP
php自动加载的两种实现方法
2010/06/21 PHP
PHP学习笔记之二 php入门知识
2011/01/12 PHP
单一index.php实现PHP任意层级文件夹遍历(Zjmainstay原创)
2012/07/31 PHP
IE和Mozilla的兼容性汇总event
2007/08/12 Javascript
关于JavaScript的with 语句的使用方法
2011/05/09 Javascript
仅Firefox中链接A无法实现模拟点击以触发其默认行为
2011/07/31 Javascript
JavaScript访问字符串中单个字符的两种方法
2015/07/03 Javascript
Bootstrap实现响应式导航栏效果
2015/12/28 Javascript
第七章之菜单按钮图标组件
2016/04/25 Javascript
BootStrap的双日历时间控件使用
2017/07/25 Javascript
微信小程序switch开关选择器使用详解
2018/01/31 Javascript
使用eslint和githooks统一前端风格的技巧
2020/07/29 Javascript
Vue-cli assets SubDirectory及PublicPath区别详解
2020/08/18 Javascript
Javascript如何实现扩充基本类型
2020/08/26 Javascript
[52:40]完美世界DOTA2联赛PWL S2 Magma vs GXR 第一场 11.29
2020/12/02 DOTA
Python中使用动态变量名的方法
2014/05/06 Python
使用PDB模式调试Python程序介绍
2015/04/05 Python
详解Python中with语句的用法
2015/04/15 Python
Python3简单实例计算同花的概率代码
2017/12/06 Python
python3+mysql查询数据并通过邮件群发excel附件
2018/02/24 Python
python之消除前缀重命名的方法
2018/10/21 Python
Python整数对象实现原理详解
2019/07/01 Python
Python如何实现在字符串里嵌入双引号或者单引号
2020/03/02 Python
python中if及if-else如何使用
2020/06/02 Python
Python hashlib模块的使用示例
2020/10/09 Python
蔻驰美国官网:COACH美国
2016/08/18 全球购物
IFCHIC台湾:欧美国际设计师品牌
2019/05/18 全球购物
Linux面试题LINUX系统类
2014/11/19 面试题
优秀团员个人的自我评价
2013/10/02 职场文书
大专生找工作自荐书
2014/06/10 职场文书
高中生期中考试失利检讨书
2014/10/23 职场文书
初中成绩单评语
2014/12/29 职场文书
2015年感恩母亲节的演讲稿
2015/03/18 职场文书
2015年党风廉政建设工作总结
2015/04/09 职场文书
详解Java实现数据结构之并查集
2021/06/23 Java/Android