1

我可以成功地阈值图像并找到图像中的边缘。我正在努力尝试准确地提取黑边的角度。

我目前正在取黑边的极值点并使用 atan2 函数计算角度,但由于混叠,根据您选择的点,角度可能会出现一定程度的变化。是否有可靠的可编程方式来选择计算角度的点?

示例图片:

古怪的棋盘

例如,Gimp Measure 工具角度为 3.12°,

Gimp 测量工具

4

4 回答 4

5

如果您正在编写自己的库,那么为这个问题创建一个强大的解决方案将允许您开发几个独立的代码块,您可以将它们串在一起以解决其他问题。我假设您想在任意旋转、不同照明条件下、存在图像噪声、带有一点非线性枕形/桶形失真等情况下找到棋盘的角。

尽管有简单的基于内核的技术可以将整个像素查找为边缘像素,但在处理填充多边形时,您会希望使用能够以亚像素精度查找边缘的算法,以便您可以执行准确的线拟合。即使从深色方块到白色方块的渐变跨越了几个像素,“真正的”边缘也会出现在某个子像素点上,而且很可能不是您通过手动点击猜测的点。

我试图在这个较早的 SO 帖子中提供边缘查找的简单总结: 图像边缘和梯度之间的关系是什么?

对于像您这样的问题,一个稳健的解决方案是沿着从暗到亮的过渡以亚像素精度找到边缘点,然后将线条拟合到边缘点,并使用线条角度。如果您正在处理真实的相机图像,并且图像中存在未经校正的径向失真,则测量精度存在一些潜在问题,但我们将忽略这些问题。

如果您想找到边缘的精确拟合,那么最好在垂直于该边缘的方向上扫描亚像素边缘。这假设我们对边缘方向有一些合理的估计。我们可以先找到边缘方向的粗略估计,然后进行精确的线拟合。

下面的算法可能看起来有太多的步骤,但我的目的是指出如何提供一个健壮的解决方案。

  1. 对黑色像素执行几次侵蚀迭代,以将黑盒彼此分开。
  2. 运行连通分量算法(blob-finding algorithm)来找到被侵蚀的黑色方块。
  3. 确定每个侵蚀正方形的中心 (x,y) 点以及定义长轴和短轴的 (x,y) 端点。
  4. 在具有以像素为单位的总面积、中心 (x,y) 点、长轴和短轴的 (x,y) 点等的结构中维护每个正方形的数据。
  5. 根据需要,消除所有太小的组件(斑点)。例如,您希望排除所有“椒盐”噪声斑点。您也可以暂时忽略被图像边缘截断的棋盘格——我们可以稍后再讨论。

然后,您将遍历您的 blob 列表并对每个 blob 执行以下操作:

  1. 确定大致垂直于棋盘正方形边缘的方向。您如何实现这一点部分取决于您在运行连接组件算法时计算的数据。在通用图像处理库中,标准的连通分量算法将为每个单独的斑点确定数十种属性和测量值:面积、圆度、长轴方向、短轴方向、长轴和短轴的端点等。矩形图形,计算最顶部、最左侧、最右侧和最底部的点就足够了,因为它们将定义四个角。
  2. 在大致垂直于边缘的方向上生成边缘扫描。这些必须在原始的、未修改的图像上执行。这通常假设您实施了双线性插值来查找子像素 (x,y) 点的灰度值,例如 (100.35, 25.72),因为您的扫描线不会完全落在整个像素上。
  3. 使用亚像素边缘点查找技术。通常,您将对扫描方向的边缘点执行曲线拟合,然后在最大梯度处找到实值 (x,y) 点。那是边缘点。
  4. 将所有亚像素边缘点存储在列表/数组/集合中。
  5. 为边缘点生成线拟合。这些可以使用 Hough、RANSAC、最小二乘法或其他技术。
  6. 从四个直线拟合中的每一个的直线方程中,计算直线角度。

该算法独立地找到每个黑色棋盘格的角度。对于这个应用程序来说这可能有点过头了,但如果你正在开发一个库,它可能会给你一些关于要实现哪些子算法以及如何构建它们的想法。例如,该算法将依赖于这些技术的实现:

  • 图像形态(例如侵蚀、扩张、关闭、打开……)
  • 实现形态学的内核操作
  • 对图像进行二值化的阈值化——Otsu 方法值得一试
  • 连通分量算法(又名 blob 查找,或 OpenCV 轮廓函数)
  • blob 的数据结构
  • blob 数据的矩计算
  • 双线性插值以查找子像素 (x,y) 值
  • 一种线性射线扫描技术,用于沿特定方向查找 (x,y) 灰度值(这也将依赖于双线性插值)
  • 一种曲线拟合技术和方法来确定最陡切线以找到边缘点
  • 稳健的线拟合技术:Hough、RANSAC 和/或最小二乘法
  • 线方程的数据结构,相关函数

综上所述,如果您愿意接受轻微的精度损失,并且您知道图像不会遭受径向失真等问题,并且您只需要找到由定义的平行线的角度所有棋盘边缘,那么您可以尝试..

  1. 简单的基于内核的边缘点查找技术(高斯平滑图像上的拉普拉斯算子)
  2. 霍夫线适合边缘点
  3. 选择符合最多票数的两条线,应该是一组水平线和另一组垂直线

还有其他一些不太准确但更容易实现的技术:

  1. 使用基于内核的寻角算子
  2. 找到角点之间的角度。

等等等等。当您开发库并创建独立函数的健壮实现时,您可以将这些函数串在一起创建特定于应用程序的解决方案,您可能会发现健壮的解决方案依赖于比您想象的更多的步骤,但它也会更清楚每个增量步骤的故障模式是什么,以及如何解决该故障模式。

于 2013-09-15T01:01:02.707 回答
0

我不确定它是否(任何接近)正确答案,但我直接反应是两次阈值:一次将除黑色之外的任何东西视为白色,一次将除白色之外的任何东西视为黑色。

找到每个角度,然后在两个角度之间进行插值。

于 2013-09-13T14:49:42.343 回答
0

您的问题几乎没有解决方案,但都有一个您似乎忽略的非常重要的问题。注意:当您尝试在图像中进行几何计算时,您使用的点必须尽可能远离另一个。您在一个正方形内获得 2 分。这些点彼此非常接近,因此点的像素位置的微小误差会导致角度的较大误差。当图像中有很多正方形时,为什么只使用一个正方形?这里有几个解决方案:

  1. 找到每个正方形的线角。图像中至少有 9 个正方形,每个正方形中有 4 条线,总共有 36 个角度(18 个大约是 3°,18 个大约是 93°)。去掉 90[度],你会得到 36 种不同的角度测量值。对它们进行排序并取中间 30 的平均值(忽略较低的 3 和较高的 3 测量值)。这将为您提供准确的结果
  2. 第二种解决方案,找到最左边方块的左极点和最右边方块的右极点。现在计算它们之间的角度。结果将更加准确,因为这些点很远。
  3. 第三种算法将为您提供准确的结果,因为它不涉及查找任何点并且不需要阈值。只需平滑图像,计算 X 和 Y 方向(gx,gy)的梯度,计算每个像素的梯度角度atan(gy,gx)并制作角度直方图。您将在 3° 和 93° 附近有 2 个显着峰值。只需通过搜索直方图中的最大值即可找到峰值。即使图像中有很多噪点,即使有抗锯齿和 jpg 伪影,即使图像上有其他图形,这也将起作用。但请记住,在计算导数之前,您必须对图像进行大量平滑处理。
于 2013-09-14T09:14:16.643 回答
0

请问,你用什么 C++ 库来编写这个代码?

Jerry 是对的,如果您实际上对图像应用阈值,它将是 2 位、黑色或白色。您可能应用的是一种限制器。

您可以通过应用您可能一直在使用的限制器然后将所有非白色像素变为黑色来制作阈值函数(如果您自己编码图像处理)。如果你有正确的设置,正方形应该是孤立的,你将能够计算角度。

完成此操作后,您可以使用路径查找算法来查找某些边缘,任何边缘都可以。如果您发现一条或多或少的直线路径,您可以像现在一样使用极值点来确定角度。由于棋盘旋转仅在 90 度范围内相关,因此您的角度应为模 90 度或 pi 超过 2 弧度。

于 2013-09-13T13:43:41.003 回答