三点三角方案有一个痛点:点完 3 次后,可能停留在仿射阶段,透视校正要碰运气(guided search 能不能搜到额外星位)。四点角方案用 4 个角星位,多 1 次点击,直接拿到 8 个约束 → direct findHomography,始终输出完整的透视矫正。


1. 选点 & 点击顺序(顺时针)

              CLICK 1              CLICK 2
                ● (3,3) ─────────── ● (3,15)
                │                    │
                │     棋盘区域       │
                │                    │
                │    ★ 天元         │
                │                    │
                │                    │
                ● (15,3) ────────── ● (15,15)
              CLICK 4              CLICK 3
点击 坐标 位置 作用
🖱️ 1 (3, 3) 左上角星位 基准锚点
🖱️ 2 (3, 15) 右上角星位 水平方向 + 上边界
🖱️ 3 (15, 15) 右下角星位 对角线方向
🖱️ 4 (15, 3) 左下角星位 垂直方向 + 下边界

2. 与三点三角的核心区别

🔺 3-point:  仿射(6-DOF) → guided search → 可能升级单应(8-DOF)
              └── 升级失败 → 仅仿射, 无透视校正

🔲 4-point:  4 角直接 → findHomography(4点, 精确解) → 全单应(8-DOF)
              └── 无需仿射中转, 始终全透视校正
              └── guided search 可选: 补搜其余5点 → 超定RANSAC
维度 🔺 3-point 🔲 4-point
点击次数 3 4 (+1)
最小变换 仿射 (无透视) 全单应 (含透视)
透视(梯形)校正 需升级, 不可靠 ✅ 始终
guided search 依赖 强依赖 不依赖
数学适定性 欠定→仿射→需升级 ✅ 精确 4 点解
RANSAC 精度 4-9 点 (超定) 4-9 点 (超定)

3. 为什么 4 点直接优于 3 点中转

3.1 直接求解

4 个点 → 8 个约束 → 恰好确定 8-DOF 单应矩阵:

vector<Point2f> src = {
    {3.0f/18,  3.0f/18},   // 左上 (3,3)
    {3.0f/18,  15.0f/18},  // 右上 (3,15)
    {15.0f/18, 15.0f/18},  // 右下 (15,15)
    {15.0f/18, 3.0f/18}    // 左下 (15,3)
};
vector<Point2f> dst = {mark1, mark2, mark3, mark4};

Mat H = findHomography(src, dst, 0);
// 4 点精确解: 非迭代, 无 RANSAC 开销

3.2 无中间态

3 点方案先走仿射(6-DOF),再从仿射预测位置搜星位升级到单应(8-DOF)。这个中间态有两个问题:

  • 仿射预测没有透视分量 → guided search 的 ROI 可能对不准
  • 如果 guided search 找不到足够额外星位 → 永远停在仿射,梯形畸变不矫正

4 点方案不存在这个问题——从一开始就是全单应。

3.3 全面透视矫正

梯形畸变、近大远小、倾斜旋转——一次性全部纠正:

// 直接透视矫正,无中间步骤
Mat H = findHomography(src, dst, 0);
Mat output;
warpPerspective(input, output, H, Size(outSize, outSize));

4. 算法流程

flowchart TD
    A["🖱️ 用户点击 4 个角星位<br/>(3,3)→(3,15)→(15,15)→(15,3)"] --> B["cv::findHomography<br/>4 点精确解, 8-DOF"]
    B --> C{"可选: 搜索剩余 5 星位?"}
    C -->|"是"| D["Guided HoughCircles<br/>在 5 个未标记位置搜"]
    C -->|"否"| E["4 点单应, 直接输出"]
    D --> F{"找到额外星位?"}
    F -->|"是"| G["5-9 点超定 RANSAC<br/>精度提升, outlier rejection"]
    F -->|"否"| E
    G --> H["warpPerspective → 正方形输出"]
    E --> H

5. 实现

class QuadRectifier {
public:
    void setCorners(cv::Point2f topLeft,     // (3,3)  左上
                    cv::Point2f topRight,    // (3,15) 右上
                    cv::Point2f bottomRight, // (15,15) 右下
                    cv::Point2f bottomLeft)  // (15,3) 左下
    {
        corners[0] = topLeft;
        corners[1] = topRight;
        corners[2] = bottomRight;
        corners[3] = bottomLeft;
    }

    void rectify(const cv::Mat& input, cv::Mat& output,
                 int outSize = 600, bool refine = true)
    {
        // 源点: 4 个角星位的归一化棋盘坐标
        cv::Point2f src[4] = {
            {3.0f/18,  3.0f/18},
            {3.0f/18,  15.0f/18},
            {15.0f/18, 15.0f/18},
            {15.0f/18, 3.0f/18}
        };

        // 直接求解单应
        cv::Mat H = cv::findHomography(
            std::vector<cv::Point2f>(src, src + 4),
            std::vector<cv::Point2f>(corners, corners + 4),
            0  // 4 点精确解, 非迭代
        );

        if (refine) {
            // 可选: 用 H 预测其余 5 个星位 → guided search → 超定 RANSAC
            auto extra = guidedSearchRemaining(input, H);
            if (!extra.empty()) {
                auto allSrc = mergeAll(src, extra);
                auto allDst = mergeAll(corners, extra);
                H = cv::findHomography(allSrc, allDst,
                                       cv::RANSAC, 3.0);
            }
        }

        cv::warpPerspective(input, output, H,
                            cv::Size(outSize, outSize));
    }

private:
    cv::Point2f corners[4];
};

6. 四点 vs 三点 vs 九点最终对比

维度 🔺 3-point 🔲 4-point 📐 9-point
点击次数 3 4 (+1) 9
数学最小保证 仿射 (无透视) 全单应 (有透视) 全单应 (超定)
透视校正 需升级, 可能失败 ✅ 始终 ✅ 超定高精度
推荐场景 快速预览, 接近正面 生产默认 要求最高精度
用户体验 良好 最佳 繁琐

7. 常见问题

Q: 3 点仿射升级到单应,和 4 点直达单应,结果有区别吗?

有。3 点的仿射中间态会导致 guided search 窗口位置基于仿射(无透视)预算,可能与真实星位有偏移。4 点直接单应的预测位置包含透视信息,guided search 更准。

Q: 如果只点 4 个角,没搜到其余 5 个星位,效果如何?

4 点精确解已包含完整透视变换。5 个额外点只是让 RANSAC 有更多内点可用于 outlier rejection——但 4 个人工标记本身就是精确的,不需要 outlier rejection。

Q: 为什么推荐 4 点作为生产默认?

多 1 次点击(3→4),换来"始终全单应"的确定性。用户不需要理解"升级/兜底"的区别——4 个点 = 完整矫正,简单直接。


8. 总结

各方案的选择指南:

                    点击成本
                       ↑    📐 9-point
                       │      (最高精度, 最慢)
                       │
                       │  🔲 4-point ← 推荐生产默认
                       │    (全单应, 始终透视矫正)
                       │
                       │  🔺 3-point
                       │    (快速预览, 可能无透视)
                       │
                       │  ⚡ 2-point
                       │    (已废弃: 共线退化)
                       └─────────────────────→ 矫正能力

4 点角星位 = 最少点击量 × 全透视矫正保证 = 生产环境最优解。