我建议您使用圆形霍夫变换imfindcircles。但是,您需要图像处理工具箱的版本 8,该版本从 R2012a 及更高版本开始提供。如果您没有这个,那么不幸的是,这将不起作用:(...但让我们假设您确实有它。但是,如果您使用的是早于 R2012a 的东西,他/她的 Dev-iL上面的评论链接到 MATLAB 文件交换上的一些代码,这些代码很可能是在圆形霍夫变换可用之前创建的:http://www.mathworks.com/matlabcentral/fileexchange/9168-detect-circles-with-various-radii-in-grayscale-image-via-hough-transform/
这是霍夫变换的一个特例,您试图在图像中找到圆圈而不是直线。这样做的好处是,即使圆是部分完成或重叠的,您也能够找到圆。
我将拍摄您上面提供的图像并对其进行一些后期处理。我将把图像转换为二进制,并删除边框,边框是白色的,包含标题。我还将填充由此产生的任何洞,以便所有对象都填充为纯白色。执行此步骤后,还会产生一些残余量化噪声,因此我将使用一个 3 x 3 方形元素的小开口。之后,我将使用 3 x 3 方形元素来闭合形状,因为我发现形状中有明显的间隙。所以:
因此,直接从您发布的位置读取您的图像:
im = imread('http://s29.postimg.org/spkab8oef/image.jpg'); %// Read in the image
im_gray = im2double(rgb2gray(im)); %// Convert to grayscale, then [0,1]
out = imclearborder(im_gray > 0.6); %// Threshold using 0.6, then clear the border
out = imfill(out, 'holes'); %// Fill in the holes
out = imopen(out, strel('square', 3));
out = imclose(out, strel('square', 3));
这是我得到的图像:
现在,应用圆形霍夫变换。其一般语法是:
[centres, radii, metric] = imfindcircles(img, [start_radius, end_radius]);
img
将是包含您的形状的二值图像,start_radius
and end_radius
将是您想要找到的圆的最小和最大半径。执行圆形霍夫变换,以便找到此范围(以像素为单位)内的任何圆形。输出是:
-
centres
:返回(x,y)
检测到的每个圆的中心位置
-
radii
:每个圆的半径
-
metric
:圆的纯度的度量。值越高意味着形状更有可能是圆形,反之亦然。
我搜索了半径在 30 到 60 像素之间的圆。所以:
[centres, radii, metric] = imfindcircles(out, [30, 60]);
然后我们可以通过组合来展示检测到的圆以及半径plot
and viscircles。所以:
imshow(out);
hold on;
plot(centres(:,1), centres(:,2), 'r*'); %// Plot centres
viscircles(centres, radii, 'EdgeColor', 'b'); %// Plot circles - Make edge blue
结果如下:
正如您所看到的,即使重叠的圆圈朝向顶部,圆形霍夫变换也能够检测到该形状中的两个不同的圆圈。
编辑 - 2014 年 11 月 16 日
您希望确保在执行此操作之前将对象分开bwboundaries
。这做起来有点棘手。我能看到你这样做的唯一方法就是你甚至不使用bwboundaries
完全并且自己做这件事。我假设您在完成所有这些操作后想要单独分析每个形状的属性,所以我建议您迭代您拥有的每个圆圈,然后将每个圆圈放在一个新的空白图像上,执行regionprops
调用该形状,然后将其附加到一个单独的数组中。您还可以通过一个单独的数组来跟踪所有圆,该数组一次将一个圆添加到该数组中。
完成所有圆后,您将获得一个结构数组,其中包含您找到的所有测量圆的所有测量属性。您将使用仅包含上方圆圈的数组,然后使用它们并将它们从原始图像中删除,这样您就只得到线条。然后你再打一个电话regionprops
在此图像上获取线条信息并将其附加到最终的结构数组中。
这是我上面概述的过程的第一部分:
num_circles = numel(radii); %// Get number of circles
struct_reg = []; %// Save the shape analysis per circle / line here
%// For creating our circle in the temporary image
[X,Y] = meshgrid(1:size(out,2), 1:size(out,1));
%// Storing all of our circles in this image
circles_img = false(size(out));
for idx = 1 : num_circles %// For each circle we have...
%// Place our circle inside a temporary image
r = radii(idx);
cx = centres(idx,1); cy = centres(idx,2);
tmp = (X - cx).^2 + (Y - cy).^2 <= r^2;
% // Save in master circle image
circles_img(tmp) = true;
%// Do regionprops on this image and save
struct_reg = [struct_reg; regionprops(tmp)];
end
上面的代码可能有点难以理解,但是让我们慢慢看一下。我首先计算出我们有多少个圆,这只是看看我们检测到了多少个半径。我保留一个单独的数组,称为struct_reg
这将附加一个regionprops
struct
对于图像中的每个圆圈和线条。我用meshgrid以确定(X,Y)
相对于包含我们的形状的图像进行坐标,以便我可以在每次迭代时在空白图像上绘制一个圆圈。为此,您只需找到相对于每个圆中心的欧几里得距离,并将像素设置为true
仅当该位置的距离小于r
。执行此操作后,您将只创建一个圆圈并将其全部过滤掉。然后你会使用regionprops
在此圈子上,将其添加到我们的circles_img
数组,其中仅包含圆圈,然后继续处理其余的圆圈。
至此,我们就已经保存了所有的圈子。这是什么circles_img
到目前为止看起来像:
您会注意到绘制的圆圈很干净,但原始图像中的实际圆圈有点锯齿状。如果我们尝试用这个干净的图像删除圆圈,您将在边界上得到一些残留像素,并且您不会完全删除圆圈本身。为了说明我的意思,如果我尝试使用以下命令删除圆圈,这就是您的图像的样子circles_img
通过它自己:
……不太好吧?
如果你想完全去除圆圈,那么通过进行形态学重建imreconstruct您可以在其中使用此图像作为种子图像,并指定原始图像是我们正在处理的图像。形态重建的工作本质上是洪水填充。您指定种子像素、要处理的图像以及工作imreconstruct
从这些种子开始,用白色进行洪水填充,直到我们到达种子像素所在的对象的边界。因此:
out_circles = imreconstruct(circles_img, out);
因此,我们得到了最终重建的圆形图像:
伟大的!现在,使用它并从原始图像中删除圆圈。一旦你这样做了,运行regionprops
再次在这最终图像上并附加到您的struct_reg
多变的。显然,在执行此操作之前保存原始图像的副本:
out_copy = out;
out_copy(out_circles) = false;
struct_reg = [struct_reg; regionprops(out_copy)];
只是为了便于讨论,这就是删除圆圈后图像的样子:
现在,我们已经分析了所有的形状。请记住我已经完成了完整的regionprops
打电话是因为我不知道你在分析中到底想要什么......所以我决定给你一切。
希望这可以帮助!