代码之家  ›  专栏  ›  技术社区  ›  illumi

边缘检测/角度

  •  1
  • illumi  · 技术社区  · 12 年前

    我可以成功地对图像进行阈值设置,并在图像中找到边缘。我正在努力准确地提取黑边的角度。

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

    示例图像:

    Wonky Checker board

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

    Gimp Measurement tool

    4 回复  |  直到 12 年前
        1
  •  5
  •   Community Mohan Dere    9 年前

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

    尽管有一些简单的基于内核的技术可以将整个像素作为边缘像素,但在处理填充多边形时,您会希望使用能够以亚像素精度找到边缘的算法,以便执行精确的线拟合。即使从暗正方形到白正方形的梯度跨越了几个像素,“真实”边缘也会在某个子像素点找到,很可能不是你手动点击猜到的点。

    我试图在这篇旧的SO帖子中提供一个关于边缘发现的简单摘要: what is the relationship between image edges and gradient?

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

    如果你想找到一条边的精确拟合,那么最好在垂直于该边的方向上扫描子像素边。这就需要我们从一开始就对边缘方向有一些合理的估计。我们可以首先找到边缘方向的粗略估计,然后进行精确的线拟合。

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

    1. 对黑色像素执行几次侵蚀迭代,以将黑盒彼此分离。
    2. 运行连接组件算法(斑点查找算法)来查找被侵蚀的黑色方块。
    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方法值得一试
    • 连接组件算法(也称为斑点查找,或OpenCV轮廓函数)
    • blob的数据结构
    • 斑点数据的力矩计算
    • 双线性插值以查找子像素(x,y)值
    • 一种线性射线扫描技术,用于沿特定方向找到(x,y)灰度值(也将依赖于双线性插值)
    • 确定最陡切线以找到边点的曲线拟合技术和方法
    • 稳健的直线拟合技术:Hough、RANSAC和/或最小二乘法
    • 线性方程的数据结构,相关函数

    话虽如此,如果你愿意接受精度的轻微损失,如果你知道图像没有径向失真等,如果你只需要找到由所有复选框边缘定义的平行线的角度,那么你可以尝试。。

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

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

    1. 使用基于内核的角点查找运算符
    2. 找出角点之间的角度。

    等等。当你开发你的库并创建独立函数的健壮实现时,你可能会发现健壮的解决方案依赖的步骤比你想象的要多,但也会更清楚每个增量步骤的失败模式是什么,以及如何解决这种故障模式。

        2
  •  0
  •   r3flss ExlUtr    12 年前

    我能问一下,你用什么C++库来编码这个吗?

    Jerry是对的,如果你真的对图像应用一个阈值,它将是2位的,黑色或白色。你可能已经应用了一种限制器。

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

    完成后,你可以使用路径查找算法找到一些边,任何边都可以。如果你找到了一条或多或少直的路径,你可以像现在这样使用极值点来确定角度。由于棋盘旋转仅在90度内相关,因此角度应为90度的模或2弧度以上的π。

        3
  •  0
  •   Jerry Coffin    12 年前

    我不确定它是否(接近) 这个 正确的答案,但我的第一反应是两次阈值:一次是将除黑色以外的任何东西都视为白色,另一次是对除白色以外的任何事物都视为黑色。

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

        4
  •  0
  •   DanielHsH    12 年前

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

    1. 找出每个正方形的线角度。图像中至少有9个正方形,每个正方形中有4条线,总共有36个角度(18个大致为3°,18个约为93°)。去掉90度,就可以得到36个不同的角度测量值。对它们进行排序,并取中间30的平均值(忽略较低的3个和较高的3个测量值)。这会给你一个准确的结果
    2. 第二个解,找到最左边正方形的左极值点和最右边正方形的右极值点。现在计算它们之间的角度。结果会更加准确,因为点离得很远。
    3. 第三种算法会给你准确的结果,因为它不需要找到任何点,也不需要阈值。只需平滑图像,计算X和Y方向的梯度 (gx,gy) ,计算每个像素中的梯度角度 atan(gy,gx) 并制作角度的直方图。在3[deg]和93[deg]附近会有两个显著的峰值。只需搜索直方图中的最大值即可找到峰值。即使图像中有很多噪波,即使有抗锯齿和jpg伪影,即使图像上有其他图形,这也会起作用。但请记住,在计算导数之前,必须对图像进行大量平滑处理。