详解相机的内参和外参,以及内外参的标定方法

1 四个坐标系

要想深入搞清楚相机的内参和外参含义, 首先得清楚以下4个坐标系的定义:

  • 世界坐标系: 名字看着很唬人, 其实没什么大不了的, 这个就是你自己定义的某一个坐标系。 比如, 你把房间的某一个点定为原点, 并且定义好方向, 这就是世界坐标系。 一般是个三维坐标系, 单位是m
  • 相机坐标系: 以相机光心为原点, 一般我们把z轴指向相机前方,x向右,y向下,是个三维坐标系, 单位是m
  • 成像平面坐标系: 这个是定义在物理成像平面的坐标系,方向定义与相机坐标系一致(没有z方向), 原点是光心在物理成像平面上的投影,是个二维坐标系, 单位是m。 注意, 我们一般都会忽略这个坐标系, 因为它是个中间过渡状态, 很少直接使用它。
  • 像素坐标系: 这个应该是最为常用, 也最为大家熟悉的坐标系, 计算机视觉任务的输出的坐标表达都是在这个坐标系。 它是个二维坐标系, 原点通常在图像的左上角, x向右,y向下, 单位是像素, 没有具体的尺度。
    在这里插入图片描述

2 内参和外参

除了世界坐标系, 后面三个坐标系只跟相机本身有关。 相机内参表达的就是这三个坐标之间的转换关系, 而相机外参表达的是相机与世界坐标系之间的转换关系。

成像的过程实质上是几个坐标系的转换。首先空间中的一点由世界坐标系转换到相机坐标系 ,然后再将其投影到物理成像平面 ( 成像平面坐标系 ) ,最后再将成像平面上的数据转换像素坐标系 。

在这里插入图片描述
从世界坐标到像素坐标总共有3步转换, 前面2个合在一起就是相机内参, 最后一个是相机外参。

上面矩阵中的参数很好理解, 也都有明确的物理含义: a 和b 表示从成像平面坐标转到像素坐标时, 分别在x和y轴上的缩放系数, u 0 u_0 u0 v 0 v_0 v0是原点的平移量。 r r r是扭曲因子, 一般为0。 f f f是相机的焦距。 R和T是旋转和平移矩阵。

把前面2个合一起, 就得到了如下更为常见的内参矩阵:

K = [ f x 0 c x 0 f y c y 0 0 1 ] \mathbf{K} = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix} K= fx000fy0cxcy1
下面的讲解中, 我们直接用这种形式,跳过成像平面坐标系。

注意, 实际上内参包含2部分: 内参矩阵K和畸变系数D, 上面只讲到了内参矩阵的原理, 没有讲畸变系数。 这里简单列一下畸变系数, 不做详细介绍。
D = [k1 k2 p1 p2 k3]
其中 k1、k2、k3 是径向畸变系数(radial distortion coefficients) p1、p2 是切向畸变系数(tangential distortion coefficients)。

3 常用的坐标转换

假设有某一个点, 在世界坐标下的坐标为 P w = ( X w , Y w , Z w ) P_w = (X_w, Y_w, Z_w) Pw=(Xw,Yw,Zw), 在相机坐标系下的坐标为 P = ( X c , Y c , Z c ) P = (X_c, Y_c, Z_c) P=(Xc,Yc,Zc), 在像素坐标系下的坐标为 P u v = ( u , v ) P_{uv} = (u, v) Puv=(u,v)

  • 世界坐标转到相机坐标:
    P = R P w + t P = RP_w + t P=RPw+t

  • 相机坐标转到像素坐标:
    P u v = 1 Z c K P P_{uv} =\frac 1{Z_c} KP Puv=Zc1KP
    展开就是:
    [ u v 1 ] = 1 Z c K [ X c Y c Z c ] = 1 Z c [ f x 0 c x 0 f y c y 0 0 1 ] [ X c Y c Z c ] \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = \frac 1{Z_c} \mathbf{K} \begin{bmatrix} X_c \\ Y_c \\ Z_c \\ \end{bmatrix} = \frac 1{Z_c} \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} X_c \\ Y_c \\ Z_c \\ \end{bmatrix} uv1 =Zc1K XcYcZc =Zc1 fx000fy0cxcy1 XcYcZc

  • 像素坐标转到像素坐标
    这个转换是最为常用的, 因为我们通常得到的都是像素坐标系下的信息, 如目标检测、分割的结果就是像素坐标, 得到像素坐标后 ,可以转为相机坐标, 进一步再转为世界坐标。另外, 在点云目标检测中, 通常要把原始的彩色图像和深度信息转换为点云, 就需要用到这个转换。
    P = Z c K − 1 P u v P = Z_cK^{-1}P_{uv} P=ZcK1Puv
    展开就是:
    X c = u − c x f x ∗ Z c X_c = \frac {u-c_x}{f_x}*Z_c Xc=fxucxZc
    Y c = v − c y f y ∗ Z c Y_c = \frac {v-c_y}{f_y}*Z_c Yc=fyvcyZc
    注意上面有个尺度因子 Z c Z_c Zc, 从相机坐标系的三维坐标投影到像素平面的二维坐标, 实际上丢失了z方向也就是深度信息。 所以如果不知道深度, 从像素坐标就无法恢复出准确的相机坐标。深度通常可以通过深度相机直接测量得到, 或通过深度估计算法预测。

4 内外参标定方法

4.1 内参标定方法

内参标定通常使用张正友标定法, 也就是常见的棋盘格标定。

代码可参考https://github.com/leo038/hand_eye_calibrate/blob/main/hand_eye_calibrate.py 中的camera_calibrate函数。

4.2 外参标定方法

外参标定的核心是:已知多个点分别在相机坐标系下的坐标和在世界坐标系下的坐标, 求它们之间的映射关系。
常用求解PnP 的方法,即已知多个点, 在像素坐标系的二维坐标, 和在世界坐标系的三维坐标,并且已知内参, 求解旋转平移矩阵。

cv2 中提供了外参标定方法, cv2.solvePnP 只需提供对应的点对即可。
完整代码实现如下:

import cv2
import numpy as np

# 定义已知的3D空间点和对应的2D图像点
objectPoints = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=np.float32)
imagePoints = np.array([[10, 20], [30, 50], [20, 70], [50, 40]], dtype=np.float32)

# 定义相机的内参矩阵和畸变系数
cameraMatrix = np.array([[1000, 0, 320], [0, 1000, 240], [0, 0, 1]], dtype=np.float32)
distCoeffs = np.zeros((4, 1), dtype=np.float32)

# 使用solvePnP函数求解相机的位姿
ret, rvec, tvec = cv2.solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, flags=cv2.SOLVEPNP_EPNP)

if ret:
    print("Rotation vector:\n", rvec)
    print("Translation vector:\n", tvec)
else:
    print("Failed to solve PnP.")

res, _ = cv2.Rodrigues(rvec)
print(f"旋转矩阵: {res}")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值