<think>
我们正在处理世界坐标系、相机坐标系、图像坐标系和像素坐标系之间的转换。根据引用资料,转换过程主要分为两个步骤:
1. 世界坐标系到相机坐标系(通过相机外参)
2. 相机坐标系到图像坐标系(通过相机内参,其中图像坐标系通常指物理单位表示的图像平面坐标,然后还需要转换到像素坐标系)
具体步骤:
- 世界坐标系(World Coordinate System, WCS)是物体在真实世界中的坐标,用三维点表示。
- 相机坐标系(Camera Coordinate System, CCS)是以相机光心为原点的坐标系,Z轴沿光轴方向。
- 图像坐标系(Image Coordinate System)是在成像平面上的二维坐标系,通常以光心在图像平面的投影为原点(一般用物理单位,如毫米)。
- 像素坐标系(Pixel Coordinate System)是图像在计算机中存储的坐标系,原点在图像的左上角,单位为像素。
转换关系:
1. 世界坐标系 -> 相机坐标系:通过刚体变换(旋转矩阵R和平移向量t)。若有一个点P在世界坐标系中的坐标为$P_w = (X_w, Y_w, Z_w)^T$,在相机坐标系中的坐标为$P_c = (X_c, Y_c, Z_c)^T$,则:
$$ P_c = R \cdot P_w + t $$
或者写成齐次坐标形式:
$$ \begin{bmatrix} X_c \\ Y_c \\ Z_c \\ 1 \end{bmatrix} = \begin{bmatrix} R & t \\ 0 & 1 \end{bmatrix} \begin{bmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{bmatrix} $$
2. 相机坐标系 -> 图像坐标系(物理单位):利用小孔成像模型。设相机焦距为f,则:
$$ x = f \frac{X_c}{Z_c}, \quad y = f \frac{Y_c}{Z_c} $$
这里得到的(x,y)是物理单位(例如毫米)表示的图像平面坐标。
3. 图像坐标系(物理单位)-> 像素坐标系:需要知道每个像素在x和y方向的物理尺寸(dx, dy,单位:毫米/像素)以及图像中心(即主点)在像素坐标系中的坐标(u0, v0)。则:
$$ u = \frac{x}{dx} + u_0, \quad v = \frac{y}{dy} + v_0 $$
通常,我们用一个内参矩阵K来表示这个变换:
$$ K = \begin{bmatrix} f_x & 0 & u_0 \\ 0 & f_y & v_0 \\ 0 & 0 & 1 \end{bmatrix} $$
其中,$f_x = f / dx$, $f_y = f / dy$。
因此,从相机坐标系到像素坐标系的齐次坐标变换可以写为:
$$ \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = \frac{1}{Z_c} K \begin{bmatrix} X_c \\ Y_c \\ Z_c \end{bmatrix} $$
注意:这里我们取齐次坐标,所以实际计算时,需要将得到的(u,v)除以第三项(即深度Z_c)来得到归一化的像素坐标。
综合起来,从世界坐标到像素坐标的变换为:
$$ \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = \frac{1}{Z_c} K \begin{bmatrix} R & t \end{bmatrix} \begin{bmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{bmatrix} $$
其中,$\begin{bmatrix} R & t \end{bmatrix}$是外参矩阵(通常为3×4矩阵,或者使用4×4齐次矩阵取前3行),K是内参矩阵(3×3)。
注意:在实际应用中,我们通常将世界坐标点表示为齐次坐标(4维向量,最后一项为1),然后左乘外参矩阵(3×4)得到相机坐标(3维向量,非齐次),再左乘内参矩阵(3×3)得到齐次像素坐标,最后除以深度(即Z_c)得到像素坐标。
代码示例(参考引用[2]):
假设我们有:
- 内参矩阵K
- 外参矩阵(这里外参矩阵可以用4×4的变换矩阵T表示,或者用旋转矩阵R和平移向量t表示)
以下是一个转换函数的示例:
```python
import numpy as np
def world_to_pixel(point_world, R, t, K):
"""将世界坐标系中的点转换到像素坐标系
参数:
point_world: 世界坐标系中的点,形状(3,)或(4,),如果是3维则自动转换为齐次坐标
R: 旋转矩阵,形状(3,3)
t: 平移向量,形状(3,)
K: 相机内参矩阵,形状(3,3)
返回:
像素坐标(u, v)
"""
# 确保point_world是齐次坐标形式(4维)
if len(point_world) == 3:
point_world = np.append(point_world, 1.0)
# 将世界坐标点转换为相机坐标系
point_camera = R @ point_world[:3] + t # 这里point_world[:3]是前三项,即非齐次部分
# 或者使用齐次变换矩阵(这里用分步计算)
# 注意:上面这种写法等价于:point_camera = R @ point_world[:3] + t.reshape(3,1) 但如果t是行向量则需要转置
# 更通用的做法是构建一个3x4的变换矩阵
T = np.hstack((R, t.reshape(3,1)))
point_camera = T @ point_world # 此时point_camera是3维向量
# 从相机坐标系到像素坐标系
# 先进行投影(得到归一化坐标)
if point_camera[2] == 0: # 避免除以0
return None
x = point_camera[0] / point_camera[2]
y = point_camera[1] / point_camera[2]
# 转换为齐次坐标
point_image = np.array([x, y, 1])
point_pixel = K @ point_image
u = point_pixel[0]
v = point_pixel[1]
return u, v
```
注意:上面的代码中,我们假设输入的t是列向量,或者能够与R水平拼接(因此t应该是3×1的列向量)。在实际使用中,需要根据输入进行调整。另外,上面的转换没有考虑畸变。
此外,引用[2]中提到外参有时用四元数(qw,qx,qy,qz)和平移向量(tx,ty,tz)表示,我们需要将四元数转换为旋转矩阵。可以使用以下函数转换:
```python
import numpy as np
from scipy.spatial.transform import Rotation as R
def quaternion_to_rotation_matrix(q):
"""将四元数转换为旋转矩阵
参数:
q: 四元数,格式为(qw, qx, qy, qz)
返回:
旋转矩阵R,形状(3,3)
"""
# 使用scipy的Rotation
r = R.from_quat([q[1], q[2], q[3], q[0]]) # 注意:scipy要求输入为(x,y,z,w),所以调整顺序
return r.as_matrix()
```
这样,如果给定了四元数和平移向量,我们可以先得到旋转矩阵R,然后使用上面的转换函数。
总结:
1. 世界坐标系到相机坐标系:通过外参(旋转矩阵R和平移向量t)进行刚体变换。
2. 相机坐标系到图像坐标系:利用透视投影(小孔成像模型),除以Z_c得到归一化坐标。
3. 图像坐标系(归一化坐标)到像素坐标系:通过内参矩阵K进行线性变换。
注意:在实际应用中,我们通常将内参矩阵K和外参矩阵[R|t]合并成一个投影矩阵P=K[R|t],然后直接使用P将世界坐标点(齐次坐标)变换到像素坐标(齐次坐标),然后除以第三个分量得到像素坐标。
参考文献:
[^1]: 计算机视觉:相机成像原理:世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换
[^2]: 世界坐标系,相机坐标系,像素坐标系转换 详细说明(附代码)
§§相关问题§§
1. 相机内参矩阵包含哪些参数?如何标定?
2. 相机外参矩阵的作用是什么?如何求解?
3. 在相机坐标系转换到像素坐标系的过程中,为什么需要除以Z_c?
4. 如何使用OpenCV实现世界坐标系到像素坐标系的转换?
</think>### 坐标系转换详解
在计算机视觉中,世界坐标系、相机坐标系、图像坐标系和像素坐标系之间的转换是核心基础。转换过程涉及相机内参和外参,以下是完整的转换流程:
#### **1. 世界坐标系 → 相机坐标系**
- **转换公式**:
$$ \begin{bmatrix} X_c \\ Y_c \\ Z_c \\ 1 \end{bmatrix} = \begin{bmatrix} R & t \\ 0 & 1 \end{bmatrix} \begin{bmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{bmatrix} $$
其中:
- $(X_w, Y_w, Z_w)$:世界坐标系坐标
- $(X_c, Y_c, Z_c)$:相机坐标系坐标
- $R$:旋转矩阵(3×3)
- $t$:平移向量(3×1)
- **示例代码**:
```python
import numpy as np
def world_to_camera(point_world, R, t):
point_homogeneous = np.append(point_world, 1) # 齐次坐标
T = np.vstack([np.hstack([R, t.reshape(3,1)]), [0,0,0,1]]) # 外参矩阵
point_camera = T @ point_homogeneous
return point_camera[:3] # 返回非齐次坐标
```
#### **2. 相机坐标系 → 图像坐标系**
- **转换公式**(透视投影):
$$ \begin{cases}
x = f_x \frac{X_c}{Z_c} \\
y = f_y \frac{Y_c}{Z_c}
\end{cases} $$
其中:
- $(x, y)$:图像坐标系坐标(物理单位,如毫米)
- $f_x, f_y$:焦距参数(内参)
#### **3. 图像坐标系 → 像素坐标系**
- **转换公式**:
$$ \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = \begin{bmatrix} \frac{1}{dx} & 0 & u_0 \\ 0 & \frac{1}{dy} & v_0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} $$
其中:
- $(u, v)$:像素坐标(像素单位)
- $(u_0, v_0)$:主点坐标(图像中心)
- $(dx, dy)$:单个像素的物理尺寸
#### **4. 完整转换(世界→像素)**
- **内参矩阵 $K$**:
$$ K = \begin{bmatrix} f_x & 0 & u_0 \\ 0 & f_y & v_0 \\ 0 & 0 & 1 \end{bmatrix} $$
- **转换公式**:
$$ Z_c \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = K \begin{bmatrix} R & t \end{bmatrix} \begin{bmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{bmatrix} $$
- **Python实现**:
```python
def world_to_pixel(point_world, K, R, t):
point_cam = world_to_camera(point_world, R, t)
x, y, z = point_cam
# 相机坐标系→归一化坐标
x_norm = x / z
y_norm = y / z
# 像素坐标
uv_homogeneous = K @ np.array([x_norm, y_norm, 1])
return uv_homogeneous[:2] # 返回(u,v)
```
#### **关键参数说明**
| 参数类型 | 参数示例 | 物理意义 |
|----------|----------------|------------------------------|
| **内参** | $f_x, f_y$ | 焦距(像素单位) |
| | $u_0, v_0$ | 主点(图像中心像素坐标) |
| | $k_1, k_2$ | 畸变系数(未展示) |
| **外参** | $R$(旋转矩阵)| 世界坐标系到相机坐标系的旋转 |
| | $t$(平移向量)| 世界坐标系到相机坐标系的平移 |
> 注:实际应用中需先进行**相机标定**获取内参(如棋盘格标定法),外参可通过PnP算法求解[^1][^2]。
---
### 应用示例
假设:
- 内参 $K = \begin{bmatrix} 800 & 0 & 320 \\ 0 & 800 & 240 \\ 0 & 0 & 1 \end{bmatrix}$
- 外参 $R = I$(单位矩阵),$t = [0, 0, 1]^T$
- 世界点 $P_w = [0.5, 0.5, 0]^T$
转换过程:
1. $P_c = [0.5, 0.5, 1]^T$(相机坐标)
2. $x = 800 \times 0.5/1 = 400$, $y = 800 \times 0.5/1 = 400$(图像坐标)
3. $u = 400 + 320 = 720$, $v = 400 + 240 = 640$(像素坐标)
---