【问题标题】:Detecting the number of pixels between connected objects检测连接对象之间的像素数
【发布时间】:2014-12-23 17:23:02
【问题描述】:

我有一个二值图像,我需要找到连接对象之间的像素数 我正在使用 MATLAB,这是我的图片

谢谢

【问题讨论】:

  • 我回滚到以前的版本。向所有人展示您的图片的样子而不是将其从您的帖子中删除是有益的。

标签: matlab image-processing bitmap pixel connected-components


【解决方案1】:

一个有趣的问题,但并不难解决。它只需要很多代码,但每一行代码的理解都非常简单。这是我将执行的基本算法:

  1. 使用Circular Hough Transform - imfindcircles 查找图像中的所有圆圈
  2. 从图片中删除圆圈
  3. 使用regionprops 计算剩余正方形对象的面积,并确定封装圆形重叠的正方形所需的最小边界框。
  4. 这些最小边界框坐标将是正方形的左上角和右下角坐标如果要完全填充。因为有些圆圈与正方形重叠,所以我们可以通过简单地取每个边界框的全部面积并减去检测到的面积来找到重叠区域。剩下的数量将是与圆圈重叠的区域。
  5. 要找出两个圆圈重叠的区域,我们需要做的就是在图像上绘制两个圆圈并进行逻辑AND操作。这将找到两个圆圈相交的区域。一旦我们有了这些,我们只需计算剩余的像素,这将是两个圆圈的重叠区域。

我将假设您有图像处理工具箱,否则我的方法将不起作用。您还需要至少 MATLAB R2012a 才能将循环霍夫变换包含在您的 MATLAB 发行版中。


步骤#1

我们需要首先反转您的图像,使白色对应于对象像素,黑色对应于背景。否则,regionpropsimfindcircles 将不起作用。我将直接从 StackOverflow 读取您的图像:

im = imread('http://i.stack.imgur.com/WQtgP.png'); %// Already binary!
im = ~im;

接下来,让我们使用圆形霍夫变换。我们会这样使用它:

[centres, radii] = imfindcircles(im, [25 60]);

im 将是我们刚刚转换的二进制图像,数组[25 60] 表示我们要检测的圆的最小和最大半径。在这种情况下,最小半径为 25,最大半径为 60。centresradii 分别表示每个圆的中心的列和行坐标以及每个圆的半径。 centres 的第一列是列坐标,第二列是行坐标。运行此代码后,我们检测到 4 个圆圈(如预期的那样)。 centresradii 看起来像这样:

centres =

  267.5005   67.5005
  233.5152  200.4808
   83.2850   83.2850
  117.6691  118.0193

radii =

   33.3178
   33.1137
   32.9332
   32.8488

这意味着在第 267.50 列第 67.50 行检测到一个圆,半径为 33.3178,从每个结果的第一行读取。您可以跟随每个变量中的其余值。 Circular Hough Transform 的优点在于它可以检测partial圆,由于圆和正方形的重叠,这非常好。如果你想查看检测到的结果,可以像这样使用viscircles

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

我将DrawBackgroundCircle 设置为false,因为当我们绘制检测到的圆圈时,默认情况下它会在圆圈上绘制白色轮廓。我不想让你将它与任何对象像素混淆,所以我将此标志设置为false

这是我们得到的数字:

酷!


步骤 #2

最简单的方法是遍历所有圆圈,创建一个圆形坐标网格,并将图像中的这些像素设置为 false 以从图像中删除这些像素。您可以通过使用meshgrid 创建行和列位置的二维网格并索引到图像以将它们设置为false。像这样的:

[X,Y] = meshgrid(1:size(im,2), 1:size(im,1));

im_no_circles = im;
for idx = 1 : numel(radii)
    r = radii(idx);
    c = centres(idx,:);

    mask = (X - c(1)).^2 + (Y - c(2)).^2 <= r^2;               
    im_no_circles(mask) = false;
end

此代码将获取每个圆圈并将图像中的相应位置设置为false。但是,图像现在的样子是:

由于量化噪声,圆圈中存在一些边缘伪影。我们可以使用bwareaopen 删除这些像素。任何面积低于一定数量的区域,将其从图像中删除。我们可以选择 50 之类的值,因为这些正方形肯定超出了 50 像素的区域,而虚假像素则没有。完成此操作后,让我们使用imopen 进行形态学打开,以消除与每个正方形相连的任何嘈杂像素,这样我们就可以真正得到没有任何圆圈的正方形形状。

所以:

im_no_circles_open = bwareaopen(im_no_circles, 50);
im_open = imopen(im_no_circles_open, strel('square', 5));

这就是我们最终得到的:

步骤#3

我们现在像这样调用regionprops

s = regionprops(im_open, 'Area', 'BoundingBox');

s 将包含一个描述我们每个方形对象的结构。如果我们检查每个属性,这就是我们得到的:

areas = [s.Area].'
bb = reshape([s.BoundingBox], [], numel(areas)).'

areas =

        4178
        4138
        4489

bb =

  134.5000   50.5000   67.0000   67.0000
  150.5000  200.5000   67.0000   67.0000
  334.5000   59.5000   67.0000   67.0000

每个正方形区域都在数组areas 中找到,而bb 是一个数组,其中每个 包含有关每个正方形的信息。具体来说,每一行的第一个和第二个元素是用于完全封装对象的每个边界框的左上角的列坐标和行坐标。第三个和第四个元素是每个边界框的宽度和高度。因此,这告诉我们每个边界框都需要一个 67 x 67 像素的框来完全封装对象。这也等于总面积如果整个边界框已满

步骤#4

MATLAB 检测框的方式是从上到下、从左到右。因此,只有两个框与任何圆圈重叠,因此这是我们需要查看的检测结果中的前两个框。因此,对于左上角的正方形和圆形重叠,总面积为:

overlap1 = bb(1,3)*bb(1,4) - areas(1)

overlap1  =

311

请记住,我们可以通过简单地将正方形的宽度和高度相乘来计算正方形的总面积。当我们用没有圆圈的正方形所占据的实际面积减去它时,我们得到了圆圈所占据的像素。

同样,对于底部的框:

overlap2 = bb(2,3)*bb(2,4) - areas(2)

overlap2  =

351

步骤#5

最后,剩下的是实际的圆圈本身。我们所要做的就是创建一个空白图像,在这个图像中绘制两个圆圈以及一个逻辑 AND 运算符,然后找到剩余部分的总面积。第一步,这两个重叠的圆圈对应:(x,y) = 83.2850, 83.2850(x,y) = 117.6691, 118.0193。这些对应于在圆形霍夫变换中检测到的最后两个圆圈。让我们得到这两个圆圈并创建我们的面具:

这样:

centre1 = centres(3,:);
centre2 = centres(4,:);
radii1 = radii(3);
radii2 = radii(4);
circle1 = (X - centre1(1)).^2 + (Y - centre1(2)).^2 <= radii1^2;
circle2 = (X - centre2(1)).^2 + (Y - centre2(2)).^2 <= radii2^2;
final_two = circle1 & circle2;

如果我们显示这张图片,这就是我们得到的:

这是可视化两个圆圈之间的重叠。现在剩下的就是简单地计算重叠:

overlap3 = sum(final_two(:))

overlap3 = 

515

哇!工作量很大!

编辑

您希望找到与上述两个圆圈相交的区域,但对于图像的其余部分。我们需要找到另外两个相交的区域。第一个位于图像底部的圆形和正方形之间,而第二个位于左上角的正方形和圆形之间。

要获得第一个,只需像我们之前所做的那样创建一个圆形蒙版,并与接触该圆圈的填充方块进行逻辑与运算。底部的圆圈是圆形霍夫变换输出中检测到的第二个圆圈。此外,受影响的方块是从regionprops 检测到的第二个方块。因此:

r = radii(2);
c = centres(2,:);
mask_circle = (X - c(1)).^2 + (Y - c(2)).^2 <= r^2;
mask_square = false(size(im));  
mask_square(floor(bb(2,2)):floor(bb(2,2)) + bb(2,4), floor(bb(2,1)):floor(bb(2,1)) + bb(2,3)) = true;
intersect1 = mask_circle & mask_square;

这是第一个相交区域的样子:


您可以将上述相同的逻辑应用于顶部的其他方形和圆形区域。您只需要选择正确的正方形和圆形。这将是检测到的第四个圆形和第一个检测到的正方形:

r = radii(4);
c = centres(4,:);
mask_circle = (X - c(1)).^2 + (Y - c(2)).^2 <= r^2;
mask_square = false(size(im));  
mask_square(floor(bb(1,2)):floor(bb(1,2)) + bb(1,4), floor(bb(1,1)):floor(bb(1,1)) + bb(1,3)) = true;
intersect2 = mask_circle & mask_square;

这是我们得到的:

【讨论】:

  • 太棒了!!谢谢 !但是如果我想要所有的交叉点怎么办?并再次感谢您提供的出色解决方案!
  • 你的意思是像图中两个圆圈的交叉点吗?
  • 嗯,就像底部的圆和正方形的交点
  • 是的。这需要更多的代码行。我会尽快修改我的帖子
  • 我的荣幸。祝你好运!
【解决方案2】:

在 2 个二进制图像之间找到重叠区域的更简单的解决方案是按元素添加两个图像(在 python 中简单地添加矩阵,我认为在 MATLAB 中是相同的)。所有重叠的像素都将获得 2 的值。对生成的图像应用二进制阈值,阈值设置为 1。这将为您提供重叠区域。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-05
    • 2016-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-04
    相关资源
    最近更新 更多