Python 中的连通分量标记算法

Connected Component Labeling Algorithm in Python

我的工作需要在图像上应用本地二元运算符。为此,我已经将图像转换为灰色,然后还对图像实施了连通分量分析。

代码如下:

  1. 添加库

     import numpy as np
     import pandas as pd
     import matplotlib.pyplot as plt
     from skimage.io import imread, imshow
     from skimage.color import rgb2gray
     from skimage.morphology import (erosion, dilation, closing, opening,area_closing, area_opening)
     from skimage.measure import label, regionprops, regionprops_table
    
  2. 渲染图像

     plt.figure(figsize=(6,6))
     painting = imread("E:/Project/for_annotation/Gupi Gain0032.jpg")
     plt.imshow(painting);
    
     plt.figure(figsize=(6,6))
    
  3. 二值化图像

     gray_painting = rgb2gray(painting)
     binarized = gray_painting<0.55
     plt.imshow(binarized);
    

4.Declaring 内核

    square = np.array([[1,1,1],
                      [1,1,1],
                      [1,1,1]])
  1. 膨胀函数

     def multi_dil(im, num, element=square):
     for i in range(num):
         im = dilation(im, element)
         return im
    
  2. 侵蚀函数

    def multi_ero(im, num, element=square):
    for i in range(num):
        im = erosion(im, element)
        return im
    
  3. 应用的函数

    plt.figure(figsize=(6,6))
    multi_dilated = multi_dil(binarized, 7)
    area_closed = area_closing(multi_dilated, 50000)
    multi_eroded = multi_ero(area_closed, 7)
    opened = opening(multi_eroded)
    plt.imshow(opened);
    
  4. 标签函数

    plt.figure(figsize=(6,6))
    label_im = label(opened)
    regions = regionprops(label_im)
    plt.imshow(label_im); 
    
  5. 提取特征

    properties = ['area','convex_area','bbox_area', 'extent', 'mean_intensity','solidity', 'eccentricity', 'orientation']
    pd.DataFrame(regionprops_table(label_im, gray_painting, 
    properties=properties))
    
  6. 过滤区域

    masks = []
    bbox = []
    list_of_index = []
    for num, x in enumerate(regions):
        area = x.area
        convex_area = x.convex_area
        if (num!=0 and (area>100) and (convex_area/area <1.05)
        and (convex_area/area >0.95)):
        masks.append(regions[num].convex_image)
        bbox.append(regions[num].bbox)   
        list_of_index.append(num)
     count = len(masks)
    
  7. 提取图像

     fig, ax = plt.subplots(2, int(count/2), figsize=(15,8))
     for axis, box, mask in zip(ax.flatten(), bbox, masks):
         red  =  painting[:,:,0][box[0]:box[2], box[1]:box[3]] * mask
         green = painting[:,:,1][box[0]:box[2], box[1]:box[3]] * mask
         blue  = painting[:,:,2][box[0]:box[2], box[1]:box[3]] * mask
         image = np.dstack([red,green,blue])
         axis.imshow(image)
     plt.tight_layout()
    
     plt.figure(figsize=(6,6))
    
     rgb_mask = np.zeros_like(label_im)
     for x in list_of_index:
         rgb_mask += (label_im==x+1).astype(int)
         red  =  painting[:,:,0] * rgb_mask
         green = painting[:,:,1] * rgb_mask
         blue  = painting[:,:,2] * rgb_mask
         image = np.dstack([red,green,blue])
     plt.imshow(image);
    

我收到一个错误。

ValueError: Number of columns must be a positive integer, not 0

有一种可能的方法与您尝试的方法相差不大。假设背景像素被分配标签 0,对象像素被分配值 1。

  • 逐行扫描图像;

  • 当遇到像素1时,设置一个新的label并进行flood fill操作,将1替换为新的label。

洪水填充可以很简单地实现:

  • 将起始像素设置为新标签;

  • 递归填充八个邻居,如果它们有一个 1。

https://en.wikipedia.org/wiki/Flood_fill

这个版本的代码非常简单。但是您会注意到它很容易溢出堆栈,因为待处理的填充数可能与图像大小一样大。

def FloodFill(X, Y, Label):
    I[X,Y]= Label
    for all 8-way neighbors (X'=X±1, Y'=Y±1, inside image):
        if I[X',Y'] == 1:
            FloodFill(X', Y', Label)

def CCL(Image I):
    Label= 1
    for Y in range(I.Height):
        for X in range(I.Width):
            if I[X, Y] == 1:
                Label+= 1
                FloodFill(X, Y, Label)

所以我会推荐扫描线版本,它涉及更多。

https://en.wikipedia.org/wiki/Flood_fill#Scanline_fill