3D视觉入门-相机篇

 引用 常用相机投影及畸变模型(针孔|广角|鱼眼)_相机畸变模型-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_28087491/article/details/107965151


针孔相机投影

习惯上将z 轴指向相机前方,x 轴向右,y 轴向下,O是摄像机的光心,也是针孔模型的针孔,现实世界空间点P,经过小孔O投影后,落在O‘-x’-y'上,成像点P‘。

#注意这里的 ( c x , c y ) 写的是 光心 或 principle point(PP),不是畸变中心(COD)

缩略词和术语

缩略词和术语
itemdesc
COD畸变中心(Center of Distortion),

在传感器上的 ( x , y )表示拟合的最佳对称中心 (unit:pixel)

DM 畸变模型(Distortion Model)
EFL有效焦距(Effective Focal Length)
Pinhole Model FL针孔模型焦距(Pinhole Model Focal Length) - 图像去畸变后的模型焦距 (unit:pixel)
FOV视野(Field of View)。FOV的大小取决于相机镜头的焦距传感器尺寸。较短的焦距和较大的传感器尺寸会导致更大的FOC,即能够捕捉到更广阔的画面范围。
PP主点(Principle Point) - 透镜光轴与传感器表面的交点
LPP透镜主平面(Lens Primary Plane)

世界坐标系到像素坐标系

世界坐标系中三维点 M=【X,Y,Z】^T 和像素坐标系中二维点 m=[u,v]^T的关系:

其中,s为缩放因子(Z c),A为相机的内参矩阵,【R T】是相机的外参矩阵

具体可表示为:

针孔相机模型

相机中三维世界中坐标点m 映射到 二维图像平面pixel 的过程能够用一个几何模型来描述,其中最简单的称之为 针孔相机模型(pinhole camera model)

像素坐标:像素点在图像中的位置,以像素点左上角为原点

图像坐标:图像中某个特征点在整幅图像的位置,此时原点位于图像的中心

世界坐标系到相机坐标系:

相机坐标系到像素坐标系:

其中,f是镜头焦距,单位为米,α、β  的单位为像素/米,fx,fy是x,y方向的焦距,单位为像素。最终形成矩阵的形式为:

从另一个角度看,我们可以把一个世界坐标点先转换到相机坐标系,再除掉它最后一维的数值(即该点距离相机成像平面的深度),这相当于把最后一维进行归一化处理,得到点 P 在相机归一化平面上的投影:

归一化坐标可以看成相机前方 z = 1处的平面上的一个点,这个 z = 1平面也称为归一化平面。归一化坐标再左乘内参就得到了像素坐标,所以我们可以把像素坐标 [ u , v ] T看成对归一化平面上的点进行量化测量的结果。从这个模型中也可以看出,如果对相机坐标同时乘以任意非零常数,归一化坐标都是一样的,这说明点的深度在投影过程中被丢失了,所以单目视觉中没法得到像素点的深度值。

内参标定是找到一个相机的物理参数的过程,标定结果是一个相机内参的结合

畸变模型

普通相机畸变包括 径向畸变切向畸变

切向畸变表达式沿半径方向的偏移量,径向畸变的形成原因是镜头制造工艺不完美,使得镜头形状存在缺陷,通常又分为桶性畸变和枕形畸变,分别代表 dr 往外偏和往里偏。

[x distorted​,y distorted​]T是畸变后点的归一化坐标

径向畸变:

真实的摄像头使用的并不是针孔,它们使用的是能一次性聚焦大量光线的镜头,这使得其能迅速生成图像,但是,镜头仍会产生失真,光线通常会在摄像机镜头的边缘出现较大或较小幅度的弯曲,这会产生图像边缘扭曲的效果,因此,线条或者物体会比真实情况呈现出或多或少的弯曲。这种失真被称为径向畸变,是最常见的失真类型。

桶形畸变(Barrel Distortion):又称桶形失真,是指光学系统引起的成像画面呈桶形膨胀状的失真现象。 桶形畸变在摄影镜头成像尤其是广角镜头成像时较为常见。
枕形畸变(Pincushion Distortion):又称枕形失真,它是指光学系统引起的成像画面向中间“收缩”的现象。 枕形畸变在长焦镜头成像时较为常见

实际情况中我们常用 r = 0 r=0r=0 处的泰勒级数展开的前几项来近似描述径向畸变,径向畸变后的归一化坐标为:

切向畸变:是由于透镜和CMOS或者CCD的安装位置误差导致(相机的组装过程中由于不能使得透镜和成像面严格平行),切向畸变需要两个额外的畸变参数来描述,切向畸变后的归一化坐标为:

其中,r2=x2+y2

对于相机坐标系中的一点 P PP,我们能够通过5个畸变系数找到这个点在像素平面上的正确位置:

由于参数过多导致数值求解不稳定, 通常只使用 k 1 , k 2 , k 3 , p 1 , p 2,我们一共需要5个畸变参数( k 1 , k 2 , k 3 , p 1 , p 2 ) 来描述透镜畸变。
将畸变后的点通过内参数矩阵投影到像素平面,得到该点在图像上的正确位置:

歪斜:图像传感器图像原尺寸在制造过程可能不是正方形,传感器歪斜和不是正方形主要对相机 x和 y方向的焦距产生影响。

在不考虑畸变的情况下,考虑主点偏移、图像传感器的特性,3D 目标点成像数学模型用以下公式可完全表达。这就是相机内部参数对成像的影响,因此 K称为内参矩阵,相机内参标定主要是标定相机的焦距、主点、歪斜(下面的s 、)等内部参数

畸变矫正

for (int v = 0; v < height; v++) {
  for (int u = 0; u < width; u++) {

    double u_distorted = 0, v_distorted = 0;

	// 计算点(u,v)对应到畸变图像中的坐标(u_distorted, v_distorted)
    double x = (u-cx)/fx;
    double y = (v-cy)/fy;

    double x2 = x*x, y2 = y*y, xy = x*y, r2 = x2 + y2;
    double x_radial = x * (1 + k1*r2 + k2*r2*r2);
    double y_radial = y * (1 + k1*r2 + k2*r2*r2);
    double x_tangential = 2*p1*xy + p2*(r2 + 2*x2);
    double y_tangential = 2*p2*xy + p1*(r2 + 2*y2);
    double xd = x_radial + x_tangential;
    double yd = y_radial + y_tangential;

    u_distorted = xd*fx + cx;
    v_distorted = yd*fy + cy;

    // 赋值(最近邻插值)
    if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < width && v_distorted < height)
        img_dst(v, u) = (*this)((int) v_distorted, (int) u_distorted);
    else
        img_dst(v, u) = 0;
  }
}

畸变矫正是计算机视觉和摄影领域中的一个常见任务,特别是在使用广角镜头时。以下是代码的逐行解释:

  1. for (int v = 0; v < height; v++) {for (int u = 0; u < width; u++) {: 这两个嵌套的循环遍历图像的每个像素点。vu 分别代表像素点的行(高度)和列(宽度)坐标。

  2. double u_distorted = 0, v_distorted = 0;: 初始化畸变校正后的像素坐标。

  3. double x = (u-cx)/fx;double y = (v-cy)/fy;: 将像素坐标 (u, v) 转换为相机坐标系中的归一化坐标 (x, y)。这里 cxcy 是图像中心(通常是光学中心),fxfy 是焦距。

  4. double x2 = x*x, y2 = y*y, xy = x*y, r2 = x2 + y2;: 计算 xy 的平方、乘积以及 xy 平方和的平方,这些值将用于后续的畸变模型计算。

  5. double x_radial = x * (1 + k1*r2 + k2*r2*r2);double y_radial = y * (1 + k1*r2 + k2*r2*r2);: 计算径向畸变。k1k2 是畸变系数,它们决定了径向畸变的程度。这部分代码应用了畸变模型来校正 xy 坐标。

  6. double x_tangential = 2*p1*xy + p2*(r2 + 2*x2);double y_tangential = 2*p2*xy + p1*(r2 + 2*y2);: 计算切向畸变。p1p2 是切向畸变系数,它们影响图像边缘的像素。

  7. double xd = x_radial + x_tangential;double yd = y_radial + y_tangential;: 计算校正后的归一化坐标。

  8. u_distorted = xd*fx + cx;v_distorted = yd*fy + cy;: 将校正后的归一化坐标转换回像素坐标。

  9. if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < width && v_distorted < height): 检查校正后的像素坐标是否在图像范围内。

  10. img_dst(v, u) = (*this)((int) v_distorted, (int) u_distorted);: 使用最近邻插值将畸变校正后的像素值赋给输出图像 img_dst(*this) 表示当前对象的调用,这里应该是一个图像对象,用于获取原始图像中对应像素的值。

  11. else img_dst(v, u) = 0;: 如果校正后的像素坐标超出图像范围,则将输出图像中对应的像素值设置为0(通常是黑色)。

这段代码的目的是将畸变图像中的每个像素映射到校正后的图像中,通过应用畸变模型来校正每个像素的位置。最终,img_dst 将包含畸变校正后的图像。

COD和PP

COD图片上两点在透镜中心相交,而PP的理论上垂直于传感器表面。也就是说在透镜装歪了的时候,COD本身也会跟着歪过来,但是PP理论上是垂直的,可能因为标定误差的原因导致它偏离了原位置

展示了切向失真,这会导致 PP 和 COD 不同

在自动驾驶和计算机视觉领域,将激光雷达(LiDAR)点云投影到相机图像平面是一个常见的需求,用于多传感器数据融合和3D场景理解。这个过程通常涉及以下步骤:

  1. 坐标系转换:首先需要将点云从激光雷达坐标系转换到相机坐标系。这通常通过一个外参矩阵(包含旋转和平移信息)来实现,该矩阵描述了激光雷达和相机之间的空间关系。

  2. 相机模型:使用相机的内参矩阵(包含焦距、主点坐标等)和针孔相机模型将3D点投影到2D图像平面。这个模型描述了3D点到2D像素点的几何关系。

  3. 畸变校正:由于相机镜头的畸变,可能需要对投影的2D点进行畸变校正,以获得准确的像素坐标。

  4. 深度信息融合:将投影到图像平面的点云与相机捕获的RGB图像结合,形成带有深度信息的RGB-D图像,这对于后续的目标检测、跟踪和场景理解等任务至关重要。

在实际操作中,可以使用如OpenCV这样的计算机视觉库来执行上述步骤。例如,使用cv::projectPoints函数可以将3D点云投影到2D图像平面,并进行畸变校正。


3D可视化点云数据及投影数据

Open3D是一个开源库,用于处理3D数据。它提供了许多用于3D几何数据的工具,包括点云、三维重建、表面重建、体积渲染等。以下是一些基本的步骤和概念,可以帮助你开始使用Open3D进行点云可视化学习

下载包:

pip install open3d

 读取点云

import open3d as o3d

# 读取点云文件
pcd = o3d.io.read_point_cloud("path_to_your_pcd_file.pcd")

 点云数据可视化

#点云数据转换为 Open3D 的 Vector3dVector

#点云数据转换为 Open3D 的 Vector3dVector
img_points_vector0 = o3d.utility.Vector3dVector(pcd.points)

# 绘制投影点创建一个 Open3D PointCloud 对象
pcd = o3d.geometry.PointCloud()
# 正确地赋值
pcd.points = img_points_vector0

#创建可视化实例
vis = o3d.visualization.Visualizer()
vis.create_window(window_name="pcd project camera image",width=800, height=600)

#向视图中添加几何对象
vis.add_geometry(pcd)

#启动可视化
vis.run()
销毁窗口
vis.destroy_window()

或者也可以写成

o3d.visualization.draw_geometries([pcd])

前者提供了根本更多的定制化选项,允许创建一个可视化窗口,可以添加多个几何对象(点云、网格等)还可以进行视图的控制和交互 。后者是一个更简单快捷的方法,可视化一个或者多个几何对象,自动创建一个窗口,不需要手动调用创建和销毁窗口的方法。

可视化之前可以对点云进行一些处理:滤波、下采样、估计法线等

eg:体素下采样减少点的数量

voxel_size = 0.05  # 体素的大小
downpcd = pcd.voxel_down_sample(voxel_size)

eg:估计点云中每个点的法线

downpcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))

 eg:点云变换-平移、旋转等

# 创建一个4x4的变换矩阵
transformation = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])

# 应用变换
pcd.transform(transformation)

eg:点云分割。如半径分割、平面分割

radii = [0.05, 0.1, 0.2]
segmented_pcd = pcd.segment_radial_search(radii)

eg:保存文件

o3d.io.write_point_cloud("path_to_save_pcd_file.pcd", segmented_pcd)

然后我们就进入到了点云到相机投影的过程啦!

首先需要齐次坐标,这是一种表示点的方法,它将传统坐标系统中的二维或三维坐标扩展到更高维度。这种方法在处理几何变换(如平移、旋转、缩放)时非常有用,因为它允许将这些变换统一表示为矩阵乘法。做这一步的目的是和标定参数维度一致可以进行矩阵乘法操作,在二维空间中,一个普通的点 P(x,y) 可以表示为齐次坐标 P′(x,y,1)。在三维空间中,点 P(x,y,z)可以表示为齐次坐标 P′(x,y,z,1)。

pcd_points= np.asarray(pcd.points)
# 将点云扩展到齐次坐标
points_homogeneous = np.hstack((pcd_points, np.ones((pcd_points.shape[0], 1))))

 

第一行是第一个点的坐标,第二行是第二个点的坐标,...,最后一行是最后一个点的坐标。

加入有一个相机的外参矩阵

T_w_c = np.array([
    [-0.007111, -0.009311, -0.999931, -5.764661],
    [0.99992, 0.0122, -0.007232, 0.085112],
    [0.012073, -0.999883, 0.009233, -125.417163],
    [0.0, 0.0, 0.0, 1.0]
])

左上角的3x3子矩阵 [-0.007111, -0.009311, -0.999931][0.99992, 0.0122, -0.007232][0.012073, -0.999883, 0.009233] 表示相机坐标系xyz相对于世界坐标系的旋转

第四列的前三个数字 -5.764660.08511-125.41716 表示世界坐标系原点在相机坐标系中的位置,即相机坐标系原点相对于世界坐标系的平移。最后一行 [0.0, 0.0, 0.0, 1.0] 确保了齐次坐标的正确应用。

T_w_c 矩阵的每一列代表相机坐标系的基向量在世界坐标系中的表示,而最后一行通常用于处理齐次坐标,确保变换可以应用于非齐次坐标(即普通的三维坐标)。

在举证乘法中,能否相乘取决于他们的相撞,行数相同就可以相乘,如果 A 是一个 m×n 矩阵,B 是一个 n×p 矩阵,那么它们的乘积 AB 将是一个 m×p 矩阵。

points_camera = T_w_c @ points_homogeneous.T
points_camera = points_camera.T

这里齐次坐标后的点云是N*4的矩阵,外参矩阵是4*4的矩阵,所以要先对齐次坐标转置后才能相乘,每个点的齐次坐标(x, y, z, 1)现在变成了行向量。points_camera = points_camera.T 这一行代码将 points_camera 转置回 Nx4 的矩阵,其中 N 是点的数量。这样,每个点的齐次坐标再次变成了列向量。最终得到N *4的矩阵。

计算投影-内参-转换为笛卡尔坐标

首先我们需要知道内参矩阵,这是一个描述相机内部特性的一个重要参数,包含了相机镜头的几何和光学特性,通常用于将三维坐标系中的点转换为相机坐标系中的二维图像。fx​,fy​ 是沿图像 x轴和 y轴的焦距(以像素为单位),u0​,v0​ 是图像的主点坐标(通常是图像的中心)

要将相机坐标系下的点投影到二维图像平面上,你需要执行以下步骤:

  1. 提取 x,y,z坐标:从 points_camera 矩阵中提取每个点的 x,y,z 坐标。

  2. 应用内参矩阵:将内参矩阵与 x,y,z标相乘,得到齐次坐标下的图像平面上的点。

  3. 从齐次坐标转换为笛卡尔坐标:将得到的齐次坐标转换为笛卡尔坐标,这通常涉及到除以齐次坐标的第三个分量(ww)。

#内参矩阵
#焦距(f_x,f_y),主点坐标(u_0,v_0)
intrinsic = np.array([
    [f_x, 0, u_0],
    [0, f_y, v_0],
    [0, 0, 1]
    ])
img_points_homogeneous = intrinsic @ points_camera[:, :3].T
img_points_homogeneous = img_points_homogeneous.T
img_points = img_points_homogeneous[:, :2] / img_points_homogeneous[:, 2].reshape(-1, 1)

相机的内参矩阵,它是一个 3x3 的矩阵,poins_camera相机坐标系是一个N*4(x,y,z,w)的矩阵,两者相乘需要取前三列相机坐标xyz即N*3矩阵再转置变为3*N,这样3*3的内参矩阵就可以@3*N的相机坐标系了,得到3*N的矩阵,N表示点数。

再转置回来得到N*3的矩阵了。得到图像平面上的二维齐次坐标。(x',y',w'),w′ 仍然是一个缩放因子,用于将齐次坐标转换回普通的笛卡尔坐标。

最后将齐次坐标转换为二维笛卡尔坐标,得到二维图像平面上的点坐标。

  • 首先【:,:2】取前列的坐标;
  • img_points_homogeneous[:, 2].reshape(-1, 1) 将第三列(w 坐标)转换为一个列向量,以便可以与 (x, y) 坐标进行逐元素的除法。
  • 除法 (x, y) / w 将齐次坐标转换为笛卡尔坐标
  • 最后我们会得到点1的二维图像坐标,点2的二维图像坐标。。。,得到【N,2】矩阵坐标。这些坐标现在是以像素为单位的二维图像坐标,可以直接用于在图像上标记这些点的位置。

在投影过程中,深度信息实际上是由三维点的 zz 坐标(在相机坐标系中)表示的,它描述了点到相机光心的距离。在透视投影中,点的 x′ 和 y′坐标会随着 z 坐标的变化而变化,从而在二维图像上产生透视效果。在将齐次坐标 (x′,y′,w′)转换为笛卡尔坐标 (u,v)时,我们通过除以 w′ 来“去除齐次化”.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值