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&#…...
leetcode sql50题
在中文站没找到对应的集合,想来自己动手拷贝过来,方便大家面试复习用,对应英文站点: https://leetcode.com/studyplan/top-sql-50/ Select #1757. 可回收且低脂的产品 链接: https://leetcode.cn/problems/recyclable-and-low-fa…...
如何在 React 中监听 div 的滚动事件
在 React 中监听 div 的滚动事件(scroll),可以通过为该 div 添加 onScroll 属性来实现。以下是一个基本的例子: ✅ 示例:监听 div 的滚动事件 import React, { useRef } from react;function ScrollComponent() {cons…...
【HarmonyOS 5】游戏开发教程
一、开发环境搭建 工具配置 安装DevEco Studio 5.1,启用CodeGenie AI助手(Settings → Tools → AI Assistant)配置游戏模板:选择"Game"类型项目,勾选手机/平板/折叠屏多设备支持 二、游戏引擎核心架构…...
【Redis】笔记|第9节|Redis Stack扩展功能
Redis Stack 扩展功能笔记(基于 Redis 7) 一、Redis Stack 概述 定位:Redis OSS 扩展模块(JSON、搜索、布隆过滤器等),提供高级数据处理能力。核心模块: RedisJSON:原生 JSON 支持…...
asp.net mvc如何简化控制器逻辑
在ASP.NET MVC中,可以通过以下方法简化控制器逻辑: ASP.NET——MVC编程_aspnet mvc-CSDN博客 .NET/ASP.NET MVC Controller 控制器(IController控制器的创建过程) https://cloud.tencent.com/developer/article/1015115 【转载…...

github开源协议选择
文章目录 怎么选协议宽松型协议 Permissive Licenses传染型协议 怎么选协议 希望代码被广泛使用,允许闭源 MIT、Apache 2.0、BSD需要专利保护 Apache 2.0强制开源衍生作品 GPL、AGPL开发库,允许闭源调用 LGPL云服务项目,防止白嫖 AGPL企业级…...

详解Jenkins Pipeline 中git 命令的使用方法
在 Jenkins Pipeline 中,git 命令是用于从版本控制系统(如 Git)拉取代码的核心步骤。其用法灵活,支持多种配置参数,但需要遵循 Jenkins 流水线语法规范。 一、基础语法 1. 声明式流水线(Declarative Pipe…...
如何确定微服务的粒度与边界
确定微服务的粒度与边界 在完成初步服务拆分之后,架构师往往会遇到另一个难题:该拆到多细?哪些功能可以归并为一个服务,哪些又必须单独部署?这就是“服务粒度与边界”的问题。本节将围绕实际架构经验,介绍…...

【python深度学习】Day 48 PyTorch基本数据类型与操作
知识点: 随机张量的生成:torch.randn函数卷积和池化的计算公式(可以不掌握,模型会自动计算的)pytorch的广播机制:加法和乘法的广播机制 ps:numpy运算也有类似的广播机制,基本一致 作…...
Go深入学习延迟语句
1 延迟语句是什么 编程的时候,经常会需要申请一些资源,比如数据库连接、文件、锁等,这些资源需要再使用后释放掉,否则会造成内存泄露。但是编程人员经常容易忘记释放这些资源,从而造成一些事故。 Go 语言直接在语言层…...