3. 角点检测原理:OpenCV中的findChessboardCorners与cornerSubPix详解
做镜头畸变校正,第一步就是找到标定板上的角点。
说白了,你得先告诉算法:「喏,这些点是棋盘格的交叉点,你按这个来算畸变参数。」
OpenCV 给了我们两个主力函数:findChessboardCorners 和 cornerSubPix。一个负责粗定位,一个负责精调。我刚开始接触标定时,以为第一个函数就够用了,结果校正出来的图像边缘总是有细微扭曲……后来才发现,少了亚像素精度的这一步,精度根本不够。
3.1 findChessboardCorners:粗定位是怎么工作的?
这个函数的核心思路,其实挺直观的。它会在图像里搜索棋盘格的黑白交叉点。
具体流程大致是这样的:
- 二值化与阈值处理:先把图像转成灰度图,然后用自适应阈值把它变成黑白分明的二值图。
- 四边形检测:在二值图里找轮廓,筛选出那些像棋盘格小方块的四边形区域。
- 角点候选生成:找到这些四边形的顶点,作为候选角点。
- 模式匹配与排序:根据你指定的棋盘格尺寸(比如 9x6),把这些候选点按行列顺序排好。
嗯,这里要注意:这个函数对光照和对比度比较敏感。我在项目中遇到过,如果标定板表面有反光,或者光线太暗,它经常找不到角点,或者找到的角点顺序是乱的。
核心参数说明:
patternSize:棋盘格的内角点数,比如 (9,6) 表示每行9个内角点,每列6个。注意不是格子数,是交叉点数。flags:可以传CALIB_CB_ADAPTIVE_THRESH让函数自己调阈值,或者CALIB_CB_NORMALIZE_IMAGE先做归一化再处理。
我的小技巧: 如果发现角点检测失败,试试把 flags 设为 CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE。这两个组合能解决大部分光照不均的问题。
3.2 cornerSubPix:为什么要做亚像素精调?
你想想看,findChessboardCorners 给出的角点坐标是整数像素级别的。但真实世界中的角点,往往落在像素之间的某个位置。
举个例子,一个角点可能在像素 (100.3, 200.7) 的位置。如果只用整数坐标去算畸变参数,误差会累积,最终校正出来的图像边缘就会「发虚」或者「扭曲」。
这就是 cornerSubPix 的用武之地。它能把整数坐标精调到亚像素级别,精度可以达到 0.1 像素甚至更高。
它的原理其实也不复杂:
- 在粗定位的角点周围,取一个小窗口。
- 在这个窗口里,计算图像的梯度方向。
- 利用梯度方向的信息,通过迭代优化,找到那个「梯度变化最剧烈」的精确位置。
我记得有一次做高精度工业检测,用整数坐标校正后,测量出来的物体尺寸总是差那么 0.02 毫米。后来加上 cornerSubPix,问题就解决了。说白了,精度就是抠出来的。
注意: cornerSubPix 的迭代次数和精度阈值要设好。我见过有人把迭代次数设成 100,结果跑起来慢得不行。一般 30 次以内就够了。
3.3 两个函数的配合使用流程
在实际代码里,这两个函数是成对出现的。流程大概是:
import cv2
import numpy as np
# 读取图像
img = cv2.imread('chessboard.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 1. 粗定位角点
ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)
if ret:
# 2. 亚像素精调
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners_sub = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
# 3. 绘制角点(可选)
cv2.drawChessboardCorners(img, (9, 6), corners_sub, ret)
你看,代码其实很短。但每一步都有讲究。
3.4 参数调优经验
我整理了一个表格,方便你对照调整:
| 参数 | 推荐值 | 说明 |
|---|---|---|
winSize (cornerSubPix) |
(11, 11) 或 (5, 5) | 搜索窗口大小。窗口越大,精度越高,但速度越慢。我一般用 (11, 11)。 |
zeroZone |
(-1, -1) | 一般设 -1,表示不排除任何区域。 |
criteria |
(EPS + MAX_ITER, 30, 0.001) | 迭代 30 次,精度到 0.001 像素。够用了。 |
flags (findChessboard) |
ADAPTIVE_THRESH + NORMALIZE | 应对光照不均的「黄金组合」。 |
避坑指南: 我曾经在标定板上贴了透明胶带,结果反光太强,findChessboardCorners 死活找不到角点。后来把胶带撕掉,用柔光灯从侧面打光,一次就成功了。所以,物理条件也很重要。
3.5 核心逻辑流程图
下面这张 SVG 图,把整个角点检测的流程串起来了。你可以对照着看,心里会更清楚:
3.6 常见问题与排查思路
做角点检测时,你可能会遇到这些问题。我按经验排了个序:
- 找不到角点:先检查棋盘格尺寸参数对不对。然后看光照是否均匀。最后试试加
CALIB_CB_ADAPTIVE_THRESH标志。 - 角点顺序错乱:通常是棋盘格图案不对称,或者标定板有遮挡。确保棋盘格完整出现在画面里。
- 精调后角点偏移:检查
winSize是不是太小了。如果窗口太小,亚像素算法找不到足够的梯度信息。 - 程序跑得慢:把
criteria里的迭代次数降到 20,或者缩小winSize。
重要提醒: 不要在一张图像上反复跑 cornerSubPix。它每次都会重新计算,但结果不会变。跑一次就够了。
好了,角点检测的原理和实战细节,基本就这些。你只要把这两个函数用熟,标定工作的地基就算打牢了。下一章我们会聊怎么用这些角点去算畸变系数,到时候你会发现,前面的精调工作,每一分努力都值。
公众号:蓝海资料掘金营,微信deep3321