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

时间:2014-10-13 04:25:44

标签: image matlab image-processing binary 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

我会有这样的事情:

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 个答案:

答案 0 :(得分:15)

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

因此,假设您的矩阵存储在A中,这是基本算法:

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

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

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

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

    ℃。如果我们没有访问过这个位置......请转到步骤#3。

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

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

    湾将此位置弹出堆栈

    ℃。如果我们访问了此位置,请继续。

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

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

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

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

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

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

  6. 不用多说,这是代码。


    %// 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顺序搜索连接的组件。上面的代码按行major或C顺序搜索。因此,您只需要按照上面的代码执行的方式首先搜索列而不是行,然后通过交换两个for循环的顺序来执行此操作。

    具体而言,而不是:

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

    你会这样做:

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

    现在应该复制bwlabel的输出。