张正友相机标定方法中标定板角点检测算法原理(Harris和Shi-Tomasi角点检测算法)

在张正友标定法中,标定板角点检测是第一步,也是保证标定精度的关键之一。通常使用棋盘格(checkerboard)图案作为标定板,其角点具有高对比度、规则排列,便于检测和亚像素精细化。下面详细介绍标定板角点检测原理与算法流程。


1、角点定义

在棋盘格图案中,角点是白格与黑格交界的交叉点,即四个颜色区域交汇处的内角点(不包括外边框角点)。如图所示:

在这里插入图片描述

若棋盘格为 8x6 格,则角点数量为 7×5=35 个。


2、 OpenCV中提供的角点检测算法接口

OpenCV 提供稳定的函数:

bool findChessboardCorners(InputArray image,
                           Size patternSize,
                           OutputArray corners,
                           int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE);

参数说明:

  • image:输入灰度图像
  • patternSize:角点行列数(列数-1, 行数-1)
  • corners:输出二维图像坐标点列表
  • flags:检测模式控制标志位,如自适应阈值、图像归一化、快速检查等

3、OpenCV角点检测核心算法流程

在OpenCV中棋盘格角点检测大致包括以下几个步骤:

1. 图像预处理

  • 转为灰度图
  • 可选:直方图均衡化、滤波平滑、CLAHE 增强等

2. 边缘与角点候选检测

  • 使用 图像梯度(Sobel)计算边缘响应

  • 应用 Harris 或 Shi-Tomasi 算法提取角点候选:

    R = det ⁡ ( M ) − k ⋅ ( trace ( M ) ) 2 R = \det(M) - k \cdot (\text{trace}(M))^2 R=det(M)k(trace(M))2

    • M M M 是像素邻域的梯度矩阵
    • R R R 是角点响应函数

3. 多边形轮廓提取

  • 对二值化图像使用 findContours() 获取轮廓
  • 分析轮廓结构并提取出类似正方形交错排列的结构

4. 匹配几何模式(匹配棋盘格)

  • 使用凸包排列和网格几何结构,判断角点是否匹配棋盘格排列
  • 结合 连通性判断斜率相似性 对候选角点排序

5. 亚像素精细化(提高检测精度)

一旦粗略角点位置获得,使用如下函数提高精度:

cornerSubPix(image, corners, winSize, zeroZone, criteria);
  • 使用图像灰度插值拟合方法,在小窗口内优化角点位置
  • 通常将精度提升到亚像素级别(如 0.01 像素)

4、现代改进算法(可选替代)

1. ChArUco 标定板

  • 棋盘格 + ArUco 标记组合(OpenCV 支持)
  • 支持部分遮挡和旋转识别
  • 用于增强鲁棒性和自动识别性

2. Deep Learning 检测角点(如 D2-Net)

  • 用于不规则纹理图案、工业标定板

5、典型完整调用流程(OpenCV)

cv::Mat image = cv::imread("chessboard.jpg", cv::IMREAD_GRAYSCALE);
cv::Size patternSize(9, 6); // 棋盘格内角点数(9列×6行)
std::vector<cv::Point2f> corners;
bool found = cv::findChessboardCorners(image, patternSize, corners,
            cv::CALIB_CB_ADAPTIVE_THRESH + cv::CALIB_CB_NORMALIZE_IMAGE);

if (found) {
    cv::cornerSubPix(image, corners, cv::Size(11,11), cv::Size(-1,-1),
                     cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 30, 0.01));
    cv::drawChessboardCorners(image, patternSize, corners, found);
    cv::imshow("Corners", image);
    cv::waitKey();
}

6、角点检测质量判断

  • OpenCV 会根据检测匹配质量自动返回 true/false
  • 可用 cv::drawChessboardCorners() 可视化确认
  • 标定前建议去除检测失败或畸变严重图像

7 、Harris 角点检测的原理

Harris 角点检测算法是图像处理中广泛使用的特征检测算法,特别适用于检测棋盘格、角落、边缘交汇等区域。下面我们将详细讲解 Harris 算法的原理、数学推导过程以及 C++ 实现代码


7.1、Harris 角点检测的原理

Harris 算法基于图像灰度变化的二阶矩阵来判断某点周围是否是角点、边缘或平坦区域。

➤ 基本思想:

若一个图像窗口向任意方向移动,且图像灰度变化显著,说明该区域是角点

➤ 数学形式化:

对图像 I ( x , y ) I(x, y) I(x,y),考虑一个窗口移动 ( u , v ) (u, v) (u,v) 后图像灰度差异:

E ( u , v ) = ∑ x , y w ( x , y ) [ I ( x + u , y + v ) − I ( x , y ) ] 2 E(u,v) = \sum_{x,y} w(x,y) [I(x+u, y+v) - I(x, y)]^2 E(u,v)=x,yw(x,y)[I(x+u,y+v)I(x,y)]2

其中:

  • w ( x , y ) w(x, y) w(x,y):窗口函数(通常为高斯核)
  • ( x , y ) (x, y) (x,y):窗口中的像素
  • ( u , v ) (u, v) (u,v):窗口偏移量

7.2、Harris 响应函数推导

步骤 1:泰勒展开近似

I ( x + u , y + v ) ≈ I ( x , y ) + I x u + I y v I(x+u, y+v) \approx I(x, y) + I_x u + I_y v I(x+u,y+v)I(x,y)+Ixu+Iyv

代入差值平方:

E ( u , v ) ≈ ∑ x , y w ( x , y ) [ I x u + I y v ] 2 = [ u v ] ∑ w ( x , y ) [ I x 2 I x I y I x I y I y 2 ] ⏟ M [ u v ] = d T M d E(u,v) \approx \sum_{x,y} w(x,y) [I_x u + I_y v]^2 = \begin{bmatrix} u & v \end{bmatrix} \underbrace{ \sum w(x,y) \begin{bmatrix} I_x^2 & I_x I_y \\ I_x I_y & I_y^2 \end{bmatrix} }_{M} \begin{bmatrix} u \\ v \end{bmatrix} = \mathbf{d}^T M \mathbf{d} E(u,v)x,yw(x,y)[Ixu+Iyv]2=[uv]M w(x,y)[Ix2IxIyIxIyIy2][uv]=dTMd

步骤 2:角点响应矩阵 M M M

M = ∑ w ( x , y ) [ I x 2 I x I y I x I y I y 2 ] M = \sum w(x,y) \begin{bmatrix} I_x^2 & I_x I_y \\ I_x I_y & I_y^2 \end{bmatrix} M=w(x,y)[Ix2IxIyIxIyIy2]

这里的 M M M 是图像的结构张量(Structure Tensor),描述局部灰度变化。

步骤 3:特征值分析判断角点类型
  • λ 1 , λ 2 \lambda_1, \lambda_2 λ1,λ2 都很大 → 角点
  • λ 1 ≫ λ 2 \lambda_1 \gg \lambda_2 λ1λ2 或相反 → 边缘
  • λ 1 , λ 2 \lambda_1, \lambda_2 λ1,λ2 都小 → 平坦区域
步骤 4:定义角点响应函数(Harris 响应)

R = det ⁡ ( M ) − k ⋅ ( trace ⁡ ( M ) ) 2 = λ 1 λ 2 − k ( λ 1 + λ 2 ) 2 R = \det(M) - k \cdot (\operatorname{trace}(M))^2 = \lambda_1 \lambda_2 - k(\lambda_1 + \lambda_2)^2 R=det(M)k(trace(M))2=λ1λ2k(λ1+λ2)2

  • k ∈ [ 0.04 , 0.06 ] k \in [0.04, 0.06] k[0.04,0.06]:经验常数
  • R > threshold R > \text{threshold} R>threshold,该点为角点

7.3、C++ 实现示例

下面是一个 原始 Harris 算法 的 C++ 实现(依赖 OpenCV 基础功能):

#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
    Mat src = imread("chessboard.jpg", IMREAD_GRAYSCALE);
    if (src.empty()) return -1;

    Mat src_float;
    src.convertTo(src_float, CV_32FC1, 1.0 / 255.0); // 转为 float

    // 计算梯度
    Mat Ix, Iy;
    Sobel(src_float, Ix, CV_32FC1, 1, 0, 3);
    Sobel(src_float, Iy, CV_32FC1, 0, 1, 3);

    // 计算结构张量的每个元素
    Mat Ixx = Ix.mul(Ix);
    Mat Iyy = Iy.mul(Iy);
    Mat Ixy = Ix.mul(Iy);

    // 高斯模糊平滑张量
    GaussianBlur(Ixx, Ixx, Size(3, 3), 1.0);
    GaussianBlur(Iyy, Iyy, Size(3, 3), 1.0);
    GaussianBlur(Ixy, Ixy, Size(3, 3), 1.0);

    // 计算角点响应 R
    Mat R = Mat::zeros(src.size(), CV_32FC1);
    float k = 0.04f;
    for (int y = 0; y < src.rows; ++y) {
        for (int x = 0; x < src.cols; ++x) {
            float a = Ixx.at<float>(y, x);
            float b = Ixy.at<float>(y, x);
            float c = Iyy.at<float>(y, x);

            float det = a * c - b * b;
            float trace = a + c;
            R.at<float>(y, x) = det - k * trace * trace;
        }
    }

    // 标记大于阈值的角点
    Mat R_norm;
    normalize(R, R_norm, 0, 255, NORM_MINMAX, CV_32FC1);
    Mat R_uint8;
    convertScaleAbs(R_norm, R_uint8);

    Mat result;
    cvtColor(src, result, COLOR_GRAY2BGR);

    for (int y = 0; y < R_norm.rows; ++y) {
        for (int x = 0; x < R_norm.cols; ++x) {
            if (R_norm.at<float>(y, x) > 100.0) {
                circle(result, Point(x, y), 3, Scalar(0, 0, 255), 1);
            }
        }
    }

    imshow("Harris Corners", result);
    waitKey(0);
    return 0;
}

7.4、OpenCV 中快速调用方式

Mat dst, dst_norm;
cornerHarris(src, dst, 2, 3, 0.04);
normalize(dst, dst_norm, 0, 255, NORM_MINMAX);

for (int y = 0; y < dst_norm.rows; y++) {
    for (int x = 0; x < dst_norm.cols; x++) {
        if ((int)dst_norm.at<float>(y, x) > 150)
            circle(src_color, Point(x, y), 5, Scalar(0, 0, 255));
    }
}

8 、Shi-Tomasi角点检测的原理

Shi-Tomasi 角点检测(也称为 Good Features to Track, GFTT)是 Harris 算法的改进版本,它对特征点的判断更为稳定,并能更好地适应后续的光流跟踪等任务。


8.1、Shi-Tomasi 算法原理

Shi-Tomasi 基于 Harris 的结构张量 M M M,但在角点判断标准上采用了更加直观且稳定的特征值最小值判断

回顾 Harris 中结构张量:

对于某一点的图像窗口,计算其图像梯度后得到:

M = [ ∑ I x 2 ∑ I x I y ∑ I x I y ∑ I y 2 ] M = \begin{bmatrix} \sum I_x^2 & \sum I_x I_y \\ \sum I_x I_y & \sum I_y^2 \end{bmatrix} M=[Ix2IxIyIxIyIy2]

该矩阵的两个特征值 λ 1 \lambda_1 λ1, λ 2 \lambda_2 λ2 描述窗口内梯度变化的方向性。


Harris 与 Harris 的对比
方法响应函数 R
Harris R = det ⁡ ( M ) − k ⋅ ( trace ( M ) ) 2 R = \det(M) - k \cdot (\text{trace}(M))^2 R=det(M)k(trace(M))2
Shi-Tomasi R = min ⁡ ( λ 1 , λ 2 ) R = \min(\lambda_1, \lambda_2) R=min(λ1,λ2)
Shi-Tomasi 判断角点的条件:
  • 若两个特征值都大,说明窗口在两个正交方向上都有显著变化(角点);
  • 使用较小的特征值做响应,确保两个方向的梯度都强。

8.2、特征值推导与计算

对于矩阵:

M = [ a b b c ] M = \begin{bmatrix} a & b \\ b & c \end{bmatrix} M=[abbc]

特征值为:

λ 1 , 2 = a + c 2 ± ( a − c 2 ) 2 + b 2 \lambda_{1,2} = \frac{a + c}{2} \pm \sqrt{\left( \frac{a - c}{2} \right)^2 + b^2} λ1,2=2a+c±(2ac)2+b2

因此最小特征值为:

R = min ⁡ ( λ 1 , λ 2 ) = a + c 2 − ( a − c 2 ) 2 + b 2 R = \min(\lambda_1, \lambda_2) = \frac{a + c}{2} - \sqrt{\left( \frac{a - c}{2} \right)^2 + b^2} R=min(λ1,λ2)=2a+c(2ac)2+b2

无需显式求解特征值矩阵,可直接用上式快速计算。


8.3、C++ 实现示例

#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;

int main() {
    Mat src_gray = imread("chessboard.jpg", IMREAD_GRAYSCALE);
    if (src_gray.empty()) return -1;

    Mat src_color;
    cvtColor(src_gray, src_color, COLOR_GRAY2BGR);

    // 计算图像梯度
    Mat Ix, Iy;
    Sobel(src_gray, Ix, CV_32FC1, 1, 0, 3);
    Sobel(src_gray, Iy, CV_32FC1, 0, 1, 3);

    // 构造结构张量分量
    Mat Ixx = Ix.mul(Ix);
    Mat Iyy = Iy.mul(Iy);
    Mat Ixy = Ix.mul(Iy);
    GaussianBlur(Ixx, Ixx, Size(3,3), 1.0);
    GaussianBlur(Iyy, Iyy, Size(3,3), 1.0);
    GaussianBlur(Ixy, Ixy, Size(3,3), 1.0);

    Mat R = Mat::zeros(src_gray.size(), CV_32FC1);

    for (int y = 0; y < src_gray.rows; ++y) {
        for (int x = 0; x < src_gray.cols; ++x) {
            float a = Ixx.at<float>(y, x);
            float b = Ixy.at<float>(y, x);
            float c = Iyy.at<float>(y, x);

            float trace = a + c;
            float det = a * c - b * b;

            // 最小特征值近似计算
            float temp = sqrtf(0.25f * (a - c) * (a - c) + b * b);
            float lambda_min = 0.5f * (trace - 2.0f * temp);
            R.at<float>(y, x) = lambda_min;
        }
    }

    // 归一化并阈值处理
    Mat R_norm;
    normalize(R, R_norm, 0, 255, NORM_MINMAX, CV_32FC1);

    // 绘制角点
    for (int y = 0; y < R_norm.rows; ++y) {
        for (int x = 0; x < R_norm.cols; ++x) {
            if (R_norm.at<float>(y, x) > 100.0f) {
                circle(src_color, Point(x, y), 3, Scalar(0, 255, 0), 1); // 绿色 Shi-Tomasi
            }
        }
    }

    imshow("Shi-Tomasi Corners", src_color);
    waitKey(0);
    return 0;
}

8.4、OpenCV 推荐调用效率更高

使用 OpenCV 提供的封装函数 goodFeaturesToTrack

std::vector<Point2f> corners;
goodFeaturesToTrack(src_gray, corners, 100, 0.01, 10);

for (const auto& pt : corners) {
    circle(src_color, pt, 3, Scalar(255, 0, 0), 1); // 蓝色角点
}
  • 参数说明:

    • 100:最多提取的角点数量
    • 0.01:质量等级(特征值最小值相对于最大值的比)
    • 10:最小间距

9、Harris 与 Shi-Tomasi 区别

特性HarrisShi-Tomasi
响应函数组合特征值最小特征值
对噪声鲁棒性一般更好
特征稳定性k 参数影响无需额外参数
光流跟踪表现更优
OpenCV APIcornerHarrisgoodFeaturesToTrack

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点云SLAM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值