OpenCV-Python 实现两张图片自动拼接成全景图
基本原理
要实现两张图片的简单拼接,必须计算homography矩阵(单应性矩阵),矩阵的计算至少需要在图中找到四个相似的点,用这个矩阵把那张图片变换后放到另一张图片相应的位置 ( 就是相当于把两张图片中定好的四个相似的点給重合在一起)。如此,就可以实现简单的全景拼接。当然,因为拼合之后图片会重叠在一起,所以需要重新计算图片重叠部分的像素值,否则结果会很难看。所以总结起来其实就两个步骤:
- 找两张图片中相似的点,计算变换矩阵(确定融合位置,通过SIFT 算法计算相似点)
- 变换一张图片放到另一张图片合适的位置,并计算重叠区域新的像素值 (这里就是图片融合所需要采取的策略)
具体实现
寻找相似点
当然,我们可以手动的寻找相似的点,但是这样比较麻烦。因为相似点越多或者相似点对应的位置越准确,所得的结果就越好,但是人的肉眼所找的位置总是有误差的,而且找出很多的点也不是一件容易的事。所以就有聪明的人设计了自动寻找相似点的算法,这里我们就用了 SIFT 算法,而 OpenCV 也给我们提供 SIFT 算法的接口,所以我们就不需要自己费力去实现了。如下是两张测试图片的原图和找出相似点后的图片。
sift = cv.xfeatures2d_SIFT().create()# find the keypoints and descriptors with SIFT# 计算出图像的关键点和sift特征向量,img1gray表示输入的原始图# 像,可以使三通道或单通道图像#第二个参数keypoints:特征点向量,向量内每一个元素是一个KeyPoint对象,包含了特征点的各种属性信息#第三个参数outImage:特征点绘制的画布图像,可以是原图像#第四个参数color:绘制的特征点的颜色信息,默认绘制的是随机彩色#第五个参数flags:特征点的绘制模式,其实就是设置特征点的那些信息需要绘制,那些不需要绘制,有以下几种模式可选kp1, des1 = sift.detectAndCompute(img1gray, None)kp2, des2 = sift.detectAndCompute(img2gray, None)
如果sift = cv2.xfeatures2d.SIFT_create()不能使用
原因:sift已经被申请专利了,所以,在opencv3.4.3.16 版本后,这个功能就不能用了。
解决方法是把版本退回到3.4.3以前。
pip uninstall opencv-python
pip install opencv-python==3.4.2.16
pip install opencv-contrib-python==3.4.2.16
其中红色的点是 SIFT 算法找出的相似点,而绿色的线表示的是在所有找出的相似的点中所筛选出的可信度更高的相似的点。因为算法找出的相似点并不一定是百分百正确的。然后就可以根据这些筛选出的相似点计算变换矩阵,当然 OpenCV 也提供了相应的接口方便我们的计算,而具体的代码实现也可以在 OpenCV 的 Python tutorial 中找到 [1]。
图片拼接
计算出变换矩阵后,接下来就是第二步,用计算出的变换矩阵对其中一张图做变换,然后把变换的图片与另一张图片重叠在一起,并重新计算重叠区域新的像素值。对于计算重叠区域的像素值,其实可以有多种方法去实现一个好的融合效果,这里就用最简单粗暴的但效果也不错的方式。直白来说就是实现一个图像的线性渐变,对于重叠的区域,靠近左边的部分,让左边图像内容显示的多一些,靠近右边的部分,让右边图像的内容显示的多一些。用公式表示就是,假设 alpha 表示像素点横坐标到左右重叠区域边界横坐标的距离,新的像素值就为 newpixel = 左图像素值 × (1 - alpha) + 右图像素值 × alpha 。这样就可以实现一个简单的融合效果,如果想实现更复杂或更好的效果,可以去搜索和尝试一下 multi-band 融合,这里就不过多赘述了。最后附上实现的结果和代码,可供参考
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
import gdalif __name__ == '__main__':top, bottom, left, right = 100, 100, 0, 500img1 = cv.imread('C:/Users/Administrator/Desktop/Gis/image22.jpg')img2 = cv.imread('C:/Users/Administrator/Desktop/Gis/image24.jpg')# cv.copyMakeBorder功能扩充src的边缘,将图像变大,然后以各种外插方式自动填充图像边界,# 这个函数实际上调用了函数cv::borderInterpolate,这个函数最重要的功能就是为了处理边界,# 比如均值滤波或者中值滤波中,使用copyMakeBorder将原图稍微放大,然后我们就可以处理边界的情况了# top, bottom, left, right分别表示在原图四周扩充边缘的大小# borderType:扩充边缘的类型,就是外插的类型,OpenCV中给出以下几种方式# *BORDER_REPLICATE(复制法,也就是复制最边缘像素。) aaaaaa|abcdefgh|hhhhhhh# *BORDER_REFLECT (轴对称法,也就是以边界为轴,对称。)fedcba|abcdefgh|hgfedcb# *BORDER_REFLECT_101(轴对称法,也就是以最边缘像素为轴,对称。)hgfedcb|abcdefgh|gfedcba# *BORDER_WRAP cdefgh|abcdefgh|abcdefg# *BORDER_CONSTANT(常量法) iiiiii|abcdefgh|iiiiiii with some specified 'i'srcImg = cv.copyMakeBorder(img1, top, bottom, left, right, cv.BORDER_CONSTANT, value=(0, 0, 0))testImg = cv.copyMakeBorder(img2, top, bottom, left, right, cv.BORDER_CONSTANT, value=(0, 0, 0))# BGR转换到灰度空间(opencv默认的彩色图像的颜色空间是BGR)img1gray = cv.cvtColor(srcImg, cv.COLOR_BGR2GRAY)img2gray = cv.cvtColor(testImg, cv.COLOR_BGR2GRAY)# 得到特征提取器的一个实例sift = cv.xfeatures2d_SIFT().create()# find the keypoints and descriptors with SIFT# 计算出图像的关键点和sift特征向量,img1gray表示输入的原始图# 像,可以使三通道或单通道图像kp1, des1 = sift.detectAndCompute(img1gray, None)kp2, des2 = sift.detectAndCompute(img2gray, None)# FLANN parameters随机kd树,平行搜索。默认trees=4FLANN_INDEX_KDTREE = 1# 创建字典index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)print(index_params)# 指定递归遍历的次数checkssearch_params = dict(checks=50)print(search_params)# 最近邻近似匹配,所以当我们需要找到一个相对好的匹配但是不需要最佳匹配的时候往往使用FlannBasedMatcher。flann = cv.FlannBasedMatcher(index_params, search_params)# des1:图片,des2:搜索的图片,matches:匹配的结果,K:阈值,越高精度越高,匹配的数量越少。# 该函数,一组返回的俩个DMatch类型DMatch。那么这个这个DMatch数据结构究竟是什么呢?# 它包含三个非常重要的数据分别是queryIdx,trainIdx,distance。# 先说一下这三个分别是什么在演示其用途:# queryIdx:测试图像的特征点描述符的下标(第几个特征点描述符),同时也是描述符对应特征点的下标。# trainIdx:样本图像的特征点描述符下标,同时也是描述符对应特征点的下标。# distance:代表这怡翠匹配的特征点描述符的欧式距离,数值越小也就说明俩个特征点越相近。# K近邻匹配,在匹配的时候选择K个和特征点最相似的点,如果这K个点之间的区别足够大,# 则选择最相似的那个点作为匹配点,通常选择K = 2,也就是最近邻匹配。# 对每个匹配返回两个最近邻的匹配,如果第一匹配和第二匹配距离比率足够大(向量距离足够远),# 则认为这是一个正确的匹配,比率的阈值通常在2左右。matches = flann.knnMatch(des1, des2, k=2)for i, matche in enumerate(matches):print(matche)# Need to draw only good matches, so create a maskmatchesMask = [[0, 0] for i in range(len(matches))]good = []pts1 = []pts2 = []# ratio test as per Lowe's paperfor i, (m, n) in enumerate(matches):if m.distance < 0.7*n.distance:good.append(m)pts2.append(kp2[m.trainIdx].pt)pts1.append(kp1[m.queryIdx].pt)matchesMask[i] = [1, 0]draw_params = dict(matchColor=(0, 255, 0),singlePointColor=(255, 0, 0),matchesMask=matchesMask,flags=0)img3 = cv.drawMatchesKnn(img1gray, kp1, img2gray, kp2, matches, None, **draw_params)plt.imshow(img3, ), plt.show()rows, cols = srcImg.shape[:2]MIN_MATCH_COUNT = 10if len(good) > MIN_MATCH_COUNT: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)M, mask = cv.findHomography(src_pts, dst_pts, cv.RANSAC, 5.0)warpImg = cv.warpPerspective(testImg, np.array(M), (testImg.shape[1], testImg.shape[0]), flags=cv.WARP_INVERSE_MAP)for col in range(0, cols):if srcImg[:, col].any() and warpImg[:, col].any():left = colbreakfor col in range(cols-1, 0, -1):if srcImg[:, col].any() and warpImg[:, col].any():right = colbreakres = np.zeros([rows, cols, 3], np.uint8)for row in range(0, rows):for col in range(0, cols):if not srcImg[row, col].any():res[row, col] = warpImg[row, col]elif not warpImg[row, col].any():res[row, col] = srcImg[row, col]else:srcImgLen = float(abs(col - left))testImgLen = float(abs(col - right))alpha = srcImgLen / (srcImgLen + testImgLen)res[row, col] = np.clip(srcImg[row, col] * (1-alpha) + warpImg[row, col] * alpha, 0, 255)# opencv is bgr, matplotlib is rgbres = cv.cvtColor(res, cv.COLOR_BGR2RGB)# show the resultplt.figure()plt.imshow(res)plt.show()else:print("Not enough matches are found - {}/{}".format(len(good), MIN_MATCH_COUNT))matchesMask = None
OpenCV-Python 实现两张图片自动拼接成全景图相关推荐
- chatgpt赋能python:Python将两张图片拼接成一张图:让您的图片处理更高效!
Python将两张图片拼接成一张图:让您的图片处理更高效! 作为一个有着10年Python编程经验的工程师,我发现Python成为了许多项目的主要语言,其中涵盖了很多关于图像处理的应用程序.在本文中, ...
- python将两张图片拼接成一张图_python实现两张图片拼接为一张图片并保存
本文实例为大家分享了python实现两张图片拼接为一张图片并保存的具体代码,供大家参考,具体内容如下 这里主要用Python扩展库pillow中Image对象的paste()方法把两张图片拼接起来 f ...
- 如何将两张图片上下拼接?
如何将两张图片上下拼接?我们在工作或学习中很多场景都会使用到截图,那么如何把两张截图上下拼接呢?如果你精通PS的话,那么这个问题对于你来说就是很简单的,用ps打开两张图片,调整好图层参数,把一张拉到另 ...
- Python 把两张图片拼起来
Linux系统下使用 Python 把两张图片拼起来 combine.py from PIL import Image from os import listdir, chdir, mkdir fr ...
- python 对比两张图片是否相同
python 对比两张图片是否相同 说明 说明 适用于对比两张图片是否完全相同 需要两张图片的大小完全一致 代码来源于网络,进行微调 from PIL import Image from PIL im ...
- 重写python requests库实现自动拼接url
重写python requests库实现自动拼接url 第一步,新建一个HttpSession类,并继承requests.Session 第二步,实现自动拼接URL 第三步,使用自动拼接的URL发送请 ...
- 如何使用Python判断两张图片是否相同?
如何使用Python判断两张图片是否相同? 在图像处理中,经常需要判断两张图片是否相同.这个问题看似简单,实际上却不容易解决.但是,使用Python语言,我们可以通过多种方式来判断图片是否相同. 方法 ...
- python将两个list合并成一个dict的方法
python将两个list合并成一个dict的方法 不使用内置函数,直接用 def Run():list2 = [1, 2, 3, 4, 5 ];list3 = ["a", &qu ...
- python实现两张图片横向和纵向拼接
本文实例为大家分享了python实现图片横向和纵向拼接的具体代码, 这里主要用Python扩展库pillow中Image对象的paste()方法把两张图片拼接起来供大家参考,具体内容如下: 一.代码: ...
最新文章
- Hdu-6249 2017CCPC-Final G.Alice’s Stamps 动态规划
- Python+selenium 自动化-chrome页面静止、冻结技术,获取web动态页面的Xpath方法,查看浏览器动态dom节点
- php 原生开发,四个优秀php原生开发实战视频教程推荐(必学)
- 使用C# 未解决的问题(VS2012)
- 如何将 namedtuple 转换为字典或常规元组
- linux ac 命令
- 项目导入mysql驱动包---简单例子
- PHP绿色集成环境在云服务器上的应用,PHPWAMP在服务器上搭建网站案例
- PLC江湖波澜不惊,自动化风云录!
- 牛客网刷题记录(3)组合逻辑
- sql服务器字段顺序怎么修改,你可能不知道SQL Server索引列的升序和降序带来的性能问题...
- 用【花生壳】穿透内网主机运用dai搭建校园实现访问外网访问只有学校内网的教务系统
- red5视频直播初探
- 电脑没有串口怎么办?
- RT-Thread 4.1.0 特性解析之LIBC与POSIX
- 工具 | Windows 功能猎手 (WFH)
- Nginx反向代理服务器基本应用
- matlab seawater下载,海洋要素计算工具箱seawater
- 网络编程9_线程-条件,定时器,队列,线程池, 协程
- 草地排水 改了又改(DCOJ6013)