OpenCV 4.8 相机标定实战:5步完成棋盘格标定与畸变矫正(附Python代码)

当你用手机拍摄建筑时,是否发现直线在画面边缘变成了曲线?这就是相机镜头畸变的典型表现。作为计算机视觉开发者,掌握相机标定技术是构建精准视觉系统的第一步。本文将带你用OpenCV 4.8实现从图像采集到畸变矫正的全流程实战,完整代码可直接集成到你的机器人导航或三维重建项目中。

1. 环境准备与标定板制作

在开始标定前,我们需要准备以下工具和环境:

# 基础环境配置(Python 3.8+)
pip install opencv-contrib-python==4.8.0 numpy matplotlib

标定板选择建议

  • 棋盘格尺寸:A4纸打印的9x6格(每个方格20mm)
  • 材质选择:哑光硬纸板避免反光
  • 打印精度验证:用游标卡尺测量实际打印尺寸

注意:棋盘格角点数指内部交叉点数量,例如9x6棋盘格实际有8x5=40个内部角点

标定板摆放技巧:

  1. 每次拍摄时改变标定板的空间姿态
  2. 确保标定板占据画面1/3以上面积
  3. 避免强光直射造成的过曝或阴影

2. 图像采集与角点检测

采集15-20张不同角度的标定板图像后,使用以下代码进行批量角点检测:

import cv2
import glob

# 初始化参数
pattern_size = (8, 5)  # 实际角点数比格子数少1
obj_points = []  # 3D世界坐标
img_points = []  # 2D图像坐标

# 生成理论角点坐标 (Z=0)
objp = np.zeros((pattern_size[0]*pattern_size[1], 3), np.float32)
objp[:,:2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)

# 遍历所有标定图像
images = glob.glob('calib_*.jpg')
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # 角点检测
    ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)
    
    if ret:
        # 亚像素级精确化
        criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
        corners_refined = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
        
        img_points.append(corners_refined)
        obj_points.append(objp)
        
        # 可视化(调试用)
        cv2.drawChessboardCorners(img, pattern_size, corners_refined, ret)
        cv2.imshow('Corners', img)
        cv2.waitKey(500)

cv2.destroyAllWindows()

常见问题排查表:

问题现象 可能原因 解决方案
角点检测失败 棋盘格未完全可见 调整拍摄角度确保完整显示
亚像素定位不准 图像模糊或过曝 重新拍摄清晰图像
坐标顺序混乱 棋盘格方向不一致 统一保持横向摆放

3. 相机参数计算与验证

获得足够角点数据后,进行相机参数标定:

# 执行标定
ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(
    obj_points, img_points, gray.shape[::-1], None, None)

print(f"内参矩阵:\n{K}")
print(f"畸变系数:\n{dist}")

# 验证重投影误差
mean_error = 0
for i in range(len(obj_points)):
    img_points2, _ = cv2.projectPoints(obj_points[i], rvecs[i], tvecs[i], K, dist)
    error = cv2.norm(img_points[i], img_points2, cv2.NORM_L2)/len(img_points2)
    mean_error += error

print(f"平均重投影误差: {mean_error/len(obj_points):.3f} 像素")

参数解读

  • 内参矩阵K: [fx, 0, cx; 0, fy, cy; 0, 0, 1]
    • fx,fy:焦距(像素单位)
    • cx,cy:主点坐标(通常接近图像中心)
  • 畸变系数dist: [k1, k2, p1, p2, k3]
    • k1,k2,k3:径向畸变系数
    • p1,p2:切向畸变系数

专业提示:当重投影误差>0.5像素时,建议检查角点检测质量或增加标定图像数量

4. 畸变矫正与效果验证

获得相机参数后,可对任意图像进行实时矫正:

def undistort_image(img, K, dist):
    h, w = img.shape[:2]
    
    # 优化内参矩阵(可选)
    new_K, roi = cv2.getOptimalNewCameraMatrix(K, dist, (w,h), 1, (w,h))
    
    # 方法1:直接矫正
    dst = cv2.undistort(img, K, dist, None, new_K)
    
    # 方法2:使用映射(适合视频流)
    mapx, mapy = cv2.initUndistortRectifyMap(K, dist, None, new_K, (w,h), 5)
    dst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
    
    # 裁剪无效区域
    x, y, w, h = roi
    dst = dst[y:y+h, x:x+w]
    
    return dst

# 测试矫正效果
test_img = cv2.imread('test.jpg')
undistorted = undistort_image(test_img, K, dist)

# 并排对比
cv2.imshow('Original vs Undistorted', np.hstack((test_img, undistorted)))
cv2.waitKey(0)

矫正效果评估要点

  1. 检查图像边缘直线是否恢复笔直
  2. 观察中心区域是否存在过度拉伸
  3. 比较矫正前后的特征点匹配精度

5. 高级技巧与工程优化

在实际项目中,这些技巧能显著提升标定质量:

多分辨率标定法

# 金字塔式标定(提升大畸变镜头标定精度)
pyramid_levels = 3
for level in range(pyramid_levels):
    scale = 2**(pyramid_levels-1-level)
    img_small = cv2.resize(img, (0,0), fx=1/scale, fy=1/scale)
    # 在小尺度图像上标定后,将结果作为下一级初始值

动态标定参数保存

import json
import datetime

calib_data = {
    'date': datetime.datetime.now().isoformat(),
    'camera_model': 'Logitech C920',
    'resolution': '1920x1080',
    'K': K.tolist(),
    'dist': dist.tolist(),
    'reprojection_error': mean_error/len(obj_points)
}

with open('camera_calib.json', 'w') as f:
    json.dump(calib_data, f, indent=2)

标定流程自动化脚本

#!/bin/bash
# 自动拍摄标定图像
for i in {1..20}; do
  fswebcam -d /dev/video0 -r 1920x1080 --no-banner "calib_$i.jpg"
  sleep 3  # 留出调整标定板时间
done

# 自动执行标定
python calibrate.py --pattern-size 8x5 --square-size 20

在机器人项目中,我习惯将标定参数直接写入ROS相机驱动配置文件。对于需要实时矫正的场景,建议使用initUndistortRectifyMap预计算映射关系,相比直接调用undistort能提升5-8倍的运行效率。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐