@[TOC](第五章 特征转换模块的选择((车道线感知))
前言
近期参与到了手写AI的车道线检测的学习中去,以此系列笔记记录学习与思考的全过程。车道线检测系列会持续更新,力求完整精炼,引人启示。所需前期知识,可以结合手写AI进行系统的学习。
一 2D到3D升采样
1.1 三维卷积
1.1.1 3D卷积作用
对于2D CNN,我们知道可以很好的处理单张图片中的信息,但是其对于视频这种由多帧图像组成的图片流,以及CT等一些医学上的3维图像就会显得束手无策。因为2D卷积没有考虑到图像之间时间维度上的物体运动信息的变化(3维CT图像也可以近似看为是二维图像在时间上的变化)。因此,为了能够对视频(包括3维医学图像)信息进行特征提取,以便用来分类及分割任务,提出了3D卷积,在卷积核中加入时间维度。
1.1.2 Pytorch中对应函数参数详解
class torch.nn.Conv3d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
说明:
bigotimes: 表示二维的相关系数计算 stride: 控制相关系数的计算步长
dilation: 用于控制内核点之间的距离,详细描述在这里
groups: 控制输入和输出之间的连接: group=1,输出是所有的输入的卷积;group=2,此时相当于有并排的两个卷积层,每个卷积层计算输入通道的一半,并且产生的输出是输出通道的一半,随后将这两个输出连接起来。
参数kernel_size,stride,padding,dilation可以是一个int的数据 - 卷积height和width值相同,也可以是一个有三个int数据的tuple数组,tuple的第一维度表示depth的数值,tuple的第二维度表示height的数值,tuple的第三维度表示width的数值
Parameters:
in_channels(int) – 输入信号的通道
out_channels(int) – 卷积产生的通道
kernel_size(int or tuple) - 卷积核的尺寸
stride(int or tuple, optional) - 卷积步长
padding(int or tuple, optional) - 输入的每一条边补充0的层数
dilation(int or tuple, optional) – 卷积核元素之间的间距
groups(int, optional) – 从输入通道到输出通道的阻塞连接数
bias(bool, optional) - 如果bias=True,添加偏置
说明:
bigotimes: 表示二维的相关系数计算 stride: 控制相关系数的计算步长
dilation: 用于控制内核点之间的距离,详细描述在这里
groups: 控制输入和输出之间的连接: group=1,输出是所有的输入的卷积;group=2,此时相当于有并排的两个卷积层,每个卷积层计算输入通道的一半,并且产生的输出是输出通道的一半,随后将这两个输出连接起来。
- 参数kernel_size,stride,padding,dilation可以是一个int的数据 - 卷积height和width值相同,也可以是一个有三个int数据的tuple数组,tuple的第一维度表示depth的数值,tuple的第二维度表示height的数值,tuple的第三维度表示width的数值
1.1.3 单通道3D卷积详解
针对单通道,与2D卷积不同之处在于,输入的图像多了一个 depth 维度,即输入为(1, depth, height, width),卷积核也多了一个k_d维度,因此卷积核在输入3D图像的空间维度(height和width维)和depth维度上均进行滑窗操作,每次滑窗与 (k_d, k_h, k_w) 窗口内的values进行相关操作,得到输出3D图像中的一个value。
针对多通道,输入大小为(3, depth, height, width),则与2D卷积的操作一样,每次滑窗与3个channels上的 (k_d, k_h, k_w) 窗口内的所有values进行相关操作,得到输出3D图像中的一个value。
这就是3D卷积的详细过程,从图中可以看出每次卷积需要考虑多个帧数的图片信息,最终卷积后的结果也是一个三维图像。其实单通道的3D卷积和多通道的2D卷积过程相差不多。下图详细表述两者的区别
这是多通道的2D卷积过程,可以看出特征图具有多个通道,将多通道的2维特征图放到一起和单通道的3维特征图一样。卷积核的大小中通道数与特征图的通道数必须保持一致,如上图所示进行一次卷积操作时得到一个值,当卷积核在长和宽方向上移动完毕时就得到了一个2维特征图。由于卷积核和输入特征图在通道方向上大小一致因此在通道方向上卷积核无法移动,这就是为何一个卷积核产生的结果是2维的。
以上是单通道的3D卷积过程,由图可见,其与多通道2D卷积的区别在于在第三个维度(可以是时间维度)卷积核的维度大小不是必须和输入图像的维度相等。因此在这个方向上卷积核也是可以移动的,最终一个3D卷积核卷积操作之后的特征图也是3维的。这就是多通道2D卷积与单通道3D卷积的区别与联系。
1.1.4 多通道3D卷积
对于单通道卷积的介绍网上资料很多,借助常见的多通道2D卷积也是很好理解的。但是在实际使用中在模型的中间层部分,就不再是单通道的3D卷积了,3维特征图将变成多通道的特征图。如何进行多通道的3D卷积网上资料所谈甚少,最终通过查看相关资料以及自己理解发现下图可以很形象的表述多通道3D卷积的过程。
借用多通道2D卷积过程可知,3D卷积核的通道数和特征图的通道数是一致的,则在单个3维特征图上的卷积过程可参见单通道3D卷积的过程,多通道3D卷积的每一次卷积操作将多个通道卷积核的结果相加作为卷积结果的一个值。则最终多通道3D卷积过程既可以考虑到多通道信息的融合问题,1个卷积核卷积后的结果也是3维特征图。如果输出需要是多通道的,则需要多个多通道3D卷积核。
1.2 立体视觉
1.2.1 立体视觉原理
使用两个或多个从不同角度拍摄的2D图像来估计每个像素的深度,从而重建3D场景。.一般而言,立体视觉系统需要有两个(或者两个以上)摄像头的支持,也就正如人类的双眼一样。下面首先介绍一下单目系统。
1.2.2 单目系统
O点为相机的光心,π是摄像头的成像平面。
从图中可以看出,如果P点与Q点在同一条直线上,那么他们在图像上的成像点就是同一个点,也就是 ,那么也就看不出来他们在距离上的差异(也就无法知道Q在前还是P在前)
1.2.3 双目系统
正是在发现了单目系统的缺陷之后,我们将系统由一个摄像头增加到两个摄像头,这样也就构成了一个立体系统。如果我们可以在两幅图像中找到对应点,我们就可以通过三角测量的方法来求得深度。
从图中可以很明显的看出,在增加了一个摄像头之后,P与Q在目标面T上的成像不在位于同一个点,而是有自己分别的成像点,也就是与。
那么,在我们给出了Reference与Target之后,我们应该如何解决参考面与目标面之间的对应关系呢?
这个时候,就需要对极约束,对极约束意味着一旦我们知道了立体视觉系统的对极几何之后,对两幅图像间匹配特征的二维搜索就转变成了沿着极线的一维搜索。
图中黑色实线为R平面一条极线,绿色为T平面一条极线。给定一幅图像上的一个特征,它在另一幅图像上的匹配视图一定在对应的极线上(图中将P,Q视为特征,可以看到在T上的成像在绿色直线上)
通常我们使用的立体视觉系统都是比较标准的系统,如图所示。
一旦我们知道了对应点的搜索区域,就可以将其从2D降到1D,这样就形成了更加方便的立体视觉,对应点都被约束再同一条极线上,也就是图中的y直线。下面给出一个实际的示例(在理想情况下,我们希望两个摄像头的参数是完全一致的,并且两个相机的位置是平行的)。
1.2.4 视差与深度计算原理
在我们已经确保两个摄像头的参数是完全一致的,并且两者的位置是平行之后,我们的关注点就落到了如何计算物体的深度信息,这也是最重要最关键的地方。下面给出的是标准立体视觉系统下的计算原理。
我们可以发现,深度Z是跟视差D成反比关系的,当视差D越小时,Z越大,物体离立体视觉系统也就越远, 当视差D越大,Z越小,物体离立体视觉系统也就越近。这一点和我们人眼系统是一样的,当我们观察离我们比较近的物体的时候,视差很大,可以获得的信息也就越多,当物体离我们很远的时候,视差很小,我们获得的信息也就很少了。
在图像处理中,我们通常用灰度值来表示视差信息,视差越大,其灰度值也就越大,在视差图像的视觉效果上表现出来就是图像越亮,物体离我们越远,其视差越小,灰度值也越小,视差图像也就越暗。
1.3 深度估计
- 工作原理:使用深度学习模型来预测2D图像中每个像素的深度。
- 优势:可以从单个2D图像中获得3D深度信息
- 应用:增强现实、虚拟现实、3D重建。
二 3D到2D降采样
2.1 投影
3d 转 2d 的流水线如下:
世界坐标系->相机坐标系->投影矩阵->像素映射->生成图片
- 世界坐标系和相机坐标系转换可以通过dcm矩阵计算求出
def dcm(origin: np.ndarray, target: np.ndarray):
"""
3 * 3 矩阵 ,{x,y,z}T 将origin坐标系转换到target坐标系的dcm旋转矩阵
Args:
origin:
target:
Returns:
"""
matrix = np.zeros((3, 3))
for i in range(3):
for j in range(3):
matrix[i, j] = np.dot(target[i], origin[j])
return matrix.T
- 投影矩阵,可以参考pyrender.camera.py中的透视投影和正交投影矩阵。也可以根据自己的需求定制
- 通过1,2两步计算出的2d投影点,会落在(-1,1)范围内。通过像素映射完成3d点到2d点的投影
完整代码:
class Camera:
def __init__(self, scale, translation, resolution, znear=0.05, zfar=1000):
self.scale = np.array(scale) # 相机缩放
self.translation = np.array(translation) # 相机位移
self.resolution = np.array(resolution) # 2d 分辨率
self.znear = znear # 近平面
self.h_s = self.resolution / 2 # h/2 w/2
self.center = self.h_s # 2d投影面中心点
def camera_matrix(self):
"""
相机外参矩阵,世界坐标系转相机坐标系
Returns:
"""
world = np.eye(3)
camera = np.eye(3)
camera[-1, -1] = -1
matrix = np.eye(4)
matrix[:3, :3] = dcm(world, camera)
return matrix
def get_projection_matrix(self) -> np.ndarray:
"""
投影矩阵 业务定制
Returns:
"""
P = np.eye(4)
P[0, 0] = self.scale[0]
P[1, 1] = self.scale[1]
P[0, 3] = self.translation[0] * self.scale[0]
P[1, 3] = -self.translation[1] * self.scale[1]
P[2, 2] = -1
return P
class Render:
def __init__(self, camera: Camera):
self.camera = camera
def p_point(self, point: np.ndarray):
"""
投影点坐标
Args:
point: 点 4D 例如[0.5,0.5,0.5,1] 3d点需要填充1
Returns:
"""
p = self.camera.get_projection_matrix().dot(self.camera.camera_matrix().dot(point))
p = p[:2] / p[-1] * self.camera.h_s * np.array([1, -1]) + self.camera.center
return p
2.5D表示
2D、2.5D和3D是描述物体和场景在空间中的表示的三种方式。2d(平面)与3d(立体)又称为二维与三维。它们之间的区别是:2d你只能看到一个面,3d你能看到所有的面。:
- 定义:
- 2D (二维): 在2D中,物体或场景只有长度和宽度两个维度。常见的2D表示包括照片、图画和屏幕上的图像。
- 2.5D (二点五维): 2.5D介于2D和3D之间。它通常描述的是一个场景从特定角度的深度信息。一个2.5D图像(例如深度图)为每个像素提供了一个深度值。
- 3D (三维): 3D表示考虑了长度、宽度和深度。它为场景中的每个点提供了完整的三维坐标。常见的3D表示包括3D模型、点云等。
- 区别:
- 维度和信息完整性: 2D缺乏深度信息;2.5D提供了从某个视角的深度信息;3D提供了完整的三维坐标信息。
- 视角依赖性: 2.5D通常与特定的视角相关,而3D表示是视角无关的。
- 数据复杂性: 2D数据最简单,只需要x和y坐标;2.5D需要x、y和深度;3D需要x、y和z三个坐标。
- 联系:
- 从2D到2.5D: 如果你有一个2D图像和与之相关的深度信息,你可以得到一个2.5D表示。例如,使用深度相机如Kinect可以得到深度图。
- 从2D到3D: 通过多个2D图像和某种形式的结构从运动或立体视觉,你可以重建出3D场景或物体。但这比从2.5D到3D更为复杂。
- 从2.5D到3D: 从深度图中可以重建3D信息,例如生成一个点云。但由于2.5D信息通常是从一个视角获得的,因此可能不能完全恢复物体或场景的所有3D信息。
简而言之,2D、2.5D和3D代表了逐渐增加的空间信息和复杂性。2.5D是一个中间表示,提供了比2D更多的深度信息,但没有3D那么完整。
2D与2.5D的关系可以看成X轴与Y轴旋转了指定的角度后形成的新的平面。
首先需要知道坐标旋转算法:
通过矩阵的知识可以知道,X轴的基向量为[1,0];Y轴的基向量为[0,1]。有X和Y轴基向量组成的矩阵是一个单位矩阵。所以常规的平面直角坐标系的任何一点可以表示为:
为了将2D坐标映射到2.5坐标,需要定义2.5D坐标系统使用的基向量。因为2.5D坐标系实际上是通过旋转X与Y轴实现的,所以通过旋转算法和上面的单位矩阵,可以得到新坐标系的X和Y轴基向量:
可以看出,将2D坐标系中的基向量转换为2.5D坐标系统的基向量时,结果其实就是旋转矩阵本身,这个旋转矩阵就是2.5D坐标系中的X和Y轴基向量。
**注意:**这里分别使用θ和α,是因为X和Y轴可以旋转不同的角度。如果 θ+α=90度,那么Sin(α)=Cos( θ );Cos(α)=Sin( θ )。上面的矩阵可以被替换为:
现在定义2D坐标系为W(x,y),2.5D坐标系为G(x,y)。2D坐标系的X轴相对于2.5D坐标系X轴顺时针旋转30°,Y轴旋转60°。通过上面的公式可以得到W(x,y)对应的G(x,y):
2.5D坐标只需利用上面的工作进行逆运算就能得到:
三 混合方法
3.1 多视图网络
- 工作原理:使用从不同角度的多个2D视图的信息来提取3D特征。
- 应用:3D物体识别、3D重建。
- 优点:能够从不同的2D视图中捕获3D信息。
3.2 融合2D和3D特征
- 工作原理:将2D图像特征与3D数据特征(例如点云)结合起来。
- 应用:3D物体检测、场景分割。
- 优点:利用了2D图像和3D结构的强大信息。