使用OpenCV透视变换技术实现坐标变换实践

news2024/11/25 2:43:28

1. 概述

1.1. 需求

在局部空间(无GPS定位)视频监控过程中,把视频识别到物体位置,投射到空间平面坐标系中,获取物体在局部空间的平面坐标。

1.2. 解决方案

使用图像透视变换技术。

1.3. 透视变换概念

透视变换是指利用透视中心、像点、目标点三点共线的条件,按透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某一角度,破坏原有的投影光线束,仍能保持承影面上投影几何图形不变的变换。简而言之,就是将一个平面通过一个投影矩阵投影到指定平面上。

透视变换(Perspective Transform)和仿射变换(Affine Transform)在图像还原、局部变化处理方面有重要意义。通常,在2D平面中,仿射变换的应用比较多;在3D平面中,透视变换占领地位较高。两种变换原理相似,结果也相似,可以针对不同场合选择适合方法。

在这里插入图片描述

2. 透视变换原理

透视变换:
( x y z ) = ( a 11 a 12 a 13 a 21 a 22 a 23 a 31 a 32 a 33 ) ( u v 1 ) \begin{pmatrix} x\\ y\\ z \end{pmatrix} = \begin{pmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{pmatrix} \begin{pmatrix} u\\ v\\ 1 \end{pmatrix} xyz = a11a21a31a12a22a32a13a23a33 uv1
上述公式中, u , v u,v u,v代表原始图像坐标, x , y x,y x,y为经过透视变换的图片坐标,其中变换矩阵为 3 × 3 3\times3 3×3形式。进而可以得到:

x = a 11 u + a 12 v + a 13 x=a_{11}u + a_{12} v+ a_{13} x=a11u+a12v+a13
y = a 21 u + a 22 v + a 23 y=a_{21}u + a_{22} v+ a_{23} y=a21u+a22v+a23
z = a 31 u + a 32 v + a 33 z=a_{31}u + a_{32} v + a_{33} z=a31u+a32v+a33

在原图上取4点坐标与新图对应,相当于列出方程组,解出变换矩阵。
通过变换矩阵,在输入原图像坐标的情况下,可以直接求解新图平面坐标。
x ′ = x z = a 11 u + a 12 v + a 13 a 31 u + a 32 v + a 33 x' = \frac{x}{z} = \frac{a_{11}u + a_{12} v+ a_{13}}{a_{31}u + a_{32} v + a_{33}} x=zx=a31u+a32v+a33a11u+a12v+a13
y ′ = y z = a 21 u + a 22 v + a 23 a 31 u + a 32 v + a 33 y' = \frac{y}{z} = \frac{a_{21}u + a_{22} v+ a_{23}}{a_{31}u + a_{32} v + a_{33}} y=zy=a31u+a32v+a33a21u+a22v+a23

其中, ( x , y ) (x,y) (x,y)是原图坐标, ( x ’ , y ’ ) (x’,y’) (x,y)是变换后的坐标; a 11 , a 12 , a 21 , a 22 , a 31 , a 32 a_{11},a_{12},a_{21},a_{22},a_{31},a_{32} a11,a12,a21,a22,a31,a32为旋转量, a 13 , a 23 , a 33 a_{13},a_{23},a_{33} a13,a23,a33为平移量。因为透视变换是非线性的,所以不能齐次性表示;透视变换矩阵为 3 × 3 3\times3 3×3

透视变换的方程组有8个未知数,所以要求解就需要找到4组映射点,四个点就刚好确定了一个三维空间。

3. OpenCV透视变换

3.1. 关于OpenCV透视变换函数

投影变换(Projective mapping)也称透视变换(Perspective transformation)是建立两平面场之间的对应关系, 将图片投影到一个新的视平面(Viewing plane)。

OpenCV 提供了 cv2.warpPerspective 函数实现投影变换的操作。

函数说明:

cv2.getPerspectiveTransform(src, dst[,solveMethod]) → MP
cv2.warpPerspective(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst
  • 函数cv2.getPerspectiveTransform 根据图像中不共线的 4 个点在变换前后的对应位置求得 ( 3 × 3 3\times3 3×3) 变换矩阵
  • 函数cv2.warpPerspective 使用该 ( 3 × 3 3\times3 3×3) 变换矩阵即可求出变换后的图像。标量进行加法运算。

参数说明:

  • src:变换前图像四边形顶点坐标
  • dst:变换后图像四边形顶点坐标
  • solveMethod:矩阵分解方法,传递给 cv2.solve 求解变换矩阵 MP
  • cv2.DECOMP_LU:选择最优轴的高斯消去法,默认方法
  • cv2.DECOMP_SVD:奇异值分解(SVD)方法
  • cv2.DECOMP_EIG:特征值分解方法,src 必须对称
  • cv2.DECOMP_QR:QR(正交三角)分解
  • cv2.DECOMP_CHOLESKY:Cholesky LLT 分解
  • MP:透视变换矩阵,3行3列
  • dsize: 输出图像的大小,二元元组 (width, height)
  • dst:变换操作的输出图像,可选项
  • flags:插值方法,整型(int),可选项
  • cv2.INTER_LINEAR:线性插值,默认选项
  • cv2.INTER_NEAREST:最近邻插值
  • cv2.INTER_AREA:区域插值
  • cv2.INTER_CUBIC:三次样条插值
  • cv2.INTER_LANCZOS4:Lanczos 插值
  • borderMode:边界像素方法,整型(int),可选项,默认值为 cv2.BORDER_REFLECT
  • borderValue:边界填充模式,可选项,默认值为 0(黑色填充)
  • 返回值:dst,透视变换操作的输出图像,ndarray 多维数组

3.2. 透视变换实践设计

为了便于坐标转换,先设定图片像素为坐标,第一步,由图1的标准图,经透视变换为图2,相当于3维空间中某个视角获取到的图像;第二步,再由图2(相当于实际情况下,视频某个空间视角获得的图像)复原,恢复到图3,过程中产生变换矩阵,以此计算新的坐标。
在这里插入图片描述

3.2.1. 透视变换图像

此部分代码为由图1变换到图2。

import cv2
import numpy as np

img = cv2.imread("A1.png")
height, width = img.shape[:2]
# print(height, width)

# 变换前的四个点
srcArr = np.float32([[0, 0], [515, 0], [0, 613], [515, 613]])
# 变换后的四个点
dstArr = np.float32([[100, 413], [415, 413], [0, 613], [515, 613]])
# 获取变换矩阵
MM = cv2.getPerspectiveTransform(srcArr, dstArr)
dst = cv2.warpPerspective(img, MM, (width, height))

# 输出保存变换后的图像
cv2.imwrite("pe.png", dst)

3.2.2. 透视变换复原图像及获取像素坐标

import cv2
import numpy as np

img = cv2.imread("pe.png")
height, width = img.shape[:2]
# print(height, width)

# 变换前的四个点
srcArr = np.float32([[100, 413], [415, 413], [0, 613], [515, 613]])
# 变换后的四个点
dstArr = np.float32([[0, 0], [515, 0], [0, 613], [515, 613]])

# 求解获取变换矩阵
MM = cv2.getPerspectiveTransform(srcArr, dstArr)
print(MM)
# 输出复原图像
dst = cv2.warpPerspective(img, MM, (width, height))
cv2.imwrite("A2.png", dst)

# 自定义坐标转换函数
def cvt_pos(u , v, mat):
    x = (mat[0][0]*u+mat[0][1]*v+mat[0][2])/(mat[2][0]*u+mat[2][1]*v+mat[2][2])
    y = (mat[1][0]*u+mat[1][1]*v+mat[1][2])/(mat[2][0]*u+mat[2][1]*v+mat[2][2])
    return (int(x), int(y))
 
# 调用函数
u, v = 100,413
x,y = cvt_pos(u, v, MM)
print(x,y)

其中,变换矩阵MM结果为:
在这里插入图片描述
最后,返回像素坐标 ( x , y ) = ( 0 , 0 ) (x, y) = (0, 0) (x,y)=(0,0)

4. 像素转换到私有坐标系

在实际场景下,经透视复原的图像像素坐标,与实际私有坐标成比例变换,获取比例的方法如下。
在这里插入图片描述
首先,选取变换的基准,在图像上选取四个(a,b,c,d)点中,其中,两个邻居的点a,b作为基准,并且,这两个点的,在图像变换(或求解变换矩阵)时像素坐标不变,其中:
L1 = L2 , h1 = h2

接着,计算获取ab两点间像素距离与实际平台私有坐标中ab两点的距离,其中:
k = D ′ D k = \frac{D'}{D} k=DD
式中的 D ′ D' D为图像中距离, D D D为私有坐标系平面上的距离。

则,私有坐标系平面上坐标为: x , y = k x ′ , k y ′ x, y = kx', ky' x,y=kx,ky

5. 小结

虽然此方案理论及实验可行,但是,工程上实施将比较麻烦,主要涉及到每个视频点的4点定位,以及实际坐标测定。

模拟路况,中间图为”视频角度的图片“,还原投射图的效果如右侧的图片,与左侧原图比较,则图中上面的车,明显变虚。
在这里插入图片描述
假设标准原图为496、887像素,实际地面宽度为992cm,相当于每个像素为2cm。求解变换矩阵为:
在这里插入图片描述
选取中间黄色车左前角,像素坐标为(261,655),还原投射图像素坐标为(270,461),对应实际地面私有坐标为(540,922)cm。

欢迎讨论。

参考:

[1]. 电子小呆比. Opencv-python 求原坐标点透视变换后对应坐标点… CSDN博客. 2022.04
[2]. 小凡. 图像处理的仿射变换与透视变换. 知乎. 2019.04
[3]. 泠山. 仿射变换和透视变换. CSDN博客. 2022.10
[4]. 秋山丶雪绪. Python OpenCV 单目相机标定、坐标转换相关代码(包括鱼眼相机). CSDN博客. 2022.12
[5]. 王吉吉丫. 图像矫正–python_OpenCV实现透视变换. CSDN博客. 2021.09
[6]. YouCans. 【OpenCV 例程200篇】34. 图像的投影变换(cv2.getPerspectiveTransform). CSDN博客. 2022.07
[7]. 草原上唱山歌. 几何变换中的仿射变换和透视变换的原理(python). CSDN博客. 2022.03
[8]. 一马归一码. Python 计算机视觉(五)特别篇 —— 透视变换. CSDN博客. 2022.04

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

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

相关文章

链路追踪组件Skywalking使用

前言Skywalking是一个国产开源框架,2015年由吴晟开源 , 2017年加入Apache孵化器,其用于追踪多微服务模块调用之间日志的追踪,协助程序员进行排除问题Skywalking架构Skywalking架构图大概如下SkyWalking OAP: SkyWalkin…

如何格式化U盘?以及优盘格式化的恢复方法

我们经常使用U盘来进行存储和传输数据,使用时间久了,可能需要我们进行优盘格式化。需要注意!优盘格式化之前,记得对里面的数据进行备份,防止数据清空造成不必要的损失。 如何进行U盘格式化?格式化优盘后&a…

“深度学习”学习日记。误差反向传播法--加法层、乘法层、激活函数层的实现

2023.1.16 1、加法层、乘法层: 前两篇文章都在讲述理论,今天实现代码操作:关于加法节点,乘法节点的内容在这篇文章。 https://blog.csdn.net/m0_72675651/article/details/128695488 在以后的学习中,将把构建神经网…

AHOcoder声码器

AHOcoder声码器 目前最常见的声码器有WORLD,STRAIGHT,Griffin_Lim等,AHocoder算是少见的,但也可以学习一下。 代码下载网址:AHOcoder 简介 AHOcoder 语音声码器由 Daniel Erro 在巴斯克大学的 AHOLAB 信…

若依配置教程(一)运行若依系统

一、下载源代码 若依的源代码是开源的,所以我们在若依的官方网站即可进行下载,若依的官网是:http://doc.ruoyi.vip/ruoyi-vue/,进入官网后,会显示代码下载的地址:https://gitee.com/y_project/RuoYi-Vue&a…

【进阶】Bean作用域和生命周期

努力经营当下,直至未来明朗! 文章目录一、通过⼀个案例来看 Bean 作⽤域的问题1. 被修改的Bean案例2. 原因分析二、作用域Scope定义1. Bean的六种作用域(重点)1)singleton2)prototype3)request4…

JVM——类加载与字节码技术(2)

三、编译期处理 所谓的 语法糖 ,其实就是指 java 编译器把* .java 源码编译为* .class 字节码的过程中,自动生成和转换的一些代码,主要是为了减轻程序员的负担,算是 java 编译器给我们的一个额外福利 【注意】以下代码的分析&am…

Python爬虫之基于 selenium 实现文献信息获取

目录初识 selenium网页分析代码实现踩过的坑最近有小伙伴后台跟我说,临近毕业,写毕业论文需要上知网查找大量的文献,但是一篇一篇看文献信息以及文献摘要又有点麻烦,能不能让我写一个爬虫去批量获取文献相关信息 我一听好家伙&am…

【算法】二叉树遍历

目录1.概述2.代码实现2.1.二叉树定义2.2.前序遍历2.3.中序遍历2.4.后序遍历2.5.层序遍历3.应用本文参考: LABULADONG 的算法网站 1.概述 (1)所谓遍历 (Traversal) 是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问…

《从零开始编写一个直播服务器》 C++ 实现一个最简单的HTTP-FLV流媒体服务器

流媒体服务系列文章 文章目录流媒体服务系列文章前言一、http flv?二、使用步骤服务器代码总结前言 HTTP FLV通过http传输,时延可控制在2秒以内,浏览器可基于bilibili开源的flv.js(采用h5 mse技术)开发,比起rtsp、rtmp等免插件播…

Spring BeanPostProcessor

BeanPostProcessor,是bean的增强器,在bean初始化前后调用,常用的方法有postProcessBeforeInitialization和postProcessAfterInitialization,在Spring启动并初始化bean前后通过它们做一些扩展操作。 1、BeanPostProcessor 接口说明…

【信管9.1】​项目沟通及过程

项目沟通及过程沟通这个东西,可以说是整个项目成功失败最关键的因素。9成以上失败的项目在最后总结的时候,沟通不畅或者信息对接问题都会占据前三甲。其实只要是做项目,那么必须有团队,有团队有人,那么沟通就是不可避免…

03 技术太卷我学APEX-关于blob数据类型的使用

03 技术太卷我学APEX-关于blob数据类型的使用 0 Oracle 的blob类型 BLOB BLOB全称为二进制大型对象(Binary Large Object)。它用于存储数据库中的大型二进制对象。可存储的最大大小为4G字节。 通常像图片、文件、音乐等信息就用BLOB字段来存储,先将文件…

外贸软件成本核算丨采购出入库有磅差怎么办

在液化天然气油料等行业,在与供应商之间的进出口贸易过程中,总是少不了会出现磅差的情况,因此就需要有磅差的约定。那什么是磅差呢?磅差指的是,供应方在发货时提供的磅单与购买方实际验收过磅数量之间的差额。一般磅差…

C++设计模式(1)——单例模式

亦称:单件模式、Singleton 意图 单例模式是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。 问题 单例模式同时解决了两个问题, 所以违反了单一职责原则: 1、保证一个类只…

CSS给元素添加边框(样式、颜色、宽度)

给元素添加边框 CSS边框属性允许你指定一个元素边框的样式和颜色, 和边框宽度。 可以使用 border 属性将边框样式,颜色,和宽度 一起设置。 如果不设置其中的某个值,也不会出问题,比如 border: solid #ff0000; 也是允许的。 使用border-style属性设置边…

ORA-39002: 操作无效 ORA-39070: 无法打开日志文件

今天在oracle12c上导数据,出现了错误。导库脚本久经考验,不应该有什么问题,但就是报错了。错误开头2句是: ORA-39002: 操作无效 ORA-39070: 无法打开日志文件 网上搜来的结果,是存放导出文件的路径不对,就…

常用API(String、ArrayList)

API(应用程序接口) Java写好的技术(功能代码),可以直接调用String概述java.lang.String类代表字符串,String类定义的变量可以用于指向字符串对象,然后操作该字符串Java程序中的所有字符串文字&a…

JavaEE进阶第三课:Spring更简单的对象存储和取出(上)

上篇文章介绍了Spring的创建和使用,讲解3了Bean对象的基本存储和取出,这篇文章我们将会介绍Spring更简单的对象存储 目录1.Bean的存储1.0准备工作1.1五大类注解1.1.2为什么要有这么多注解1.2方法注解1.1.1方法注解需要搭配类注解一起使用1.2.2方法重载怎…

详细实例说明+典型案例实现 对迭代法进行全面分析 | C++

第四章 迭代法 目录 ●第四章 迭代法 ●前言 ●一、迭代法是什么? 1.简要介绍 2.代码示例(简单理解) 3.生活实例 ●二、迭代法的典型案例——开平方&帕斯卡三角形 1.开平方 2.帕斯卡三角形 ●总结 前言 简单的来…