从原理到实践

在线体验各类最新模型,更有模型 免费Token 额度领取!
立即体验
简介: 相机标定是计算机视觉中的关键步骤,用于将真实世界的3D点映射到图像的2D平面。通过标定,可以消除镜头畸变、获取物体的真实尺寸,并实现精确的3D重建和姿态估计。标定过程通常使用棋盘格标定板,通过检测角点的3D和2D坐标来计算相机的内参矩阵和畸变系数。本文介绍了标定的原理、工具使用方法、代码解析及实际应用技巧,帮助用户高效完成标定工作。

为什么需要相机标定?

计算机视觉中,真实世界的3D点需要映射到2D图像平面上。这个过程受镜头畸变和相机内部参数影响。相机标定就是确定这些参数的过程,它能:

  • 消除镜头畸变(鱼眼、桶形失真等)
  • 获取物体真实尺寸(从像素到实际距离)
  • 实现精确的3D重建和姿态估计

相机标定原理

标定的核心是棋盘格标定板,其规则图案提供了已知的3D空间坐标点:

  1. 对象点:棋盘格角点的3D坐标(X, Y, Z)
  2. 图像点:棋盘格在图像中的2D角点位置
  3. 相机模型:通过求解投影方程确定:
  • 内参矩阵(焦距、主点坐标)
  • 畸变系数(径向、切向畸变)

标定工具使用指南

准备工作

  1. 打印8x6棋盘格(每个方格2.5x2.5cm),这里的aly.4dthai.com黑白交汇点的数量,若计算黑白格子,则为9x7
  2. 固定相机位置(避免中途移动)
  3. 准备不同角度和距离的拍摄位置

操作步骤

代码语言:bash

AI代码解释

python3 calibrate_camera.py

标定过程的场景实际截图

上图中使用的是打印的棋盘格,既不平整又不防止反光和漫射,仅供演示。

  1. 运行程序启动相机
  2. 将棋盘格置于不同位置(倾斜/旋转/远近)
  3. 按空格键保存有效帧(棋盘格需完整显示)
  4. 收集15-20张图像后按'q'键结束
  5. 程序自动计算并保存参数到camera_params.npz

标定结果包含:内参矩阵(3x3) 畸变系数(k1, k2, p1, p2, k3) 重投影误差(评估标定质量)

标定流程图

代码解析

核心类CameraCalibrator实现标定全流程:

代码语言:python

代码运行次数:0

运行

AI代码解释

# 角点检测(关键步骤)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, self.board_size, None)
# 亚像素优化(提高精度)
corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), self.criteria)
# 标定计算
ret, mtx, dist, _, _ = cv2.calibrateCamera(
    self.objpoints, self.imgpoints, img_size, None, None
)
# 重投影误差评估
imgpoints2, _ = cv2.projectPoints(objpoints, rvecs, tvecs, mtx, dist)
error = cv2.norm(imgpoints, imgpoints2, cv2.NORM_L2)/len(imgpoints2)

实际应用技巧

  1. 棋盘格要求
  • 使用哑光材质避免反光
  • 保持棋盘格平整无褶皱
  • 方格尺寸需精确测量
  1. 拍摄技巧
  • 覆盖图像不同区域(中心/边缘)
  • 包含各种旋转角度(±30°以上)
  • 近/中/远距离都要覆盖
  1. 质量评估
  • 重投影误差<0.5像素为优秀
  • 误差>1像素需重新标定
  • 检查边缘区域的畸变校正效果

标定结果应用

获取参数后,可轻松校正新图像:

代码语言:python

代码运行次数:0

运行

AI代码解释

params = np.load("camera_params.npz")
mtx = params['camera_matrix']
dist = params['dist_coeffs']
# 畸变校正
undistorted = cv2.undistort(image, mtx, dist)

结语

精确的相机标定是计算机视觉应用的基石。本文提供的工具简化了标定流程,结合实践技巧,可实现毫米级精度的测量任务。标定后,您将获得更准确的AR导航、三维重建和工业检测结果。

源码获取

代码语言:python

代码运行次数:0

运行

AI代码解释

"""
相机标定工具
使用说明:
1. 打印一个棋盘格标定板(推荐使用8x6的棋盘格,每个方格的尺寸为2.5cm x 2.5cm)
2. 将标定板放在不同位置和角度,让程序采集多张图片(建议15-20张)
3. 按空格键保存当前帧,按'q'键完成标定
"""
import cv2
import numpy as np
import os
from datetime import datetime
from typing import Optional, Tuple
class CameraCalibrator:
    def __init__(self, board_size=(8, 6), square_size=0.025):
        """
        初始化相机标定器
        
        参数:
            board_size: 棋盘格内部角点数量 (width, height)
            square_size: 每个方格的实际大小(单位:米)
        """
        self.board_size = board_size
        self.square_size = square_size
        self.criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
        
        # 准备对象点,如 (0,0,0), (1,0,0), (2,0,0) ....,(8,5,0)
        self.objp = np.zeros((board_size[0] * board_size[1], 3), np.float32)
        self.objp[:, :2] = np.mgrid[0:board_size[0], 0:board_size[1]].T.reshape(-1, 2) * square_size
        
        # 存储对象点和图像点的数组
        self.objpoints = []  # 3D点(世界坐标系)
        self.imgpoints = []  # 2D点(图像平面)
        
        # 创建保存标定图像的目录
        self.calib_dir = "calibration_images"
        os.makedirs(self.calib_dir, exist_ok=True)
        
    def find_corners(self, img: np.ndarray) -> tuple[bool, Optional[np.ndarray]]:
        """
        在图像中查找并优化棋盘格角点位置
        
        参数:
            img: 输入的BGR彩色图像,形状为(H, W, 3)
            
        返回:
            tuple[bool, Optional[np.ndarray]]: 
                - 第一个元素(bool): 是否成功检测到棋盘格角点
                - 第二个元素: 如果检测成功,返回优化后的角点坐标数组,形状为(N, 1, 2),
                  其中N是角点数量;如果检测失败,返回None
        """
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        ret, corners = cv2.findChessboardCorners(gray, self.board_size, None)
        
        if ret:
            # 提高角点检测精度
            corners2 = cv2.cornerSubPix(
                gray, corners, (11, 11), (-1, -1), 
                (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
            )
            return True, corners2
        return False, None
    
    def add_calibration_image(self, img: np.ndarray, corners: np.ndarray) -> None:
        """
        添加标定图像及其角点数据到标定数据集
        
        参数:
            img: 包含棋盘格的BGR彩色图像,形状为(H, W, 3)
            corners: 检测到的棋盘格角点坐标数组,形状为(N, 1, 2),
                    其中N是角点数量,通常为board_size[0] * board_size[1]
            
        返回:
            None
            
        副作用:
            - 将3D对象点(self.objp)添加到objpoints列表
            - 将2D图像点(corners)添加到imgpoints列表
            - 将图像保存到calibration_images目录下
        """
        self.objpoints.append(self.objp)
        self.imgpoints.append(corners)
        
        # 保存标定图像
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = os.path.join(self.calib_dir, f"calib_{timestamp}.jpg")
        cv2.imwrite(filename, img)
        print(f"已保存标定图像: {filename}")
    
    def calibrate(self, img_size: tuple[int, int]) -> tuple[Optional[np.ndarray], Optional[np.ndarray], Optional[float]]:
        """
        执行相机标定计算
        
        参数:
            img_size: 图像尺寸,格式为(宽度, 高度)
            
        返回:
            tuple: 包含三个元素的元组:
                - camera_matrix (np.ndarray | None): 3x3相机内参矩阵,格式为:
                    [[fx, 0,  cx],
                     [0,  fy, cy],
                     [0,  0,   1]]
                      其中(fx, fy)是焦距,(cx, cy)是主点坐标
                      
                - dist_coeffs (np.ndarray | None): 畸变系数,格式为[k1, k2, p1, p2, k3, ...]
                  - k1, k2, k3: 径向畸变系数
                  - p1, p2: 切向畸变系数
                  
                - mean_error (float | None): 平均重投影误差(像素)
                
                如果标定失败(如图像数量不足),则返回(None, None, None)
        """
        if len(self.objpoints) < 5:
            print("错误:需要至少5张标定图像")
            return None, None, None
            
        ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
            self.objpoints, self.imgpoints, img_size, None, None
        )
        
        # 计算重投影误差
        mean_error = 0.0
        for i in range(len(self.objpoints)):
            imgpoints2, _ = cv2.projectPoints(
                self.objpoints[i], rvecs[i], tvecs[i], mtx, dist
            )
            error = cv2.norm(self.imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
            mean_error += error
        
        mean_error /= len(self.objpoints)
        print(f"重投影误差: {mean_error:.8f} 像素")
        print("\n相机内参矩阵:")
        print(mtx)
        print("\n畸变系数 (k1, k2, p1, p2, k3, ...):")
        print(dist[0])
        
        return mtx, dist[0], mean_error
def main():
    # 初始化标定器 (8x6 棋盘格,每个方格2.5cm x 2.5cm)
    calibrator = CameraCalibrator(board_size=(8, 6), square_size=0.025)
    
    # 打开相机 (0 通常是内置摄像头)
    cap = cv2.VideoCapture(0)
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
    
    print("\n相机标定程序")
    print("1. 准备一个8x6的棋盘格标定板")
    print("2. 将棋盘格放在相机前不同位置和角度")
    print("3. 按空格键保存当前帧,按'q'键完成标定")
    print("4. 建议保存15-20张不同角度的图像")
    
    while True:
        ret, frame = cap.read()
        if not ret:
            print("无法获取相机画面")
            break
            
        # 查找棋盘格角点
        ret_corners, corners = calibrator.find_corners(frame)
        
        # 如果找到角点,绘制出来
        if ret_corners:
            cv2.drawChessboardCorners(frame, calibrator.board_size, corners, ret_corners)
        
        # 显示已保存的图像数量
        cv2.putText(frame, f"Saved: {len(calibrator.objpoints)}/20", 
                   (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
        
        # Display help text
        cv2.putText(frame, "Press SPACE to save, 'q' to finish", 
                   (10, frame.shape[0] - 20), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
        
        cv2.imshow("Camera Calibration (SPACE to save, 'q' to finish)", frame)
        
        key = cv2.waitKey(1) & 0xFF
        if key == ord(' '):  # 空格键保存当前帧
            if ret_corners:
                calibrator.add_calibration_image(frame.copy(), corners)
                if len(calibrator.objpoints) >= 20:
                    print("已保存20张图像,可以按'q'键完成标定")
            else:
                print("未检测到完整的棋盘格,请调整位置后重试")
        elif key == ord('q'):  # 'q'键退出
            break
    
    # 执行标定
    if len(calibrator.objpoints) >= 5:
        print("\n正在计算相机参数...")
        mtx, dist, error = calibrator.calibrate((frame.shape[1], frame.shape[0]))
        
        if mtx is not None:
            # 保存相机参数到文件
            np.savez("camera_params.npz", 
                    camera_matrix=mtx, 
                    dist_coeffs=dist,
                    reprojection_error=error)
            print("\n相机参数已保存到 camera_params.npz")
    
    # 释放资源
    cap.release()
    cv2.destroyAllWindows()
    print("\n标定完成!")
if __name__ == "__main__":
    main()
相关文章
|
计算机视觉 Python
OpenCV形态学运算中梯度运算、顶帽、黑帽运算讲解与实战应用(附Python源码)
OpenCV形态学运算中梯度运算、顶帽、黑帽运算讲解与实战应用(附Python源码)
773 0
|
5G 芯片
带你读《无人机网络与通信》之二:空对地与空对空数据链路通信
本书针对无人机系统两个关键问题—通信组网和管控体系做了比较全面和深入的描述和探讨,特别是以大量笔墨分析了现有无线通信解决方案,对比了不同通信协议,得出了很有价值的研究结论。无人机的跨越式发展将涉及公共安全管理的问题,构建管控体系是当务之急,分级管理以及制定相应的适航标准是一件大事情,本书对此也进行了系统的、建设性的讨论。未来,高档无人机和无人机集群将对环境具有更强的感知能力和自适应能力,还有对任务的自规划和学习、调整能力,本书讨论的内容将为它们的发明、部署和监督提供宝贵的信息。
|
3月前
|
人工智能 缓存 BI
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro,跑完 Skills —— OA 审批、大屏、报表、部署 5 大实战场景后的真实体验 ![](https://oscimg.oschina.net/oscnet/up608d34aeb6bafc47f
9094 23
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
|
11月前
|
JSON 人工智能 前端开发
JSON基础知识与实践
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,基于JavaScript语言的子集,具有易读、易解析和跨语言等优点。它广泛应用于前后端数据交换、API设计、配置文件存储及移动应用开发等场景。JSON数据由键值对构成,支持字符串、数值、布尔值、数组和对象等类型,结构清晰且可嵌套,适合网络传输。自2001年由Douglas Crockford提出后,JSON因其简洁性和灵活性逐渐成为互联网主流数据格式之一,并被标准化为ECMA-404。
819 0
|
算法 计算机视觉 Python
python利用opencv进行相机标定获取参数,并根据畸变参数修正图像附有全部代码(流畅无痛版)
该文章详细介绍了使用Python和OpenCV进行相机标定以获取畸变参数,并提供了修正图像畸变的全部代码,包括生成棋盘图、拍摄标定图像、标定过程和畸变矫正等步骤。
python利用opencv进行相机标定获取参数,并根据畸变参数修正图像附有全部代码(流畅无痛版)
|
11月前
|
人工智能 JSON 编译器
Code和Clang配置C++开发环境
本文介绍了如何在VS Code中配置C++开发环境,包括安装VS Code、C++扩展、Clang编译器,创建并运行Hello World项目,使用IntelliSense、调试程序及自定义配置等内容,帮助开发者快速上手C++开发。
1252 0
|
11月前
|
算法 Go 计算机视觉
YOLOv8结构解读
YOLOv8是Ultralytics团队开发的最新目标检测模型,基于YOLO系列的高效单阶段架构,进一步优化了精度与速度。相比YOLOv5,YOLOv8在Backbone中移除了Focus模块,优化了CSP结构;在Neck部分增强了特征融合能力;Head部分采用Anchor-Free与解耦头设计,提升检测精度。此外,YOLOv8引入了更先进的损失函数、自对抗训练和动态标签分配等策略,显著提高了模型性能与鲁棒性。
3305 0
|
Ubuntu NoSQL 编译器
在Ubuntu 20.04上构建RISC-V和QEMU环境
以上的步骤只是开始,RISC-V的世界里有无数的未知等待你去探索。加油,勇敢的探险家。
825 18
|
机器学习/深度学习 存储 人工智能
《脉动阵列:AI硬件加速的“秘密武器”》
脉动阵列(Systolic Array)是一种高效的并行计算架构,灵感源自人体血液循环系统。它通过网格排列的处理单元(PE),以同步并行方式处理数据,尤其在矩阵乘法和卷积运算中表现出色,极大提升了AI计算效率。其优势包括降低内存带宽需求、高运算吞吐率和设计简洁,但也面临灵活性有限、全局同步难等挑战。尽管如此,脉动阵列仍为AI硬件加速提供了重要支持,推动了人工智能技术的发展。
1904 14
|
编解码 Ubuntu 算法
Ubuntu桌面图标指南:一文了解如果设置ubuntu 软件图标
Ubuntu桌面图标指南:一文了解如果设置ubuntu 软件图标
5431 0