【问题标题】:Find outer checkerboard corners查找外部棋盘角
【发布时间】:2014-10-19 16:06:19
【问题描述】:

在下图中,我有绿点的 2D 位置,我想计算红点的位置,或者,作为中间步骤,我想计算蓝点的位置。全部为 2D。

当然,我不只是想为上面的图片找到那些位置。最后,我想要一个自动算法,它采用一组棋盘角点来计算外角。

我需要得到的坐标尽可能准确,所以我认为我需要一个解决方案,它不仅考虑到外部绿点,而且还使用所有其他绿点的位置来计算最佳适合外角(红色或蓝色)。

如果 OpenCV 可以做到这一点,请指出我的方向。

【问题讨论】:

  • @beaker,嗯..我不确定这应该如何帮助我。 findChessboardCorners 搜索四边形 - 也就是说,棋盘需要完全为空 - 而不是搜索角。如何为我的目的使用该教程?其中有哪一部分对我来说特别重要?
  • 抱歉,我没有读到您已经检测到了绿点。您是否尝试过solvePnP/solvePnPRansac 来找到理想化棋盘上的点与图像中找到的点之间的转换?然后,您可以将变换应用于理想化的角点,以找出它们在 2D 图像中的位置。

标签: opencv computer-vision perspective object-recognition


【解决方案1】:

一般来说,如果你只检测到一些内角,而不是全部内角,问题就无法解决。这是因为配置对平移是不变的 - 将物理棋盘移动整个正方形会在图像上产生相同的检测角位置,但由于物理角不同

此外,配置对于在棋盘平面中旋转 180 度也是不变的,除非您仔细区分每个角相邻的正方形的颜色,否则对于旋转 90 度和相对于中心和中线。

这意味着,除了检测角点之外,还需要从图像中提取物理棋盘格的一些特征,这些特征可以用来打破上述不变性。最简单的中断是检测一行一列的所有 9 个角,或者至少检测它们的末端角。它们可以通过施加它们的线成90度角的条件直接用于校正图像。但是,由于遮挡或检测器故障,这可能会被证明是不可能的,可能需要更复杂的方法。

例如,您可以尝试直接检测棋盘边缘,即边界处的粗黑线。例如,一种方法是检测附近的字母和数字,并使用这些位置将线检测器限制在附近区域。

顺便说一句,如果您发布的照片​​只是一条红鲱鱼,并且您有兴趣检测一般的棋盘状图案,并且可以控制图案的种类,那么还有更强大的方法可以做到这一点。我个人最喜欢的是"known 2D crossratios" pattern of Matsunaga and Kanatani.

【讨论】:

  • 我不明白第一段:如果你移动板子,那么检测到的角也会随着板子移动。这就是我的期望和我想要的。
  • 您说“可能需要更复杂的方法”。 ——这些复杂的方法正是我所要求的。您可以在我的问题中的图片上看到,我知道像遮挡这样的问题,这些是我要解决的问题。
  • > 如果你移动棋盘,那么……我说的是“移动……整个方块”。您希望并期望识别您检测到的角点,这样您就可以知道棋盘边界的位置。我说如果你只有几个内角,这是不可能的。这是因为在这种情况下,可能有多个物理板的姿势可能会产生相同检测到的角。
  • 啊,现在我明白了。不,如果检测到的角是例如 H3 和 G4 的交界处,对我来说并不重要。或者换一种说法,我知道从图片中的一组绿点来看,我无法判断电路板是否旋转了 90 度。我的问题是,从这组绿点开始,无论以什么顺序,找到一组蓝点或红点。
  • 是的,我理解您的问题 - 我告诉您,您认为不重要的东西实际上是必不可少的 :-) 您无法仅使用检测到的 inner 角来解决它(即你的 green 的),除非你以某种方式破坏了平移不变性,即使你愿意忽略旋转和反射。为了说服自己是这种情况,请尝试在图像中标记几个“内部”绿色角落,然后找到与它们兼容的棋盘翻译。
【解决方案2】:

我使用以下解决方案稳健但不准确地解决了这个问题:

  • 找到至少有 3 个绿点与该线非常匹配的线。 (图中红色细线)
  • 保留边界线:从这些线中,将点仅保留在线的一侧或非常靠近线的位置。
  • 过滤边界线:从边界线中,选取 4 个最好的/点数最多的边界线。 (图片中的粗白线)
  • 计算剩余 4 条边界线的交点(没有一条线完全平行,因此产生 6 个交点,我们只需要其中 4 个)。
  • 从交叉口中,删除距离交叉口平均位置最远的一个,直到只剩下 4 个。
  • 这是 4 个蓝点。

然后您可以将这 4 个点输入 OpenCV 的 findPerspectiveTransform 函数以找到透视变换(也称为单应性):

Point2f* srcPoints = (Point2f*) malloc(4 * sizeof(Point2f));    
std::vector<Point2f> detectedCorners = CheckDet::getOuterCheckerboardCorners(srcImg);
for (int i = 0; i < MIN(4, detectedCorners.size()); i++) {
    srcPoints[i] = detectedCorners[i];
}

Point2f* dstPoints = (Point2f*) malloc(4 * sizeof(Point2f));
int dstImgSize = 400;
dstPoints[0] = Point2f(dstImgSize * 1/8, dstImgSize * 1/8);
dstPoints[1] = Point2f(dstImgSize * 7/8, dstImgSize * 1/8);
dstPoints[2] = Point2f(dstImgSize * 7/8, dstImgSize * 7/8);
dstPoints[3] = Point2f(dstImgSize * 1/8, dstImgSize * 7/8);

Mat m = getPerspectiveTransform(srcPoints, dstPoints);

对于我们的示例图像,findPerspectiveTranform 的输入和输出如下所示:

input
    (349.1, 383.9) -> ( 50.0,  50.0)
    (588.9, 243.3) -> (350.0,  50.0)
    (787.9, 404.4) -> (350.0, 350.0)
    (506.0, 593.1) -> ( 50.0, 350.0)
output
    (      1.6     -1.1    -43.8 )
    (      1.4      2.4  -1323.8 )
    (      0.0      0.0      1.0 )

然后您可以将图像的透视图转换为棋盘坐标:

Mat plainBoardImg;
warpPerspective(srcImg, plainBoardImg, m, Size(dstImgSize, dstImgSize));

结果如下图:

对于我的项目,不再需要您在问题板上看到的红点,但我确信它们可以通过反转单应性然后使用逆进行反向转换轻松地计算出来点 (0, 0)(0, dstImgSize)(dstImgSize, dstImgSize)(dstImgSize, 0)

该算法运行起来非常可靠,但是它并没有使用所有可用信息,因为它只使用外部点(那些与白线相连的点)。它不使用内部点的任何数据来提高准确性。我仍然想找到一个更好的解决方案,它使用内部点的数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-23
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多