OpenCV 图像拼接

news2025/5/22 14:07:03

一、图像拼接的介绍

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

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

三、代码实现

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

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)

导入库: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.coloradmin.cn/o/2336905.html

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

相关文章

数学建模AI智能体(4.16大更新)

别的不说就说下面这几点&#xff0c;年初内卷到现在&#xff0c;就现阶段AI水平&#xff0c;卷出了我比较满意的作品&#xff0c;这里分享给各位同学&#xff0c;让你们少走弯路&#xff1a; 1.轻松辅导学生 2.帮助学习 3.突破知识壁垒&#xff0c;缩短与大佬的差距 4.打破…

Linux》》bash 、sh 执行脚本

通常使用shell去运行脚本&#xff0c;两种方法 》bash xxx.sh 或 bash “xxx.sh” 、sh xxx.sh 或 sh “xxx.sh” 》bash -c “cmd string” 引号不能省略 我们知道 -c 的意思是 command&#xff0c;所以 bash -c 或 sh -c 后面应该跟一个 command。

如何用“AI敏捷教练“破解Scrum项目中的“伪迭代“困局?

一、什么是“伪迭代”&#xff1f; “伪迭代”是指团队表面上采用Scrum框架&#xff0c;但实际运作仍沿用瀑布模式的现象。例如&#xff1a;迭代初期开发人员集中编码、末期测试人员突击测试&#xff0c;导致资源分配不均&#xff1b;需求拆分粗糙&#xff0c;团队无法在固定时…

使用 vxe-table 来格式化任意的金额格式,支持导出与复制单元格格式到 excel

使用 vxe-table 来格式化任意的金额格式&#xff0c;支持导出与复制单元格格式到 excel 查看官网&#xff1a;https://vxetable.cn gitbub&#xff1a;https://github.com/x-extends/vxe-table gitee&#xff1a;https://gitee.com/x-extends/vxe-table 安装 npm install vx…

金币捕鱼类手游《海洋管家》源码结构与系统分层解析

在休闲互动类移动应用开发中&#xff0c;捕鱼类项目因玩法成熟、逻辑清晰而成为不少开发者接触多端架构与模块化管理的重要起点。本文以一款名为《海洋管家》的项目源码为样例&#xff0c;简要解析其整体结构与主要功能模块&#xff0c;供有类似项目需求或系统学习目的的开发者…

Go语言实现OAuth 2.0认证服务器

文章目录 1. 项目概述1.1 OAuth2 流程 2. OAuth 2.0 Storage接口解析2.1 基础方法2.2 客户端管理相关方法2.3 授权码相关方法2.4 访问令牌相关方法2.5 刷新令牌相关方法 2.6 方法调用时序2.7 关键注意点3. MySQL存储实现原理3.1 数据库设计3.2 核心实现 4. OAuth 2.0授权码流程…

【2025年认证杯数学中国数学建模网络挑战赛】C题 数据预处理与问题一二求解

目录 【2025年认证杯数学建模挑战赛】C题数据预处理与问题一求解三、数据预处理及分析3.1 数据可视化3.2 滑动窗口相关系数统计与动态置信区间耦合分析模型3.3 耦合关系分析结果 四、问题一代码数据预处理问题一 【2025年认证杯数学建模挑战赛】C题 数据预处理与问题一求解 三…

2025年最新Web安全(面试题)

活动发起人小虚竹 想对你说&#xff1a; 这是一个以写作博客为目的的创作活动&#xff0c;旨在鼓励大学生博主们挖掘自己的创作潜能&#xff0c;展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴&#xff0c;那么&#xff0c;快来参加吧&#xff01…

开源微调混合推理模型:cogito-v1-preview-qwen-32B

一、模型概述 1.1 模型特点 Cogito v1-preview-qwen-32B 是一款基于指令微调的生成式语言模型&#xff08;LLM&#xff09;&#xff0c;具有以下特点&#xff1a; 支持直接回答&#xff08;标准模式&#xff09;和自我反思后再回答&#xff08;推理模式&#xff09;。使用 I…

Golang|Channel 相关用法理解

文章目录 用 channel 作为并发小容器channel 的遍历channel 导致的死锁问题用 channel 传递信号用 channel 并行处理文件用channel 限制接口的并发请求量用 channel 限制协程的总数量 用 channel 作为并发小容器 注意这里的 ok 如果为 false&#xff0c;表示此时不仅channel为空…

C++ - #命名空间 #输入、输出 #缺省参数 #函数重载 #引用 # const 引用 #inline #nullptr

文章目录 前言 一、实现C版本的hello world 二、命名空间 1、namespace 的价值 2、namespace 的定义 (1.域会影响一个编译器编译语法时的查找规则 (2、域会影响生命周期 (3、命名空间域只能定义在全局 (4、编译器会自动合并相同命名空间中的内容 (5、C标准库放在命名…

JSON处理工具/框架的常见类型及详解,以Java语言为例

以下是JSON处理工具/框架的常见类型及详解&#xff0c;以Java语言为例&#xff1a; 一、主流JSON处理工具对比 Jackson&#xff08;推荐&#xff09; 特点&#xff1a;高性能、功能丰富&#xff0c;支持注解&#xff08;如JsonProperty&#xff09;、树形模型&#xff08;Json…

中间件--ClickHouse-1--基础介绍(列式存储,MPP架构,分布式计算,SQL支持,向量化执行,亿万级数据秒级查询)

1、概述 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。它由俄罗斯的互联网巨头Yandex为解决其内部数据分析需求而开发&#xff0c;并于2016年开源。专为大规模数据分析&#xff0c;实时数据分析和复杂查询设计&#xff0c;具有高性能、实时数据和可扩展性等…

Java中的经典排序算法:插入排序、希尔排序、选择排序、堆排序与冒泡排序(如果想知道Java中有关插入排序、希尔排序、选择排序、堆排序与冒泡排序的知识点,那么只看这一篇就足够了!)

前言&#xff1a;排序算法是计算机科学中的基础问题之一&#xff0c;它在数据处理、搜索算法以及各种优化问题中占有重要地位&#xff0c;本文将详细介绍几种经典的排序算法&#xff1a;插入排序、选择排序、堆排序和冒泡排序。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解…

K8S+Prometheus+Consul+alertWebhook实现全链路服务自动发现与监控、告警配置实战

系列文章目录 k8s服务注册到consul prometheus监控标签 文章目录 系列文章目录前言一、环境二、Prometheus部署1.下载2.部署3.验证 三、kube-prometheus添加自定义监控项1.准备yaml文件2.创建新的secret并应用到prometheus3.将yaml文件应用到集群4.重启prometheus-k8s pod5.访…

uniapp-商城-25-顶部模块高度计算

计算高度&#xff1a; 使用computed进行顶部模块的计算。 总高度&#xff1a;bartotalHeight log 介绍--收款码这一条目 也就是上一章节的title的高度计算 bodybarheight。 在该组件中&#xff1a; js部分的代码&#xff1a; 包含了导出的名字&#xff1a; shop-head…

非关系型数据库(NoSQL)与 关系型数据库(RDBMS)的比较

非关系型数据库&#xff08;NoSQL&#xff09;与 关系型数据库&#xff08;RDBMS&#xff09;的比较 一、引言二、非关系型数据库&#xff08;NoSQL&#xff09;2.1 优势 三、关系型数据库&#xff08;RDBMS&#xff09;3.1 优势 四、结论 &#x1f496;The Begin&#x1f496;…

蓝桥杯2024国B数星星

小明正在一棵树上数星星&#xff0c;这棵树有 n 个结点 1,2,⋯,n。他定义树上的一个子图 G 是一颗星星&#xff0c;当且仅当 G 同时满足&#xff1a; G 是一棵树。G 中存在某个结点&#xff0c;其度数为 ∣VG​∣−1。其中 ∣VG​∣ 表示这个子图含有的结点数。 两颗星星不相…

中科院1区顶刊Expert Systems with Applications ESO:增强型蛇形算法,性能不错

Snake Optimizer&#xff08;SO&#xff09;是一种优化效果良好的新颖算法&#xff0c;但由于自然规律的限制&#xff0c;在探索和开发阶段参数更多是固定值&#xff0c;因此SO算法很快陷入局部优化并慢慢收敛。本文通过引入新颖的基于对立的学习策略和新的动态更新机制&#x…

Flask+Influxdb+grafna构建电脑性能实时监控系统

Influx下载地址&#xff0c;这里下载了以下版本influxdb-1.8.5_windows_amd64.zip 运行前需要先启动Influx数据库&#xff1a; 管理员方式运行cmd->F:->cd F:\influxdb\influxdb-1.8.5-1->influxd -config influxdb.conf&#xff0c;以influxdb.conf配置文件启动数…