用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/MIUAV/vibe-coding-ros2 --skill camera-calibration命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | camera-calibration |
| description | OpenCV4 相机标定技能 - 单目/双目标定、畸变校正、立体匹配、深度估计、3D 重建 |
| user-invocable | true |
| argument-hint | 相机标定 OR 畸变校正 OR 立体匹配 OR 深度估计 OR 3D重建 OR 内参 OR 外参 |
相机标定与立体视觉完整指南
当需要以下帮助时使用此技能:
import cv2
import numpy as np
import glob
# 生成棋盘格标定板
def generate_chessboard(size=(9, 6), square_size=1.0, save_path='chessboard.png'):
objp = np.zeros((size[0]*size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:size[0], 0:size[1]].T.reshape(-1, 2) * square_size
fig, ax = plt.subplots(figsize=(8, 6))
ax.set_xlim(0, size[0])
ax.set_ylim(0, size[1])
ax.set_aspect('equal')
ax.set_facecolor('white')
for i in range(size[0] + 1):
ax.axvline(i, color='black', linewidth=1)
for i in range(size[1] + 1):
ax.axhline(i, color='black', linewidth=1)
plt.axis('off')
plt.savefig(save_path, dpi=150, bbox_inches='tight',
facecolor='white', edgecolor='none')
plt.close()
# 或使用 OpenCV 打印标定板
# 国际象棋:9x6 角点,80mm 格子
# 圆形标定板:4x11 圆点阵列
import cv2
import numpy as np
import glob
# 标定板参数
objp = np.zeros((6*9, 3), np.float32)
objp[:, :2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2)
objp *= 25 # 格子大小 mm
# 存储所有图像的点
objpoints = [] # 3D 点
imgpoints = [] # 2D 点
images = glob.glob('calibration/*.jpg')
img_size = None
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_size = gray.shape[::-1]
# 查找棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)
if ret:
objpoints.append(objp)
# 亚像素精度优化
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
imgpoints.append(corners2)
# 可视化
cv2.drawChessboardCorners(img, (9, 6), corners2, ret)
cv2.imshow('Corners', img)
cv2.waitKey(500)
cv2.destroyAllWindows()
# 标定
ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, img_size, None, None)
()
()
np.savez(,
camera_matrix=camera_matrix,
dist_coeffs=dist_coeffs,
rvecs=rvecs,
tvecs=tvecs)
# 加载标定结果
data = np.load('calibration.npz')
camera_matrix = data['camera_matrix']
dist_coeffs = data['dist_coeffs']
# 读取图像
img = cv2.imread('test.jpg')
h, w = img.shape[:2]
# 方法1:矫正映射
new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix(
camera_matrix, dist_coeffs, (w, h), 1, (w, h))
mapx, mapy = cv2.initUndistortRectifyMap(
camera_matrix, dist_coeffs, None, new_camera_matrix, (w, h), 5)
undistorted = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
# 方法2:一步校正
undistorted = cv2.undistort(img, camera_matrix, dist_coeffs, None, new_camera_matrix)
# 裁剪 ROI 区域
x, y, w, h = roi
undistorted = undistorted[y:y+h, x:x+w]
import cv2
import numpy as np
import glob
# 左右相机标定板点
objp = np.zeros((6*9, 3), np.float32)
objp[:, :2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2) * 25
objpoints_l, imgpoints_l = [], []
objpoints_r, imgpoints_r = [], []
# 读取左右图像
images_l = sorted(glob.glob('stereo/left/*.jpg'))
images_r = sorted(glob.glob('stereo/right/*.jpg'))
for fname_l, fname_r in zip(images_l, images_r):
img_l = cv2.imread(fname_l)
img_r = cv2.imread(fname_r)
gray_l = cv2.cvtColor(img_l, cv2.COLOR_BGR2GRAY)
gray_r = cv2.cvtColor(img_r, cv2.COLOR_BGR2GRAY)
ret_l, corners_l = cv2.findChessboardCorners(gray_l, (9, 6), None)
ret_r, corners_r = cv2.findChessboardCorners(gray_r, (9, 6), None)
if ret_l and ret_r:
objpoints_l.append(objp)
objpoints_r.append(objp)
# 亚像素优化
corners_l = cv2.cornerSubPix(gray_l, corners_l, (11,11), (-1,-1),
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))
corners_r = cv2.cornerSubPix(gray_r, corners_r, (11,11), (-1,-1),
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, , ))
imgpoints_l.append(corners_l)
imgpoints_r.append(corners_r)
ret_l, mtx_l, dist_l, _, _ = cv2.calibrateCamera(objpoints_l, imgpoints_l, gray_l.shape[::-], , )
ret_r, mtx_r, dist_r, _, _ = cv2.calibrateCamera(objpoints_r, imgpoints_r, gray_r.shape[::-], , )
ret, mtx_l, dist_l, mtx_r, dist_r, R, T, E, F = cv2.stereoCalibrate(
objpoints_l, imgpoints_l, imgpoints_r,
mtx_l, dist_l, mtx_r, dist_r,
gray_l.shape[::-],
flags=cv2.CALIB_FIX_INTRINSIC)
()
()
# 立体校正
R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify(
mtx_l, dist_l, mtx_r, dist_r,
gray_l.shape[::-1], R, T)
# 计算映射表
map1_l, map2_l = cv2.initUndistortRectifyMap(mtx_l, dist_l, R1, P1, gray_l.shape[::-1], cv2.CV_32FC2)
map1_r, map2_r = cv2.initUndistortRectifyMap(mtx_r, dist_r, R2, P2, gray_r.shape[::-1], cv2.CV_32FC2)
# 校正图像
rect_l = cv2.remap(img_l, map1_l, map2_l, cv2.INTER_LINEAR)
rect_r = cv2.remap(img_r, map1_r, map2_r, cv2.INTER_LINEAR)
# SGBM 立体匹配
stereo = cv2.StereoSGBM_create(minDisparity=0, numDisparities=64, blockSize=9)
disparity = stereo.compute(rect_l, rect_r)
# 转换为深度图
depth = (mtx_l[0,0] * T[0,0]) / disparity
# ROS2 相机标定
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import cv2
import numpy as np
class CameraCalibrator(Node):
def __init__(self):
super().__init__('camera_calibrator')
self.bridge = CvBridge()
self.objpoints = []
self.imgpoints = []
self.objp = np.zeros((6*9, 3), np.float32)
self.objp[:, :2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2)
self.subscription = self.create_subscription(
Image, '/camera/image_raw', self.calibration_callback, 10)
def calibration_callback(self, msg):
img = self.bridge.imgmsg_to_cv2(msg, 'bgr8')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)
if ret:
corners2 = cv2.cornerSubPix(gray, corners, (,), (-,-),
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, , ))
.objpoints.append(.objp)
.imgpoints.append(corners2)
cv2.drawChessboardCorners(img, (, ), corners2, ret)
cv2.imshow(, img)
cv2.waitKey()
():
ret, mtx, dist, _, _ = cv2.calibrateCamera(
.objpoints, .imgpoints, img_size, , )
mtx, dist
标定板准备:
标定图像采集:
标定质量评估:
双目标定: