计算机视觉--图像拼接

图像拼接

  • 单应性变换
    • 仿射变换
    • 图像扭曲实现
    • 图像嵌入(图中图)
  • RANSAC算法
    • 算法介绍
    • 图片收集
    • 无RANSAC优化和有RANSAC优化的代码实现
    • 差别
  • 总结

单应性变换

单应性变换是指一个平面上的点通过一个矩阵变换映射到另一个平面上的点,这个变换矩阵是一个 3 × 3 3 \times 3 3×3 的矩阵,称为单应性矩阵。单应性变换可以分为仿射变换和投影变换两种类型。

仿射变换

在单应性变换中,仿射变换是其中一种特殊的变换。仿射变换是指在变换前后,保持原来的平行线还是平行线,并且保持原来的比例关系不变。一个二维平面上的仿射变换可以表示为:

[ x ′ y ′ 1 ] = [ a b c d e f 0 0 1 ] [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} a & b & c \\ d & e & f \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} xy1 = ad0be0cf1 xy1

其中, ( x , y ) (x,y) (x,y) 是原来平面上的一个点, ( x ′ , y ′ ) (x',y') (x,y) 是变换后平面上的对应点。矩阵中的参数 a , b , c , d , e , f a,b,c,d,e,f a,b,c,d,e,f 决定了变换的效果。

具体地,仿射变换包括平移、旋转、缩放和错切四种基本变换。其中,平移可以通过将矩阵中的 c c c f f f 分别设置为平移的距离 t x t_x tx t y t_y ty 来实现;旋转可以通过将矩阵中的 a a a d d d 设置为 cos ⁡ θ \cos \theta cosθ sin ⁡ θ \sin \theta sinθ b b b e e e 设置为 − sin ⁡ θ -\sin \theta sinθ cos ⁡ θ \cos \theta cosθ 来实现;缩放可以通过将矩阵中的 a a a e e e 分别设置为 s x s_x sx s y s_y sy 来实现;错切可以通过将矩阵中的 b b b 设置为 k x k_x kx d d d 设置为 k y k_y ky 来实现。其中, t x t_x tx t y t_y ty 表示平移的距离, θ \theta θ 表示旋转的角度, s x s_x sx s y s_y sy 表示缩放的比例, k x k_x kx k y k_y ky 表示错切的程度。

图像扭曲实现

以下是原图:
在这里插入图片描述

import cv2
import numpy as np

# 读取图片
img = cv2.imread('background/background.jpg')

# 图像的高和宽
rows, cols = img.shape[:2]

# 设置扭曲前后的三个点的坐标
pts1 = np.float32([[0, 0], [cols - 1, 0], [0, rows - 1]])
pts2 = np.float32([[cols * 0.2, rows * 0.1], [cols * 0.9, rows * 0.2], [cols * 0.1, rows * 0.9]])

# 生成变换矩阵
M = cv2.getAffineTransform(pts1, pts2)

# 进行仿射变换
dst = cv2.warpAffine(img, M, (cols, rows))

# 显示原图和扭曲后的图像
cv2.imshow('image', img)
cv2.imshow('affine', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

扭转后的图片:
在这里插入图片描述

图像嵌入(图中图)

被嵌入图像:在这里插入图片描述
嵌入至以下图像中:在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
from numpy import array
from PCV.geometry import warp
from PIL import Image
from pylab import *
from scipy import ndimage

# 读取两张灰度图像
im1 = cv2.imread(r'background/in.jpg', cv2.IMREAD_GRAYSCALE)
im2 = cv2.imread(r'background/background.jpg', cv2.IMREAD_GRAYSCALE)

# 设置仿射变换的四个点
tp = array([[164,538,540,264],[40,36,405,405],[1,1,1,1]])

# 进行仿射变换
im3 = warp.image_in_image(im1,im2,tp)

# 显示三张图像
figure()
gray()

# 显示第一张图像
imshow(im1)
show()

# 显示第二张图像
imshow(im2)
show()

# 显示第三张图像
imshow(im3)
show()

嵌入后的结果:
在这里插入图片描述

RANSAC算法

算法介绍

RANSAC(Random Sample Consensus)是一种基于随机采样的迭代算法,用于估计数据集中的模型参数。它主要用于从一组有噪声的数据中估计出一个最优的数学模型。

下面是 RANSAC 算法的基本流程:

  1. 随机采样:从原始数据中随机选择一定数量的样本来构造一个初始模型,这个初始模型可以用来进行后续的计算。

  2. 模型拟合:使用所选样本来拟合一个数学模型。

  3. 内点选择:计算每个数据点到所估计的模型的距离,如果距离小于给定的阈值,则将该数据点视为“内点”,否则将其视为“外点”。

  4. 判断收敛:判断是否有足够的内点,如果内点数目达到了一定的阈值,就认为模型已经收敛。

  5. 重复以上步骤:重复以上步骤,直到满足收敛条件或达到预先设定的最大迭代次数。

图片收集

图片就地取材,在宿舍后方连续拍摄2张可拼接的图片:
在这里插入图片描述

无RANSAC优化和有RANSAC优化的代码实现

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

def blend_images(s, t):
    # 给图片加边框
    s = cv.copyMakeBorder(s, 50, 50, 0, 250, cv.BORDER_CONSTANT, value=(0,0,0))
    t = cv.copyMakeBorder(t, 50, 50, 0, 250, cv.BORDER_CONSTANT, value=(0,0,0))

    # 获取图片大小
    w,h = s.shape[:2]

    # 转换为灰度图
    g1 = cv.cvtColor(s, cv.COLOR_BGR2GRAY)
    g2 = cv.cvtColor(t, cv.COLOR_BGR2GRAY)

    # 创建SIFT对象
    sift = cv.SIFT_create()

    # 检测关键点和计算描述符
    k1, d1 = sift.detectAndCompute(g1, None)
    k2, d2 = sift.detectAndCompute(g2, None)

    # 创建FLANN匹配器
    F = cv.FlannBasedMatcher(dict(algorithm=1, trees=5), dict(checks=50))

    # 匹配关键点
    m = F.knnMatch(d1, d2, k=2)
    mask = [[0, 0] for _ in range(len(m))]

    # 根据阈值筛选匹配点
    l1 = [j for i, (j, k) in enumerate(m) if j.distance < 0.7 * k.distance]
    mask = [[1, 0] if j.distance < 0.7 * k.distance else [0, 0] for i, (j, k) in enumerate(m)]

    # 获取图片大小
    r, c = s.shape[:2]

    # 如果匹配点大于10个,计算单应性矩阵
    if len(l1) > 10:
        sc = np.float32([k1[m.queryIdx].pt for m in l1]).reshape(-1, 1, 2)
        ds = np.float32([k2[m.trainIdx].pt for m in l1]).reshape(-1, 1, 2)
        M, mask = cv.findHomography(sc, ds, cv.RANSAC, 4.0)
        MM = np.array(M)
        # 透视变换
        wimg = cv.warpPerspective(t, np.array(MM), (t.shape[1], t.shape[0]), flags=cv.WARP_INVERSE_MAP)

        # 获取左右边界
        left = next(col for col in range(0, c) if s[:, col].any() and wimg[:, col].any())
        right = next(col for col in range(c-1, 0, -1) if s[:, col].any() and wimg[:, col].any())

        # 创建结果图像
        res = np.zeros([r, c, 3], np.uint8)
        for row in range(0, r):
            for col in range(0, c):
                if not s[row, col].any():
                    res[row, col] = wimg[row, col]
                elif not wimg[row, col].any():
                    res[row, col] = s[row, col]
                else:
                    slen = float(abs(col - left))
                    tlen = float(abs(col - right))
                    alpha = slen / (slen + tlen)
                    # 混合图像
                    res[row, col] = np.clip(s[row, col] * (1-alpha) + wimg[row, col] * alpha, 0, 255)

        # 以下为不做ransac处理的结果结果
        M0, mask0 = cv.findHomography(sc, ds, cv.RANSAC, 0)
        MM0 = np.array(M0)
        wimg0 = cv.warpPerspective(t, np.array(MM0), (t.shape[1], t.shape[0]), flags=cv.WARP_INVERSE_MAP)
        res0 = np.zeros([r, c, 3], np.uint8)
        for row in range(0, r):
            for col in range(0, c):
                if not s[row, col].any():
                    res0[row, col] = wimg0[row, col]
                elif not wimg0[row, col].any():
                    res0[row, col] = s[row, col]
                else:
                    slen = float(abs(col - left))
                    tlen = float(abs(col - right))
                    alpha = slen / (slen + tlen)
                    # 混合图像
                    res0[row, col] = np.clip(s[row, col] * (1-alpha) + wimg0[row, col] * alpha, 0, 255)
        return res, res0

# 读取左右两张图片
i1 = cv.imread(r'left.jpg')
i2 = cv.imread(r'right.jpg')
# 调整图片大小
i1 = cv.resize(i1,(756,1008))
i2 = cv.resize(i2,(756,1008))

res, res0 = blend_images(i1, i2)

# 展示结果
plt.subplot(1, 2, 1)
plt.imshow(cv.cvtColor(res0, cv.COLOR_BGR2RGB))
plt.title('res0')
plt.subplot(1, 2, 2)
plt.imshow(cv.cvtColor(res, cv.COLOR_BGR2RGB))
plt.title('res')
plt.show()

# 保存结果
cv.imwrite('res0.jpg', res0)
cv.imwrite('res.jpg', res)

生成的全景结果:
在这里插入图片描述

差别

理论上这两种方法应该会存在些许差别,但是通过这次实验我们很难看出,当然我在本机上也运行过其他图片,拼接的结果都几乎相似。

总结

本次实验主要涉及到计算机视觉领域中图像特征提取、匹配、单应性矩阵估计以及图像融合等方面的内容。这一过程中,我们使用了一些常用的库和工具,例如numpy、cv2和matplotlib等。通过学习本实验,我对图像拼接的原理、方法和实现方式有了更深入的了解和认识,同时也掌握了使用OpenCV进行图像拼接的基本技能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端网页设计必逛的六个宝藏网站(非常值得收藏)

&#x1f389;个人主页&#xff1a;这个昵称我想了20分钟 ✨往期专栏&#xff1a; 【速成之路】jQuery 【SQL server速成之路】 素材网站 ✨iconfont阿里巴巴矢量图标库  ✨美叶  ✨IconPark  ✨pexels  ✨COLOR  ✨Uigradients ✨iconfont阿里巴巴矢量图标库 网站入…

使用Vue+axios+Vuex实现登录后前端数据本地化存储实战

前言 这已经是《Vue + SpringBoot前后端分离项目实战》专栏的前端部分第8篇博客了,服务端部分由天哥(天哥主页)负责,目前专栏目录如下: Vue + SpringBoot前后端分离项目实战 - 前端部分1. 手把手带你做一套毕业设计-征程开启2. 我应该把毕业设计做到什么程度才能过关?3.…

【burpsuite安全练兵场-服务端8】文件上传漏洞-7个实验(全)

前言&#xff1a; 介绍&#xff1a; 博主&#xff1a;网络安全领域狂热爱好者&#xff08;承诺在CSDN永久无偿分享文章&#xff09;。 殊荣&#xff1a;CSDN网络安全领域优质创作者&#xff0c;2022年双十一业务安全保卫战-某厂第一名&#xff0c;某厂特邀数字业务安全研究员&…

CTF比赛必备常用工具

文中介绍的所有工具&#xff0c;均在压缩包中&#xff0c;结合本文更便于大家下载使用&#xff0c;快速上手。 CTF常用工具下载 CTF比赛必备常用工具 一、什么是CTF二、比赛中工具的重要性三、常用MISC&#xff08;杂项&#xff09;工具1. Audacity &#xff08;提取莫斯密码辅…

HTML5 <s> 标签、HTML5 <sub> 和 <sup> 标签

HTML5 <s> 标签 定义和用法 <s> 标签定义加删除线的文本。HTML 5 中不再支持这个标签。请使用 CSS 代替。 HTML 4.01 与 HTML 5 之间的差异 在 HTML 4.01 中不赞成使用 <s> 标签。 在 HTML 5 中不支持 <s> 标签。 提示和注释 提示&#xff1a;请…

【Unity3D】材质 Material ( 材质简介 | 创建材质 | 设置材质属性 | 对 3D 物体应用材质 | 资源拖动到 Inspector 检查器中的 Material 属性中 )

文章目录 一、材质 Material 简介二、创建材质三、设置材质属性四、对 3D 物体应用材质五、资源拖动到 Inspector 检查器中的 Material 属性中 一、材质 Material 简介 材质 Material 用于描述 3D 物体的 表面细节 : 颜色 : 物体的颜色金属 : 物体是否是 金属材质光滑度 : 物…

【软考数据库】第十章 系统开发与运行

目录 10.1 系统实施 10.1.1 信息系统生命周期 10.1.2 能力成熟度模型 10.1.3 软件过程开发模型 10.1.4 信息系统开发方法 10.1.5 系统分析与设计 10.1.6 结构化开发 10.2 系统测试 10.2.1 测试原则和方法 10.2.2 测试阶段 10.2.3 测试用例设计 10.2.4 调试 10.2.…

【OAI】部署5GSA独立组网网络切片例程及例程解析

文章目录 摘要引言关键技术介绍5G核心网核心网网元功能 网络切片OAIDocker官方例程详解整体介绍具体详解网络切片架构部署概览与说明1-41.预先准备5.部署OAI 5G核心网6.获取基站仿真docker镜像7.部署基站仿真8.通信测试9.分析结果10. 使用多切片的UE11. 解除部署11.1解除RAN部署…

【Axure教程】中继器表格寻找和标记数据

在系统表格中&#xff0c;我们想在表格中快速找到对应的数据&#xff0c;通常我们会用条件筛选来完成&#xff0c;但是用筛选的方式&#xff0c;其他数据就看不到了&#xff0c;少了两种条件之间的对比。所以如果需要数据对比的情况下&#xff0c;我们更多的是用标记数据的方式…

基于 DDR3 的串口传图帧缓存系统设计实现(fifo2mig_axi )

文章目录 前言一、接口转换模块设计二、fifo2mig_axi 模块二、接口转换模块仿真四、fifo2mig_axi_tb五、仿真展示 前言 结合串口接收模块和 tft 显示屏控制模块&#xff0c;设计一个基于 DDR3 的串口传图帧缓存系统。 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面…

电影《银河护卫队3》观后感

上周看了电影《银河护卫队3》&#xff0c;本部电影&#xff0c;主要是围绕着主角团队中的一个队员展开叙事的&#xff0c;在团队中&#xff0c;这名队员叫“火箭”&#xff0c;是一只经过基因改造过的浣熊。 当初进行改造的团队&#xff0c;是一家拥有基因改造技术的团队&…

程序员:面试造飞机,入职拧螺丝?真难···

刚开始工作的时候&#xff0c;我也想不通这个问题&#xff0c;甚至很鄙视这种现象。后面当了面试官&#xff0c;做到了公司中层管理&#xff0c;也会站在公司以及行业角度去重新思考这个问题。 为什么这种现象会越来越普遍呢&#xff1f;尤其在 IT 行业愈加明显。 面试看的是…

命令firewalld和firewall-cmd用法

firewalld命令跟firewall-cmd 1.启动firewalld服务 systemctl start firewalld.service2.关闭firewalld服务 systemctl stop firewalld.service3.重启firewalld服务 systemctl restart firewalld.service4.查看firewalld状态 systemctl status firewalld.service5.开机自启…

chatgpt-4它的未来是什么?该如何应用起来?

在当今快节奏的数字通信世界中&#xff0c;ChatGPT已成为一个强大的在线聊天平台&#xff0c;改变了人们互动和沟通的方式。凭借其先进的AI功能、用户友好的界面和创新技术&#xff0c;ChatGPT已成为个人和企业的热门选择。 然而&#xff0c;ChatGPT的未来有望更加激动人心和具…

Java多线程synchronized Lock volatile,看完这一篇就够了

线程安全问题 一、对线程安全的理解&#xff08;实际上是内存安全&#xff09;二、线程同步的实现方法synchronized实现同步Lock实现同步volatile实现同步JUC的Callable实现同步 三、守护线程四、ThreadLocal原理和使用场景五、Thread类的常用方法&#xff0c;以及线程的状态六…

junit如何在多模块项目中使用

文章目录 前言一、最简单的单元测试二、springboot多模块测试单元1.问题2.解决 总结 前言 相信后端的小伙伴对于junit测试应该不陌生,当我们写好了一些功能之后,由于不太放心是否会出现问题,我们会选择自测; 第一种 通过类似postman之类的,直接走接口测试第二种 由于构造数据…

Spring Boot集成ShardingSphere实现数据加密及数据脱敏 | Spring Cloud 48

一、前言 通过以下系列章节&#xff1a; Spring Boot集成ShardingSphere实现数据分片&#xff08;一&#xff09; | Spring Cloud 40 Spring Boot集成ShardingSphere实现数据分片&#xff08;二&#xff09; | Spring Cloud 41 Spring Boot集成ShardingSphere实现数据分片&…

SoringBoot——pom文件:starter

先来看一看&#xff1a; 这次我们来介绍SpringBoot的pom文件的另一个好玩的地方&#xff1a;starter。 starter的中文含义是启动器&#xff0c;所以有时候我们在Maven仓库找依赖的时候&#xff0c;如果开启了自动翻译就会经常会看见一个奇怪的词叫做某某弹簧启动器&#xff0…

tomcat集群下的session共享和负载均衡(memcache实现)

环境 操作系统&#xff1a;windows tomcat1&#xff1a;Apache Tomcat/7.0.52&#xff08;8085&#xff09; tomcat2&#xff1a;Apache Tomcat/7.0.52&#xff08;8086&#xff09; jdk&#xff1a;1.8.0_251 nginx&#xff1a;nginx-1.20.1&#xff08;8070&#xff09; memc…

day4-字符设备驱动基础上_基础框架

大纲&#xff1a; 设备分类 申请和注销设备号 函数指针复习 注册字符设备 字符设备驱动框架解析 读操作实现 写操作实现 ioctl操作实现 printk 多个次设备的支持 一、Linux内核对设备的分类 linux的文件种类&#xff1a; -&#xff1a;普通文件 (文件内容、文件名、…