摄像头校准之白平衡&畸变&坏点

1. 源由

昨天讨论了关于摄像头校准方面的问题,其实刚开始时工作的时候在相机、摄像机方面从事一段时间的开发,大体上还是应用类的研发工作。

当时是基于SDK方案,在其基础上做一些参数调整,但是总的逻辑大致是一致的。现在技术发展已经远超当年,尤其有强大的后端数据处理时,要处理这个校准问题的手段就更加多了。

回忆总结下,关于摄像头校准方面的一些校准内容,如果还有其他没有提及的,请各位路过的兄弟们指点下,我后续补充:

2. 校准内容

  1. 畸变校准
  2. 白平衡校准
  3. 坏点校准
  4. TBD … … (请各位路过、看过的兄弟评论留言指导,谢谢)

3. 畸变校准

摄像头畸变校准是指校正镜头引起的图像畸变,使图像中的直线和比例恢复正常。常见的畸变包括径向畸变和切向畸变。以下是摄像头畸变校准的详细步骤,包括畸变模型、参数计算和图像校正。

一、畸变模型

  1. 径向畸变(Radial Distortion)

    • 描述图像中直线弯曲的现象。径向畸变分为桶形畸变(Barrel Distortion)和枕形畸变(Pincushion Distortion)。
    • 典型的径向畸变系数: k 1 , k 2 , k 3 k_1, k_2, k_3 k1,k2,k3
  2. 切向畸变(Tangential Distortion)

    • 由镜头与图像传感器不对齐引起,导致图像中的直线发生倾斜。
    • 典型的切向畸变系数: p 1 , p 2 p_1, p_2 p1,p2

畸变模型的数学表示如下:

x distorted = x ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + 2 p 1 x y + p 2 ( r 2 + 2 x 2 ) ] x_{\text{distorted}} = x(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) + 2p_1 xy + p_2 (r^2 + 2x^2)] xdistorted=x(1+k1r2+k2r4+k3r6)+2p1xy+p2(r2+2x2)]
y distorted = y ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + p 1 ( r 2 + 2 y 2 ) + 2 p 2 x y y_{\text{distorted}} = y(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) + p_1 (r^2 + 2y^2) + 2p_2 xy ydistorted=y(1+k1r2+k2r4+k3r6)+p1(r2+2y2)+2p2xy

其中, r 2 = x 2 + y 2 r^2 = x^2 + y^2 r2=x2+y2

二、校准步骤

1. 准备工作

  1. 棋盘格图案

    • 打印标准棋盘格图案,并确保其尺寸精确。
  2. 拍摄校准图像

    • 将棋盘格图案放置在不同的角度和位置,拍摄多张图像。确保棋盘格覆盖摄像头视野的不同部分。

2. 特征点检测

使用OpenCV库检测棋盘格的角点。

import cv2
import numpy as np
import glob

# 设置棋盘格的尺寸
chessboard_size = (9, 6)
square_size = 1.0  # 设置棋盘格每个方块的实际尺寸

# 准备棋盘格的世界坐标系点(3D点)
objp = np.zeros((chessboard_size[0]*chessboard_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
objp *= square_size

# 用于存储所有图像的对象点和图像点
objpoints = []  # 3d point in real world space
imgpoints = []  # 2d points in image plane.

# 读取所有校准图像
images = glob.glob('calibration_images/*.jpg')

for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 检测棋盘格角点
    ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)

    if ret:
        objpoints.append(objp)
        corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))
        imgpoints.append(corners2)

        # 绘制并显示角点
        cv2.drawChessboardCorners(img, chessboard_size, corners2, ret)
        cv2.imshow('Chessboard corners', img)
        cv2.waitKey(500)

cv2.destroyAllWindows()

3. 计算内参数和畸变系数

使用calibrateCamera函数计算摄像头的内参数矩阵和畸变系数。

# 进行摄像头校准
ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

print("Camera matrix:")
print(camera_matrix)
print("\nDistortion coefficients:")
print(dist_coeffs)

4. 畸变校正

使用计算得到的参数进行图像畸变校正。

# 示例:矫正一张图像
img = cv2.imread('calibration_images/example.jpg')
h, w = img.shape[:2]

# 获取新的相机矩阵
new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix(camera_matrix, dist_coeffs, (w, h), 1, (w, h))

# 矫正图像
dst = cv2.undistort(img, camera_matrix, dist_coeffs, None, new_camera_matrix)

# 裁剪图像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]

cv2.imshow('Undistorted Image', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

三、验证和优化

1. 视觉验证

观察校正后的图像,确保直线恢复正常,图像畸变得到有效校正。

2. 误差评估

计算重投影误差,评估校准的精度。

mean_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], camera_matrix, dist_coeffs)
    error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
    mean_error += error

print("Total error: ", mean_error / len(objpoints))

3. 参数优化

根据误差评估结果,调整拍摄方式或增加校准图像数量,优化校准参数。

4. 白平衡校准

摄像头的白平衡校准是为了确保在不同的光照条件下,摄像头拍摄的图像中的白色看起来是白色,从而使图像的颜色还原更准确。这对于计算机视觉应用和图像处理非常重要。下面是详细的白平衡校准步骤,包括自动白平衡和手动白平衡的方法。

一、白平衡基础

白平衡校准的目标是调整图像的色温,使得图像中中性的灰色或白色区域在不同光照条件下依然保持中性,即没有颜色偏差。

二、自动白平衡(AWB)

  1. 灰世界假设法

    • 假设图像中所有颜色的平均值是中性的灰色,通过调整图像的红色、绿色和蓝色通道,使其平均值相同。
    import cv2
    import numpy as np
    
    def gray_world_awb(img):
        b, g, r = cv2.split(img)
        avg_b = np.mean(b)
        avg_g = np.mean(g)
        avg_r = np.mean(r)
    
        avg_gray = (avg_b + avg_g + avg_r) / 3
    
        b = cv2.addWeighted(b, avg_gray / avg_b, 0, 0, 0)
        g = cv2.addWeighted(g, avg_gray / avg_g, 0, 0, 0)
        r = cv2.addWeighted(r, avg_gray / avg_r, 0, 0, 0)
    
        return cv2.merge([b, g, r])
    
    img = cv2.imread('test_image.jpg')
    awb_img = gray_world_awb(img)
    cv2.imshow('AWB Image', awb_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  2. 图像统计法

    • 利用图像直方图统计信息调整白平衡,算法会计算每个通道的统计量,并做相应的调整。
    def simple_color_balance(img, percent):
        half_percent = percent / 200.0
        
        channels = cv2.split(img)
        out_channels = []
        for channel in channels:
            assert len(channel.shape) == 2
            flat = channel.flatten()
            flat = np.sort(flat)
            
            n_cols = flat.shape[0]
            
            low_val = flat[int(n_cols * half_percent)]
            high_val = flat[int(n_cols * (1.0 - half_percent))]
            
            thresholded = np.clip(channel, low_val, high_val)
            thresholded = ((thresholded - low_val) / (high_val - low_val) * 255.0).astype(np.uint8)
            
            out_channels.append(thresholded)
        
        return cv2.merge(out_channels)
    
    img = cv2.imread('test_image.jpg')
    balanced_img = simple_color_balance(img, 1)
    cv2.imshow('Balanced Image', balanced_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

三、手动白平衡

  1. 使用标准灰卡

    • 拍摄一张包含标准灰卡(18% 灰)的图像,调整图像的红、绿、蓝通道,使灰卡区域呈现中性灰色。
  2. 计算校正系数

    • 计算灰卡区域的平均颜色值,分别获取红、绿、蓝三个通道的校正系数。
    def manual_white_balance(img, gray_card_region):
        x, y, w, h = gray_card_region
        gray_card = img[y:y+h, x:x+w]
        
        avg_b = np.mean(gray_card[:, :, 0])
        avg_g = np.mean(gray_card[:, :, 1])
        avg_r = np.mean(gray_card[:, :, 2])
        
        avg_gray = (avg_b + avg_g + avg_r) / 3
        
        b, g, r = cv2.split(img)
        b = cv2.addWeighted(b, avg_gray / avg_b, 0, 0, 0)
        g = cv2.addWeighted(g, avg_gray / avg_g, 0, 0, 0)
        r = cv2.addWeighted(r, avg_gray / avg_r, 0, 0, 0)
        
        return cv2.merge([b, g, r])
    
    img = cv2.imread('test_image.jpg')
    gray_card_region = (50, 50, 100, 100)  # 假设灰卡位于这个区域
    wb_img = manual_white_balance(img, gray_card_region)
    cv2.imshow('Manual WB Image', wb_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

四、验证和优化

  1. 视觉验证

    • 观察校准后的图像,确保图像中的白色和中性灰色区域没有明显的颜色偏差。
  2. 误差评估

    • 使用色度计或标准颜色板测量校准后的颜色值,与预期值进行比较,评估校准误差。
  3. 重复调整

    • 根据误差评估结果,重复调整白平衡参数,直到达到满意的校准效果。

5. 坏点校准

CMOS传感器的坏点(dead pixels)校准是指检测和校正图像传感器上失效的像素,以提高图像质量。坏点通常表现为图像上的黑点、白点或固定的彩色点。坏点校准的目的是通过替换坏点的像素值,使图像看起来更自然。以下是详细的CMOS坏点校准步骤:

一、坏点检测

1. 确定坏点类型

坏点通常分为三类:

  • 热像素(Hot Pixels):在低曝光时间下表现正常,但在长曝光时间下表现为亮点。
  • 暗像素(Dead Pixels):无论曝光时间多长,都表现为黑点。
  • 呆滞像素(Stuck Pixels):在所有曝光时间下都保持恒定的亮度,通常是白点或彩色点。

2. 拍摄黑暗场图像

将摄像头盖住或在完全黑暗的环境中拍摄多张图像,坏点在这些图像中通常表现得很明显。

3. 阈值检测

使用阈值法检测坏点。阈值可以基于像素值的统计特性来设置。

import cv2
import numpy as np

# 读取一张黑暗场图像
dark_frame = cv2.imread('dark_frame.jpg', cv2.IMREAD_GRAYSCALE)

# 设定阈值,假设超过200的像素为坏点
threshold = 200
_, bad_pixel_map = cv2.threshold(dark_frame, threshold, 255, cv2.THRESH_BINARY)

cv2.imshow('Bad Pixel Map', bad_pixel_map)
cv2.waitKey(0)
cv2.destroyAllWindows()

二、坏点校正

1. 邻域平均法

用坏点周围的有效像素的平均值替换坏点值。

def correct_bad_pixels(image, bad_pixel_map):
    corrected_image = image.copy()
    bad_pixels = np.argwhere(bad_pixel_map == 255)
    for y, x in bad_pixels:
        neighbors = []
        for dy in [-1, 0, 1]:
            for dx in [-1, 0, 1]:
                ny, nx = y + dy, x + dx
                if 0 <= ny < image.shape[0] and 0 <= nx < image.shape[1] and not (dy == 0 and dx == 0):
                    neighbors.append(image[ny, nx])
        corrected_image[y, x] = np.mean(neighbors)
    return corrected_image

# 读取一张包含坏点的图像
image_with_bad_pixels = cv2.imread('image_with_bad_pixels.jpg', cv2.IMREAD_GRAYSCALE)
corrected_image = correct_bad_pixels(image_with_bad_pixels, bad_pixel_map)

cv2.imshow('Corrected Image', corrected_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

2. 双边滤波法

使用双边滤波保留图像边缘的同时平滑坏点。

corrected_image = cv2.bilateralFilter(image_with_bad_pixels, d=9, sigmaColor=75, sigmaSpace=75)

cv2.imshow('Corrected Image with Bilateral Filter', corrected_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

三、验证和优化

1. 视觉验证

检查校正后的图像,确保坏点被有效校正,图像质量明显提升。

2. 自动化检测和校正

将坏点检测和校正步骤集成到图像处理管道中,实现自动化处理。

3. 重复拍摄和校准

定期拍摄黑暗场图像,更新坏点地图,并进行校正,确保图像质量稳定。

6. 总结

必须从校准的原理、方法的角度彻底的了解来龙去脉,才能在实际应用中更好的达成应用目的。

  • 摄像头畸变校准

摄像头畸变校准通过检测特征点、计算内参数和畸变系数,以及进行图像校正,能够有效地减少图像中的畸变,提高图像的精度和质量。通过定期校准和优化,可以确保摄像头在各种应用场景中的性能稳定。

  • 摄像头白平衡校准

通过自动白平衡和手动白平衡的方法,可以有效地对摄像头进行白平衡校准,从而保证在不同光照条件下,摄像头拍摄的图像颜色准确性。根据具体应用场景和需求,可以选择适合的方法进行白平衡校准。

  • 摄像头CMOS校准

CMOS坏点校准是通过检测坏点并用邻域平均或双边滤波等方法进行校正,以提高图像质量的过程。通过自动化的检测和校正,可以有效地减少图像中的坏点影响,提升整体图像效果。根据应用需求和实际情况,选择合适的坏点校正方法,确保摄像头的性能和图像质量达到最佳。

7. 补充资料 - 光学校准

摄像头校准的主要目的是确定摄像头的内参数和外参数。这些参数在计算机视觉和图像处理任务用于将现实世界中的三维坐标映射到二维图像平面上。

通过校准,可以精确地得到摄像头的内参数和外参数,从而能够进行图像校正、3D重建、姿态估计等计算机视觉任务。这些参数的准确性对于很多应用场景来说至关重要,比如机器人导航、增强现实、测量和监控等。

具体来说,摄像头校准需要确定以下参数:

一、内参数(Intrinsic Parameters)

  1. 焦距(Focal Length, f x , f y f_x, f_y fx,fy):

    • 摄像头镜头的焦距,分别在x和y方向上的焦距。
    • 通常表示为一个矩阵的对角元素。
      f x , f y f_x, f_y fx,fy
  2. 光学中心(Principal Point, c x , c y c_x, c_y cx,cy):

    • 图像平面上的光学中心(也称为主点),通常在图像的中心。
      c x , c y c_x, c_y cx,cy
  3. 畸变系数(Distortion Coefficients)

    • 描述镜头的畸变情况,包括径向畸变和切向畸变。
    • 典型的畸变系数有五个参数:
      • 径向畸变: k 1 , k 2 , k 3 k_1, k_2, k_3 k1,k2,k3
      • 切向畸变: p 1 , p 2 p_1, p_2 p1,p2
  4. 内参数矩阵(Intrinsic Matrix, K K K):

    • 包含焦距和光学中心的3x3矩阵:

K = [ f x 0 c x 0 f y c y 0 0 1 ] K = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix} K= fx000fy0cxcy1

二、外参数(Extrinsic Parameters)

  1. 旋转矩阵(Rotation Matrix, R R R):

    • 描述摄像头坐标系相对于世界坐标系的旋转关系。
  2. 平移向量(Translation Vector, t t t):

    • 描述摄像头坐标系相对于世界坐标系的平移关系。

三、计算畸变校正矩阵

  1. 新相机矩阵(Optimal New Camera Matrix)
    • 在进行图像矫正时,通过计算得到一个新的相机矩阵,可以使图像中的畸变尽量减少,并保留最大的有效像素区域。

示例:内参数和外参数矩阵

内参数矩阵 K K K

K = [ f x 0 c x 0 f y c y 0 0 1 ] K = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix} K= fx000fy0cxcy1

外参数矩阵 [ R ∣ t ] [R|t] [Rt]

外参数矩阵由旋转矩阵和平移向量组成:

[ R ∣ t ] = [ r 11 r 12 r 13 t x r 21 r 22 r 23 t y r 31 r 32 r 33 t z ] [R|t] = \begin{bmatrix} r_{11} & r_{12} & r_{13} & t_x \\ r_{21} & r_{22} & r_{23} & t_y \\ r_{31} & r_{32} & r_{33} & t_z \end{bmatrix} [Rt]= r11r21r31r12r22r32r13r23r33txtytz

  • 34
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值