当我们查看分割结果时,会发现分割内部偶尔会出现这种分割孔洞(hole)。
常识告诉我们,这个器官内部是没有孔洞的,因此,我们通过后处理的方法把它填上,可以提高分割准确度。
这种三维孔洞,我们希望有一种便捷方法,可以直接填补这种三维孔洞。可以使用 SITK 的二值孔洞填补方法。 sitk.BinaryFillhole
- sitk.BinaryFillhole
注意: 该函数只针对二值图像(值为0或1)
import SimpleITK as sitk
import os
import glob
imglist= glob.glob('./*.nii.gz')
save_dir = './fillhole'
for img in imglist:
img_nii = sitk.ReadImage(img, outputPixelType=sitk.sitkUInt16)
img_fill = sitk.BinaryFillhole(img_nii)
img_savedir = os.path.join(save_dir, img.split('/')[-1])
sitk.WriteImage(img_fill, img_savedir)
该例子显示了如何批量填补孔洞,并且保存下来。
- 自写一个。
我是主张能用开源工具就用工具,毕竟这些工具都是大牛写的,值得信赖。
但是,我这里还是提供一个github上写的算法。
大致思路: 这个算法提供了三维填充和二维填充,二维填充是选择一个面(如横断面或者冠状面)进行一层一层的填充。
from scipy.ndimage.morphology import binary_fill_holes
import numpy as np
from scipy import ndimage
import nibabel as nib
from skimage.measure import label
import matplotlib.pyplot as plt
def hole_filling(bw, hole_min, hole_max, fill_2d=True):
bw = bw > 0
if len(bw.shape) == 2:
background_lab = label(~bw, connectivity=1)
fill_out = np.copy(background_lab)
component_sizes = np.bincount(background_lab.ravel())
too_big = component_sizes > hole_max
too_big_mask = too_big[background_lab]
fill_out[too_big_mask] = 0
too_small = component_sizes < hole_min
too_small_mask = too_small[background_lab]
fill_out[too_small_mask] = 0
elif len(bw.shape) == 3:
if fill_2d:
fill_out = np.zeros_like(bw)
for zz in range(bw.shape[1]):
background_lab = label(~bw[:, zz, :], connectivity=1) # 1表示4连通, ~bw[zz, :, :]1变为0, 0变为1
# 标记背景和孔洞, target区域标记为0
out = np.copy(background_lab)
# plt.imshow(bw[:, :, 87])
# plt.show()
component_sizes = np.bincount(background_lab.ravel())
# 求各个类别的个数
too_big = component_sizes > hole_max
too_big_mask = too_big[background_lab]
out[too_big_mask] = 0
too_small = component_sizes < hole_min
too_small_mask = too_small[background_lab]
out[too_small_mask] = 0
# 大于最大孔洞和小于最小孔洞的都标记为0, 所以背景部分被标记为0了。只剩下符合规则的孔洞
fill_out[:, zz, :] = out
# 只有符合规则的孔洞区域是1, 背景及target都是0
else:
background_lab = label(~bw, connectivity=1)
fill_out = np.copy(background_lab)
component_sizes = np.bincount(background_lab.ravel())
too_big = component_sizes > hole_max
too_big_mask = too_big[background_lab]
fill_out[too_big_mask] = 0
too_small = component_sizes < hole_min
too_small_mask = too_small[background_lab]
fill_out[too_small_mask] = 0
else:
print('error')
return
return np.logical_or(bw, fill_out) # 或运算,孔洞的地方是1,原来target的地方也是1
参数简介
- bw: array, 待填补的数组
- hole_min: 孔洞像素的个数最小值,一般为0
- hole_max: 孔洞像素的个数最大值。
- fill_2d: True:二维填充。False:三维填充
只有当孔洞像素值个数在 [hole_min, hole_max] 才会被填补。
算法里面涉及了大量的与或非运算。
调用
filled = hole_filling(arr, 0, 100, fill_2d=True) # 通过改变bw[zz, :, :],选择轴
你可以对比一下,这三种算法(第二个里面是2种算法)哪个更适合你。
深度学习,分割后处理之通过连通成分分析去除假阳性区域,提高分割准确度
常用的分割后处理方法就是 连通成分分析和填补孔洞,你学会了吗。
我是Tina, 我们下篇博客见~
白天工作晚上写文,呕心沥血
觉得写的不错的话最后,求点赞,评论,收藏