如何在 Matlab 中找到二进制图像中的所有连通分量?

Posted

技术标签:

【中文标题】如何在 Matlab 中找到二进制图像中的所有连通分量?【英文标题】:How to find all connected components in a binary image in Matlab? 【发布时间】:2014-12-07 14:21:51 【问题描述】:

我一直在尝试使用二进制图像中的 8 个邻居来查找所有连接的组件,而不使用函数“bwlabel”。

比如我的输入矩阵是:

a =

     1     1     0     0     0     0     0
     1     1     0     0     1     1     0
     1     1     0     0     0     1     0
     1     1     0     0     0     0     0
     0     0     0     0     0     1     0
     0     0     0     0     0     0     0

我想要这样的东西:

a =

     1     1     0     0     0     0     0
     1     1     0     0     2     2     0
     1     1     0     0     0     2     0
     1     1     0     0     0     0     0
     0     0     0     0     0     3     0
     0     0     0     0     0     0     0

此图像中有 3 个连接的对象。

【问题讨论】:

【参考方案1】:

这是图像处理中的常见问题。有许多变化,例如填充图像中的区域,或者查找哪些像素属于同一区域。一种常见的方法是使用depth first search。这个想法是你从左到右和从上到下遍历你的图像,对于遇到的任何等于 1 的像素,你将它们添加到堆栈中。对于堆栈中的每个像素,您从堆栈中弹出,然后查看围绕该像素的相邻像素。您添加到堆栈中的任何 1 像素。您需要在您已经访问过的任何像素处保留一个附加变量,不要将它们添加到堆栈中。当堆栈为空时,我们已经找到了整个区域的那些像素,因此您使用唯一 ID 标记这些像素。然后重复此过程,直到图像中的区域用完为止。

因此,鉴于您的矩阵存储在A,这是基本算法:

    初始化一个与A 大小相同的数组,即logical。这将记录我们检查或访问过的像素。还将输出数组B 初始化为全零,为您提供您正在寻找的所有连接组件。任何最终为零的位置都不属于任何连通分量。还要初始化一个 ID 计数器,用于跟踪每个连接组件的标签。

    对于我们矩阵中的每个位置:

    一个。如果位置是0,则将此位置标记为已访问并继续。

    b.如果我们已经访问过这个位置,那么继续。

    c。如果我们还没有访问过这个位置......请转到第 3 步。

    将此未访问的位置添加到堆栈中。

    一个。虽然这个堆栈不是空的......

    b.从堆栈中弹出这个位置

    c。如果我们访问过此位置,请继续。

    d。否则,将此位置标记为已访问并使用连接组件 ID 标记此位置。

    e。给定这个位置,查看 8 个相邻像素。

    f。移除这个列表中那些被访问过的像素,不等于1或者超出矩阵的范围

    g.无论剩下什么位置,都将它们添加到堆栈中。

    一旦堆栈为空,递增计数器,然后返回步骤#2。

    继续直到我们访问了数组中的所有位置。

废话不多说,代码如下。


%// Step #1
visited = false(size(A));
[rows,cols] = size(A);
B = zeros(rows,cols);
ID_counter = 1;

%// Step 2
%// For each location in your matrix...
for row = 1 : rows
    for col = 1 : cols
        %// Step 2a
        %// If this location is not 1, mark as visited and continue
        if A(row,col) == 0
            visited(row,col) = true;

        %// Step 2b
        %// If we have visited, then continue
        elseif visited(row,col)
            continue;

        %// Step 2c
        %// Else...
        else
            %// Step 3
            %// Initialize your stack with this location
            stack = [row col];

            %// Step 3a
            %// While your stack isn't empty...
            while ~isempty(stack)
                %// Step 3b
                %// Pop off the stack
                loc = stack(1,:);
                stack(1,:) = [];

                %// Step 3c
                %// If we have visited this location, continue
                if visited(loc(1),loc(2))
                    continue;
                end

                %// Step 3d
                %// Mark location as true and mark this location to be
                %// its unique ID
                visited(loc(1),loc(2)) = true;
                B(loc(1),loc(2)) = ID_counter;

                %// Step 3e
                %// Look at the 8 neighbouring locations
                [locs_y, locs_x] = meshgrid(loc(2)-1:loc(2)+1, loc(1)-1:loc(1)+1);
                locs_y = locs_y(:);
                locs_x = locs_x(:);

                 %%%% USE BELOW IF YOU WANT 4-CONNECTEDNESS
                 % See bottom of answer for explanation
                 %// Look at the 4 neighbouring locations
                 % locs_y = [loc(2)-1; loc(2)+1; loc(2); loc(2)];
                 % locs_x = [loc(1); loc(1); loc(1)-1; loc(1)+1];

                %// Get rid of those locations out of bounds
                out_of_bounds = locs_x < 1 | locs_x > rows | locs_y < 1 | locs_y > cols;

                locs_y(out_of_bounds) = [];
                locs_x(out_of_bounds) = [];

                %// Step 3f
                %// Get rid of those locations already visited
                is_visited = visited(sub2ind([rows cols], locs_x, locs_y));

                locs_y(is_visited) = [];
                locs_x(is_visited) = [];

                %// Get rid of those locations that are zero.
                is_1 = A(sub2ind([rows cols], locs_x, locs_y));
                locs_y(~is_1) = [];
                locs_x(~is_1) = [];

                %// Step 3g
                %// Add remaining locations to the stack
                stack = [stack; [locs_x locs_y]];
            end

            %// Step 4
            %// Increment counter once complete region has been examined
            ID_counter = ID_counter + 1;
        end
    end %// Step 5
 end   

使用您的示例矩阵,这就是我得到的B

B =

     1     1     0     0     0     0     0
     1     1     0     0     2     2     0
     1     1     0     0     0     2     0
     1     1     0     0     0     0     0
     0     0     0     0     0     3     0
     0     0     0     0     0     0     0

在 4 连接的社区中搜索

修改代码在一个4连通区域内搜索,即只有北东西南,看到%// Look at the 8 neighbouring locations的部分,即:

 %// Look at the 8 neighbouring locations
 [locs_y, locs_x] = meshgrid(loc(2)-1:loc(2)+1, loc(1)-1:loc(1)+1);
 locs_y = locs_y(:);
 locs_x = locs_x(:);

要以 4 连接方式进行搜索,您只需修改此代码以仅给出这些基本方向:

 %// Look at the 4 neighbouring locations
locs_y = [loc(2)-1; loc(2)+1; loc(2); loc(2)];
locs_x = [loc(1); loc(1); loc(1)-1; loc(1)+1];

其余代码保持不变。

匹配 MATLAB 的 bwlabel 函数

如果您想匹配 MATLAB 的 bwlabel 函数的输出,bwlabel 按列主要或 FORTRAN 顺序搜索连通分量。上面的代码按行主要或 C 顺序搜索。因此,您只需要首先搜索列而不是行,就像上面的代码所做的那样,您可以通过交换两个 for 循环的顺序来做到这一点。

具体来说,而不是这样做:

for row = 1 : rows
    for col = 1 : cols
        ....
        ....

你会这样做:

for col = 1 : cols
    for row = 1 : rows
        ....
        ....

现在应该会复制bwlabel 的输出。

【讨论】:

你的代码太棒了,它完成了我真正想要的。有点难懂,但我明白了,你是一个优秀的程序员,再次感谢。 不错的代码。您能否告诉我如何在数组中的图中显示所有路径。例如:路径将如下所示:path_1 =[3,2,6,7,4,8,5]; @kgk 我不知道你在问什么。请使用我在上面的答案中给出的示例给我预期的输出。

以上是关于如何在 Matlab 中找到二进制图像中的所有连通分量?的主要内容,如果未能解决你的问题,请参考以下文章

对图像中连通域进行标记并计算面积matlab

在matlab中,怎么把图片中的图像轮廓坐标找出来?

如何在 MATLAB 中反转二进制图像?

matlab联通域都长度和宽度

matlab 如何消除信号的高频分量

matlab用代码生成的图片如何显示误差系数