在下图中,我有绿点的2D位置,我想计算红点的位置,或者,作为中间步骤,我想计算蓝点的位置。全部是2D。
如果当然,我不仅希望找到上面这些图片的位置。最后,我想要一个自动算法,它采用一组棋盘角点来计算外角。
我需要得到的坐标尽可能准确,所以我认为我需要一个解决方案,不仅考虑外部绿点,而且还使用所有其他绿点'计算最佳外角(红色或蓝色)的位置。
如果OpenCV可以做到这一点,请指出我的方向。
答案 0 :(得分:1)
一般情况下,如果您只检测到一些(但不是全部)内角,则问题无法解决。这是因为配置对于平移是不变的 - 通过整个方块移动物理棋盘会在图像上产生相同的检测角位置,但是由于不同的物理角落。
此外,在棋盘平面中旋转180度也是不变的,除非你小心区分每个角落附近的方格的颜色,旋转90度和相对于中心的反射和中线。
这意味着,除了检测角落之外,还需要从图像中提取物理棋盘的一些功能,这些功能可用于打破上述不变性。最简单的方法是检测一行和一列的所有9个角,或者至少检测它们的末端角。它们可以通过施加其线条成90度角的条件直接用于矫正图像。然而,由于遮挡或探测器故障,这可能变得不可能,并且可能需要更复杂的方法。
例如,您可以尝试直接检测棋盘边缘,即边界处的黑色粗线。例如,一种方法是检测附近的字母和数字,并使用这些位置将线路探测器限制在附近区域。
顺便说一句,如果您发布的照片只是一个红色的鲱鱼,并且您有兴趣检测一般类似棋盘格的模式,并且可以控制模式的类型,那么有更强大的方法可以实现。我个人最喜欢的是"known 2D crossratios" pattern of Matsunaga and Kanatani.
答案 1 :(得分:1)
我使用以下解决方案强有力地解决了这个问题,但并不准确:
然后,您可以将这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));
结果如下图所示:
对于我的项目,不再需要你在问题板上看到的红点,但是我确信它们可以通过反转它然后使用倒数来轻松地从单应性中计算出来。 -tranform the points (0, 0)
,(0, dstImgSize)
,(dstImgSize, dstImgSize)
和(dstImgSize, 0)
。
该算法的工作效率令人惊讶,但它并未使用所有可用信息,因为它仅使用外部点(与白线连接的那些点)。它不使用内点的任何数据来获得额外的准确性。我仍然希望找到一个更好的解决方案,它使用内点的数据。