【问题标题】:Matlab: separate connected componentsMatlab:分离连接的组件
【发布时间】:2015-01-06 23:48:35
【问题描述】:

我正在解决检测硬币的图像处理问题。

我在这里有一些像这样的图像:

并想将错误连接的硬币分开。
我们已经尝试了 MATLAB-Homepage 中所述的分水岭方法:

尤其是因为第一个示例正是我们的问题。

但是,正如您在此处看到的那样,我们得到了一个非常混乱的分离:

我们已经使用regionprops Extrema 参数提取了硬币的区域,并将分水岭仅投射到需要的区域。

我将不胜感激任何有关此问题的帮助,甚至是另一种将其分开的方法。

【问题讨论】:

  • 在应用分水岭算子之前,可以简单地通过对距离图像使用 h-minima 变换 (imhmin) 或其他形式的平滑来解决此问题。链接教程页面中的第三个示例正是这样做的。

标签: matlab image-processing watershed connected-components


【解决方案1】:

如果你有图像处理工具箱,我也可以建议 Circular Hough Transformimfindcircles。但是,这至少需要 R2012a 版本,所以如果你没有它,这将不起作用。

为了完整起见,我假设您拥有它。如果您想保持图像不变,这是一个很好的方法。如果您不知道Hough Transform 是什么,它是一种在图像中查找直线的方法。 圆形霍夫变换是一种特殊情况,旨在找到图像中的圆圈。

圆形霍夫变换的另一个优点是它能够检测图像中的部分圆形。这意味着图像中连接的那些区域,我们可以将它们检测为单独的圆圈。您如何称呼imfindcircles 的方式如下:

[centers,radii] = imfindcircles(A, radiusRange);

A 将是您的对象的二进制图像,radiusRange 是一个二元素数组,用于指定您要在图像中检测到的圆的最小和最大半径。输出是:

  • centersN x 2 数组告诉您在图像中检测到的每个圆心的 (x,y) 坐标 - x 是列,y 是行。
  • radii:对于检测到的每个对应中心,这也给出了检测到的每个圆的半径。这是一个N x 1 数组。

imfindcircles 的其他参数可能对您有用,例如 Sensitivity。更高的灵敏度意味着它能够检测到更不均匀的圆形,例如您在图像中显示的内容。它们不是完美的圆形,但它们是圆形。默认灵敏度为 0.85。我将其设置为 0.9 以获得良好的结果。此外,在处理您的图像时,我发现半径范围从 50 像素到 150 像素。因此,我这样做了:

im = im2bw(imread('http://dennlinger.bplaced.net/t06-4.jpg'));
[centers,radii] = imfindcircles(im, [50 150], 'Sensitivity', 0.9);

第一行代码直接从 StackOverflow 读取图像。我还将其转换为logical 或纯黑白,因为您上传的图像类型为uint8。此图像存储在im。接下来,我们在我们描述的方法中调用imfindcircles

现在,如果我们想可视化检测到的圆圈,只需使用imshow 显示您的图像,然后使用viscircles 在图像中绘制圆圈。

imshow(im);
viscircles(centers, radii, 'DrawBackgroundCircle', false);

viscircles 默认情况下在轮廓上绘制白色背景的圆圈。我想禁用此功能,因为您的图像有白色圆圈,我不想显示错误的轮廓。这就是我用上面的代码得到的:

因此,您可以从中得到centersradii 变量。 centers 会告诉你每个检测到的圆的中心,而radii 会告诉你每个圆的半径是多少。


现在,如果您想模拟 regionprops 正在做什么,我们可以遍历所有检测到的圆圈并将它们物理绘制到 2D 地图上,其中每个圆圈都将由 ID 号标记。因此,我们可以这样做:

[X,Y] = meshgrid(1:size(im,2), 1:size(im,1));
IDs = zeros(size(im));
for idx = 1 : numel(radii)
    r = radii(idx);
    cen = centers(idx,:);

    loc = (X - cen(1)).^2 + (Y - cen(2)).^2 <= r^2;
    IDs(loc) = idx;
end

我们首先使用meshgrid 定义一个矩形网格,并初始化一个与图像大小相同的全零 IDs 数组。接下来,对于每个圆的每对半径和中心,我们定义一个以该点为中心的圆,该圆向外延伸给定半径。然后,我们将它们用作 IDs 数组中的位置,并将其设置为该特定圆圈的唯一 ID。 IDs 的结果将类似于 bwlabel 的输出。因此,如果您想提取idx 圈子所在的位置,您可以:

cir = IDs == idx;

出于演示目的,这就是 IDs 数组在我们缩放 ID 使其适合可见性[0-255] 范围后的样子:

imshow(IDs, []);

因此,每个带有不同灰色阴影的阴影圆圈表示一个独特的圆圈,该圆圈是用imfindcircles 检测到的。

但是,某些硬币的灰色阴影可能有点模糊,因为它与背景融为一体。我们可以将其可视化的另一种方法是将不同的颜色映射应用于 IDs 数组。我们可以尝试使用cool 颜色图,颜色总数为背景的唯一圆圈数+1。因此,我们可以这样做:

cmap = cool(numel(radii) + 1);
RGB = ind2rgb(IDs, cmap);
imshow(RGB);

上面的代码将创建一个颜色映射,以便每个圆圈都映射到cool 颜色映射中的唯一颜色。下一行应用了一个映射,其中每个 ID 都与ind2rgb 的颜色相关联,我们最终显示了图像。

这是我们得到的:

【讨论】:

  • imfindcircles 是图像处理工具箱的一部分。
  • 感谢您的快速回答。我找到了这个功能,我认为这是一种非常优雅的方式。一个更简单的问题仍然存在:我现在想标记连接的组件,所以我想将一个圆圈的所有像素添加到一个连接的组件。我怎样才能做到这一点?
  • 我应该补充一点,我(现在)只是将所有大于我的边距的白色区域添加到连接的组件(bwconncomp),并且只是想拆分/编辑我错误识别的硬币。
  • @AshishUthama doh!其实我是想说。谢谢!
  • @dennlinger 这需要我现在无法进行的编辑。去上班的途中。我会尽快编辑!
【解决方案2】:

编辑:以下解决方案更适合不需要拟合精确周长的情况,尽管可以使用简单的启发式方法根据在原始图像中找到的中心来近似原始图像中硬币的半径腐蚀了一个。

假设您可以访问图像处理工具箱,请在原始黑白图像上尝试imerode。它将对您的图像应用侵蚀形态算子。事实上,Matlab webpage with the documentation of that function 有一个与您的问题/图像非常相似的示例,它们使用磁盘结构。

假设您提交的图像名为 ima.jpg 并且是代码的本地图像,请运行以下代码(基于上面链接的示例):

ima=imread('ima.jpg');
se = strel('disk',50);
eroded = imerode(ima,se);
imshow(eroded)

您将看到以下图像作为输出。完成此操作后,您可以使用 bwlabel 标记连接的组件并计算您可能想要的任何属性,例如,计算硬币的数量或检测它们的中心。

【讨论】:

  • 首先:感谢您的快速回答。我们已经对 imerode/imdilate 函数进行了一些实验,但我仍然不完全确定它是否适合我的情况,因为似乎我只会将那些像素放入连接的组件中,这些像素在 imerode 之后仍然是白色的,对吧?所以从技术上讲,我会得到,说真正是白点的 70%,还是我错了?我应该指定我需要大部分像素,因为我想自动识别不同类型的硬币。
  • 是的,您没有指定您需要原始问题中的像素。你介意我问为什么你需要大部分像素吗?该运算符似乎大致保留了硬币之间的相对区域,因此您可以根据知道其中一枚硬币从原始图像缩小到侵蚀图像的程度来估计一般收缩率(或将其平均在几枚硬币上以获得平滑估计),然后按该比例缩放收缩的连接组件区域。
  • 但是,由于您有 imfindcircles 可用,您可能希望坚持其他解决方案并在极地空间工作。
  • 我们实际上尝试了您的方法并提出了相同的想法,但我们得到了相当宽的收缩率(从〜2.5-4.5,取决于硬币),但我仍然感谢您的回答!
猜你喜欢
  • 2016-01-09
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
  • 1970-01-01
  • 2018-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多