背景
在进行胸部CT影像分析或深度学习建模前,常需要去除胸腔以外的无关区域。例如床板、床垫、身体外部空气等。这些区域不仅占用存储空间,还会对模型训练造成噪声干扰。
我们会介绍基于传统的方法和深度学习的方法。
本文重点介绍一种基于阈值分割 + 连通域提取的简易方案,
既稳健又易于批量处理。
方法原理
Step 1. 阈值分割(Thresholding)
CT图像中的灰度值是以HU(Hounsfield Unit)表示:
- 空气约为-1000 HU;
- 软组织、肺间质大于-700 HU;
- 骨骼在+300 HU以上。
因此,只要设定一个阈值(例如-700 HU), 就可以将空气区域去除,保留人体及床板部分。
Step 2. 3D 连通域分析(Connected Component Labeling)
人体通常是扫描体积中最大的连通区域,而床板、被单、噪声等属于较小的独立结构。我们对前景进行 3D 连通域标记,并保留体积最大的一个。
Step 3. 后处理(填洞 + 去小碎片)
为了让掩膜更平滑、连贯,可进行简单的形态学修补:
具体代码如下:
air_threshold = -700 # 阈值,可根据数据调整 # 1️⃣ 阈值分割:去除空气区域 fg = arr > air_threshold # True 表示可能是身体、床板等实体;False 表示空气 # 2️⃣ 3D 连通域分析:找到哪些体素连在一起(前景团块) # connectivity=1 表示6邻域(保守,不易粘连不同结构) lab = label(fg, connectivity=1) # 3️⃣ 选出体素最多的那一团(最大连通域),通常对应人体 if lab.max() == 0: body_mask = np.zeros_like(fg, dtype=bool) else: counts = np.bincount(lab.ravel()) # 统计每个标签的体素数量 largest_label = counts[1:].argmax() + 1 # 找出面积最大的标签编号(跳过背景0) body_mask = (lab == largest_label) # 得到人体主体掩膜 # 4️⃣ 打印一下人体区域占比(越接近1说明占整幅图像比例越高) body_ratio = body_mask.mean() print("人体区域占比:", round(body_ratio, 4))提取的人体mask如下:
Step 4. 应用掩膜并保存结果
将人体掩膜外的区域全部置为空气(-1024 HU),得到“去床板”的干净CT体积:
# 应用掩膜并保存结果 # 关键思路: # - 用刚才生成的人体掩膜,把人体以外的区域(床板、空气等)全部置为空气值; background_hu = -1024.0 # 空气的典型 HU 值 arr_clean = arr.copy() arr_clean[~body_mask] = background_hu # 掩膜外全部设为空气 # 输出路径设置 out_dir = os.path.dirname(nifti_path) base = os.path.basename(nifti_path).replace(".nii.gz","") out_img_path = os.path.join(out_dir, f"{base}_masked.nii.gz") nib.save(nib.Nifti1Image(arr_clean.astype(np.float32), affine, header), out_img_path) print("已保存清理后图像:", out_img_path)优点与适用场景
✅ 优点
- 无需模型与GPU;
- 处理速度快,可批量运行;
- 对存在空气缝隙的CT非常稳定。
⚠️ 局限:
- 若身体与床板紧贴(无空气间隙),可能粘连;
- 若手臂或被单与床板连接,会误保留。
比如以下案例:
本次实验代码采用jupyter形式,更方便大家学习。代码展示如下:
批量代码:
对这部分感兴趣的同学可以联系我要完整的代码,改成你的数据集就可以使用。里面由非常多的中文说明。
文章持续更新,可以关注微公【Radiology AI Camp】获取最新动态。人手有限,文中涉及的链接前往微公对应文章查看。关注Tina姐,一起学习进步~
我是Tina, 我们下篇博客见~
白天工作晚上写文,呕心沥血
觉得写的不错的话最后,求点赞,评论,收藏。或者一键三连