CV01_相机成像原理与坐标系之间的转换
目录
0.引言:小孔成像->映射表达式
1. 相机自身的运动如何表征?->外参矩阵E
1.1 旋转
1.2 平移
2. 如何投影到“像平面”?->内参矩阵K
2.1 图像平面坐标转换为像素坐标系
3. 三维到二维的维度是如何丢失的?->透视变换
4. 坐标变换的应用
附:像平面投影实际的偏差问题
参考资料
0.引言:小孔成像->映射表达式
视觉包含着大量的信息,是几乎所有生物感知环境的主要工具,也是机器人的重要传感器之一,但是相机究竟是如何成像的呢?
现在我们假设一种情况:黑暗的环境下,空中有一个发光的小球,小球对面是铺满了墙壁的一张纸,我们会发现小球照亮了整个纸面,但是小球的二维投影圆却没有呈现在纸上。
现在我们再给一个平面放在小球和纸之间,这个平面中心有一个小孔,光透过小孔,我们发现纸面整体虽然变暗了,但是小球二维投影圆却能清晰看见了。这就是小孔成像。
但是小孔到底起着怎样的作用?从数学角度看,小孔就是一个映射或者函数表达式,它让“物点”与“像点”的空间位置有了一一对应的关系。如果没有“小孔”,那么物点会“漫射”到所有像点,自然不会呈现任何形状。


小孔成像就是相机的基本原理,因为镜头本身和小孔一样,也是一个映射。镜头将物点投射到图像传感器上,将光强信号转换到成电流信号,电流信号再由运算电路转化成数字信号,合成数字图像,就是照片。
下面我们就来具体求解这个镜头代表的映射函数的具体形式,从而构建物和像之间的数学关系。

1. 相机自身的运动如何表征?->外参矩阵E
首先,我们建立一个世界坐标系W,而我们的相机自身也有一个坐标系,我们称为相机坐标系C,而且相机坐标系C可以在世界坐标系W中运动,这就引出几个问题:
1.相机自身的运动如何表征?
2.世界坐标系中的“物点”投影到相机的成像平面,这又如何表示?
3.三维到二维的维度是如何丢失的?
4.这些坐标的变换有什么应用?
问题一个一个解决。我们先看第一个问题,为了明白背后的原理我先采用二维坐标(三维坐标是同理的),这样便于理解。
从世界坐标系变换到相机坐标系属于刚体变换。首先介绍刚体运动的概念:刚体(即形状和大小不变的理想化物体)进行的运动,这种运动保持了物体内部所有点之间的相对距离不变。刚体变换通常包括“平移”和”旋转“。
(注意说明旋转和平移时用的是二维举例,三维坐标系的原理是相同的,推广即可)
1.1 旋转
我们先看旋转。现在在二维空间(三维空间同理)有一点P,其W坐标系下(x,y),红色的相机坐标系原来与世界坐标系重合,现在其旋转了θ角:


那么,P点在做了旋转运动了的相机坐标系C下的坐标(x',y')是多少呢?
这个问题我们回头解决,我们先看一下二维平面内的旋转运动是如何表达的:
二维空间内一点P的坐标为(x,y),其绕原点旋转θ后得到P’的坐标是(x',y'),如何求x',y'?

假设OP连线与X轴夹角为 α,由于旋转不改变OP长度,所以有这个等式

利用和角公式展开可得到x',相似,我们也能得到y'
我们利用线性代数的知识写成矩阵的形式,这就得到一个旋转矩阵R,也是一个二维的线性变换
我们记作p'=Rp,R为上面的旋转矩阵。
我们回到刚才的问题,用同样的思路,还是有OP长度相等的关系,我们计算可得x'y'
还是用矩阵来表达,我们就得到了一个坐标基变换的表达式
上面的矩阵我们记作A,写作p'=Ap(其实这就是坐标基变换)

矩阵A的作用就是对同一点不同坐标系下的表达式进行转化
如果我们把刚刚得到的R和A相乘,结果会发现是一个单位矩阵I,则A和R就是互逆的。换句话说:再量相同的情况下,旋转点和坐标系变换是互逆的操作。
这一点非常有用,也就是只要我们知道了相机的运动,就可以求表示相机运动的逆矩阵来求空间物点在运动后的相机坐标系下的表达。
1.2 平移
上面的是旋转运动,下面我们看一下平移运动。
点P移动到P',(x',y')的坐标很容易得到。但是我们要注意,平移不是线性变换,也就是说我们不能用矩阵表示平移运动。但是如果我们硬要用矩阵来表示平移呢?----这就引出了齐次坐标(homogeneous)
我们可以用如下方式表示点P的平移
上面的矩阵其实就是三维剪切变换,带入到三维中通过线性变换来达到“平移”的效果

那条白色的线其实都发生了成比例的缩放,缩放因子就是最后一个维度的值ω,ω=1时称为归一化平面,齐次坐标ω不同的点在笛卡尔坐标系下是同一个点。
这就是所谓的透视投影(中心投影变换)。透视中心就在ω=0的所谓无穷远点处。
这样我们就明白,我们可以通过高维的剪切变换来实现低维度的平移变换,从而解决了平移运动的矩阵表示问题。
我们通常把线性变换+ 平移称为“仿射变换”(Affine)
同样地,对于相机坐标系的平移,我们可以通过直接求平移矩阵T的逆矩阵来得到基变换矩阵A。
然后我们把刚刚提到的平移和旋转合在一起,拼成一个矩阵,就得到了能够转换二维(三维同理)世界坐标到运动的相机坐标系的桥梁(线性变换)。
我们把这个矩阵写成更一般的形式,T和R分别表示平移和旋转,我们称E为相机的“外参矩阵”(Extrinsic Matrix)
写成这种形式的好处是,可以统一的表达有限维空间的情况,比如二维和三维

(注:基于欧拉角的旋转矩阵,其具体形式与旋转轴是否固定以及旋转顺序有关)
如此一来,我们就把第一个问题解决了。
2. 如何投影到“像平面”?->内参矩阵K
我们已经把世界坐标系通过外参矩阵E转换到了运动了的相机坐标系下了,现在我们把目光聚焦到相机坐标系就可以了。像平面与XY平面的距离我们称为焦距f,相机坐标系下一点P(x,y,z)与坐标原点所连直线与像平面的交点P'就是:P在像平面上或者说焦平面上的投影点。

现在我们来求P' 的坐标,根据两个三角形相似,可得
则P'的坐标为
我们再定义像平面上的坐标为这就是图像坐标系下的坐标值。
写成齐次坐标下矩阵的形式为
, z代表点p的深度信息。
2.1 图像平面坐标转换为像素坐标系
像素坐标系和图像坐标系都在成像平面上,只是各自的原点和度量单位不一样。图像坐标系的原点为相机光轴与成像平面的交点,通常情况下是成像平面的中点或者叫principal point。图像坐标系的单位是mm,是物理单位,而像素坐标系的单位是pixel,我们平常描述一个像素点都是几行几列。所以这两者之间的转换关系如下:

。
其中,dx和dy分别表示每一列和每一行分别代表多少mm,即1pixel=dxmm。以齐次坐标形式表示为:
那么将上面得线性变换矩阵与之前的作矩阵乘法,有
,其中fx,fy是焦距(mm)像素值表示(pixel)
右侧的矩阵即为相机的内参矩阵,我们记作K。我们可以看到,这个矩阵其实也是一个仿射变换的形式。
这样,我们的第二个问题也解决了。
3. 三维到二维的维度是如何丢失的?->透视变换
我们把内外参矩阵写在一起,就得到了之前“小孔成像”所代表的映射的表达式
这里本质上是一个透视变换,即齐次坐标转笛卡尔坐标(降维了),相机成像三维到二维的维度丢失,就是在这里发生的,得到的归一化像素坐标系下的uv值就是我们要找的图像坐标了。
一句话总结第三个问题的答案:相机坐标系到归一化像素坐标系的透视变换(投影)
4. 坐标变换的应用
1.相机标定:
每一个相机生产出来之后都要进行标定,这样才能把相机的内参写进产品手册卖给客户,而外参会随着相机运动变化而变化,一般把相机固定之后再校正。常用的标定方法是张正友老师提出的棋盘格校定法,此外还有直接线性法DLT。但无论哪种方法,本质都是求解内外参矩阵E和K。
2.视觉测量:
iphone的measure也是通过内外参矩阵,才能把图像像素的距离和真实的物理距离对应起来。
3.视觉导航:
如果我们用摄像头作为传感器进行导航和定位,尤其是在视觉SLAM中,如果我们不知道相机的内外参矩阵,又怎么能通过摄像头提供的图像信息解算真实世界的位置,构建真实世界的物理地图呢?
附:像平面投影实际的偏差问题
在实际情况下,相机的成像并没有那么理想。
首先,由于图像传感器的尺寸和形状误差,导致像平面沿xy轴有不同尺度的缩放(scale):
再者,相机在实际生产过程中存在公差和不确定性因素,导致Z轴或者说主光轴未穿过像平面的中心而产生偏移(offset):
最后,由于工艺问题,像平面不再是矩形而是平行四边形,我们用θ来刻画这种偏斜(skew)。我们会发现这本质也是一个坐标基变换的问题,从垂直XY轴,变成了非垂直的xy轴,从正交基变为了非正交基。

参考资料
参考1
参考2
参考3
相关文章:
CV01_相机成像原理与坐标系之间的转换
目录 0.引言:小孔成像->映射表达式 1. 相机自身的运动如何表征?->外参矩阵E 1.1 旋转 1.2 平移 2. 如何投影到“像平面”?->内参矩阵K 2.1 图像平面坐标转换为像素坐标系 3. 三维到二维的维度是如何丢失的?…...
Android Lint
文章目录 Android Lint概述工作流程Lint 问题问题种类警告严重性检查规则 用命令运行 LintAndroidStudio 使用 Lint忽略 Lint 警告gradle 配置 Lint查找无用资源文件 Android Lint 概述 Lint 是 Android 提供的 代码扫描分析工具,它可以帮助我们发现代码结构/质量…...
【算法刷题 | 动态规划14】6.28(最大子数组和、判断子序列、不同的子序列)
文章目录 35.最大子数组和35.1题目35.2解法:动规35.2.1动规思路35.2.2代码实现 36.判断子序列36.1题目36.2解法:动规36.2.1动规思路36.2.2代码实现 37.不同的子序列37.1题目37.2解法:动规37.2.1动规思路37.2.2代码实现 35.最大子数组和 35.1…...
vue3 vxe-grid列中绑定vxe-switch实现数据更新
1、先上一张图: <template #valueSlot"{ row }"><vxe-switch :value"getV(row.svalue)" change"changeSwitch(row)" /></template>function getV(value){return value 1;};function changeSwitch(row) {console.l…...
Hive SQL:实现炸列(列转行)以及逆操作(行转列)
目录 列转行行转列 列转行 函数: EXPLODE(ARRAY):将ARRAY中的每一元素转换为每一行 EXPLODE(MAP):将MAP中的每个键值对转换为两行,其中一行数据包含键,另一行数据包含值 数据样例: 1、将每天的课程&#…...
MD5算法详解
哈希函数 是一种将任意输入长度转变为固定输出长度的函数。 一些常见哈希函数有:MD5、SHA1、SHA256。 MD5算法 MD5算法是一种消息摘要算法,用于消息认证。 数据存储方式:小段存储。 数据填充 首先对我们明文数据进行处理,使其…...
ES6的代理模式-Proxy
语法 target 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理handler 一个通常以函数作为属性的对象,用来定制拦截行为 const proxy new Proxy(target, handle)举个例子 <s…...
排序(堆排序、快速排序、归并排序)-->深度剖析(二)
前言 前面介绍了冒泡排序、选择排序、插入排序、希尔排序,作为排序中经常用到了算法,还有堆排序、快速排序、归并排序 堆排序(HeaSort) 堆排序的概念 堆排序是一种有效的排序算法,它利用了完全二叉树的特性。在C语言…...
七一建党节|热烈庆祝中国共产党成立103周年!
时光荏苒,岁月如梭。 在这热情似火的夏日, 我们迎来了中国共产党成立103周年的重要时刻。 这是一个值得全体中华儿女共同铭记和庆祝的日子, 也是激励我们不断前进的重要时刻。 103年, 风雨兼程,砥砺前行。 从嘉兴…...
Spring Boot应用知识梳理
一.简介 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的工具。它简化了基于 Spring 的应用程序的配置和部署过程,提供了一种快速、便捷的方式来构建独立的、生产级别的 Spring 应用程序。 Spring Boot 的一些主要优点包括: 1. 简化配置…...
Spring中利用重载与静态分派
Spring中利用重载与静态分派 在Java和Spring框架中,重载(Overloading)和静态分派(Static Dispatch)是两个非常重要的概念,它们在处理类方法选择和执行过程中扮演着关键角色。本文旨在深入探讨Spring环境下…...
文本三剑客之awk:
文本三剑客awk: grep 查 sed 增删改查 主要:增改 awk 按行取列 awk awk默认的分隔符:空格,tab键,多个空格自动压缩为一个。 awk的工作原理:根据指令信息,逐行的读取文本内容,然…...
SpringSecurity-授权示例
用户基于权限进行授权 定义用户与权限 authorities()。 package com.cms.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.userdetails.User; import…...
选哪个短剧系统源码好:全面评估与决策指南
在短剧内容创作和分享日益流行的今天,选择合适的短剧系统源码对于构建一个成功的短剧平台至关重要。短剧系统源码不仅关系到平台的稳定性和用户体验,还直接影响到内容创作者和观众的互动质量。本文将提供一份全面的评估指南,帮助您在众多短剧…...
AI时代的软件工程:挑战与改变
人工智能(AI)正以惊人的速度改变着我们的生活和工作方式。作为与AI关系最为密切的领域之一,软件工程正经历着深刻的转变。 1 软件工程的演变 软件工程的起源 软件工程(Software Engineering)是关于如何系统化、规范化地…...
Zuul介绍
Zuul 是 Netflix 开源的一个云平台网络层代理,它主要用于路由、负载均衡、中间件通信和动态路由。Zuul 本质上是一个基于 JVM 的网关,它提供了以下功能: 1.路由:Zuul 允许客户端和服务器之间的所有入站和出站请求通过一个中心化的…...
7-1作业
1.实验目的:完成字符收发 led.h #ifndef __GPIO_H__ #define __GPIO_H__#include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_uart.h"//RCC,GPIO,UART初始化 void init();//字符数据发送 void set_tt…...
ElasticSearch安装、配置详细步骤
一、环境及版本介绍 操作系统: Windows 10 软件版本: elasticsearch-7.17.22、kibana-7.17.22、IK-7.17.22 开发环境选择软件版本应提前考虑正式系统环境,否则会产生软件与服务器环境不兼容的问题出现,ElasticSearch与环境支持…...
【Mybatis 与 Spring】事务相关汇总
之前分享的几篇文章可以一起看,形成一个体系 【Mybatis】一级缓存与二级缓存源码分析与自定义二级缓存 【Spring】Spring事务相关源码分析 【Mybatis】Mybatis数据源与事务源码分析 Spring与Mybaitis融合 SpringManagedTransaction: org.mybatis.spri…...
Leetcode 2065. 最大化一张图中的路径价值(DFS / 最短路)
Leetcode 2065. 最大化一张图中的路径价值 暴力DFS 容易想到,从0点出发DFS,期间维护已经走过的距离(时间)和途径点的权值之和,若访问到0点则更新答案,若下一步的距离与已走过的距离和超出了maxTime&#…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
恶补电源:1.电桥
一、元器件的选择 搜索并选择电桥,再multisim中选择FWB,就有各种型号的电桥: 电桥是用来干嘛的呢? 它是一个由四个二极管搭成的“桥梁”形状的电路,用来把交流电(AC)变成直流电(DC)。…...
Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程
鸿蒙电脑版操作系统来了,很多小伙伴想体验鸿蒙电脑版操作系统,可惜,鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机,来体验大家心心念念的鸿蒙系统啦!注意:虚拟…...
STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...
HTTPS证书一年多少钱?
HTTPS证书作为保障网站数据传输安全的重要工具,成为众多网站运营者的必备选择。然而,面对市场上种类繁多的HTTPS证书,其一年费用究竟是多少,又受哪些因素影响呢? 首先,HTTPS证书通常在PinTrust这样的专业平…...
表单设计器拖拽对象时添加属性
背景:因为项目需要。自写设计器。遇到的坑在此记录 使用的拖拽组件时vuedraggable。下面放上局部示例截图。 坑1。draggable标签在拖拽时可以获取到被拖拽的对象属性定义 要使用 :clone, 而不是clone。我想应该是因为draggable标签比较特。另外在使用**:clone时要将…...
