OpenCV-图像去畸变方法

OpenCV 图像去畸变方法详解

在计算机视觉应用中,相机镜头通常会引入各种类型的畸变,主要包括径向畸变和切向畸变。OpenCV 提供了多种方法来校正这些畸变,本文将详细介绍基于相机内参和畸变系数的图像去畸变方法。

1. 畸变模型

在进行去畸变之前,需要了解相机的畸变模型。OpenCV 使用以下参数来描述畸变:

  • 径向畸变系数:k1, k2, k3, k4, k5, k6
  • 切向畸变系数:p1, p2

畸变系数通常表示为一个向量:[k1, k2, p1, p2, k3, k4, k5, k6]

2. 关键函数详解

2.1 cv2.getOptimalNewCameraMatrix()

此函数用于计算去畸变后的最优相机内参矩阵和感兴趣区域(ROI)。

函数签名

newCameraMatrix, validPixROI = cv2.getOptimalNewCameraMatrix(
    cameraMatrix, 
    distCoeffs, 
    imageSize, 
    alpha, 
    newImgSize=None, 
    centerPrincipalPoint=None
)

参数说明

参数 类型 描述
cameraMatrix numpy.ndarray 3x3 的原始相机内参矩阵
distCoeffs numpy.ndarray 畸变系数向量 (1x4, 1x5, 1x8, 1x12, or 1x14)
imageSize tuple 图像尺寸 (width, height)
alpha float 自由缩放参数,范围 [0, 1]
newImgSize tuple 去畸变图像的尺寸 (width, height),默认与原图相同
centerPrincipalPoint bool 是否将主点居中,默认为 False

返回值

  • newCameraMatrix: 3x3 的新相机内参矩阵
  • validPixROI: 有效像素区域 (x, y, width, height)

alpha 参数说明

  • alpha = 0: 裁剪掉所有无效区域,图像中不会出现黑边
  • alpha = 1: 保留所有原始像素,可能会有黑边
  • 0 < alpha < 1: 介于两者之间的折中方案

2.2 cv2.initUndistortRectifyMap()

此函数用于计算去畸变和矫正映射,生成用于 remap 函数的映射矩阵。

函数签名

map1, map2 = cv2.initUndistortRectifyMap(
    cameraMatrix, 
    distCoeffs, 
    R, 
    newCameraMatrix, 
    size, 
    m1type
)

参数说明

参数 类型 描述
cameraMatrix numpy.ndarray 3x3 的原始相机内参矩阵
distCoeffs numpy.ndarray 畸变系数向量
R numpy.ndarray 3x3 的矫正旋转矩阵(通常为 None 或单位矩阵)
newCameraMatrix numpy.ndarray 3x3 的新相机内参矩阵(通常由 getOptimalNewCameraMatrix 生成)
size tuple 校正图像的尺寸 (width, height)
m1type int 映射数据类型(cv2.CV_32FC1 或 cv2.CV_16SC2)

返回值

  • map1: x 坐标的映射矩阵
  • map2: y 坐标的映射矩阵(当 m1type 为 CV_16SC2 时,map2 为 None)

m1type 参数说明

  • cv2.CV_32FC1: 生成两个 32 位浮点型映射矩阵(map1 和 map2)
  • cv2.CV_16SC2: 生成一个双通道的 16 位有符号整数映射矩阵(只有 map1)

2.3 cv2.remap()

此函数根据映射矩阵对图像进行重采样,实现去畸变效果。

函数签名

dst = cv2.remap(
    src, 
    map1, 
    map2, 
    interpolation, 
    borderMode=None, 
    borderValue=None
)

参数说明

参数 类型 描述
src numpy.ndarray 输入图像
map1 numpy.ndarray x 坐标的映射矩阵
map2 numpy.ndarray y 坐标的映射矩阵
interpolation int 插值方法
borderMode int 边界像素外推方法,默认为 cv2.BORDER_REFLECT_101
borderValue tuple 用于 cv2.BORDER_CONSTANT 的边界值

插值方法

  • cv2.INTER_NEAREST: 最近邻插值
  • cv2.INTER_LINEAR: 双线性插值(推荐)
  • cv2.INTER_CUBIC: 三次样条插值
  • cv2.INTER_LANCZOS4: Lanczos 插值

边界模式

  • cv2.BORDER_CONSTANT: 填充固定值
  • cv2.BORDER_REPLICATE: 复制边界像素
  • cv2.BORDER_REFLECT: 镜像反射边界
  • cv2.BORDER_REFLECT_101: 镜像反射边界(不包括边界像素)

3. 完整实现示例

import cv2
import numpy as np

def undistort_image(input_image, camera_matrix, dist_coeffs):
    """
    基于相机内参和畸变系数进行图像去畸变
    返回:去畸变图像 + ROI区域 + 新内参
    
    参数说明:
    input_image:原始畸变图像(BGR格式)
    camera_matrix:相机内参矩阵 (3x3)
        [[fx, 0, cx],
         [0, fy, cy],
         [0, 0, 1]]
    dist_coeffs:畸变系数向量 (k1, k2, p1, p2[, k3[, k4, k5, k6]])
    """
    h, w = input_image.shape[:2]
    image_size = (w, h)

    # 1. 计算新内参和ROI (平衡图像区域和有效内容)
    # alpha=1: 保留所有原始像素(可能有黑边)
    # alpha=0: 裁剪掉所有无效区域
    alpha = 0
    new_cam_matrix, roi = cv2.getOptimalNewCameraMatrix(
        cameraMatrix=camera_matrix,
        distCoeffs=dist_coeffs,
        imageSize=image_size,
        alpha=alpha,
        newImgSize=image_size
    )

    # 2. 计算去畸变映射 (x映射和y映射)
    mapx, mapy = cv2.initUndistortRectifyMap(
        cameraMatrix=camera_matrix,
        distCoeffs=dist_coeffs,
        R=None,  # 无旋转
        newCameraMatrix=new_cam_matrix,
        size=image_size,
        m1type=cv2.CV_32FC1  # 32位浮点型映射
    )

    # 3. 应用映射执行去畸变
    undistorted = cv2.remap(
        src=input_image,
        map1=mapx,
        map2=mapy,
        interpolation=cv2.INTER_LINEAR,
        borderMode=cv2.BORDER_CONSTANT
    )

    # 提取ROI坐标 (可选的裁剪区域)
    x, y, w_roi, h_roi = roi
    if w_roi != 0 and h_roi != 0:
        # 裁剪掉黑边
        undistorted = undistorted[y:y+h_roi, x:x+w_roi]

    return undistorted, new_cam_matrix, roi

# ======= 使用示例 =======
if __name__ == "__main__":
    # 示例参数(需替换为实际标定结果)
    camera_matrix = np.array([
        [800, 0, 320],
        [0, 800, 240],
        [0, 0, 1]
    ])
    
    # k1, k2, p1, p2, k3 格式
    dist_coeffs = np.array([-0.35, 0.15, 0.001, -0.003, 0.0])
    
    # 读取原始畸变图像
    distorted_img = cv2.imread("distorted_image.jpg")
    
    # 执行去畸变
    result, new_matrix, roi = undistort_image(
        input_image=distorted_img,
        camera_matrix=camera_matrix,
        dist_coeffs=dist_coeffs
    )
    
    # 打印关键信息
    print("原始内参:\n", camera_matrix)
    print("新内参:\n", new_matrix)
    print("ROI区域 (x, y, w, h):", roi)
    
    # 保存结果
    cv2.imwrite("undistorted_result.jpg", result)
    
    # 显示对比 (左:原图 | 右:去畸变结果)
    cv2.imshow("Comparison", np.hstack((distorted_img, result)))
    cv2.waitKey(0)
    cv2.destroyAllWindows()

4. 性能优化建议

4.1 映射重用优化

对于视频流处理,推荐预计算映射矩阵并在每帧中重用:

# 初始化阶段
mapx, mapy = cv2.initUndistortRectifyMap(
    cameraMatrix, 
    distCoeffs, 
    None, 
    newCameraMatrix, 
    imageSize, 
    cv2.CV_32FC1
)

# 视频处理循环
while True:
    frame = capture.read()
    undistorted_frame = cv2.remap(
        frame, 
        mapx, 
        mapy, 
        interpolation=cv2.INTER_LINEAR
    )
    # 处理去畸变后的帧

4.2 简化方法

对于简单场景,可以使用 cv2.undistort() 函数一步完成去畸变:

undistorted = cv2.undistort(
    src=distorted_image,
    cameraMatrix=camera_matrix,
    distCoeffs=dist_coeffs,
    newCameraMatrix=new_camera_matrix
)

但这种方法灵活性较低,无法获得映射矩阵用于后续处理。

5. 注意事项

  1. 参数获取:实际应用中,相机内参和畸变系数需要通过相机标定获得
  2. 畸变系数:OpenCV 支持 4-8 个畸变参数
  3. 精度选择:使用 CV_32FC1 类型可以获得更高精度的映射结果,但代价是存储和计算资源消耗
  4. 裁剪优化:通过 alpha 参数可以在保留黑色背景的情况下裁剪掉无效内容
  5. 视频优化:对于视频处理,推荐预计算映射并在每帧中重复使用
posted @ 2025-08-08 00:40  aaooli  阅读(438)  评论(0)    收藏  举报