python全景图像拼接_Python+OpenCV实现图像的全景拼接

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

环境:python3.5.2 + openCV3.4

1.算法目的

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

2.算法步骤

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

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

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

20203595827724.jpg

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

20203595851005.jpg

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

步骤2:特征点匹配

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

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

20203595913000.jpg

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

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

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

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

通过迭代多次,以满足最多特征匹配点的特征矩阵H作为结果。

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

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

20203595935939.jpg

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

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

202035100243811.jpg

实现结果

我在宿舍里又多照了几组照片来实验:

室内宿舍场景的特征点匹配:

202035100323231.jpg

拼接结果:

202035100353936.jpg

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

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

特征点匹配:

202035100422480.jpg

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

拼接结果:

202035100452098.jpg

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

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()

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/543197.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

非你莫属 java全球排名_TIOBE:2018年5月全球编程语言排行榜

原标题&#xff1a;TIOBE&#xff1a;2018年5月全球编程语言排行榜TIOBE 刚刚发布了 5 月编程语言排行榜。Scala 经过长时间的沉淀&#xff0c;本月终于进入了 TIOBE 榜单的前 20 名。Scala 是一种 JVM 上运行的函数式编程语言&#xff0c;可与 Java 一起或作为 Java 的替代品使…

人生中最【无用】的二十件事

学会取舍&#xff0c;将是你人生的必修课。 丢掉这20件东西&#xff0c;你一定会成为更好的自己 我们之所以会觉得活的痛苦&#xff0c;活的乏味&#xff0c;根源在于你始终无法做到潇洒的放手。顾忌太多&#xff0c;想要的太多&#xff0c;最终一无所获。 你从幼稚走向成熟的过…

python web服务器 apache_Windows下搭建Apache+Django+Python Web服务环境

最近在学Django&#xff0c;想用Apache搭建一个服务器环境&#xff0c;因此在网上看了好多资料&#xff0c;很多都是用Python2.6和Apache2.2搭建的环境&#xff0c;不过我还是想用Python35和Apache24来搭建&#xff0c;具体环境如下&#xff1a; 参考资料&#xff1a; Apache配…

java删除javaee_JavaEE--集合--删除List中指定元素

一、Java 循环删除list中指定元素增强for循环删除指定元素Testpublic void test(){String[] strs{"a", "b", "c", "f", "b", "e", "d"};Listlist new ArrayList<>(Arrays.asList(strs));for (St…

无法从传输连接中读取数据:远程主机强迫_电力远程抄表中的应用

系统概述锐谷智联无线传输终端设备DT7710S&#xff0c;实现远程抄表应用。系统构成通过使用DT7710S&#xff0c;实现电表、水表等数据实时向中心传输。供应商可以通过数据中心的快速读取数据节省人力和时间。项目描述工业自动化的发展&#xff0c;远程抄表已成为智能电网系统中…

vmware nat模式网络不通_【EVENG入门】 03EVENG网络运行环境(桥接模式、NAT模式、Cloud连接虚拟设备)...

EEmulated&#xff0c;模拟VVirtual&#xff0c;虚拟EEnvironment&#xff0c;环境NNext&#xff0c;下一个GGeneration&#xff0c;一代也就是EVE-NG下一代模拟器虚拟环境当然&#xff0c;EVE也是黄昏&#xff0c;NG就是女鬼了EVE-NG黄昏的女鬼演示视频在此操作文档在此以下是…

金融行业 | 电子银行系统性能优化解决方案

随着互联网金融的蓬勃发展&#xff0c;以及来自支付宝等新兴网络金融机构的强大冲击&#xff0c;传统银行业纷纷加大对手机银行和网络银行等电子银行业务的投入力度&#xff0c;用户也从网银时代的足不出户转账付款发展到今天随时随地通过手机获取各种金融服务&#xff0c;前端…

Erlang/Elixir 社区摘要: 2016-05-21

Elixir 雷达, 49期 https://app.rdstation.com.br/mail/336031fa-3fe3-4d59-b05b-a86f8affa7c9如何使用环境变量来配置Exrm发布的应用 http://blog.plataformatec.com.br/2016/05/how-to-config-environment-variables-with-elixir-and-exrm/Merkle Trees in Elixir Merkle Tre…

git master主分支_Git分支管理策略及简单操作

前几天整理了一下之前项目的开发代码&#xff0c;当时使用了Git来进行代码版本管理。虽然本人熟悉常用的Git操作&#xff0c;但是对分支的管理经验非常欠缺。拿这个项目来说&#xff0c;在项目中有不下20个分支&#xff0c;每个分支间的继承关系相当之混乱&#xff0c;非常不利…

java在什么环境变量_Java-环境变量

很多同学不太明白环境变量的到底是什么具体有什么用? 这里阿风为不太明白的同学介绍一下.首先我们打开环境变量的位置.,这里以win10系统作为介绍在桌面邮件属性,如下然后点击高级系统变量接着点击环境变量然后你就能看到环境变量的具体设置界面了如图所示,环境变量分为用户变量…

git rebase用法_Git入门实战

如果给所有软件列出一个排行榜的话&#xff0c;那么Git定会名列前茅。Git对于代码项目的管理是具有划时代意义的&#xff0c;向Linus致敬&#xff0c;不但写出了可以与Windows争锋的Linux系统&#xff0c;还设计实现了如此强大的版本管理工具。当我了解Git后&#xff0c;真后悔…

linux md5 加密字符串和文件方法

linux md5 加密字符串和文件方法 MD5算法常常被用来验证网络文件传输的完整性&#xff0c;防止文件被人篡改。MD5全称是报文摘要算法&#xff08;Message-Digest Algorithm 5&#xff09;&#xff0c;此算法对任意长度的信息逐位进行计算&#xff0c;产生一个二进制长度为128位…

【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(5.5) 登录功能的实现,完善登录功能...

索引 【无私分享&#xff1a;从入门到精通ASP.NET MVC】从0开始&#xff0c;一起搭框架、做项目 目录索引 简述 今天我们来完善我们的登录功能 项目准备 我们用的工具是&#xff1a;VS 2013 SqlServer 2012 IIS7.5 希望大家对ASP.NET MVC有一个初步的理解&#xff0c;理论性的…

jsonobject转list集合_怎样优雅的操作集合,CollectionUtils工具类正确使用姿势

点击上方"码之初"关注&#xff0c;选择"设为星标"与精品技术文章不期而遇来源&#xff1a;cnblogs.com/qdhxhz/p/10787130.html这篇讲的CollectionUtils工具类是在apache下的, 而不是springframework下的CollectionUtils。个人觉得CollectionUtils在真实项…

[c#基础]使用抽象工厂实现三层

引言 昨天加了一天班&#xff0c;今天闲来无事&#xff0c;就在想如何将之前的三层和最近一直在学的设计模式给联系在一起&#xff0c;然后就动手弄了个下面的小demo。 项目结构 项目各个层实现 Wolfy.Model层中有一个抽象类BaseModel.cs&#xff0c;User.cs是用户实体类&#…

场效应管原理_场效应管——不就是一个电控开关?

管在mpn中&#xff0c;它的长相和我们常面讲的三极管非常像&#xff0c;所以有不少修朋友好长时间还分不清楚&#xff0c;统一的把这些长相相同的三极管、场效应管、双二极管、还有各种稳压IC统统称作“三个脚的管管”&#xff0c;呵呵&#xff0c;如果这样麻木不分的话&#x…

三次握手面试题java_java面试题三次握手和四次挥手-嗨客网

题目对 tcp 了解吗&#xff1f;讲讲它的三次握手和四次挥手&#xff1f;为什么需要三次握手&#xff0c;为什么需要四次挥手。答案三次握手第一次握手&#xff1a;客户端向服务器发送连接请求&#xff0c;这个时候报文首部中的同步为 SYN 1&#xff0c;同时生成一个随机序列号…

运维经验分享(三)-- 解决Ubuntu下crontab不能正确执行脚本的问题

原创作品&#xff0c;允许转载&#xff0c;转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://dgd2010.blog.51cto.com/1539422/1676490 运维经验分享作为一个专题&#xff0c;目前共7篇文章 《运维经验分享&#xff08;一&#xff0…

一个jsp能取到父类jsp的值吗_「Javaweb」ssm整合权限控制框架shiro,你知道怎么做吗?...

为美好而努力——羊羽科技说。最近在开发自己的网站&#xff0c;需要权限控制功能&#xff0c;在网上找了一下&#xff0c;找到了我接下来要介绍的shiro框架。shiro框架是Apache公司维护的开源产品之一&#xff0c;其官网对其的简介是这样的&#xff1a;shiro官网简介翻译过来就…

php设置用户头像,PHP针对多用户实现更换头像功能

一个网站&#xff0c;其实说白了就是某几个特定功能的组合&#xff0c;而更换用户头像就在这些功能之中。今天就来做个测试&#xff0c;针对不同的用户&#xff0c;实现头像上传功能。先给大家展示下成品效果图&#xff1a;思路针对不同的用户上传头像&#xff0c;我们要为每一…