Matlab:分离连接的组件

4
我正在处理有关检测硬币的图像处理问题。
我有一些像这样的图片:Binary components,并且想要将错误连接的硬币分开。
我们已经尝试过MATLAB主页上提到的分水岭方法:the-watershed-transform-strategies-for-image-segmentation.html,特别是第一个例子恰好是我们的问题。
但是我们得到了一种非常混乱的分离结果,如下所示:enter image description here
我们已经使用regionprops极值参数提取了硬币的区域,并仅对需要的区域进行了分水岭操作。
我会感激任何解决问题的帮助或其他分离方法。

1
这个问题可以通过在距离图像上使用h-minima变换(imhmin)或其他形式的平滑处理,然后再应用分水岭算子来简单解决。链接教程页面中的第三个示例正是这样做的。 - Cris Luengo
2个回答

3
如果您拥有图像处理工具箱,我还可以建议通过imfindcircles使用圆形哈夫变换。然而,这需要至少R2012a版本,因此如果您没有它,这将无法工作。
为了完整起见,我将假设您已经安装了它。如果您不知道霍夫变换是什么,那么它是一种在图像中查找直线的方法。圆形霍夫变换是一个特殊情况,旨在查找图像中的圆。
圆形霍夫变换的附加优势是它能够检测到图像中的部分圆。这意味着我们可以将连接的图像区域检测为单独的圆。您将如何调用imfindcircles,请参考以下方式:
[centers,radii] = imfindcircles(A, radiusRange);
A是您的对象二进制图像,radiusRange是一个二元数组,指定您要在图像中检测的圆的最小和最大半径。输出结果为:
  • centers:一个 N 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中读取您的图像。我还将其转换为逻辑或真正的黑白,因为您上传的图像是uint8类型。这个图像存储在im中。接下来,我们按照所描述的方法调用imfindcircles
现在,如果我们想要可视化检测到的圆,只需使用imshow显示您的图像,然后使用viscircles在图像中绘制圆。
imshow(im);
viscircles(centers, radii, 'DrawBackgroundCircle', false);

viscircles默认在轮廓上方用白色背景绘制圆圈。我想要禁用这个选项,因为图片已经有了白色的圆圈,我不想显示虚假的轮廓。使用以上代码得到的结果如下:

enter image description here

因此,你可以从中获得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定义一个矩形点网格,并初始化一个大小与图像相同的全零ID数组。接下来,对于每个圆的半径和中心点,我们定义以该点为中心向外延伸给定半径的圆。然后将这些位置用作ID数组中的位置,并将其设置为该特定圆的唯一ID。 IDs的结果将类似于bwlabel的输出。因此,如果您想要提取idx圆的位置,您可以执行以下操作:
cir = IDs == idx;

为了演示,这是当我们将ID缩放以适合可见的[0-255]范围时,IDs数组的样子:

imshow(IDs, []);

enter image description here

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

然而,对于某些硬币来说,灰度可能有点模糊,因为它与背景混合在一起。我们可以尝试将不同的颜色映射应用于ID数组以可视化它们。我们可以尝试使用cool颜色映射,并将总颜色数设置为唯一圆的数量+1(包括背景)。因此,我们可以像这样做:

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

以上代码将创建一个颜色映射,使得每个圆都被映射到cool颜色映射中的唯一颜色。接下来的代码行应用了一个映射,其中每个ID都与ind2rgb中的一种颜色相关联,并最终显示图像。
我们得到的结果如下所示:

enter image description here


imfindcircles是图像处理工具箱的一部分。 - Ashish Uthama
谢谢您的快速回答。我找到了这个函数,我认为它是一种相当优雅的方式。还有一个更简单的问题:我现在想要标记连接的组件,所以我想将圆中的所有像素添加到一个连接的组件中。我该如何实现这一点? - dennlinger
我应该补充说明,目前我只是将所有大于我的边距的白色区域添加到连接组件(bwconncomp)中,并且只想拆分/编辑我错误识别的硬币。 - dennlinger
@AshishUthama 哎呀!我本来就是想这么说的。谢谢! - rayryeng
@dennlinger 那需要一次我现在无法完成的编辑。我正在上班的路上。我很快就会编辑! - rayryeng
@dennlinger - 完成。祝你好运! - rayryeng

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

假设您可以访问图像处理工具箱,请尝试在原始的黑白图像上使用imerode。它将对您的图像应用一种侵蚀形态学运算符。实际上,Matlab网页与该函数文档中有一个与您的问题/图像非常相似的示例,并且他们使用了一个圆盘结构。

运行以下代码(基于上面链接的示例),假设您提交的图像名为ima.jpg并且与代码在同一目录下:

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

然后,你将会看到紧随其后的图像作为输出。在你这样做之后,你可以使用bwlabel来标记连接的组件并计算任何你想要的属性,例如,计算硬币的数量或检测它们的中心。

enter image description here


首先,感谢您的快速回复。我们已经尝试了一下imerode/imdilate函数,但我仍然不确定它是否适合我的情况,因为似乎在imerode之后,我只会得到那些仍然是白色的像素连接组件,对吗?所以从技术上讲,我会得到实际上是白点的70%左右,或者我错了吗?我应该明确指出我需要大多数像素,因为我想自动区分不同类型的硬币。 - dennlinger
是的,您在最初的问题中没有指定需要像素。请问您需要大部分像素的原因是什么?这个运算符似乎粗略地保持了硬币之间的相对面积,因此您可以根据知道一个硬币从原始图像到侵蚀图像缩小了多少来估计一般的收缩比率(或者平均几个硬币来获得平滑的估计),然后按比例缩小连接组件区域。 - Lord Henry Wotton
然而,由于您可以使用imfindcircles,因此您可能希望坚持使用其他解决方案并在极坐标空间中工作。 - Lord Henry Wotton
我们实际上尝试了你的方法,并得出了类似的想法,但我们得到了相当大的收缩比率(从约2.5-4.5,取决于硬币),但我仍然感谢你的回答! - dennlinger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接