背景知识

接下来考虑将上部分得到的辅助线绘制到前相机画面中. 首先介绍一些相机内外参的背景知识, 然后才能理解投影操作. 以下大部分内容来自: https://thomasfermi.github.io/Algorithms-for-Automated-Driving/LaneDetection/CameraBasics.html

相机感光元件是一个平面:

放大后如下图:

每个像素由(红, 绿, 蓝)三基色组成:

平面上的每个像素坐标(u, v)如下, 可见相机平面坐标是以左上角为原点的平面直角坐标系:

相机成像是我们熟悉的小孔成像原理:

上图展示了简单的小孔成像, 光线从实物上反射, 穿过相机的针孔, 到达相机底片被光线传感器捕获. 从侧面看是这样的:

上图涉及三个坐标系:

  1. 物理世界的坐标系, 例如UTM, 表示树的坐标(x, y, z)
  2. 相机坐标: 以相机针孔为坐标原点的三位坐标系
  3. 相机平面坐标: 相机感光平面表示的(u, v)坐标系

如果我们想把之前生成的辅助线投影到画面中, 需要: 辅助线坐标 → 相机坐标 → 相机平面坐标. opencv中有现成的方法可供调用cv2.fisheye.projectPoints. 理论知识可以参考:

  1. https://thomasfermi.github.io/Algorithms-for-Automated-Driving/LaneDetection/CameraBasics.html
  2. https://docs.opencv.org/4.x/dc/dbb/tutorial_py_calibration.html
  3. https://docs.opencv.org/3.4/d9/d0c/group__calib3d.html#ga61585db663d9da06b68e70cfbf6a1eac

实现


def projection(left_x, left_y, right_x, right_y, extrinsics, K, D):
    """
    将辅助线投影到相机平面
    :param left_x: 左线x坐标
    :param left_y: 左线y坐标
    :param right_x: 右线x坐标
    :param right_y: 右线y坐标
    :param img: 图片
    :param extrinsics: 外参, 世界坐标 -> 相机坐标的转换关系
    :param K: 内参, 相机坐标 -> 相机平面的转换关系
    :param D: 内参, 鱼眼相机的畸变参数
    :return: [左线, 右线]
    """
 
    roll_vel_cam = cv2.decomposeProjectionMatrix(extrinsics)[6][:, 0][0]
    pitch_road_cam = roll_vel_cam + 90
    rvec = np.float32([-pitch_road_cam / 180 * np.pi, 0, 0])
    tvec = np.float32([0, 0, 0])
 
    lines = []
    for [x, z] in [[left_x, left_y], [right_x, right_y]]:
        y = 1.3879948854446411  # 相机高度, 相机坐标下, y是竖直的
        a = np.c_[x, np.repeat(y, len(x)), z]
        points_3d = np.expand_dims(a, axis=0)
 
        points_2d, _ = cv2.fisheye.projectPoints(points_3d, rvec, tvec, K, D)
        lines.append(points_2d[0])
 
    return lines

至此, 我们就可以根据方向盘转角生成辅助线了.