二值图连通域检测

  • 参考
    https://cloud.tencent.com/developer/article/1680644

  • 文中提到有两种方法 ,重点研究了Two-pass

  • 如果将图换成如下的0-1方式,不是0-255方式

     binary_img = np.zeros((4,7), dtype=np.int16)
     index= [[0,2],[0,5],
             [1,0],[1,1],[1,2],[1,4],[1,5],[1,6],
             [2,2],[2,5],
             [3,1],[3,2],[3,4],[3,6]]
     for i in index:
         binary_img [i[0],i[1]] = np.int16(1)
    
  • 则作者给出的代码几乎不能使用,如下所示

    def neighbor_value(binary_img:np.array, offsets,reverse=False):
        rows,cols= binary_img.shape
        label_idx= 0
        rows_ =[0,rows,1] if reverse==False else [rows-1,-1,-1]
        cols_ = [0,cols,1] if reverse==False else [cols-1,-1,-1]
    
        for row in range(rows_[0],rows_[1],rows_[2]):
            for col in range(cols_[0],cols_[1],cols_[2]):
                if binary_img[row][col] < 0.5 : continue
                minlabel=256
                for offset in offsets:
                    neighbor_row= min(max(0,row+offset[0]), rows-1)
                    neighbor_col = min(max(0,col+offset[1]),cols-1)
                    neighbor_val = binary_img[neighbor_row,neighbor_col]
                    if neighbor_val  <0.5 : continue
                    if neighbor_val < minlabel: minlabel = neighbor_val
                if minlabel== 255:
                    label_idx+=1
                    minlabel = label_idx
                 
                binary_img[row][col] = minlabel
        return binary_img
    
  • if minlabel== 255这句包含了太多的中间逻辑
    1.假定了连通域个数小于255,一般也够用了
    2.3X3当前块内可能有多个255 其他都是0
    3.没有写出来的else 则说明取的是八邻域中非255的元素的最小值 是minlabel

  • OK , 用0-1 binary_iamge来实现下 ,要做的是展开上述逻辑

def neighbor_value(binary_img:np.array, offsets,reverse=False):
    rows,cols= binary_img.shape
    label_idx= 1 #从2号块开始赋值
    rows_ =[0,rows,1] if reverse==False else [rows-1,-1,-1]
    cols_ = [0,cols,1] if reverse==False else [cols-1,-1,-1]

    for row in range(rows_[0],rows_[1],rows_[2]):
        for col in range(cols_[0],cols_[1],cols_[2]):
            if binary_img[row][col] < 0.5 : continue
             
            Neighbors=[]
            for offset in offsets:
                neighbor_row= min(max(0,row+offset[0]), rows-1)
                neighbor_col = min(max(0,col+offset[1]),cols-1)
                neighbor_val = binary_img[neighbor_row,neighbor_col]
                if neighbor_val  <0.5 : continue
                Neighbors.append(neighbor_val)
              
            toLabel= 0
            #here len(Neighbors) >=0
            #包含多个1 和一个1 的情况
            #第一次遍历时候标号从正向递增 ,反向再次遍历时候 Neighbors的最小值为2 
            #即只走else分支
            if max(Neighbors) ==1:
                    label_idx+=1
                    toLabel=label_idx
            else :
                #根据数学原理, Neighbors所有元素非0 肯定有大于1的元素就是最小号块
                #排开1 的所有非0元素的最小值
                toLabel=min([x for x in Neighbors if x !=1  ])
            binary_img[row][col] = toLabel
    return binary_img
  • 下面是完整的python代码
OFFSET_8 = [[-1,-1],[0,-1],[1,-1],
            [-1,0],[0,0],[1,0],
           [-1,1],[0,1],[1,1],
    ]
def neighbor_value(binary_img:np.array, offsets,reverse=False):
    rows,cols= binary_img.shape
    label_idx= 1 #从2号块开始赋值
    rows_ =[0,rows,1] if reverse==False else [rows-1,-1,-1]
    cols_ = [0,cols,1] if reverse==False else [cols-1,-1,-1]

    for row in range(rows_[0],rows_[1],rows_[2]):
        for col in range(cols_[0],cols_[1],cols_[2]):
            if binary_img[row][col] < 0.5 : continue
             
            Neighbors=[]
            for offset in offsets:
                neighbor_row= min(max(0,row+offset[0]), rows-1)
                neighbor_col = min(max(0,col+offset[1]),cols-1)
                neighbor_val = binary_img[neighbor_row,neighbor_col]
                if neighbor_val  <0.5 : continue
                Neighbors.append(neighbor_val)
              
            toLabel= 0
            #here len(Neighbors) >=0
            #包含多个1 和一个1 的情况
            #第一次遍历时候标号从正向递增 ,反向再次遍历时候 Neighbors的最小值为2 
            #即只走else分支
            if max(Neighbors) ==1:
                    label_idx+=1
                    toLabel=label_idx
            else :
                #根据数学原理, Neighbors所有元素非0 肯定有大于1的元素就是最小号块
                #排开1 的所有非0元素的最小值
                toLabel=min([x for x in Neighbors if x !=1  ])
            binary_img[row][col] = toLabel
    return binary_img
    
def Two_Pass(binary_img:np.array, neighbor_hoods):
    offsets= OFFSET_8
    binary_img = neighbor_value(binary_img, offsets,False)
    print("first pass")
    print(binary_img)
    binary_img = neighbor_value(binary_img, offsets, True)
    return binary_img 
if __name__=="__main__":
    binary_img = np.zeros((4,7), dtype=np.int16)
    index= [[0,2],[0,5],
            [1,0],[1,1],[1,2],[1,4],[1,5],[1,6],
            [2,2],[2,5],
            [3,1],[3,2],[3,4],[3,6]]
    for i in index:
        binary_img [i[0],i[1]] = np.int16(1)
    print("original binary image")
    print(binary_img)

    print("tow_pass")
    binary_img = Two_Pass(binary_img,NEIGHBOUR_HOODS_8)
    print(binary_img)

#using 1 test
#original binary image
#[[0 0 1 0 0 1 0]
# [1 1 1 0 1 1 1]
# [0 0 1 0 0 1 0]
# [0 1 1 0 1 0 1]]
#tow_pass
#first pass
#[[0 0 2 0 0 3 0]
# [4 2 2 0 3 3 3]
# [0 0 2 0 0 3 0]
# [0 2 2 0 3 0 3]]

#[[0 0 2 0 0 3 0]
# [2 2 2 0 3 3 3]
# [0 0 2 0 0 3 0]
# [0 2 2 0 3 0 3]]

go-on ,继续研究下区域生长算法

  • 回溯算法 ,树中每个节点可以选择的元素是八邻域
  • 终止条件:已经被标注的像素点或超出图像区间的点
  • 每次找到新的区块进行同样的查找逻辑
  • 包括主函数 和一个递归函数,两者都要用的元素是否为0 和是否已经被标注的判断
  • 完整代码
#区域生长算法 Seed-Filling
def recursive_seed(binary_img:np.array, seed_row,seed_col,offsets,num,max_num ):
    #一般终止条件写在这里
    # if var  in  [0,2..100] #说明已经标注
    var = binary_img[seed_row,seed_col]
    if var == 0 or var<=max_num and var >=2  :return    

    rows,cols= binary_img.shape
    binary_img[seed_row][seed_col] = num
    #回溯: 每层遍历时候可以选择的对象
    for offset in offsets:
        neighbor_row= min(max(0,seed_row+offset[0]), rows-1)
        neighbor_col = min(max(0,seed_col+offset[1]),cols-1)
        #if var ... 终止条件 也可以写在循环中
        recursive_seed(binary_img, neighbor_row, neighbor_col,offsets, num,max_num)

#max_num=100 说明最多标注一百块
def Seed_Filling(binary_img, max_num=100):
    offsets = OFFSET_8
    num=2  #从2开始编号
    rows,cols= binary_img.shape
    for row in range(rows):
        for col in range(cols):
            var = binary_img[row][col]
            # if var  in  [0,2..100] #说明已经标注
            if var == 0 or var<=max_num and var >=2  :continue
            recursive_seed(binary_img, row,col, offsets,num,max_num)
            num+=1
 
if __name__=="__main__":

    binary_img = np.zeros((4,7), dtype=np.int16)
    index= [[0,2],[0,5],
            [1,0],[1,1],[1,2],[1,4],[1,5],[1,6],
            [2,2],[2,5],
            [3,1],[3,2],[3,4],[3,6]]
    for i in index:
        binary_img [i[0],i[1]] = np.int16(1)
    print("original binary image")
    print(binary_img)

    print("seed filling")
    Seed_Filling(binary_img)
    print(binary_img)

## 区域生长 result
#original binary image
#[[0 0 1 0 0 1 0]
# [1 1 1 0 1 1 1]
# [0 0 1 0 0 1 0]
# [0 1 1 0 1 0 1]]
#seed filling
#[[0 0 2 0 0 3 0]
# [2 2 2 0 3 3 3]
# [0 0 2 0 0 3 0]
# [0 2 2 0 3 0 3]]
#请按任意键继续. . .
posted @ 2021-03-20 11:01  boyang987  阅读(193)  评论(0编辑  收藏  举报