泳池边缘检测 table

Edge detection on pool table

我目前正在研究一种算法来检测游泳池的游戏区域 table。为此,我捕获了一张图像,将其转换为灰度图像,并在其上使用了 Sobel 算子。现在我想将游戏区域检测为一个有 4 个角的盒子,位于 table.

的 4 个角

检测 table 的边缘非常简单,然而,事实证明检测 4 个角并不那么容易,因为池中有口袋 table。现在我只想在每条边上画一条线,从这些线,我可以计算交点,这是我 table.

的角

我被困在这里,因为我还没有想出一个好的解决方案来在我的图像中找到这些线条。当我使用 Sobel 算子时,我可以很容易地看到它。但是检测它并计算角点位置的好方法是什么?

编辑:我添加了一些示例图片

基本图像:

灰度图像

索贝尔滤波器(仅限水平)

泳池的游乐区 table 通常具有独特的颜色,例如绿色或蓝色。我会首先尝试基于颜色的分割方法。 MATLAB 中的 Color Thresholder 应用程序为您提供了一种尝试不同颜色空间和阈值的简单方法。

以下答案假定您已经找到了图像中线条的位置。然而,这可以通过直接查看像素并查看它们是否在 "line" 中来完成 "easily"。如果图像也先进行了校正,通常更容易检测到这一点,即旋转矩形(池 table)更像这样:[] 而不是 /=/。然后这只是扫描像素的情况,如果旁边有相似颜色的像素,假设它们之间有一条线。

代码通过遍历图像中找到的行来工作。只要每条线的端点落在 xy 坐标的公差范围内,它就被标记为角点。一旦找到角点,我就取它们之间的平均值来找到角点所在的位置。例如:

结束于10, 10的水平线和起始于12, 12的垂直线如果tolerance为2或更多,将被认为是一个角。找到的角落将位于:11, 11

注意:这只是为了找到左上角,但可以很容易地进行调整以找到所有这些角。这样做的原因是因为在我使用它的应用程序中,首先将每个数组排序为首先找到相关值的顺序更快,请参阅:Why is processing a sorted array faster than an unsorted array?.

另请注意,我的代码会为每条线找到第一个角,这可能不适用于您,这主要是出于性能原因。然而,代码可以很容易地调整以找到所有线的所有角,然后 select "more likely" 角或对它们进行平均。

另请注意,我的答案写在 C#

private IEnumerable<Point> FindTopLeftCorners(IEnumerable<Line> horizontalLines, IEnumerable<Line> verticalLines)
{
    List<Point> TopLeftCorners = new List<Point>();

    Line[] laHorizontalLines = horizontalLines.OrderBy(l => l.StartPoint.X).ThenBy(l => l.StartPoint.Y).ToArray();
    Line[] laVerticalLines = verticalLines.OrderBy(l => l.StartPoint.X).ThenBy(l => l.StartPoint.Y).ToArray();

    foreach (Line verticalLine in laVerticalLines)
    {
        foreach (Line horizontalLine in laHorizontalLines)
        {
            if (verticalLine.StartPoint.X <= (horizontalLine.StartPoint.X + _nCornerTolerance) && verticalLine.StartPoint.X >= (horizontalLine.StartPoint.X - _nCornerTolerance))
            {
                if (horizontalLine.StartPoint.Y <= (verticalLine.StartPoint.Y + _nCornerTolerance) && horizontalLine.StartPoint.Y >= (verticalLine.StartPoint.Y - _nCornerTolerance))
                {
                    int nX = (verticalLine.StartPoint.X + horizontalLine.StartPoint.X) / 2;
                    int nY = (verticalLine.StartPoint.Y + horizontalLine.StartPoint.Y) / 2;

                    TopLeftCorners.Add(new Point(nX, nY));
                    break;
                }
            }
        }
    }

    return TopLeftCorners;
}

其中Line是下面的class:

public class Line
{
    public Point StartPoint { get; private set; }

    public Point EndPoint { get; private set; }

    public Line(Point startPoint, Point endPoint)
    {
        this.StartPoint = startPoint;
        this.EndPoint = endPoint;
    }
}

_nCornerTolerance是可配置数量的int

如果颜色分割(如@Dima 所建议的那样)有效,请使用轮廓跟踪获取斑点的轮廓。然后通过 Douglas-Peucker 算法将轮廓简化为四边形(或多边形)。你应该这样找到四个 table 边。

为了更准确,您可以通过局部搜索跨越边缘的过渡来细化边缘位置并执行线拟合。然后将线相交得到角。

对于一般解决方案,会有很多噪音来源:rails 周围的布料问题、rails 上的木材纹理(或无纹理)、不同的光照、阴影、污渍在布上,粉笔在rails,等等。

当颜色和光照不可靠时,以及当您想找到几何对象的边缘时,最好考虑边缘像素而不是 gray/color 像素。

前段时间我想制作一个基于 phone 的应用程序来保存球的位置以供以后查看,包括在线查看,所以我对这个问题有点了解。虽然我可以为您当前的问题提供一些指导,但我发现您会 运行 在每个步骤中遇到新问题,因此我会尝试提供更完整的答案。

  1. 将图像转换为灰度。如果我们不能让算法在灰度下工作,我们将不可避免地 运行 陷入颜色问题。 (见下文)
  2. [TBD] 进行一些预处理以减少噪声。
  3. 使用 Sobel 或(如果必须)Canny 查找边缘点。
  4. 运行 Hough 线检测,但有一些注意事项和参数化,如下所述。
  5. 找到描述梯形四边形的直线。 (这可能是两个内四边形:一个在床上的栏杆内,另一个稍大的四边形位于顶部的 cloth/wood 栏杆边缘。)
  6. (可选)使用侧袋帮助确定四边形的方向。
  7. 使用仿射变换将透视变形的 table 床映射到 [谢天谢地] 已知相对尺寸的矩形。我们提前知道床的尺寸,因此您可以将扭曲的矩形重新映射到合适的矩形。 (我们暂时忽略一些光学效果。)
  8. 将彩色图像重新映射到透视校正矩形。您可能需要调整一些球的位置。

一般说明:

  • 一般意义上的按颜色过滤可能很困难。人们很容易将布料简单地认为是绿色、蓝色或红色(或其他颜色),但是当您查看实际的 RGB 值并尝试分离颜色时,您会开始意识到在颜色上工作的噩梦是。
  • 光学失真可能会丢失一些边缘。
  • 远处的短轨可能很难检测到,但您可以这样做:找到两条长线 rails 的内线,然后在两条 rails 之间垂直搜索第一个强水平线-ish 边缘在图像的远端。那将是最短的轨道。
  • 尽管为了方便起见,您可能想使用 phone 摄像头,但使用 Kinect 摄像头或类似(最好更小)的设备会使问题更容易解决。您不仅会同时拥有颜色数据和 3D 数据,而且还会消除一些光照问题,因为深度数据不依赖于可见光照。
  • 对于您的应用,请考虑将轨道边缘的搜索区域限制为透视变形的矩形。用户可能能够调整搜索区域。这可以极大地简化处理过程,并且可以帮助您解决 table 未点亮(可能是这种情况)的问题。