在OpenCV中使用方形棋盘进行相机校准

如何创建校准模式。

您可以在https://github.com/opencv/opencv/blob/4.x/doc/pattern.png中找到棋盘图案

您可以在https://github.com/opencv/opencv/blob/4.x/doc/acircles_pattern.png中找到圆形板图案

您可以在https://github.com/opencv/opencv/blob/4.x/doc/charuco_board_pattern.png中找到

ChAruco 板图案(7X5 ChAruco 板,方形尺寸:30 毫米,标记尺寸:15 毫米,aruco 字典:DICT_5X5_100,页面宽度:210 毫米,页面高度:297 毫米)

创建您自己的图案

现在,如果您想创建自己的模式,您将需要 python 来使用opencv/doc/pattern_tools/gen_pattern.py at 4.x · opencv/opencv · GitHub

例子

在文件 chessboard.svg 中创建一个棋盘格图案,有 9 行、6 列,正方形大小为 20 毫米:

python gen_pattern.py -o chessboard.svg --rows 9 --columns 6 --type checkerboard --square_size 20

在文件 circleboard.svg 中创建一个圆形板图案,有 7 行、5 列,半径为 15 毫米:

python gen_pattern.py -o circleboard.svg --rows 7 --columns 5 --type circles --square_size 15

在文件 acircleboard.svg 中创建一个圆形板图案,其中 7 行、5 列,正方形尺寸为 10 毫米,圆与圆之间的间距较小:

python gen_pattern.py -o acircleboard.svg --rows 7 --columns 5 --type acircles --square_size 10 --radius_rate 2

为 findChessboardCornersSB() 创建氡棋盘,并在 (7 4)、(7 5)、(8 5) 单元格中设置标记:

python gen_pattern.py -o radon_checkerboard.svg --行 10 --列 15 --类型 radon_checkerboard -s 12.1 -m 7 4 7 5 8 5

在 charuco_board.svg 中创建一个 ChAruco 板图案,该图案有 7 行、5 列、正方形大小 30 毫米、aruco 标记大小 15 毫米,并使用 DICT_5X5_100 作为 aruco 标记的词典(它包含在 DICT_ARUCO.json 文件中):

python gen_pattern.py -o charuco_board.svg --rows 7 --columns 5 -T charuco_board --square_size 30 --marker_size 15 -f DICT_5X5_100.json.gz

如果要更改单位,请使用 -u 选项(mm 英寸、px、m)

如果要更改页面大小,请使用 -w 和 -h 选项

如果你想在 ChAruco 板上使用自己的字典,你应该在文件名中写上你的字典。例如

python gen_pattern.py -o charuco_board.svg --行 7 --列 5 -T charuco_board -f my_dictionary.json

您可以使用 opencv/samples/cpp/aruco_dict_utils.cpp 在 my_dictionary.json 文件中生成标记数量为 30、标记大小为 5 位的字典。

bin/example_cpp_aruco_dict_utils.exe my_dict.json -nMarkers=30 -markerSize=5

如何根据一组棋盘图像校准相机。

测试数据:使用数据/象棋文件夹中的图像。

  • 通过在 cmake 配置中将 BUILD_EXAMPLES 设置为 ON 来使用示例编译 OpenCV。
  • 转到 bin 文件夹并使用 imagelist_creator 创建图像的 XML/YAML 列表。
  • 然后,运行校准示例以获取相机参数。使用等于 3cm 的正方形大小。

姿态估计

现在,让我们编写代码来检测图像中的棋盘并找出它与相机的距离。您可以将此方法应用于您在图像中检测到的任何具有已知 3D 几何形状的物体。

测试数据:使用数据文件夹中的 chess_test*.jpg 图像。

创建一个空的控制台项目。加载测试图像:

Mat img = imread(argv[1], IMREAD_GRAYSCALE);

使用 findChessboard 函数检测此图像中的棋盘:

bool found = findChessboardCorners( img, boardSize, ptvec, CALIB_CB_ADAPTIVE_THRESH );
  • 现在,编写一个函数,生成一个 vector<Point3f> 数组,其中包含任意坐标系中棋盘的 3d 坐标。为简单起见,我们选择一个系统,使得棋盘的一个角位于原点,棋盘位于z = 0平面
  • 从 XML/YAML 文件读取相机参数:
FileStorage fs( filename, FileStorage::READ );
Mat intrinsics, distortion;
fs["camera_matrix"] >> intrinsics;
fs["distortion_coefficients"] >> distortion;

现在我们准备通过运行“solvePnP”来找到棋盘姿势:

vector<Point3f> boardPoints;
// fill the array
...

solvePnP(Mat(boardPoints), Mat(foundBoardCorners), cameraMatrix,
                     distCoeffs, rvec, tvec, false);

问题:如何计算从相机原点到任意一个角的距离? 答案:由于我们的图像位于 3D 空间中,因此首先我们要计算相对相机姿势。这将为我们提供 3D 到 2D 的对应关系。接下来,我们可以应用简单的 L2 范数来计算任意点(角的端点)之间的距离。

<think>我们被要求使用OpenCV进行相机校准以获取焦距参数。根据引用[1][3][4][5]中的信息,相机校准是获取内参(包括焦距$f_x, f_y$和主点$c_x, c_y$)的关键步骤。下面我将分步骤详细说明如何使用OpenCV进行相机校准。 ### 步骤1: 准备校准板 首先,需要准备一个已知几何图案的校准板(如棋盘格)。棋盘格是一个由黑白方块组成的网格,通常使用8x6或9x7等规格(内部角点数)。每个方块的尺寸需要精确已知(例如,每格25mm×25mm)。 ### 步骤2: 采集图像 使用校准的摄像头从不同角度和距离拍摄棋盘格的图像(通常需要15-20张)。确保图像覆盖整个画面,包括各个角落,以更好地估计畸变参数。 ### 步骤3: 检测角点 使用OpenCV的`findChessboardCorners()`函数检测每张图像中的棋盘格角点位置(以像素坐标表示)。该函数将返回一个布尔值(表示是否成功检测到角点)和检测到的角点坐标列表。 ```python import cv2 import numpy as np # 设置棋盘格的尺寸(内部角点数,例如8x6表示每行8个角点,每列6个角点) pattern_size = (8, 6) # 准备存储角点的列表 obj_points = [] # 世界坐标系中的3D点(实际物理坐标) img_points = [] # 图像坐标系中的2D点(像素坐标) # 创建世界坐标系中的角点坐标(假设棋盘格在z=0平面上) objp = np.zeros((pattern_size[0] * pattern_size[1], 3), dtype=np.float32) objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) # 每格单位长度(例如1) # 遍历所有图像 image_paths = [...] # 替换为图像路径列表 for path in image_paths: img = cv2.imread(path) 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) corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria) # 存储点 obj_points.append(objp) img_points.append(corners2) # 可视化(可选) cv2.drawChessboardCorners(img, pattern_size, corners2, ret) cv2.imshow('Corners', img) cv2.waitKey(500) cv2.destroyAllWindows() ``` ### 步骤4: 相机校准 使用`calibrateCamera()`函数计算相机内参(包括焦距$f_x, f_y$)和畸变系数。该函数利用世界坐标点(`obj_points`)和对应的图像坐标点(`img_points`)进行校准。 ```python # 执行相机校准 ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera( obj_points, img_points, gray.shape[::-1], None, None ) # 输出焦距参数(camera_matrix是一个3x3矩阵) fx = camera_matrix[0, 0] # x轴方向的焦距(像素单位) fy = camera_matrix[1, 1] # y轴方向的焦距(像素单位) cx = camera_matrix[0, 2] # 主点x坐标(像素单位) cy = camera_matrix[1, 2] # 主点y坐标(像素单位) print(f"焦距 (fx, fy): ({fx:.2f}, {fy:.2f}) 像素") print(f"主点 (cx, cy): ({cx:.2f}, {cy:.2f}) 像素") ``` ### 解释焦距有两个值的原因(引用[4]) 从物理上讲,焦距$f$只有一个值(单位:毫米)。但由于相机传感器像素在水平和垂直方向上可能不是正方形(即像素尺寸$dx$和$dy$不同),导致在像素坐标系中,水平焦距$f_x = f / dx$和垂直焦距$f_y = f / dy$可能不同。因此,校准结果会得到两个焦距值。 ### 步骤5: 评估校准结果(可选) 可以通过重投影误差来评估校准质量: ```python mean_error = 0 for i in range(len(obj_points)): img_points_repro, _ = cv2.projectPoints(obj_points[i], rvecs[i], tvecs[i], camera_matrix, dist_coeffs) error = cv2.norm(img_points[i], img_points_repro, cv2.NORM_L2) / len(img_points_repro) mean_error += error print(f"平均重投影误差: {mean_error / len(obj_points):.4f} 像素") ``` 误差值越小,校准精度越高(通常小于0.5像素可接受)。 ### 步骤6: 保存和加载校准参数(可选) ```python # 保存参数 np.savez("calibration_params.npz", camera_matrix=camera_matrix, dist_coeffs=dist_coeffs) # 加载参数 data = np.load("calibration_params.npz") camera_matrix = data["camera_matrix"] dist_coeffs = data["dist_coeffs"] ``` ### 注意事项 - **畸变校正**:校准后得到的畸变系数(`dist_coeffs`)可用于校正图像畸变(引用[2])。使用`cv2.undistort()`函数: ```python undistorted_img = cv2.undistort(img, camera_matrix, dist_coeffs) ``` - **优化相机矩阵**:如引用[2]所述,可以使用`cv2.getOptimalNewCameraMatrix()`调整输出图像区域(通过参数`alpha`控制缩放)。 通过以上步骤,即可获取相机的焦距参数($f_x, f_y$),用于后续的距离测量等应用[^1][^3][^4]。 §§相关问题§§ 1. 如何利用校准后的相机参数进行实际距离测量?[^1][^5] 2. 相机校准过程中常见的错误有哪些?如何避免? 3. 如何处理校准结果中重投影误差过大的情况? </think> ### 使用OpenCV进行相机校准获取焦距参数的方法 #### 1. **校准原理** 相机校准的核心是确定**内参矩阵**,其中包含焦距参数$f_x$和$f_y$(引用[3][4]): $$ \text{相机矩阵} = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix} $$ - $f_x, f_y$:x轴和y轴方向的焦距(像素单位) - $c_x, c_y$:主点坐标(通常接近图像中心,如640×480图像中≈(320,240))(引用[1]) 焦距有两个值是因为像素在x/y方向的实际物理尺寸可能不同(引用[4]): $$f_x = \frac{f}{\text{dx}}, \quad f_y = \frac{f}{\text{dy}}$$ 其中$f$是物理焦距(mm),dx/dy是单个像素的宽/高(mm)。 #### 2. **校准步骤** ##### (1) 准备校准使用已知尺寸的棋盘格图案(推荐OpenCV标准棋盘格): - 例如:9×6角点的棋盘格,每个方格尺寸30mm×30mm - 打印后贴于平整硬板表面 ##### (2) 采集多角度图像 ```python import cv2 import numpy as np # 设置棋盘格参数 pattern_size = (9, 6) # 内部角点数 (列,行) square_size = 0.03 # 每个方格物理尺寸(米) # 存储3D-2D对应点 obj_points = [] # 世界坐标系中的点 (Z=0) img_points = [] # 图像坐标系中的点 # 生成世界坐标 (0,0,0), (1,0,0), (2,0,0) ..., (8,5,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) * square_size ``` ##### (3) 检测角点并存储 ```python # 遍历所有校准图像 for img_path in image_paths: img = cv2.imread(img_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 查找角点 ret, corners = cv2.findChessboardCorners( gray, pattern_size, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK ) if ret: # 亚像素级精确化 corners_refined = cv2.cornerSubPix( gray, corners, (11,11), (-1,-1), criteria=(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) ) obj_points.append(objp) img_points.append(corners_refined) ``` ##### (4) 执行相机校准 ```python # 执行校准 (核心步骤) ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera( obj_points, img_points, gray.shape[::-1], None, None ) # 提取焦距参数 fx = camera_matrix[0, 0] # x轴焦距(像素) fy = camera_matrix[1, 1] # y轴焦距(像素) cx = camera_matrix[0, 2] # 主点x坐标 cy = camera_matrix[1, 2] # 主点y坐标 print(f"焦距(fx, fy): ({fx:.2f}, {fy:.2f}) 像素") print(f"主点(cx, cy): ({cx:.2f}, {cy:.2f}) 像素") ``` #### 3. **优化校准结果** ##### (1) 评估重投影误差 ```python mean_error = 0 for i in range(len(obj_points)): # 重投影3D点到2D图像 img_points2, _ = cv2.projectPoints( obj_points[i], rvecs[i], tvecs[i], camera_matrix, dist_coeffs ) # 计算误差 error = cv2.norm(img_points[i], img_points2, cv2.NORM_L2) / len(img_points2) mean_error += error print(f"平均重投影误差: {mean_error/len(obj_points):.4f} 像素") ``` - **误差标准**:<0.5像素为优秀,>1像素需重新校准(引用[2]) ##### (2) 优化相机矩阵(引用[2]) ```python # 获取优化后的相机矩阵 h, w = gray.shape[:2] new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix( camera_matrix, dist_coeffs, (w,h), 1, (w,h) ) # 应用畸变校正 undistorted_img = cv2.undistort( img, camera_matrix, dist_coeffs, None, new_camera_matrix ) ``` #### 4. **关键注意事项** 1. **图像数量**:至少需要10-15张不同角度图像(覆盖画面各区域) 2. **角度要求**: - 棋盘格需倾斜(30°-45°) - 包含靠近边缘和中心的图像 - 避免对称角度 3. **光照均匀**:避免反光和阴影影响角点检测 4. **焦距差异**:若$f_x$与$f_y$相差>5%,检查镜头安装或传感器问题(引用[4]) #### 5. **校准结果应用示例(距离测量)** 获取焦距后,可结合物体尺寸计算距离(引用[5]): ```python def calculate_distance(obj_width_px, obj_width_real, fx): """ 根据物体像素宽度计算距离 """ return (fx * obj_width_real) / obj_width_px # 示例:图像中车宽200像素,实际车宽1.8米 distance = calculate_distance(200, 1.8, fx) # 使用校准得到的fx print(f"距离: {distance:.2f} 米") ``` 通过此流程获得的焦距参数$f_x, f_y$可用于单目距离测量、三维重建等应用(引用[1][3][5])。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值