Matlab:分离连接的组件

Matlab: separate connected components

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

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

想把假连接的币分开。
我们已经尝试了 MATLAB-Homepage:

中所述的分水岭方法

特别是因为第一个例子正是我们的问题。

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

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

对于解决该问题的任何帮助,甚至是将其分开的其他方法,我将不胜感激。

编辑:以下解决方案更适用于不需要拟合精确周长的情况,尽管可以使用简单的启发式算法来根据在被侵蚀的中心发现中心。

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

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

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

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

如果你有图像处理工具箱,我也可以建议 Circular Hough Transformimfindcircles。但是,这至少需要 R2012a 版本,因此如果您没有,则无法使用。

为了完整起见,我假设您已经拥有它。如果您想保持图像不变,这是一个很好的方法。如果您不知道 Hough Transform 是什么,它是一种在图像中查找直线的方法。 circular Hough Transform 是一个特例,旨在寻找图像中的圆。

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

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

A 将是对象的二值图像,radiusRange 是一个 two-element 数组,用于指定要在图像中检测的圆的最小和最大半径。输出是:

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

您可能会发现 imfindcircles 的其他参数很有用,例如 Sensitivity。更高的灵敏度意味着它能够检测到更多 non-uniform 的圆形,例如您在图像中显示的形状。它们不是完美的圆形,但它们是圆形。默认灵敏度为 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);

第一行代码直接从 Whosebug 读取图像。我还将其转换为 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 and initialize an IDs array of all zeroes that is the same size as the image. Next, for each pair of radii and centres for each circle, we define a circle that is centered at this point that extends out for the given radius. We then use these as locations into the IDs array and set it to a unique ID for that particular circle. The result of IDs will be that which resembles the output of bwlabel 定义一个矩形点网格。因此,如果你想提取 idx 圆所在的位置,你可以这样做:

cir = IDs == idx;

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

imshow(IDs, []);

因此,每个不同灰色阴影的阴影圆圈表示使用 imfindcircles 检测到的唯一圆圈。

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

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

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

这是我们得到的: