OpenCV 图像拼接

一、图像拼接的介绍

图像拼接是一种将多幅具有部分重叠内容的图像合并成一幅完整、无缝且具有更广阔视野或更高分辨率图像的技术。其目的是通过整合多个局部图像来获取更全面、更具信息价值的图像内容。

二、图像拼接的原理
图像拼接的核心目标是将多幅有重叠区域的图像进行准确对齐和融合,形成一个连续、无缝的大图像。其基本原理主要包括以下几个关键步骤:
特征提取:在每幅图像中寻找具有代表性的特征点,如角点、边缘点等。常用的特征提取算法有 SIFT(尺度不变特征变换)、SURF(加速稳健特征)和 ORB(Oriented FAST and Rotated BRIEF)等。这些特征点具有独特的属性,能够在不同的光照、尺度和旋转条件下保持相对稳定。
特征匹配:对不同图像中的特征点进行匹配,找出它们之间的对应关系。这一步骤的目的是确定哪些特征点来自同一物理位置,从而为后续的图像对齐提供基础。常见的特征匹配方法有暴力匹配(Brute-Force Matching)和基于快速最近邻搜索库(FLANN)的匹配等。
图像对齐:根据特征匹配的结果,计算出图像之间的变换关系,如旋转、平移和缩放等。然后使用这些变换关系将图像进行对齐,使得它们的重叠区域能够精确重合。常用的变换模型有仿射变换和透视变换。
图像融合:将对齐后的图像进行融合,消除拼接处的明显痕迹,使拼接后的图像看起来自然、连续。融合的方法有多种,如简单的平均融合、渐入渐出融合等。

三、代码实现

1.导入库与定义辅助函数

import cv2
import numpy as np
import sysdef cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)def detectAndDescribe(image):gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)  # 将彩色图片转换成灰度图descriptor = cv2.SIFT_create()(kps, des) = descriptor.detectAndCompute(gray, None)  # 将结果转换成NumPy数组kps_float = np.float32([kp.pt for kp in kps])return (kps, kps_float, des)

导入库:cv2 是 OpenCV 库,用于图像处理;numpy 用于数值计算;sys 用于系统相关操作,如退出程序。
cv_show 函数:该函数用于显示图像,cv2.imshow 用于在窗口中显示图像,cv2.waitKey(0) 表示无限等待用户按下任意键,以保持窗口显示。
detectAndDescribe 函数:将输入的彩色图像转换为灰度图像,然后使用 SIFT(尺度不变特征变换)算法检测关键点并计算描述符。最后将关键点的坐标转换为 float32 类型的 NumPy 数组,返回关键点、关键点坐标数组和描述符。

2.读取图像并提取特征

imageA = cv2.imread("1.jpg")
cv_show('imageA', imageA)
imageB = cv2.imread("2.jpg")
cv_show('imageB', imageB)
(kpsA, kps_floatA, desA) = detectAndDescribe(imageA)
(kpsB, kps_floatB, desB) = detectAndDescribe(imageB)

读取图像:使用 cv2.imread 函数读取两张图像 1.jpg 和 2.jpg,并使用 cv_show 函数显示这两张图像。
提取特征:调用 detectAndDescribe 函数分别对两张图像进行特征提取,得到每张图像的关键点、关键点坐标数组和描述符。

3.特征匹配与筛选

matcher = cv2.BFMatcher()
rawMatches = matcher.knnMatch(desB, desA, 2)
good = []
matches = []
for m in rawMatches:if len(m) == 2 and m[0].distance < 0.65 * m[1].distance:good.append(m)matches.append((m[0].queryIdx, m[0].trainIdx))
print(len(good))
print(matches)
vis = cv2.drawMatchesKnn(imageB, kpsB, imageA, kpsA, good, None, flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
cv_show("Keypoint Matches", vis)

特征匹配:创建一个暴力匹配器 cv2.BFMatcher,使用 knnMatch 方法对图像 B 和图像 A 的描述符进行匹配,k=2 表示为每个描述符找到两个最近邻匹配。
筛选匹配结果:遍历所有匹配结果,若匹配结果包含两个元素且第一个匹配的距离小于第二个匹配距离的 0.65 倍,则认为该匹配是可靠的,将其添加到 good 列表中,并记录匹配点的索引到 matches 列表中。
显示匹配结果:打印可靠匹配的数量和匹配点的索引,使用 cv2.drawMatchesKnn 函数绘制匹配点,并使用 cv_show 函数显示匹配结果。

4.透视变换矩阵计算

# 透视变换
if len(matches) > 4:ptsB = np.float32([kps_floatB[i] for (i, _) in matches])  # matches是通过阈值筛选之后的特征点对象ptsA = np.float32([kps_floatA[i] for (_, i) in matches])(H, mask) = cv2.findHomography(ptsB, ptsA, cv2.RANSAC, 10)
else:print('图片未找到4个以上的匹配点')sys.exit()

判断匹配点数量:如果可靠匹配点的数量大于 4,则可以进行透视变换矩阵的计算。
提取匹配点坐标:从关键点坐标数组中提取可靠匹配点的坐标,分别存储在 ptsB 和 ptsA 中。
计算透视变换矩阵:使用 cv2.findHomography 函数,采用 RANSAC 算法计算图像 B 到图像 A 的透视变换矩阵 H。
处理匹配点不足的情况:如果可靠匹配点的数量小于等于 4,则打印提示信息并退出程序。

5.图像拼接与显示

result = cv2.warpPerspective(imageB, H, (imageB.shape[1] + imageA.shape[1], imageB.shape[0]))
cv_show('resultB', result)
result[0:imageA.shape[0], 0:imageA.shape[1]] = imageA
cv_show('result', result)

透视变换:使用 cv2.warpPerspective 函数将图像 B 进行透视变换,变换后的图像大小为图像 B 和图像 A 的宽度之和,高度为图像 B 的高度。
显示透视变换后的图像:使用 cv_show 函数显示透视变换后的图像。
图像拼接:将图像 A 复制到透视变换后的图像的左上角,实现图像拼接。
显示拼接结果:使用 cv_show 函数显示最终的拼接结果。

综上所述,这段代码的主要功能是读取两张图像,提取图像的 SIFT 特征,进行特征匹配和筛选,计算透视变换矩阵,将图像 B 进行透视变换并与图像 A 进行拼接,最后显示拼接结果。

完整代码:

import cv2
import numpy as np
import sys
def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)
def detectAndDescribe(image):gray =cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)# 将彩色图片转换成灰度图descriptor =cv2.SIFT_create()#(kps, des) = descriptor.detectAndCompute(gray, None)  # 将结果转换成NumPy数组kps_float = np.float32([kp.pt for kp in kps])return (kps,kps_float,des)
imageA = cv2.imread("1.jpg")
cv_show( 'imageA',imageA)
imageB = cv2.imread("2.jpg")
cv_show( 'imageB',imageB)
(kpsA,kps_floatA, desA)= detectAndDescribe(imageA)
(kpsB,kps_floatB,desB)= detectAndDescribe(imageB)
matcher =cv2.BFMatcher()
rawMatches =matcher.knnMatch(desB,desA,2)
good =[]
matches =[]
for m in rawMatches:if len(m) == 2 and m[0].distance < 0.65 * m[1].distance:good.append(m)matches.append((m[0].queryIdx, m[0].trainIdx))
print(len(good))
print(matches)
vis = cv2.drawMatchesKnn(imageB, kpsB, imageA, kpsA, good,  None,flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
cv_show( "Keypoint Matches",vis)
# 透视变换
if len(matches)>4:ptsB = np.float32([kps_floatB[i]for(i,_)in matches])# matches是通过阈值鍗选之后的特征点对象ptsA = np.float32([kps_floatA[i]for(_,i)in matches])(H, mask) = cv2.findHomography(ptsB, ptsA, cv2.RANSAC, 10)
else:print('图片未找到4个以上的匹配点')sys.exit()
result = cv2.warpPerspective(imageB, H,  (imageB.shape[1]+ imageA.shape[1], imageB.shape[0]))
cv_show( 'resultB',result)
result[0:imageA.shape[0],0:imageA.shape[1]]= imageA
cv_show( 'result', result)

结果显示:

四、图像拼接的优缺点

图像拼接是将多幅具有重叠区域的图像拼接成一幅全景图像或高分辨率图像的技术。以下是图像拼接的一些优缺点:
优点
获得全景视野:可以将多幅局部图像拼接成一幅全景图像,提供更广阔的视野,让人们能够更全面地观察场景。例如,在拍摄大型风景、建筑或活动场景时,通过图像拼接可以将多个局部画面组合成一个完整的全景画面,展现出更宏大的场景。
提高图像分辨率:通过将多幅低分辨率图像拼接在一起,可以在一定程度上提高图像的整体分辨率。这对于一些需要高分辨率图像的应用,如医学图像分析、卫星图像观测等非常有帮助,可以获取更多的细节信息。
增强图像信息:拼接过程中,由于多幅图像的重叠部分包含了相同场景的不同视角信息,拼接后的图像能够融合这些信息,从而增强图像的细节和纹理,使图像更加清晰和准确。
灵活性高:可以根据需要选择不同的图像进行拼接,适应各种不同的拍摄环境和需求。例如,在不同时间、不同角度拍摄的图像,只要有适当的重叠区域,都可以进行拼接,以获得独特的视觉效果或满足特定的分析要求。
缺点
图像配准难度:要实现精确的图像拼接,需要准确地找到多幅图像之间的对应关系,即进行图像配准。如果图像的特征不明显、存在光照变化、视角差异较大等情况,图像配准就会变得困难,可能导致拼接结果出现错位、变形等问题。
拼接算法复杂度:为了获得高质量的拼接效果,需要使用复杂的算法来处理图像的融合、消除拼接缝等问题。这些算法通常需要较高的计算资源和时间成本,特别是对于高分辨率图像或大量图像的拼接,计算量会显著增加,可能导致拼接过程缓慢。
光照和色彩不一致:不同图像之间可能由于拍摄时间、光线条件、相机设置等因素而存在光照和色彩差异。在拼接过程中,如果不进行有效的处理,这些差异会在拼接处形成明显的边界或色彩突变,影响拼接图像的视觉效果和质量。
遮挡和运动模糊:如果在拍摄过程中,场景中有物体发生运动,或者不同图像之间存在遮挡情况,那么在拼接时就会出现问题。运动物体在不同图像中的位置不同,可能导致拼接后出现重影或模糊;而遮挡会使图像的重叠区域信息不完整,影响拼接的准确性。

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

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

相关文章

第十一章 网络编程

在TCP/IP协议中&#xff0c;“IP地址TCP或UDP端口号”唯一标识网络通讯中的一个进程。 因此可以用Socket来描述网络连接的一对一关系。 常用的Socket类型有两种&#xff1a;流式Socket&#xff08;SOCK_STREAM&#xff09;和数据报式Socket&#xff08;SOCK_DGRAM&#xff09…

ffmpeg实现视频流抽帧

ffmpeg 实现视频流抽帧 抽取实时视频帧 如果你的实时视频是通过 RTSP、UDP 或其他协议获取的&#xff0c;可以直接调用 FFmpeg 命令来抽取帧。 ffmpeg 命令 示例 1 ffmpeg -i rtsp://your_rtsp_stream_url -vf fps1 -update 1 output.jpg说明&#xff1a; -i rtsp://your…

【GIT】放弃”本地更改,恢复到远程仓库的状态git fetch origin git reset --hard origin/分支名

如果你想完全放弃本地更改&#xff0c;恢复到远程仓库的状态&#xff0c;可以按照以下步骤操作&#xff1a; 获取远程最新版本 首先执行&#xff1a; git fetch origin这条命令会把远程仓库的最新提交拉取到你的本地&#xff0c;但不会自动合并到你的当前分支。 硬重置你的当前…

flutter doctor 信号号超时

报错如下&#xff1a; :\Users\Administrator>flutter doctor Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel stable, 3.27.4, on Microsoft Windows [版本 10.0.22631.5189], locale zh-CN) [√] Windows Version (Installed versi…

【Linux】系统入门

【Linux】系统初识 起源开源 闭源版本内核内核编号 Linux的安装双系统(不推荐)WindowsLinuxvmware虚拟机vitualbox操作系统的镜像centos 7/ubuntu云服务器租用 Linux的操作lsmkdir 文件名pwdadduser userdel -rrm文件名cat /proc/cpuinfolinux支持编程vim code.c./a.out 运行程…

mybatis-plus整合springboot与使用方式

注解 TableField(exist false)&#xff1a;表示该属性不为数据库表字段&#xff0c;但又是必须使用的。 整合springboot pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xs…

[第十六届蓝桥杯 JavaB 组] 真题 + 经验分享

A&#xff1a;逃离高塔(AC) 这题就是简单的签到题&#xff0c;按照题意枚举即可。需要注意的是不要忘记用long&#xff0c;用int的话会爆。 &#x1f4d6; 代码示例&#xff1a; import java.io.*; import java.util.*; public class Main {public static PrintWriter pr ne…

GPU服务器声音很响可以怎么处理

当GPU服务器运行时噪音过大&#xff0c;通常是由于高负载下散热风扇高速运转所致。以下是分步骤的解决方案&#xff0c;帮助您有效降低噪音并保持设备稳定运行&#xff1a; 一、排查噪音来源 定位声源 • 使用 声级计 或手机分贝检测APP&#xff0c;确定最大噪音位置&#xff0…

STM32平衡车开发实战教程:从零基础到项目精通

STM32平衡车开发实战教程&#xff1a;从零基础到项目精通 一、项目概述与基本原理 1.1 平衡车工作原理 平衡车是一种基于倒立摆原理的两轮自平衡小车&#xff0c;其核心控制原理类似于人类保持平衡的过程。当人站立不稳时&#xff0c;会通过腿部肌肉的快速调整来维持平衡。平…

C#设计模式-状态模式

状态模式案例解析&#xff1a;三态循环灯的实现 案例概述 本案例使用 状态模式&#xff08;State Pattern&#xff09; 实现了一个 三态循环灯 的功能。每点击一次按钮&#xff0c;灯的状态会按顺序切换&#xff08;状态1 → 状态2 → 状态3 → 状态1...&#xff09;&#xff…

Mac系统升级node.js版本和npm版本并安装pnpm

1.升级node.js版本 第一步&#xff1a;查询当前node.js版本 node -v第二步&#xff1a;清除node.js的缓存 sudo npm cache clean -f第三步&#xff1a;验证缓存是否清空 npm cache verify第四步&#xff1a;安装n工具&#xff0c;n工具是专门用于管理node.js版本的工具 su…

[net 5] udp_dict_server 基于udp的简单字典翻译(服务器与业务相分离)

目录 1. 功能了解 1.1. 啥是 dic_server? 1.2. dic_server 的小目标 2. 基本框架 2.1. 基本文件框架 2.2. 业务与服务器解耦 -> 回调函数 3. 字典 3.1. 字典配置文件 3.2. 构建字典类 3.2.1. 字典类的基本成员 3.2.2. 字典类构造 3.2.2.1. 构造 3.2.2.2. 信息加…

七种驱动器综合对比——《器件手册--驱动器》

九、驱动器 名称 功能与作用 工作原理 优势 应用 隔离式栅极驱动器 隔离式栅极驱动器用于控制功率晶体管&#xff08;如MOSFET、IGBT、SiC或GaN等&#xff09;的开关&#xff0c;其核心功能是将控制信号从低压侧传输到高压侧的功率器件栅极&#xff0c;同时在输入和输出之…

EM储能网关ZWS智慧储能云应用(8) — 电站差异化支持

面对不同项目、种类繁多的储能产品&#xff0c;如何在储能云平台上进行电站差异化支持尤为关键&#xff0c;ZWS智慧储能云从多方面支持储能电站差异化。 简介 随着行业发展&#xff0c;市场“内卷”之下&#xff0c;各大储能企业推陈出新的速度加快。面对不同项目、种类繁多…

图像预处理-色彩空间补充,灰度化与二值化

一.图像色彩空间转换 1.1 HSV颜色空间 HSV颜色空间使用色调&#xff08;Hue&#xff09;、饱和度&#xff08;Saturation&#xff09;和亮度&#xff08;Value&#xff09;三个参数来表示颜色 一般对颜色空间的图像进行有效处理都是在HSV空间进行的&#xff0c;然后对于基本…

Midnight Flag CTF 2025

周末还是三个比赛&#xff0c;可惜不好弄。不是远端连不上就是远端打不开。再有就是太难了。 Crypto ABC 这个题还是不算难的。给了两个30位数的平方和&#xff0c;并且pu1*baser0,qu2*baser1其中r 都很小&#xff0c;可以copper。 只是sage里的two_squres不管用&#xff0…

深度学习--激活函数

激活函数通过计算加权和并加上偏置来确定神经元是否应该倍激活&#xff0c;它们将输入信号转换为输出的可微运算。大多数激活函数都是非线性的&#xff0c;由于激活函数是深度学习的基础&#xff0c;下面简要介绍一些常见的激活函数。 1 RelU函数 最受欢迎的激活函数是修正线性…

深入解析 OrdinalEncoder 与 OneHotEncoder:核心区别与实战应用

标题&#xff1a;深入解析 OrdinalEncoder 与 OneHotEncoder&#xff1a;核心区别与实战应用 摘要&#xff1a; 本文详细探讨了机器学习中类别特征编码的两种核心方法——OrdinalEncoder 和 OneHotEncoder。通过对比两者的功能、特点、适用场景及代码实现&#xff0c;帮助读者…

CTF web入门之命令执行 完整版

web29 文件名过滤 由于flag被过滤,需要进行文件名绕过,有以下几种方法: 1.通配符绕过 fla?.* 2.反斜杠绕过 fl\ag.php 3.双引号绕过 fl’‘ag’.php 还有特殊变量$1、内联执行等 此外 读取文件利用cat函数,输出利用system、passthru 、echo echo `nl flag.php`; ec…

【Linux实践系列】:用c/c++制作一个简易的进程池

&#x1f525; 本文专栏&#xff1a;Linux Linux实践项目 &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 人生没有标准答案&#xff0c;你的错题本也能写成传奇。 ★★★ 本文前置知识&#xff1a; 匿名管道 1.前置知识回顾…