棋盘格完成相机标定:获取内参矩阵与镜头畸变参数

上一篇我们讲述了如何用python生成出棋盘格,接下来我们来讲讲如何用这个棋盘格进行相机标定。

相机标定要解决的核心问题就是:把三维世界中的点投影到图像像素坐标系时,获取其中的“成像变换”。一个相机从理想的小孔模型出发,像素点的位置主要由两部分决定,一是相机自身的“成像比例与主点位置”等内部属性(内参),二是镜头带来的非线性弯曲(畸变)。当相机姿态变化,还会引入相机相对于标定板的旋转和平移(外参)。

棋盘格之所以常用,是因为它提供了大量几何结构规则、角点易检测的特征点。我们知道这些角点在真实世界的平面坐标(例如方格边长是20mm,则角点网络坐标可以用(i*s,j*s,0)表示,i,j就是横竖的方格位置,s就是方格固定的边长),同时又能在图像上检测出对应的像素坐标,通过多张不同姿态的棋盘格照片建立“世界点—-像素点”的匹配,OpenCV就可以求解出相机内参矩阵K和畸变系数dist,并给出每张图对应的外参(rvec,tvec)。

其中K通常写作[[fx,0,cx],[0,fy,cy],[0,0,1]],fx,fy表示水平方向/竖直方向的等效焦距(以像素为单位),cx,cy是主点(通常接近图像中心),而畸变dist常见为5个或更多参数,最常用的模型包含径向畸变k1,k2,k3(越往边缘越“鼓”)和切向畸变p1,p2(镜头装配不完美导致的斜切变形)。

当你完成标定获取如上的数据后,你就可以将这些参数用于后续的三维重建、测距、SLAM、姿态估计等任务。

下面是我提供的一个OpenCV标定脚本,需要你先采集同一张棋盘格在不同角度、不同位置、不同距离下大约15~30张图片,要保证画面中棋盘格覆盖不同区域(尤其是四角边缘),避免每张都居中且姿态单一。图片要清晰,不能过曝,角点要可见(不能倾斜到角点检测失败)。运行脚本后,程序会输出相机内参矩阵、畸变参数,并给出重投影误差(越小越好,通常0.2~0.8像素属于比较常见的范围,取决于分辨率与拍摄质量)。

import os, glob
import numpy as np
import cv2

# ========= 你需要修改的参数 =========
images_dir = "./calib_images"
pattern = "*.jpg"          # 或 *.png
inner_cols = 9             # 内角点列数
inner_rows = 6             # 内角点行数
square_size = 20.0         # 方格边长(单位自定:mm 或 m,但要一致)
# ===================================

chessboard_size = (inner_cols, inner_rows)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-6)

# 世界坐标系下的棋盘格角点(Z=0 平面)
objp = np.zeros((inner_rows * inner_cols, 3), np.float32)
objp[:, :2] = np.mgrid[0:inner_cols, 0:inner_rows].T.reshape(-1, 2)
objp *= square_size

objpoints, imgpoints = [], []
image_paths = sorted(glob.glob(os.path.join(images_dir, pattern)))
if not image_paths:
    raise FileNotFoundError(f"找不到图片:{images_dir}/{pattern}")

img_size = None
used = 0

for p in image_paths:
    img = cv2.imread(p)
    if img is None:
        continue
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    if img_size is None:
        img_size = (gray.shape[1], gray.shape[0])  # (w,h)

    found, corners = cv2.findChessboardCorners(gray, chessboard_size)
    if not found:
        print(f"[跳过] 未检测到角点: {os.path.basename(p)}")
        continue

    corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
    objpoints.append(objp)
    imgpoints.append(corners)
    used += 1

print(f"\n总图片: {len(image_paths)},有效用于标定: {used}")
if used < 8:
    print("提示:有效图片偏少,建议 15~30 张,且棋盘覆盖画面各区域(尤其四角)。")

# 标定:返回 RMS 重投影误差、相机内参 K、畸变参数 dist、每张图外参 rvec/tvec
ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(
    objpoints, imgpoints, img_size, None, None
)

print("\n===== 标定结果 =====")
print("图像尺寸 (w,h):", img_size)
print("RMS 重投影误差 ret:", ret)
print("\n内参矩阵 K:\n", K)
print("\n畸变参数 dist (k1,k2,p1,p2,k3[,...]):\n", dist.ravel())

# 计算“平均每点重投影误差”(像素),比 ret 更直观
total_sq = 0.0
total_n = 0

for i in range(used):
    projected, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], K, dist)
    diff = imgpoints[i].reshape(-1, 2) - projected.reshape(-1, 2)
    total_sq += np.sum(diff ** 2)
    total_n += len(projected)

mean_error = np.sqrt(total_sq / total_n)
print("\n平均每点重投影误差(像素):", mean_error)

# 保存结果(npz 简单实用)
np.savez("camera_calib.npz",
         camera_matrix=K,
         dist_coeffs=dist,
         image_size=np.array(img_size),
         rms=np.array(ret),
         mean_reproj_error=np.array(mean_error))

print("\n已保存: camera_calib.npz")

这是角点检测成功的图:

参考文献:

https://docs.opencv.org/master/dc/dbb/tutorial_py_calibration.html

--------------

本文标题为:

棋盘格完成相机标定:获取内参矩阵与镜头畸变参数

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇