计算机视觉--图像拼接

news2024/12/29 0:02:40

图像拼接

  • 单应性变换
    • 仿射变换
    • 图像扭曲实现
    • 图像嵌入(图中图)
  • 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进行图像拼接的基本技能。

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

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

相关文章

java聊天室的设计与实现代码

聊天室是一个简单的通信应用&#xff0c;可以帮助您与客户和朋友保持联系&#xff0c;并且可以让您更轻松地与其他员工联系。然而&#xff0c;您将不得不确保每个人都知道他们正在做什么。 一旦聊天室开始&#xff0c;它就会变得非常复杂&#xff0c;因为有许多用户可能会同时登…

【三十天精通Vue 3】第二十五天 Vue3 与 Axios 后端数据交互

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录 引言一、Vue3 与 Axios 概述二、Axios 安装与基本使用2.1 安装 Ax…

NECCS|全国大学生英语竞赛C类|词汇和语法|语法题|时态|22:30~11:44

15题 10min 10:20&#xff5e;10:25 test2 10:25&#xff5e;10:47 test1订正 10:44&#xff5e;11:47 理论学习 涉及的语法点主要包括&#xff1a; 动词的时态和语态 非谓语动词 虚拟语气 主谓一致 倒装句 强调句 比较级 名词性从句 定语…

【SQL篇】面试篇之子查询

1303 求团队人数 # 写法1 # Write your MySQL query statement below select employee_id, count(*) over(partition by team_id) as team_size from Employee# 写法2 # Write your MySQL query statement below select employee_id, team_size from Employee e join (select t…

优雅编程,从空格、空行、缩进、注释开始

很多初学者的代码其实都不够“漂亮”&#xff0c;那是因为没有养成好的编码习惯。本篇博客以C语言为例&#xff0c;总结一些好习惯。其实&#xff0c;很多习惯都是肌肉记忆&#xff0c;举个例子&#xff1a;请你写一个程序&#xff0c;输入2个整数并输出它们的和。有些朋友可能…

springboot+vue前后端分离项目打包成jar包及运行

将 Spring Boot 和 Vue.js 项目打包成 jar 包需要按照以下步骤操作&#xff1a; 在项目的根目录中&#xff0c;使用命令行进入 Vue.js 项目的根目录&#xff0c;然后运行以下命令&#xff1a; npm run build这个命令将会构建 Vue.js 项目&#xff0c;并在项目的 dist 目录中生…

Rust-Rocket框架笔记

Rust-Rocket框架笔记 Rocket-Learn-docRocket Addr视频地址 What is RocketQuickStart下载Rocket-Rust运行Rust-Rocket-Hello-错误-端口占用解决查看端口占用情况添加Rocket.toml配置文件更改Rocket默认启动端口启动成功 GetStart-Hello world创建项目cargoIDEA 添加依赖添加Ro…

使用eclipse创建一个图书管理系统(2)---------逻辑的实现

就像使用C语言写代码一样。比如要用​​​​​​C语言写一个小游戏的代码&#xff0c;我们的逻辑实现是在哪里实现的啊&#xff1f;是不是在一个test.c源文件里面啊&#xff1f;没错&#xff0c;就是的&#xff01;在之前的文章里&#xff0c;我说过我定义的Main函数就像C语言里…

【《中国工业经济》数据复现】数字化转型与企业分工:专业化还是纵向一体化

一.研究内容 本文使用机器学习方法刻画微观企业数字化水平&#xff0c;并在构建数理模型的基础上实证考察了企业数字化转型对企业分工的影响及其机理。结果表明&#xff0c;企业数字化转型显著提升了中国上市企业专业化分工水平。机制分析表明&#xff0c;数字化转型对企业专业…

实现chatgpt自然对话

1.概述 ChatGPT是当前自然语言处理领域的重要进展之一&#xff0c;通过预训练和微调的方式&#xff0c;ChatGPT可以生成高质量的文本&#xff0c;可应用于多种场景&#xff0c;如智能客服、聊天机器人、语音助手等。本文将详细介绍ChatGPT的原理、实战演练和流程图&#xff0c…

C/C++每日一练(20230503)

目录 1. 输出最长的递增数字字符串 &#x1f31f;&#x1f31f; 2. 缺失的第一个正数 &#x1f31f;&#x1f31f;&#x1f31f; 3. 最大矩形 &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日…

SaaS模医学检验信息管理系统源码,系统预设几十种报告模板,可在几分钟内批量生成报告

实验室信息管理系统云LIS源码 SaaS模式运维管理系统 云LIS系统源码是一款全面的实验室信息管理系统源码&#xff0c;其主要功能包括样本管理、检测项目管理、质控管理、报告管理、数据分析、两癌筛查等多个方面。具有独立的配套SaaS模式运维管理系统&#xff0c;支持远程运维&…

Redis高频面试题,使用场景

一、缓存 1、什么是缓存穿透 ? 怎么解决 ? 缓存穿透 查询一个不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致每次请求都查数据库。 解决 方案一&#xff1a;缓存空数据&#xff0c;查询返回的数据为空&#xff0c;仍把这个空结果进行…

《花雕学AI》28:革命性的 ChatGPT for SEO——让您的排名飙升 50%!

引言&#xff1a; 如果您想写篇有吸引力的文章&#xff0c;或者您是一个博客和网站的拥有者&#xff0c;那么您一定知道 SEO&#xff08;搜索引擎优化&#xff09;的重要性。SEO 可以帮助您提高相应的流量、转化率和收入&#xff0c;但是 SEO 也是一个复杂和耗时的过程&#x…

【开源项目】Dynamic-Tp核心流程源码解读

序.介绍 dynamic-tp 是一款动态线程池组件&#xff0c;可以实现线程池的实时动态调参及监控报警&#xff0c;线程池配置放在配置中心统一管理&#xff0c;达成业务代码零侵入&#xff0c;支持多配置中心的选择和常见的第三方组件的线程池的集成管理。 官网: https://dynamict…

C++学习day--01 C生万物

1、C/C学习中遇到的问题&#xff1a; 1. 大部分初学者&#xff0c;学习 C/C 都是从入门到放弃。 C/C太难吗&#xff1f; 2. 90% 以上的初学者&#xff0c;学完 C/C 以后&#xff0c;考试完了&#xff0c;书看完了&#xff0c; 但还是不会做项目 是学的不够好吗&#xff1…

基于KZG多项式承诺方案的RLN

1. 引言 RLN——Rate-Limiting Nullifier为PSE团队主导的项目&#xff0c;源自&#xff1a; Barry White Hat 2019年博客 Semaphore RLN, rate limiting nullifier for spam prevention in anonymous p2p setting RLN&#xff08;Rate-Limiting Nullifier&#xff09;是一种…

Servlet原理

什么是Servlet? Servlet是JavaWeb应用程序中的一种Java类&#xff0c;用于接收和处理来自客户端的请求&#xff0c;并将生成的响应发送回客户端。 Servlet是按照Java Servlet规范开发的&#xff0c;可以通过Servlet容器&#xff08;如Tomcat&#xff09;来管理和运行。Servl…

二十二、SQL 数据分析实战(案例1~案例10)

文章目录 案例1&#xff1a;用户信息表 stu_table案例2&#xff1a;员工绩效表 score_table案例3&#xff1a;销售冠军信息表 month_table案例4&#xff1a;月销售额记录表 sale_table案例5&#xff1a;每季度员工绩效得分表 score_info_table案例6&#xff1a;员工信息表 stu_…

【大数据】Hadoop总结

本文对于Hadoop中的HDFS和MapReduce的相关面试重点进行了总结&#xff0c;下篇将介绍调优、数据倾斜等进阶知识。 Hadoop总结 一、概述1. Hadoop特性2. HDFS结构HDFS 架构 二、HDFS分布式文件系统1 概述2. HDFS存储数据架构图NameNodeDataNode 3 HDFS优点4 HDFS缺点&#xff08…