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 相关文章推荐
Python3访问并下载网页内容的方法
Jul 28 Python
在Python程序中操作MySQL的基本方法
Jul 29 Python
Python搭建APNS苹果推送通知推送服务的相关模块使用指南
Jun 02 Python
python装饰器初探(推荐)
Jul 21 Python
利用python操作SQLite数据库及文件操作详解
Sep 22 Python
浅谈python实现Google翻译PDF,解决换行的问题
Nov 28 Python
对dataframe数据之间求补集的实例详解
Jan 30 Python
Python 中的 global 标识对变量作用域的影响
Aug 12 Python
Python 一行代码能实现丧心病狂的功能
Jan 18 Python
python3连接mysql获取ansible动态inventory脚本
Jan 19 Python
Python requests模块安装及使用教程图解
Jun 30 Python
Django如何实现密码错误报错提醒
Sep 04 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 heredoc和phpwind的模板技术使用方法小结
2008/03/28 PHP
php md5下16位和32位的实现代码
2008/04/09 PHP
用PHP+MySQL搭建聊天室功能实例代码
2012/08/20 PHP
PHP session_start()问题解疑(详细介绍)
2013/07/05 PHP
ThinkPHP框架搭建及常见问题(XAMPP安装失败、Apache/MySQL启动失败)
2016/04/15 PHP
PHP 观察者模式深入理解与应用分析
2019/09/25 PHP
NodeJS中利用Promise来封装异步函数
2015/02/25 NodeJs
详解js中class的多种函数封装方法
2016/01/03 Javascript
深入浅析JS Function()构造函数
2016/08/22 Javascript
xmlplus组件设计系列之分隔框(DividedBox)(8)
2017/05/02 Javascript
xmlplus组件设计系列之文本框(TextBox)(3)
2017/05/03 Javascript
详解webpack 多入口配置
2017/06/16 Javascript
jQuery实现简单日期格式化功能示例
2017/09/19 jQuery
jQuery+SpringMVC中的复选框选择与传值实例
2018/01/08 jQuery
200行代码实现blockchain 区块链实例详解
2018/03/14 Javascript
angularjs实现的购物金额计算工具示例
2018/05/08 Javascript
结合Vue控制字符和字节的显示个数的示例
2018/05/17 Javascript
vue+VeeValidate 校验范围实例详解(部分校验,全部校验)
2018/10/19 Javascript
jQuery利用cookie 实现本地收藏功能(不重复无需多次命名)
2019/11/07 jQuery
python将图片文件转换成base64编码的方法
2015/03/14 Python
python实现RabbitMQ的消息队列的示例代码
2018/11/08 Python
Python Pandas 箱线图的实现
2019/07/23 Python
Python爬虫 批量爬取下载抖音视频代码实例
2019/08/16 Python
python/Matplotlib绘制复变函数图像教程
2019/11/21 Python
CSS3实现时间轴特效
2020/11/02 HTML / CSS
美国围栏公司:Walpole Outdoors
2019/11/19 全球购物
单身联谊活动方案
2014/01/29 职场文书
大二法英学生职业生涯规划范文
2014/02/27 职场文书
党课培训主持词
2014/04/01 职场文书
销售人员求职信
2014/07/22 职场文书
委托书范本
2014/09/13 职场文书
2014年优秀班主任工作总结
2014/12/16 职场文书
大国崛起观后感
2015/06/02 职场文书
《酸的和甜的》教学反思
2016/02/18 职场文书
成人成长感言如何写?
2019/08/16 职场文书
小程序与后端Java接口交互实现HelloWorld入门
2021/07/09 Java/Android