4

从包含透明区域和彩色区域的 PNG 图像中,我想生成一个具有 N 边(N 可配置)的多边形,以接近图像的最佳边缘。我希望这个多边形由一系列向量定义。

例如,让我们考虑下图:+ 链接到 plus。我可以通过计算每个像素周围透明像素的数量来设法检测图像的边缘。我得到以下矩阵:

0000000000000000
0000053335000000
0000030003000000
0000030003000000
0000020002000000
0533210001233500
0300000000000300
0300000000000300
0300000000000300
0533210001233500
0000020002000000
0000030003000000
0000030003000000
0000053335000000
0000000000000000
0000000000000000

我认为,基于这个矩阵,我应该能够得到所有角的坐标,从而得到向量,但我不知道怎么做。在这种情况下,我希望我的程序返回:

[7,2]->[11,2]
[11,2]->[11,6]
[11,6]->[15,6]
... 

你们中有人有建议或链接吗?

最终,我还想要 90 度和 0 度以外的近似角度,但这确实是为了第二阶段。

4

3 回答 3

2

我想你会发现 CV 工具包中的一些工具对你有用。您最好利用这些资源,而不是推出自己的解决方案。

我认为您有兴趣提取的两个特征是边缘和角落。

边缘,就像你想要的那样,可以让你接近形状的轮廓。您现在可能不感兴趣的是边缘检测技术。这些会将您的图像转换为边缘/空间的二进制图像。相反,您需要研究Hough 变换,它可以为图像中的每条线提供端点。如果您正在处理看起来很明确的、实心的、直线,那么这应该会很好。您已将您的问题标记为 Ruby,所以也许您可以查看 OpenCV(OpenCV 是用 C 编写的,但有ruby ​​-opencv和javacv项目要绑定)。这是OpenCV 的霍夫变换文档. 但是,您可能会发现一件事是霍夫变换不会为您提供连接的线。这取决于图像中实际线条的规律性/不规律性。因此,您可能需要手动将线的端点连接到结构中。

Corners,对于您提供的图像可能非常有效。标准算法是Harris 角点检测。与霍夫变换类似,您可以使用此技术返回图像中“最重要的”特征。这种技术以提供一致的结果而闻名,即使对于同一事物的不同图像也是如此。因此,它通常用于模式识别等。但是,如果您的图像与提供的图像一样简单,您很可能能够以这种方式提取所有形状的角。然后,在给定预定义的 N 边的情况下,获取图像的形状只需以有意义的方式连接点即可。

绝对应该使用这两个功能空间并了解它们是如何工作的,并且您可能可以同时使用这两个功能空间以获得更好的结果。

顺便说一句,如果您的图像确实是透明的颜色/强度,您可以将图像转换为“二进制图像”。请注意,这不仅仅是二进制数据。相反,这意味着您只表示两种颜色,一种由 表示,另一种由0表示1。这样做会打开一整套适用于灰度和二值图像的工具。例如,您在上面手动计算的数字矩阵称为距离变换,可以使用 OpenCV 等工具轻松高效地完成。

于 2013-01-06T10:37:44.460 回答
1

霍夫变换是一种标准技术,用于在给定一组点的情况下查找线、多边形和其他形状。它可能正是您在这里寻找的。您可以使用霍夫变换来查找图像中所有可能的线段,然后将附近的线段组合在一起以获得一组近似图像的多边形。

希望这可以帮助!

于 2013-01-06T05:24:28.220 回答
0

在这种简单的情况下,您可以执行以下三个步骤:找到形状的质心,根据 x 轴与当前点和质心形成的线之间的角度对兴趣点进行排序,遍历排序的点.

在这种情况下,质心的 x 坐标是每个兴趣点的 x 坐标之和除以兴趣点的总数(分别为质心的 y 坐标)。要计算角度,只需使用几乎任何语言的 atan2 即可。您的兴趣点是显示为 1 或 5 的那些,否则它不是角落(根据您的输入)。

不要误以为 Hough 会解决您的问题,即它不会给出您所追求的排序坐标。这也是一种昂贵的方法。此外,给定您的矩阵,您已经拥有其他方法无法比拟的完美信息(当然,问题是重复您提出的如此好的结果——在这些情况下,霍夫可能会证明是有用的)。

我的 Ruby 非常糟糕,因此请使用以下代码作为解决问题的指南:

include Math

data = ["0000000000000000",
    "0000053335000000",
    "0000030003000000",
    "0000030003000000",
    "0000020002000000",
    "0533210001233500",
    "0300000000000300",
    "0300000000000300",
    "0300000000000300",
    "0533210001233500",
    "0000020002000000",
    "0000030003000000",
    "0000030003000000",
    "0000053335000000",
    "0000000000000000",
    "0000000000000000"]

corner_x = []
corner_y = []
data.each_with_index{|line, i|
    line.split(//).each_with_index{|col, j|
        if col == "1" || col == "5"
            # Cartesian coords.
            corner_x.push(j + 1)
            corner_y.push(data.length - i)
        end
    }
}

centroid_y = corner_y.reduce(:+)/corner_y.length.to_f
centroid_x = corner_x.reduce(:+)/corner_x.length.to_f

corner = []
corner_x.zip(corner_y).each{|c|
    dy = c[1] - centroid_y
    dx = c[0] - centroid_x
    theta = Math.atan2(dy, dx)
    corner.push([theta, c])
}

corner.sort!
corner.each_cons(2) {|c|
    puts "%s->%s" % [c[0][1].inspect, c[1][1].inspect]
}

这导致:

[2, 7]->[6, 7]
[6, 7]->[6, 3]
[6, 3]->[10, 3]
[10, 3]->[10, 7]
[10, 7]->[14, 7]
[14, 7]->[14, 11]
[14, 11]->[10, 11]
[10, 11]->[10, 15]
[10, 15]->[6, 15]
[6, 15]->[6, 11]
[6, 11]->[2, 11]

哪些是从最左下角开始的逆时针顺序的顶点(在笛卡尔坐标中,从最左下角的 (1, 1) 开始)。

于 2013-01-07T19:13:34.733 回答