python實現3D連通域後處理

skimage 實現3D數組連通域處理

在一些計算機視覺任務中,需要對模型的輸出做一些後處理以優化視覺效果,連通域就是一種常見的後處理方式。尤其對於分割任務,有時的輸出mask會存在一些假陽(小的無用輪廓),通過3D連通域找出面積較小的獨立輪廓並去除可以有效地提升視覺效果。skimage是python的一個圖像處理庫,功能強大並且使用方便。這裏主要使用了skimage裏的measure模塊,官方也提供了相關方法的說明及使用

1. 任務說明

模型輸出了一個n*w*h的三維數組,數組中包含了模型對某一類(或幾類)的分割結果,從形態學角度看在3D上每一類分割結果都應該是連續的,因此可以使用3D連通域去除那些不連通的小區域。

1.1 連通域

連通域(Connected Component)指的是圖像中具有相同像素值且位置相鄰的前景像素點組成的圖像區域(Region)。連通域一般有兩種,分別爲4連通和8連通,下面借用skimage的示意圖。

1-connectivity     2-connectivity     diagonal connection close-up

     [ ]           [ ]  [ ]  [ ]             [ ]
      |               \  |  /                 |  <- hop 2
[ ]--[x]--[ ]      [ ]--[x]--[ ]        [x]--[ ]
      |               /  |  \             hop 1
     [ ]           [ ]  [ ]  [ ]

連通域的實現有兩種方式,一是two-pass算法,通過並查集(union-find)實現;二是Seed-Filling 算法,基於圖形學中的區域生長,可以遞歸實現。

1.2 measure方法

skimage.measure.label

  • 函數功能:把輸入的整數數組進行連通域標記
  • 函數定義:skimage.measure.label(input, neighbors=None, background=None, return_num=False, connectivity=None)
  • 參數說明:

input: int類型的ndarray,需要標記的圖像;
neighbors: 廢棄的參數,被connectivity替代;
background: int類型,可選,將像素爲該值的視爲背景並設置爲0,默認爲0;
return_num: bool,可選,是否返回標記的區域數量;
connectivity: int,可選,最大爲輸入的ndims,選擇是幾連通(對於輸入爲2D來說,1即爲4連通,2爲8連通)。

  • 返回值:

labels: 形狀和類型與input一致,連通的區域使用相同的整數標記
nums: 標籤數,等於最大標籤索引,僅在return_num爲True時返回。

skimage.measure.regionprops

  • 函數功能:測量標記圖像區域的屬性
  • 函數定義:skimage.measure.regionprops(label_image, intensity_image=None, cache=True, coordinates=None)
  • 參數說明:

label_image: (N, M) ndarray,經過標記的圖像,0當作背景忽略;
intensity_image: (N, M) ndarray,與標籤圖像大小相同的強度(即輸入)圖像。 默認爲無;
cache: bool,可選,確定是否緩存計算的區域屬性。 對於緩存的屬性,計算速度要快得多,而內存消耗卻增加了。
coordinates: 已淘汰,不推薦使用。

  • 返回值:

properties:屬性列表,列表中每一個對象對應一個標記的區域,可以訪問對象的屬性獲取區域的屬性。

  • 常用的屬性: area:int, 區域面積;slice: tuple,對應的區域。詳細見官方文檔。

2. 代碼實現

def connected_component(image):
    # 標記輸入的3D圖像
    label, num = measure.label(image, connectivity=1, return_num=True)
    if num < 1:
        return image
        
	# 獲取對應的region對象
    region = measure.regionprops(label)
    # 獲取每一塊區域面積並排序
    num_list = [i for i in range(1, num+1)]
    area_list = [region[i-1].area for i in num_list]
    num_list_sorted = sorted(num_list, key=lambda x: area_list[x-1])[::-1]
	# 去除面積較小的連通域
    if len(num_list_sorted) > 3:
        # for i in range(3, len(num_list_sorted)):
        for i in num_list_sorted[3:]:
            # label[label==i] = 0
            label[region[i-1].slice][region[i-1].image] = 0
        num_list_sorted = num_list_sorted[:3]
    return label
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章