You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

网格标定板角点检测失败,求单应性映射实现方案

棋盘格角点检测失败与精准筛选解决方案

一、修复cv2.findChessboardCorners检测失败问题

先从图像预处理和参数调整入手,这是解决检测失败的最直接手段:

  • 自适应阈值二值化:光照不均是棋盘格检测失败的常见原因,用自适应阈值能更好保留棋盘格边缘:
    binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
    
  • 形态学强化:用闭运算消除小噪点,强化网格线条:
    kernel = np.ones((3,3), np.uint8)
    binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
    
  • 调整检测参数:添加鲁棒性flags,覆盖光照不均、图像模糊等场景:
    ret, corners = cv2.findChessboardCorners(binary, (3,4), flags=cv2.CALIB_CB_ADAPTIVE_THRESH | cv2.CALIB_CB_NORMALIZE_IMAGE | cv2.CALIB_CB_FAST_CHECK)
    
  • 核对网格尺寸:确认(3,4)是内角点的行列数(比如4列3行的格子,对应内角点3x4),不要搞反格子数和内角点数。

如果以上方法仍失败,再用Harris角点的精准筛选方案。

二、Harris角点的精准筛选(无固定位置场景)

利用棋盘格角点的规则网格拓扑特性筛选,不需要区域限制:

  1. 提取强Harris角点:
    dst = cv2.cornerHarris(gray, 2, 3, 0.04)
    dst = cv2.dilate(dst, None)
    threshold = 0.01 * dst.max()
    candidate_points = np.argwhere(dst > threshold)[:, [1,0]].astype(np.float32)  # 转换为(x,y)坐标
    
  2. 筛选规则网格点:
    • 计算所有候选点的水平/垂直间距,找到最频繁出现的间距(即相邻角点的像素间距);
    • 按x坐标分组为列,筛选出点间距均匀的列;
    • 对每列按y坐标排序,筛选出行间距均匀的行;
    • 最终保留符合3行4列(或对应你的网格尺寸)的角点集,并按从左到右、从上到下的顺序排列,确保和后续映射坐标对应。

三、生成映射坐标与计算单应性

假设筛选出3行4列的内角点,相邻角点间距1cm:

  • 生成目标映射坐标:以左上角角点为原点,每个角点的目标坐标为(col*1.0, row*1.0)(单位:cm);
  • 计算单应性矩阵:
    # 检测到的图像角点(已排序)
    src_points = np.array(chess_corners, dtype=np.float32)
    # 目标映射坐标
    dst_points = np.array([[col*1.0, row*1.0] for row in range(3) for col in range(4)], dtype=np.float32)
    # 计算单应性矩阵
    H, mask = cv2.findHomography(src_points, dst_points, cv2.RANSAC, 5.0)
    

完整示例代码

import cv2
import numpy as np

img = cv2.imread('chessboard.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 先尝试findChessboardCorners
binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
kernel = np.ones((3,3), np.uint8)
binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)

ret, corners = cv2.findChessboardCorners(binary, (3,4), flags=cv2.CALIB_CB_ADAPTIVE_THRESH | cv2.CALIB_CB_NORMALIZE_IMAGE | cv2.CALIB_CB_FAST_CHECK)

if ret:
    # 亚像素优化
    corners = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria=(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))
    cv2.drawChessboardCorners(img, (3,4), corners, ret)
    cv2.imshow('Detected Chessboard', img)
    cv2.waitKey(0)
    
    # 生成目标坐标并计算单应性
    dst_points = np.array([[col*1.0, row*1.0] for row in range(3) for col in range(4)], dtype=np.float32)
    H, mask = cv2.findHomography(corners, dst_points, cv2.RANSAC, 5.0)
    print("单应性矩阵:\n", H)
else:
    # 改用Harris角点筛选
    dst = cv2.cornerHarris(gray, 2, 3, 0.04)
    dst = cv2.dilate(dst, None)
    threshold = 0.01 * dst.max()
    candidate_points = np.argwhere(dst > threshold)[:, [1,0]].astype(np.float32)
    
    # 计算候选点的水平/垂直间距,找最频繁的间距
    dx = []
    dy = []
    for i in range(len(candidate_points)):
        for j in range(i+1, len(candidate_points)):
            dx_abs = abs(candidate_points[i][0] - candidate_points[j][0])
            dy_abs = abs(candidate_points[i][1] - candidate_points[j][1])
            if 10 < dx_abs < 100:  # 过滤过小/过大的距离
                dx.append(dx_abs)
            if 10 < dy_abs < 100:
                dy.append(dy_abs)
    
    if dx and dy:
        dx_mode = np.argmax(np.bincount(np.round(dx).astype(int)))
        dy_mode = np.argmax(np.bincount(np.round(dy).astype(int)))
        
        # 按x坐标分组为列
        sorted_x = sorted(candidate_points, key=lambda p: p[0])
        cols = []
        current_col = [sorted_x[0]]
        for p in sorted_x[1:]:
            if abs(p[0] - current_col[-1][0]) < dx_mode * 0.3:
                current_col.append(p)
            else:
                cols.append(current_col)
                current_col = [p]
        cols.append(current_col)
        
        # 筛选有足够点数且行间距均匀的列
        valid_cols = []
        for col in cols:
            if len(col) < 3:
                continue
            col_sorted = sorted(col, key=lambda p: p[1])
            gaps = [col_sorted[i+1][1] - col_sorted[i][1] for i in range(len(col_sorted)-1)]
            if all(abs(gap - dy_mode) < dy_mode * 0.3 for gap in gaps):
                valid_cols.append(col_sorted)
        
        if len(valid_cols) >=4:
            # 提取排序后的棋盘格角点
            chess_corners = []
            for row in range(3):
                for col in valid_cols[:4]:
                    chess_corners.append(col[row])
            chess_corners = np.array(chess_corners, dtype=np.float32)
            
            # 生成目标坐标并计算单应性
            dst_points = np.array([[col*1.0, row*1.0] for row in range(3) for col in range(4)], dtype=np.float32)
            H, mask = cv2.findHomography(chess_corners, dst_points, cv2.RANSAC, 5.0)
            print("筛选出的棋盘格角点:\n", chess_corners)
            print("单应性矩阵:\n", H)
            
            # 绘制筛选后的角点
            for p in chess_corners:
                cv2.circle(img, (int(p[0]), int(p[1])), 3, (0,255,0), -1)
            cv2.imshow('Filtered Corners', img)
            cv2.waitKey(0)
    else:
        print("未找到足够的候选角点")

cv2.destroyAllWindows()

内容的提问来源于stack exchange,提问作者AlgoManiac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 13:35:40