OpenCV快速入门:相机标定——单目视觉和双目视觉


前言

在当今科技日益发展的时代,计算机视觉作为人工智能的重要分支,已经深入到我们生活的各个领域。在这个广阔的领域中,相机标定是一个基础且关键的步骤,它直接影响到视觉系统的精度和效能。尤其是在单目视觉和双目视觉的应用中,准确的相机标定成为了实现高效和精确视觉感知的前提。

单目视觉(Monocular Vision)和双目视觉(Binocular Vision)是计算机视觉中两种最基本的视觉形式。单目视觉指的是只使用一个相机进行图像捕捉和处理,它是最常见的视觉形式,广泛应用于各种智能设备和监控系统中。单目视觉系统的主要挑战在于它无法直接从图像中获取深度信息,这就要求我们通过算法和模型来推断出场景的三维结构。而双目视觉则是模拟人类的双眼视觉机制,通过两个相机从略微不同的角度捕捉图像,从而能够直接计算出图像中对象的深度信息。这种方式使得双目视觉系统在处理三维空间信息时更加精确和高效,尤其在机器人导航、自动驾驶汽车等领域展现出巨大的潜力。

本文将简要介绍单目视觉和双目视觉相机标定的基本原理和实践方法。

OpenCV Logo


一、相机标定的基本原理

相机标定是计算机视觉领域中一个极为重要的过程,它涉及到了理解和校正相机捕捉图像的方式。这个过程对于提高图像处理的精度和效果至关重要,尤其是在单目视觉和双目视觉的应用中。

1.1 相机模型与坐标系

在深入相机标定的实践之前,了解其背后的原理是必要的。相机模型和坐标系构成了相机标定的理论基础。

1.1.1 相机模型

相机模型用于描述三维世界中的点是如何映射到二维图像上的。其中,最常见的模型是针孔相机模型(Pinhole Camera Model)。

在针孔相机模型中,一个三维空间中的点 P ( x , y , z ) P(x, y, z) P(x,y,z) 通过一个投影过程被映射到二维图像平面上的点 p ( u , v ) p(u, v) p(u,v)。这个映射过程通常涉及到几何和线性代数中的一些基本概念。

映射过程可以用以下公式表示:
u = f x x z + c x u = f_x \frac{x}{z} + c_x u=fxzx+cx
v = f y y z + c y v = f_y \frac{y}{z} + c_y v=fyzy+cy

扫描二维码关注公众号,回复: 17223924 查看本文章

这里, ( f x , f y ) (f_x, f_y) (fx,fy) 是相机的焦距,分别在图像平面的 x 轴和 y 轴上的缩放因子; ( c x , c y ) (c_x, c_y) (cx,cy) 是图像平面的主点,通常是图像中心。

在这个模型中,三维空间中的点首先通过针孔(理想化的一个小孔)投影到一个假想的图像平面上,然后根据相机的内参(如焦距和主点位置)转换到实际的图像坐标系中。这个过程虽然简化了现实世界的复杂性,但为计算机视觉提供了一个强大的基础模型,用于理解和处理三维世界与二维图像之间的关系。

1.1.2 坐标系

在相机标定和计算机视觉领域中,重要的是理解和处理不同的坐标系,尤其是世界坐标系相机坐标系

  1. 世界坐标系(World Coordinate System)
  • 这是一个固定的坐标系,通常用于描述真实世界中对象的位置。
  • 世界坐标系是一个参考坐标系,用于定义对象在空间中的绝对位置。
  • 它是一个三维坐标系,通常以某个方便的点作为原点,如场景的一个角或者某个特定的地标。
  1. 相机坐标系(Camera Coordinate System)
  • 相机坐标系是一个以相机为中心的坐标系,用于描述相机视角下点的位置。
  • 在这个坐标系中,原点通常位于相机的光学中心,x轴和y轴分别平行于相机的图像平面,而z轴则沿着相机的视线方向。
  • 相机坐标系是相对于相机位置和方向的,因此当相机移动时,相机坐标系也会随之移动。

1.2 相机内参与外参

在相机标定中,理解和确定相机的内参(Intrinsic Parameters)和外参(Extrinsic Parameters)是至关重要的。这两组参数共同定义了从三维世界坐标系到二维图像坐标系的完整映射过程。内参和外参的准确获取是计算机视觉应用成功的关键。例如,在增强现实中,为了将虚拟对象正确地叠加在真实世界的图像上,必须精确知道相机的位置和朝向(外参),以及如何将三维空间正确映射到二维图像上(内参)。

1.2.1 内部参数

内部参数(Intrinsic Parameters)描述的是相机本身的特性,这些参数与相机的位置和朝向无关。它们主要包括:

  1. 焦距:相机镜头的焦距,通常表示为图像平面上的像素单位。焦距影响图像的放大程度。

  2. 主点坐标:图像传感器上,成像中心点(通常接近图像中心)的坐标。这个点是三维世界中的“相机中心”在图像平面上的映射。

  3. 畸变系数:包括径向畸变和切向畸变参数,用于描述和校正由于镜头设计和组装不完美造成的图像畸变。

内参是通过相机标定过程获取的,它们一旦被确定,对于同一相机在不同情况下都是固定的。

1.2.2 外部参数

外部参数(Extrinsic Parameters)描述的是相机相对于世界坐标系的位置和姿态。这些参数与相机如何放置和指向有关。它们包括:

  1. 旋转矩阵:描述了相机从世界坐标系到相机坐标系的旋转。

  2. 平移向量:描述了世界坐标系原点到相机坐标系原点(即相机光学中心)的平移。

外参是场景依赖的,即每次相机位置或方向改变时,外参也需要重新确定。

1.3 镜头畸变

镜头畸变(Lens Distortion)是相机镜头无法完美映射现实世界到图像平面上的一种表现,它导致了图像的几何形状与实际物体的形状存在差异。在计算机视觉和相机标定中,处理镜头畸变是非常重要的一部分。

  1. 畸变类型

    • 径向畸变(Radial Distortion):最常见的畸变类型,通常表现为图像的边缘部分比中心部分更加扭曲。在径向畸变中,图像中的直线可能呈现为曲线。
    • 切向畸变(Tangential Distortion):由于镜头和图像感应器之间的不完美平行对齐,可能导致图像某些部分被稍微偏移。
  2. 畸变校正

    • 通过相机标定可以获得畸变参数,然后利用这些参数进行畸变校正,以恢复图像的真实几何结构。
    • 校正算法通常会调整图像中的每个像素位置,以补偿畸变效果。

下面是径向畸变图例,分别是:正径向畸变,原图,负径向畸变
畸变图片
径向畸变代码实现:

import numpy as np
import cv2


def apply_radial_distortion(image, k1, k2):
    height, width = image.shape[:2]
    # 计算图像中心
    center_x, center_y = width / 2, height / 2

    # 准备畸变后的图像
    distorted_image = np.zeros_like(image)

    # 遍历每个像素
    for i in range(height):
        for j in range(width):
            # 计算相对于中心的坐标
            x = (j - center_x) / center_x
            y = (i - center_y) / center_y
            r = np.sqrt(x ** 2 + y ** 2)

            # 应用畸变模型
            x_distorted = x * (1 + k1 * r ** 2 + k2 * r ** 4)
            y_distorted = y * (1 + k1 * r ** 2 + k2 * r ** 4)

            # 计算畸变后的像素位置,并确保它在图像范围内
            distorted_j = int(center_x * (x_distorted + 1))
            distorted_i = int(center_y * (y_distorted + 1))

            if 0 <= distorted_j < width and 0 <= distorted_i < height:
                distorted_image[i, j] = image[distorted_i, distorted_j]

    return distorted_image


# 棋盘格参数
chessboard_size = (8, 6)
square_size = 50

# 创建画布
image_size = (300, 400)
image = np.zeros((image_size[0], image_size[1], 3), dtype=np.uint8) + 255

# 画棋盘格
for i in range(chessboard_size[1]):
    for j in range(chessboard_size[0]):
        top_left_x = j * square_size
        top_left_y = i * square_size
        bottom_right_x = (j + 1) * square_size
        bottom_right_y = (i + 1) * square_size
        color = (0, 0, 0) if (i + j) % 2 == 0 else (255, 255, 255)
        cv2.rectangle(image, (top_left_x, top_left_y), (bottom_right_x, bottom_right_y), color, -1)

# 应用径向畸变
distorted_image3 = apply_radial_distortion(image, 0.05, -0.05)
distorted_image4 = apply_radial_distortion(image, -0.05, 0.05)

# 显示原始和畸变后的图像
cv2.imshow('Distorted Image', cv2.hconcat([distorted_image3,
                                           np.zeros((image.shape[0], 10, 3), dtype=np.uint8) + 127,
                                           image,
                                           np.zeros((image.shape[0], 10, 3), dtype=np.uint8) + 127,
                                           distorted_image4]))
cv2.waitKey(0)
cv2.destroyAllWindows()

1.4 透视变换

透视变换(Perspective Transformation)是一种用于描述和实现三维空间中物体在二维图像平面上投影的数学变换。在计算机视觉中,它用于模拟相机如何捕捉到三维世界,并且是理解图像和空间关系的重要部分。

  1. 变换原理

    • 透视变换考虑了物体距离相机远近不同导致的视觉效果,即远处的物体看起来比近处的物体小。
    • 这种变换通常不是线性的,需要通过特定的数学公式进行描述。
  2. 应用

    • 在计算机图形学和计算机视觉中,透视变换广泛应用于图像的矫正、三维重建、增强现实(AR)和虚拟现实(VR)等领域。
    • 通过透视变换,可以将一张平面图像转换为具有不同视角或视点的图像,或者用于校正图像中的透视畸变。

理解镜头畸变和透视变换对于进行高精度的图像处理和分析至关重要,特别是在需要从图像中恢复或理解三维场景信息的应用中。

1.5 标定的重要性和应用场景

相机标定在计算机视觉领域扮演着至关重要的角色。它不仅影响着图像分析和处理的准确性,还是许多高级视觉应用成败的关键。以下是相机标定的一些重要性和应用场景:

  1. 提高图像分析准确性:通过标定,可以精确了解相机的内参和外参,从而在图像处理中更准确地处理畸变,校正透视,从而提高图像分析的准确性。

  2. 机器人导航:在机器人导航中,精确的相机标定能够帮助机器人更准确地理解其环境,包括物体的大小、位置和空间关系,从而实现更加有效和安全的导航。

  3. 自动驾驶汽车:自动驾驶汽车依赖于相机来感知周围环境。准确的标定是确保汽车能够正确理解其所在环境,如道路标志、障碍物、行人和其他车辆的位置的基础。

  4. 增强现实(AR):在增强现实应用中,正确的相机标定允许虚拟对象与现实世界的图像无缝融合,提供更加真实的用户体验。

  5. 单目视觉应用:虽然单目系统无法直接获取深度信息,但通过标定获得的相机参数可以用来估计场景的几何结构和物体的大致深度。

  6. 双目视觉应用:在双目视觉系统中,两个相机的精确标定是计算物体深度和进行三维重建的关键。它允许系统通过比较来自两个不同视点的图像来估计物体的确切位置和深度。

  7. 高级图像处理:在高级图像处理领域,如三维建模、场景重建、物体追踪等,准确的相机标定是实现高质量输出的基础。

相机标定不仅是理解和分析图像的基础,也是实现高级计算机视觉功能的关键。在许多高科技领域,如自动化、机器人技术、虚拟现实等,相机标定的作用不可或缺。

二、单目视觉

在计算机视觉领域,单目视觉是最基本和广泛应用的一种形式。它指的是使用单个相机捕捉和分析图像。

2.1 单目视觉的原理

单目视觉系统主要依靠一个相机来捕获图像,并通过计算机视觉算法来解释和分析这些图像。虽然单目视觉无法直接提供深度信息,但它在处理二维图像、进行图像识别和分类等方面非常有效。

2.1.1 单目视觉的原理

单目视觉的核心原理是从二维图像中提取有用信息,用于理解和分析三维世界。这通常涉及以下几个步骤:

  1. 图像捕获:相机捕获现实世界的二维表示。

  2. 特征提取:算法识别图像中的关键特征点或边缘。这可能包括线条、角点、轮廓等。

  3. 特征匹配与跟踪:在连续的图像帧中跟踪这些特征点,以了解物体或场景的动态变化。

  4. 三维场景恢复:虽然单目视觉不能直接测量深度,但可以通过其他方法如运动视差、尺度不变特征变换(SIFT)等来间接推断深度信息。

  5. 图像理解:应用特定的算法(如物体检测、图像分类)来解释图像内容。

2.1.2 单目视觉的公式

在单目视觉中,一个重要的概念是针孔相机模型,这是最简单的成像模型,用于描述三维世界如何映射到二维图像上。其数学表示为:

p = K [ R ∣ t ] P p = K [R | t] P p=K[Rt]P

  • P P P 表示三维世界中的一个点。
  • p p p 是该点在图像平面上的投影。
  • K K K 是内部参数矩阵,包含焦距和主点坐标。
  • R R R t t t 是相机的旋转和平移向量,代表外部参数。

2.1.3 应用领域

单目视觉系统广泛应用于多个领域,包括:

  • 监控系统:利用单目相机进行实时监控,识别和跟踪人或物体。
  • 自动驾驶辅助:用于车辆的车道检测、交通标志识别等。
  • 机器人导航:帮助机器人理解环境,进行路径规划和避障。
  • 增强现实:在现实世界的图像上叠加虚拟信息。

2.2 实现单目视觉标定的步骤

单目视觉标定是一个关键过程,它帮助我们获取相机的内部参数和畸变参数,从而准确地理解相机如何捕获现实世界中的场景。以下是实现单目视觉标定的基本步骤。

2.2.1 准备标定板

标定板是相机标定中不可或缺的工具,它提供了一个已知结构的参考模式,以便于在图像中识别和定位。最常用的标定板是棋盘格和圆点格。

  • 棋盘格:由交替的黑白方块组成,适用于角点检测。
  • 圆点格:由一系列的圆点组成,适用于更精确的特征点定位。

选择标定板时,需要确保标定板的尺寸和模式对于所使用的相机和应用场景是合适的。

2.2.2 捕获标定图像

使用相机从不同的角度和位置拍摄标定板。这一步骤至关重要,因为它直接影响到标定的准确性和鲁棒性。一般建议:

  • 从多个角度和距离拍摄标定板,以确保足够的视角覆盖。
  • 确保标定板在每张图像中都清晰可见。

2.2.3 提取角点

对于每张拍摄的标定板图像,需要在图像中识别和提取标定板上的角点或特征点。这通常通过使用OpenCV中的特定函数完成,如 cv2.findChessboardCorners 用于棋盘格。

2.2.4 计算内参和畸变参数

一旦收集了足够的图像并从中提取了特征点,接下来使用OpenCV中的 cv2.calibrateCamera 函数计算相机的内参和畸变参数。内参包括焦距和主点坐标,而畸变参数则描述了镜头的畸变特性。

这些参数是理解相机如何捕捉图像的关键,对于后续的图像处理和分析至关重要。例如,通过这些参数,可以对捕获的图像进行畸变校正,从而获得更为准确的视觉数据。

2.3 单目视觉相机标定实战

下面通过一个实例来演示如何在OpenCV中进行单目视觉相机标定。
棋盘格图片

import numpy as np
import cv2
import matplotlib.pyplot as plt

# 棋盘格参数
cross_points = (9, 6)
square_size = 1.0  # 假设棋盘格每个方块的大小为1.0单位

# 加载图像
image_path = 'Chessboard_Photo.jpg'
image = cv2.imread(image_path)
image = cv2.resize(image, (int(image.shape[1] / 2), int(image.shape[0] / 2)))
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 寻找棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, cross_points, None)

# 如果找到足够的角点,则进行标定
if ret == True:
    # 准备对象点
    objp = np.zeros((cross_points[0] * cross_points[1], 3), np.float32)
    objp[:, :2] = np.mgrid[0:cross_points[0], 0:cross_points[1]].T.reshape(-1, 2)
    objp *= square_size

    # 将对象点和图像点放入数组中
    objpoints = []  # 真实世界中的点
    imgpoints = []  # 图像中的点

    objpoints.append(objp)
    imgpoints.append(corners)

    # 进行相机标定
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

    # 标记角点并显示
    img = cv2.drawChessboardCorners(image.copy(), cross_points, corners, int(ret))

    # 透视变换
    # 原始图像尺寸
    h, w = image.shape[:2]

    # 获取角点的坐标
    top_left, top_right, bottom_right, bottom_left = corners[0][0], corners[8][0], corners[-1][0], corners[-9][0]
    pts1 = np.float32([top_left, top_right, bottom_right, bottom_left])

    # 计算四个角点到图像边缘的最大距离
    maxDistToLeftEdge = max(top_left[0], bottom_left[0])
    maxDistToRightEdge = max(w - top_right[0], w - bottom_right[0])
    maxDistToTopEdge = max(top_left[1], top_right[1])
    maxDistToBottomEdge = max(h - bottom_left[1], h - bottom_right[1])

    # 使用最大距离来定义目标图像的大小
    maxWidth =int(w + maxDistToLeftEdge + maxDistToRightEdge)
    maxHeight =int( h + maxDistToTopEdge + maxDistToBottomEdge)

    # 计算目标点的坐标,使整张图片在透视变换后能居中显示
    pts2 = np.float32([
        [maxDistToLeftEdge, maxDistToTopEdge],
        [maxWidth - maxDistToRightEdge - 1, maxDistToTopEdge],
        [maxWidth - maxDistToRightEdge - 1, maxHeight - maxDistToBottomEdge - 1],
        [maxDistToLeftEdge, maxHeight - maxDistToBottomEdge - 1]
    ])

    # 获取透视变换矩阵
    M = cv2.getPerspectiveTransform(pts1, pts2)

    # 应用透视变换
    dst_perspective = cv2.warpPerspective(image, M, (maxWidth, maxHeight))

    # 显示原图及透视变换后的图片
    plt.figure(figsize=(8, 6))
    plt.subplot(1, 2, 1)
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.title('Original Image with Corners')

    plt.subplot(1, 2, 2)
    plt.imshow(cv2.cvtColor(dst_perspective, cv2.COLOR_BGR2RGB))
    plt.title('Perspective Transform')

    plt.show()

else:
    print("找不到足够的角点,请检查图片是否适合棋盘格标定。")

Perspective Transform

三、双目视觉

双目视觉是计算机视觉领域的一个重要分支,它利用两个相机从略微不同的角度捕捉图像,模拟人类的双眼视觉。这种方法在获取深度信息和三维场景重建方面比单目视觉有着显著优势。

3.1 双目视觉的原理与应用

双目视觉系统模拟了人类的双眼视觉机制,通过两个空间上分开的相机捕获同一场景,从而获得场景的深度信息。以下是双目视觉的基本原理和其应用的详细介绍。

3.1.1 双目视觉的原理

双目视觉的核心在于利用两个相机的视差来计算深度信息。视差是指同一物体在两个相机视角中的图像位置差异。以下是双目视觉的数学表示:

  • 假设两个相机分别位于 C 1 C_1 C1 C 2 C_2 C2,它们观察同一物体点 P P P 在两个相机成像平面上的投影分别为 p 1 p_1 p1 p 2 p_2 p2
  • b b b 为两相机间的基线距离(即 C 1 C_1 C1 C 2 C_2 C2 之间的距离)。
  • f f f 为相机的焦距。

那么,物体点 P P P 的深度 Z Z Z 可以通过以下公式计算:

Z = b × f d Z = \frac{b \times f}{d} Z=db×f

其中, d d d 是两个相机成像平面上对应点 p 1 p_1 p1 p 2 p_2 p2 之间的视差。

3.1.2 双目视觉的应用

双目视觉系统的应用非常广泛,主要包括:

  1. 三维重建:通过计算物体的深度信息,可以重建场景的三维结构,应用于虚拟现实、游戏等领域。
  2. 机器人导航:利用深度信息帮助机器人进行空间感知和路径规划,特别适用于自动化和工业机器人。
  3. 增强现实:结合真实场景和计算机生成的图像,需要精确的空间和深度信息以提供更真实的体验。
  4. 自动驾驶汽车:为自动驾驶系统提供深度感知能力,用于障碍物检测、车道识别和环境理解。

这些应用领域中,双目视觉的深度感知能力发挥着至关重要的作用,为智能系统提供了比单目视觉更为丰富和准确的三维空间信息。

3.2 双目视觉与单目视觉的对比

在计算机视觉系统中,单目视觉和双目视觉都扮演着重要的角色,但它们在深度信息获取和处理方面有着本质的区别。以下是这两种视觉系统的主要比较:

单目视觉

  • 原理:使用一个相机捕获图像,依赖于二维图像数据。
  • 深度获取:单目视觉系统依赖于算法推断深度信息,例如通过分析物体大小变化、纹理梯度、遮挡关系等来估算深度。
  • 优势
    • 成本效益:需要的硬件少,成本较低。
    • 简单性:系统设置和处理流程相对简单。
  • 局限性
    • 深度信息不准确:算法推断的深度不如物理测量准确。
    • 对环境依赖大:在纹理丰富或明显的场景中效果较好。

双目视觉

  • 原理:使用两个相机从不同角度捕获同一场景的图像,模拟人类的双眼视觉。
  • 深度获取:双目视觉系统通过比较两个视角的图像差异来直接计算深度信息,通常使用视差映射(disparity map)来表示。
  • 优势
    • 深度信息准确:能够直接测量并计算深度信息,结果更准确。
    • 三维感知能力:更适合于进行空间定位和三维重建。
  • 局限性
    • 成本和复杂性:需要更多的硬件和复杂的校准过程。
    • 计算要求更高:处理两个图像和计算深度需要更高的计算能力。

尽管单目视觉在成本和简易性方面有优势,但它在深度感知和准确性方面受到限制。相比之下,双目视觉虽然在硬件要求和处理复杂度上更高,但能提供更精确的深度信息和三维空间感知能力。选择哪一种视觉系统取决于应用的具体需求、预算和预期的精度。

3.3 实现双目视觉标定的步骤

双目视觉标定不仅涉及到每个相机的内部参数,还需要对两个相机间的关系(如相对位置和方向)进行校准。基本步骤包括:

  1. 单独标定每个相机:首先分别对两个相机进行单目视觉标定。
  2. 标定相机之间的关系:计算两个相机之间的旋转和平移矩阵,这被称为立体校正。
  3. 立体校正和重投影:校正两个相机的图像,使其在同一平面上对齐,便于后续的深度计算。

3.4 OpenCV中的相关函数与方法

OpenCV提供了一系列函数来支持双目视觉的标定和深度计算:

  • cv2.stereoCalibrate:用于计算两个相机之间的关系。
  • cv2.stereoRectify:进行立体校正。
  • cv2.createStereoBMcv2.createStereoSGBM:创建立体匹配对象以计算深度图。

目前手头还没有双摄像头,等有资金了配置一套再补充这部分内容 o(╥﹏╥)o 。


总结

本博客简要介绍了相机标定的基本原理、单目视觉和双目视觉的关键概念及其在实际中的应用。从相机模型和坐标系的基础知识出发,我们探讨了内外参数的重要性,以及如何处理镜头畸变和透视变换。这为理解相机如何捕获和转换图像提供了坚实的理论基础。

在单目视觉部分,我们深入了解了其工作原理,以及单目视觉在各个领域的广泛应用。同时,详细介绍了实现单目视觉标定的步骤,从准备标定板到计算内参和畸变参数,提供了实战指导。

对于双目视觉,我们探讨了其原理及应用,特别强调了其在深度信息获取上的优势。此外,通过双目视觉与单目视觉的对比,突显了双目视觉在三维空间感知方面的独特价值。

猜你喜欢

转载自blog.csdn.net/qq_31463571/article/details/134619531