全景图拼接算法实现与改进

您所在的位置:网站首页 3d打印分割与拼接 全景图拼接算法实现与改进

全景图拼接算法实现与改进

2024-07-10 17:23:56| 来源: 网络整理| 查看: 265

点击上方“3D视觉工坊”,选择“星标”

干货第一时间送达

本文由知乎作者z.defying授权转载,不得擅自二次转载。原文链接:https://zhuanlan.zhihu.com/p/83225676?mType=Group

目录:

函数介绍

图像拼接算法实现

图像拼接算法改进

https://www.pyimagesearch.com/2018/12/17/image-stitching-with-opencv-and-python/

本文参考上面这个链接,实现多张图像的拼接,构建一张全景图。

根据多个图像创建全景图的步骤为:

检测两张图像的关键点特征(DoG、Harris等)

计算不变特征描述符(SIFT、SURF或ORB等)

根据关键点特征和描述符,对两张图像进行匹配,得到若干匹配点对,并移除错误匹配;

使用Ransac算法和匹配的特征来估计单应矩阵(homography matrix);

通过单应矩阵来对图像进行仿射变换;

两图像拼接,重叠部分融合;

裁剪以获得美观的最终图像。

原理比较复杂,本文先不讲解,OpenCV中已经实现了全景图拼接的算法,它们是cv2.createStitcher (OpenCV 3.x) 和 cv2.Stitcher_create(OpenCV 4) 。

该算法对以下条件具有较好的鲁棒性:

输入图像的顺序

图像的方向

光照变化

图像噪声

一、函数介绍

OpenCV 3.x 的 cv2.createStitcher 函数原型为:

createStitcher(...) createStitcher([, try_use_gpu]) -> retval

这个函数有一个参数 try_use_gpu,它可以用来提升图像拼接整个过程的速度。

OpenCV 4 的 cv2.Stitcher_create 函数原型为:

Stitcher_create(...) Stitcher_create([, mode]) -> retval . @brief Creates a Stitcher configured in one of the stitching . modes. . . @param mode Scenario for stitcher operation. This is usually . determined by source of images to stitch and their transformation. . Default parameters will be chosen for operation in given scenario. . @return Stitcher class instance.

要执行实际的图像拼接,我们需要调用 .stitch 方法:

OpenCV 3.x: stitch(...) method of cv2.Stitcher instance stitch(images[, pano]) -> retval, pano OpenCV 4.x: stitch(...) method of cv2.Stitcher instance stitch(images, masks[, pano]) -> retval, pano . @brief These functions try to stitch the given images. . . @param images Input images. . @param masks Masks for each input image specifying where to . look for keypoints (optional). . @param pano Final pano. . @return Status code.

该方法接收一个图像列表,然后尝试将它们拼接成全景图像,并进行返回。

变量 status=0表示图像拼接是否成功。

二、图像拼接算法实现

先把三张图片读取出来存放到列表里:

img_dir = 'pictures/stitching' names = os.listdir(img_dir) images = [] for name in names: img_path = os.path.join(img_dir, name) image = cv2.imread(img_path) images.append(image)

图片顺序没有影响,我试了一下,不同的图片顺序,输出全景图都相同。

然后构造图像拼接对象stitcher, 要注意的是,OpenCV 3 和 4 的构造器是不同的。

import imutils stitcher = cv2.createStitcher() if imutils.is_cv3() else cv2.Stitcher_create()

再把图像列表传入.stitch函数,该函数会返回状态和拼接好的全景图(如果没有错误):

status, stitched = stitcher.stitch(images)

完整代码如下:

import os import cv2 import imutils img_dir = 'pictures/stitching' names = os.listdir(img_dir) images = [] for name in names: img_path = os.path.join(img_dir, name) image = cv2.imread(img_path) images.append(image) stitcher = cv2.createStitcher() if imutils.is_cv3() else cv2.Stitcher_create() status, stitched = stitcher.stitch(images) if status==0: cv2.imwrite('pictures/stitch.jpg', stitched)

OpenCV真的很强大,这短短几行,就实现了拼接全景图。

全景图如下:

呃,全景图是实现了,但是周围出现了一些黑色区域。

这是因为构建全景时会做透视变换,透视变换时会产生这些黑色区域。

所以需要做进一步处理,裁剪出全景图的最大内部矩形区域,也就是只保留下图中红色虚线边框内的全景区域。

三、图像拼接算法改进

获取图像列表并得到初步全景图,这两步还是相同的:

img_dir = 'pictures/stitching' names = os.listdir(img_dir) images = [] for name in names: img_path = os.path.join(img_dir, name) image = cv2.imread(img_path) images.append(image) stitcher = cv2.createStitcher() if imutils.is_cv3() else cv2.Stitcher_create() status, stitched = stitcher.stitch(images)

在全景图四周各添加10像素宽的黑色边框,以确保能够找到全景图的完整轮廓:

stitched = cv2.copyMakeBorder(stitched, 10, 10, 10, 10, cv2.BORDER_CONSTANT, (0, 0, 0))

再将全景图转换为灰度图,并将不为0的像素全置为255,作为前景,其他像素灰度值为0,作为背景。

gray = cv2.cvtColor(stitched, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)

现在有了全景图的二值图,再应用轮廓检测,找到最大轮廓的边界框,

注:和轮廓相关的详细讲解可以查看 这篇文章。

cnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnt = max(cnts, key=cv2.contourArea) # 获取最大轮廓 mask = np.zeros(thresh.shape, dtype="uint8") x, y, w, h = cv2.boundingRect(cnt) # 绘制最大外接矩形框(内部填充) cv2.rectangle(mask, (x, y), (x + w, y + h), 255, -1)

这个白色矩形框是整个全景图可以容纳下的最小矩形区域。

接下来就是最难,也是最巧妙的部分了,先创建mask的两个副本:

minRect,这个mask的白色区域会慢慢缩小,直到它刚好可以完全放入全景图内部。

sub,这个mask用于确定minRect是否需要继续减小,以得到满足要求的矩形区域。

minRect = mask.copy() sub = mask.copy() # 开始while循环,直到sub中不再有前景像素 while cv2.countNonZero(sub) > 0: minRect = cv2.erode(minRect, None) sub = cv2.subtract(minRect, thresh)

不断地对minRect进行腐蚀操作,然后用minRect减去之前得到的阈值图像,得到sub,

再判断sub中是否存在非零像素,如果不存在,则此时的minRect就是我们最终想要的全景图内部最大矩形区域。

sub和minRect在while循环中的变化情况如下动图所示:

因为OpenCV中灰度图像素值范围0-255,如果两个数相减得到负数的话,会直接将其置为0;如果两个数相加,结果超过了255的话,则直接置为255。

比如下面这个图,左图中白色矩形可以完全包含在全景图中,但不是全景图的最大内接矩形,用它减去右边的阈值图,

因为黑色像素减白色像素,会得到黑色像素,所以其结果图为全黑的图。

所以上面那个while循环最终得到的minRect就是减去阈值图得到全黑图的面积最大的矩形区域。

好了,我们已经得到全景图的内置最大矩形框了,接下来就是找到这个矩形框的轮廓,并获取其坐标:

cnts, hierarchy = cv2.findContours(minRect.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnt = max(cnts, key=cv2.contourArea) # 计算最大轮廓的边界框 (x, y, w, h) = cv2.boundingRect(cnt) # 使用边界框坐标提取最终的全景图 stitched = stitched[y:y + h, x:x + w]

得到最终结果图如下:

完整代码如下:

import os import cv2 import imutils import numpy as np img_dir = '/images' names = os.listdir(img_dir) images = [] for name in names: img_path = os.path.join(img_dir, name) image = cv2.imread(img_path) images.append(image) stitcher = cv2.createStitcher() if imutils.is_cv3() else cv2.Stitcher_create() status, stitched = stitcher.stitch(images) # 四周填充黑色像素,再得到阈值图 stitched = cv2.copyMakeBorder(stitched, 10, 10, 10, 10, cv2.BORDER_CONSTANT, (0, 0, 0)) gray = cv2.cvtColor(stitched, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY) cnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnt = max(cnts, key=cv2.contourArea) mask = np.zeros(thresh.shape, dtype="uint8") x, y, w, h = cv2.boundingRect(cnt) cv2.rectangle(mask, (x, y), (x + w, y + h), 255, -1) minRect = mask.copy() sub = mask.copy() # 开始while循环,直到sub中不再有前景像素 while cv2.countNonZero(sub) > 0: minRect = cv2.erode(minRect, None) sub = cv2.subtract(minRect, thresh) cnts, hierarchy = cv2.findContours(minRect.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnt = max(cnts, key=cv2.contourArea) x, y, w, h = cv2.boundingRect(cnt) # 使用边界框坐标提取最终的全景图 stitched = stitched[y:y + h, x:x + w] cv2.imwrite('final.jpg', stitched)

上述内容,如有侵犯版权,请联系作者,会自行删文。

推荐阅读:

吐血整理|3D视觉系统化学习路线

那些精贵的3D视觉系统学习资源总结(附书籍、网址与视频教程)

超全的3D视觉数据集汇总

大盘点|6D姿态估计算法汇总(上)

大盘点|6D姿态估计算法汇总(下)

机器人抓取汇总|涉及目标检测、分割、姿态识别、抓取点检测、路径规划

汇总|3D点云目标检测算法

汇总|3D人脸重建算法

那些年,我们一起刷过的计算机视觉比赛

总结|深度学习实现缺陷检测

深度学习在3-D环境重建中的应用

汇总|医学图像分析领域论文

大盘点|OCR算法汇总

重磅!3DCVer-学术交流群已成立

欢迎加入我们公众号读者群一起和同行交流,目前有3D视觉、CV&深度学习、SLAM、三维重建、点云后处理、自动驾驶、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、学术交流、求职交流等微信群,请扫描下面微信号加群,备注:”研究方向+学校/公司+昵称“,例如:”3D视觉 + 上海交大 + 静静“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进去相关微信群。原创投稿也请联系。

▲长按加群或投稿

▲长按关注我们



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭