基于KITTI数据集的无人驾驶感知与传感器融合实现—(1)—相机标定

学习前言

  这里主要是将的相机标定(Camera标定)的代码实现,主要用到了OpenCV和numpy库,原理的话不会着重讲,如果想了解原理的话可以参考一下我写的另外几篇博客。1.1-针孔摄像机&透镜.& 1.2 摄像机几何. &2.1 摄像机标定.& 2.2 径向畸变摄像机标定.& 透视变换(还在写)。
  加油!加油!从咸鱼变活鱼!
  在此之前还是附上原作者的项目网站:https://aistudio.baidu.com/aistudio/projectdetail/1870088.。有兴趣的同学可以进去看看。
在这里插入图片描述

一、OpenCV相机标定

  小孔成像模型: 外部光线经过小孔,倒立成像于相平面上,这种模型可限制光源光线数量,避免光线叠加,光强过亮失真,但同样,在微弱光线环境下的感光性较弱,而Camera的感光性对于无人车视觉感知系统是一个十分重要的指标,影响召回率。
  透镜成型模型: 车载Camera通常使用透镜模型,使多条射入光线聚焦于相平面上,相比小孔模型具备更好的感光性,但透镜存在一个很大的问题——图像畸变。
在这里插入图片描述

1、核心API介绍

  1)retval, corners = cv2.findChessboardCorners(image, patternSize[, corners[, flags]])

  API功能:查找棋盘内角的位置。该函数尝试确定输入图像是否是棋盘图案的视图,并找到棋盘内部的角。如果找到了所有角并将它们按一定顺序放置(逐行,在每一行中从左到右)(存储在corners中),该函数将返回一个非零值(retval)。否则,如果函数无法找到所有角或对其重新排序,则它将返回0。例如,常规棋盘具有8 x 8正方形和7 x 7个内部角,即黑色正方形相互接触的点。检测到的坐标是近似值,为了更准确地确定其位置,该函数调用cornerSubPix。如果返回的坐标不够准确,则还可以将函数cornerSubPix与不同的参数一起使用。

  参数:
    image: 源棋盘视图。它必须是8位灰度或彩色图像。
    patternSize:每个棋盘行和列的内角数(columns列,rows行)->就是这个棋盘有多少内角是需要我们自己数清楚然后输入的。
    corners:检测到的角的输出数组。
    flags:各种操作标志,可以为零或以下值的组合:
       CALIB_CB_ADAPTIVE_THRESH:使用自适应阈值处理将图像转换为黑白图像(根据平均图像亮度计算)。
      CALIB_CB_NORMALIZE_IMAGE:在应用固定或自适应阈值设置之前,使用equalizeHist对图像伽玛进行归一化。
      CALIB_CB_FILTER_QUADS:使用其他条件(如轮廓区域,周长,正方形)来过滤在轮廓检索阶段提取的假四边形。
      CALIB_CB_FAST_CHECK:对查找棋盘角的图像进行快速检查,如果未找到,则将调用快捷化。
                       当未观察到棋盘时,这可以大大降低退化状态下的通话速度。
  返回:
    retval: bool类型数据,如果返回的是0,则表示没有找到所有角点,返回非0值,表示检测到了。
    corners :数组,所有角点的位置并将它们按一定顺序放置(逐行,在每一行中从左到右)

  2)image = cv2.drawChessboardCorners(image, patternSize, corners, patternWasFound)

  API功能:渲染检测到的棋盘角。 该功能绘制检测到的单个棋盘角,如果找不到该棋盘,则将其绘制为红色圆圈,如果找到该棋盘,则将其绘制为与线相连的彩色角。

  参数:
    image: 目标图像。它必须是8位彩色图像。
    patternSize:每个棋盘行和列的内角数(columns列,rows行)->也是自己输入
    corners:检测到的角点数组,findChessboardCorners的输出。
    patternWasFound :指示是否找到完整板的参数。findChessboardCorners的返回值应在此处传递。
  返回:
    image : 画好了的图片。

  findChessboardCorners和drawChessboardCorners函数将点画出来的话,效果是这样子的:在这里插入图片描述

  3)retval, cameraMatrix, distCoeffs, rvecs, tvecs = cv2.calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs[, flags[, criteria]])

  API功能:从校准图案的多个视图中查找相机的内部和外部参数。该函数估计每个视图的固有摄像机参数和外部参数。该算法基于相机标定的(就是学习前言里面讲的那三章内容)。必须指定每个视图中3D对象点的坐标及其对应的2D投影。这可以通过使用具有已知几何形状和易于检测的特征点的对象来实现。

  参数:
    objectPoints: 它是校准图案坐标空间中校准图案点的向量的向量。 外部向量包含与图案视图数量一样多的元素。如果在每个视图中显示相同的校准图案并且完全可见,则所有矢量将相同。虽然,可以在不同的视图中使用部分遮挡的图案,甚至使用不同的图案。然后,向量将不同。尽管这些点是3D的,但如果使用的校准图案是平面装备,它们都位于校准图案的XY坐标平面中(因此在Z坐标中为0)。在旧界面中,来自不同视图的所有对象点向量都被串联在一起。可以理解为我们需要自己敲一个数组来表示这些内角点在世界坐标系中的位置,而且 z z z轴都为0。
    imagePoints:它是校准图案点的投影向量的向量。 imagePoints.size()和objectPoints.size()以及imagePoints [i].size()和objectPoints [i]。每个i的size()必须分别相等。在旧界面中,来自不同视图的所有对象点向量都被串联在一起。
    imageSize:仅用于初始化相机固有矩阵的图像大小。
    cameraMatrix:输入/输出3x3浮点相机投影矩阵 。(一般都写None,因为我们大部分时候都是要求这个矩阵)
[ f x 0 c x 0 f y c y 0 0 1 ] \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 &1 \end{bmatrix} fx000fy0cxcy1    distCoeffs: 失真系数的输入/输出向量 。 k 1 , k 2 , p 1 , p 2 [ , k 3 [ , k 4 , k 5 , k 6 [ , s 1 , s 2 , s 3 , s 4 [ , τ x , τ y ] ] ] ] k_1,k_2,p_1,p_2[,k_3[,k_4,k_5,k_6[,s_1,s_2,s_3,s_4[,τ_x,τ_y]]]] k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4[,τx,τy]]]] 4、5、8、12或14个元素
  返回:
    retval: 画好了的图片。
    cameraMatrix: 3x3浮点相机投影矩阵。就是输入参数的那个cameraMatrix,可以做输出也可以做输入。
    distCoeffs: 失真系数。同上。
    rvecs: 旋转矩阵 R R R。即,每个第i个旋转向量与相应的第i个平移向量(请参见下一个输出参数描述)一起将校准图案从对象坐标空间(在其中指定了对象点)带到摄像机坐标空间。用更专业的术语来说,第i个旋转和平移矢量的元组执行从对象坐标空间到相机坐标空间的基础变化。由于其双重性,该元组等效于校准图案相对于相机坐标空间的位置。
    tvecs : 平移矩阵 T T T。同上

  4)dst = cv.undistort(src,cameraMatrix,distCoeffs [,dst [,newCameraMatrix]])

  API功能:变换图像以补偿镜头失真。 该功能可变换图像以补偿径向和切向透镜畸变。源图像中没有对应像素的目标图像中的那些像素用零填充(黑色)。可以使用calibrateCamera确定相机矩阵和失真参数。

  参数:
    src:输入(失真)图像。
    cameraMatrix:输入摄像机投影矩阵 。就是cv2.calibrateCamera函数返回值中的cameraMatrix。
    distCoeffs :失真系数的输入向量。cv2.calibrateCamera函数返回值中的distCoeffs。如果向量为NULL /空,则假定失真系数为零。
    dst :输出(校正)的图像,其大小和类型与src相同。一般为None,因为我基本上用返回值的。
    newCameraMatrix:扭曲图像的相机矩阵。默认情况下,它与cameraMatrix相同,但是您可以通过使用其他矩阵来缩放和移动结果。

  返回:
    dst : 输出(校正)的图像,其大小和类型与src相同。

  效果图:在这里插入图片描述

2、代码实现

import glob
import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg


# 使用标定函数cv2.calibrateCamera()和去除畸变函数cv2.undistort()
def cal_undistort(img, objpoints, imgpoints):
    undist = np.copy(img)
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, undist.shape[:2], None, None)
    undist = cv2.undistort(undist, mtx, dist, None, mtx)
    return undist


# 建立objpoints 和 imgpoints空列表
objpoints = []
imgpoints = []

# 画网格指定objpoints位置
objp = np.zeros((6 * 8, 3), np.float32)
a = np.mgrid[0:8, 0:6]
a = a.T
a = a.reshape(-1, 2)
objp[:, :2] = a

# 读入图片并转为灰度
# 类似于拟定棋盘的内角在现实空间中的坐标
fname = "test_image/test_chessboard.jpg"
img = mpimg.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

# 应用findchessboard函数找到图像角点
ret, corners = cv2.findChessboardCorners(gray, (8, 6), None)

if ret == True:
    # 分别将角点和目标点位置写入列表,并将角点画出
    imgpoints.append(corners)
    objpoints.append(objp)
    gray = cv2.drawChessboardCorners(gray, (8, 6), corners, ret)


    # 对图片进行去除畸变
    image = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)
    image = cv2.drawChessboardCorners(image, (8, 6), corners, ret)
    undistorted = cal_undistort(image, objpoints, imgpoints)


    # 可视化
    f, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 9))
    f.tight_layout()
    ax1.imshow(img)
    ax1.set_title('Original Image', fontsize=30)
    ax2.imshow(undistorted)
    ax2.set_title('Undistorted Image', fontsize=30)
    plt.subplots_adjust(left=0., right=1, top=0.9, bottom=0.)
    plt.show()
    

在这里插入图片描述

二、OpenCV相机标定——进阶(透视变换)

   可以看出图像中间点undistort效果比较好,边缘处还是存在变形,这是因为目标点objpoints范围未完全覆盖所有角点。
   因此,也可以利用感知转换方程,人为地在图像中选点,并决定其目标点位,精确地对图片进行转换:

1、核心API介绍

  1)retval = cv2.getPerspectiveTransform (src,dst)

  API功能:根据四对对应点计算透视变换。 该函数计算透视图转换的3×3矩阵,以便:
[ t i x i ′ t i y i ′ t i ] = m a p − m a t r i x ⋅ [ x i y i 1 ] \begin{bmatrix} t_ix'_i \\ t_iy'_i \\ t_i \end{bmatrix} =map -matrix \cdot\begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix} tixitiyiti=mapmatrixxiyi1
  其中 d s t ( i ) = ( x i ′ , y i ′ ) , s r c ( i ) = ( x i , y i ) , i = 0 , 1 , 2 , 3 dst(i)=(x'_i,y'_i), src(i)=(x_i,y_i),i=0,1,2,3 dst(i)=(xi,yi),src(i)=(xi,yi),i=0,1,2,3

  参数:
    src:源图像中四边形顶点的坐标。(需要手动获取)
    dst:目标图像中相应四边形顶点的坐标。
  返回:
    retval :由源图像中矩形到目标图像矩形变换的矩形。

  2)dst = cv2.warpPerspective (src,M,dsize [,dst [,flags [,borderMode [,borderValue]]]]])

  API功能:将透视变换应用于图像。 函数warpPerspective使用指定的矩阵转换源图像: d s t ( x , y ) = s r c ( M 11 x + M 12 y + M 13 M 31 x + M 32 y + M 33 , M 21 x + M 22 y + M 23 M 31 x + M 32 y + M 33 ) dst(x,y)=src(\frac{M_{11}x+M_{12}y+M_{13}}{M_{31}x+M_{32}y+M_{33}},\frac{M_{21}x+M_{22}y+M_{23}}{M_{31}x+M_{32}y+M_{33}}) dst(x,y)=src(M31x+M32y+M33M11x+M12y+M13,M31x+M32y+M33M21x+M22y+M23)
  当标志WARP_INVERSE_MAP被设置时。先用inverse将变换代入上式,否则函数不能就地运行。

  参数:
    src: 输入图像。
    M: 3 ✖ 3 3✖3 33的转换矩阵。就是getPerspectiveTransform 函数的输出矩阵。
    dsize :输出图像的大小。
    flags :插值方法(INTER_LINEAR(默认)或INTER_NEAREST)与可选标志WARP_INVERSE_MAP的组合→ (设置M为逆变换(dst → src ))。
    borderMode :像素外推方法即边界补偿方式(BORDER_CONSTANT或BORDER_REPLICATE)。
    borderValue:边界补偿的值;默认情况下,它等于0。(就是黑色的)
  返回:
    dst : 输出图像,其大小为dsize,并且类型与src相同。

2、代码实现

import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from PIL import Image


def cal_undistort(img, objpoints, imgpoints):
    undist = np.copy(img)
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, undist.shape[:2], None, None)
    undist = cv2.undistort(undist, mtx, dist, None, mtx)
    return undist


# 建立objpoints 和 imgpoints空列表
objpoints = []
imgpoints = []

# 画网格指定objpoints位置
objp = np.zeros((6 * 8, 3), np.float32)
a = np.mgrid[0:8, 0:6]
a = a.T
a = a.reshape(-1, 2)
objp[:, :2] = a

# 读入图片并转为灰度
fname = "test_image/test_chessboard.jpg"
img = mpimg.imread(fname)
nx = 8
ny = 6
# 对图片边界的抵偿值
off_set = 100
img_size = (img.shape[1], img.shape[0])
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (nx, ny), None)

if ret == True:
    # 分别将角点和目标点位置写入列表,并将角点画出
    imgpoints.append(corners)
    objpoints.append(objp)
    gray = cv2.drawChessboardCorners(gray, (8, 6), corners, ret)

    # 对图片进行去除畸变
    image = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)
    image = cv2.drawChessboardCorners(image, (8, 6), corners, ret)
    undistorted = cal_undistort(image, objpoints, imgpoints)

    src = np.float32([corners[0], corners[nx - 1], corners[-1], corners[-nx]])
    dst = np.float32(
        [[off_set, off_set], [img_size[0] - off_set, off_set], [img_size[0] - off_set, img_size[1] - off_set],
         [off_set, img_size[1] - off_set]])
    M = cv2.getPerspectiveTransform(src, dst)
    warped = cv2.warpPerspective(undistorted, M, img_size, flags=cv2.INTER_LINEAR)

    f, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 9))
    f.tight_layout()
    ax1.imshow(img)
    ax1.set_title('Original Image', fontsize=30)
    ax2.imshow(warped)
    ax2.set_title('Undistorted and Warped Image', fontsize=30)
    plt.subplots_adjust(left=0., right=1, top=0.9, bottom=0.)
    plt.show()

在这里插入图片描述
是不是比之前的效果要好一些?

三、棋盘图片

链接: link.
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fire丶Chicken

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值