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

Pep*_*pez 7 binary matlab image image-processing connected-components

我一直在尝试使用二进制图像中的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
Run Code Online (Sandbox Code Playgroud)

我会有这样的事情:

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
Run Code Online (Sandbox Code Playgroud)

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

ray*_*ica 15

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

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

  1. 初始化一个数组是这样的大小相同Alogical.这将记录我们检查或访问过的像素.同时将输出数组初始化B为全零,为您提供所需的所有连接组件.最后为零的任何位置都不属于任何连接的组件.同时初始化一个ID计数器,用于跟踪每个连接组件标签的内容.

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

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

    湾 如果我们已访问过此位置,请继续.

    C.如果我们没有访问过此位置...请转到步骤3.

  3. 将此未访问的位置添加到堆栈.

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

    湾 将此位置弹出堆栈

    C.如果我们访问过此位置,请继续.

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

    即 给定此位置,查看8个相邻像素.

    F.删除此列表中已访问过的那些像素,不等于1或超出矩阵的范围

    G.无论剩下哪个位置,都要将它们添加到堆栈中.

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

  5. 继续前行,直到我们访问了阵列中的所有位置.

不用多说,这是代码.


%// 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   
Run Code Online (Sandbox Code Playgroud)

使用您的示例矩阵,这是我得到的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
Run Code Online (Sandbox Code Playgroud)

搜索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(:);
Run Code Online (Sandbox Code Playgroud)

要以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];
Run Code Online (Sandbox Code Playgroud)

其余代码保持不变.

匹配MATLAB的bwlabel功能

如果要匹配MATLAB bwlabel函数的输出,请bwlabel按列major或FORTRAN顺序搜索连接的组件.上面的代码按行major或C顺序搜索.因此,您只需要按照上面的代码执行的方式首先搜索列而不是行,然后通过交换两个for循环的顺序来执行此操作.

具体来说,而不是做:

for row = 1 : rows
    for col = 1 : cols
        ....
        ....
Run Code Online (Sandbox Code Playgroud)

你会这样做:

for col = 1 : cols
    for row = 1 : rows
        ....
        ....
Run Code Online (Sandbox Code Playgroud)

这应该现在复制输出bwlabel.

  • 你的代码是惊人的,它做我真正想要的.这有点难以理解,但我明白了,你是一位优秀的程序员,非常感谢你. (2认同)