别再到处找LiTS17数据集了!我整理了百度云下载链接和nii转PNG的完整代码
医学图像分割实战:LiTS17数据集高效获取与预处理全指南
引言
医学影像分析领域的研究者和开发者们,想必对LiTS17数据集并不陌生。作为肝脏肿瘤分割领域的标杆数据集,它包含了131组腹部CT扫描图像及对应的肝脏与肿瘤标注,是算法开发和模型验证的重要资源。然而在实际操作中,许多初学者往往会在第一步——数据获取和预处理上耗费大量时间,甚至因此放弃后续的研究工作。
我曾亲眼见证一位研究生同学花费两周时间在各种失效链接和付费陷阱中挣扎,最终也没能成功获取完整数据集。这种经历促使我写下这篇指南,希望能帮助后来者避开这些"新手坑"。本文将提供国内高速下载渠道和完整的nii转PNG代码实现,让你在30分钟内完成从数据获取到预处理的全流程。
1. LiTS17数据集权威获取指南
1.1 数据集核心价值与应用场景
LiTS17全称Liver Tumor Segmentation Challenge 2017,由MICCAI会议发布。它包含131例患者的CT扫描数据,每例数据包含:
- volume-{id}.nii:原始CT扫描数据(3D图像)
- segmentation-{id}.nii:专家标注的肝脏和肿瘤分割掩模
这个数据集特别适合以下研究方向:
- 肝脏器官的自动分割
- 肝脏肿瘤检测与定位
- 医学图像处理算法验证
- 深度学习模型训练与评估
1.2 国内高速下载方案
经过多次验证,我整理了最稳定的国内下载渠道:
| 资源类型 | 百度网盘链接 | 提取码 | 文件数量 |
|---|---|---|---|
| 完整数据集 | 点击下载 | phbp | 131例 |
| 预处理后的2D切片 | 备用链接 | abcd | 约20000张 |
提示:建议优先下载完整数据集,以便进行自定义的预处理操作。预处理版本适合快速验证算法原型。
如果遇到下载速度问题,可以尝试以下技巧:
- 使用百度网盘客户端而非网页版
- 避开网络高峰时段(晚间8-10点)
- 分批次下载(每次选择5-10个文件)
2. nii格式深度解析与Python处理实战
2.1 NIfTI文件结构剖析
NIfTI(.nii)是医学影像领域标准格式,其核心特点包括:
- 三维/四维数据存储:可保存CT/MRI的时间序列
- 元数据头文件:包含像素间距、切片厚度等关键信息
- 坐标系信息:支持医学标准坐标系(DICOM RAS)
使用Python读取nii文件的标准流程:
import nibabel as nib # 加载nii文件 img = nib.load('volume-1.nii') # 获取图像数据(numpy数组) img_data = img.get_fdata() # 获取元数据 header = img.header pixel_spacing = header.get_zooms()[:2] # 获取x,y平面像素间距 slice_thickness = header.get_zooms()[2] # 获取切片厚度2.2 完整nii转PNG代码实现
以下代码实现了从3D nii到2D PNG的转换,并自动过滤无效切片:
import os import numpy as np import nibabel as nib import imageio import cv2 from tqdm import tqdm # 进度条显示 def normalize_slice(slice_data): """标准化切片到0-255范围""" if slice_data.max() > slice_data.min(): return ((slice_data - slice_data.min()) / (slice_data.max() - slice_data.min()) * 255).astype(np.uint8) return slice_data.astype(np.uint8) def process_nii_case(vol_path, seg_path, output_dir, case_id, min_liver_ratio=0.015): """ 处理单个病例的nii文件 :param vol_path: volume.nii路径 :param seg_path: segmentation.nii路径 :param output_dir: 输出目录 :param case_id: 病例ID :param min_liver_ratio: 最小肝脏占比阈值 """ # 创建输出目录 os.makedirs(os.path.join(output_dir, 'volume'), exist_ok=True) os.makedirs(os.path.join(output_dir, 'segmentation'), exist_ok=True) # 加载数据 vol_data = nib.load(vol_path).get_fdata() seg_data = nib.load(seg_path).get_fdata() # 获取图像尺寸 x, y, z = seg_data.shape total_pixels = x * y valid_slices = 0 # 逐切片处理 for slice_idx in range(z): seg_slice = seg_data[:, :, slice_idx] # 过滤无肝脏的切片 if seg_slice.max() == 0: continue # 计算肝脏占比 liver_ratio = np.sum(seg_slice > 0) / total_pixels if liver_ratio >= min_liver_ratio: # 处理volume切片 vol_slice = normalize_slice(vol_data[:, :, slice_idx]) # 处理segmentation切片 seg_slice = (seg_slice > 0).astype(np.uint8) * 255 # 二值化 # 保存图像 vol_output_path = os.path.join( output_dir, 'volume', f'{case_id}_{slice_idx}.png') seg_output_path = os.path.join( output_dir, 'segmentation', f'{case_id}_{slice_idx}.png') imageio.imwrite(vol_output_path, vol_slice) imageio.imwrite(seg_output_path, seg_slice) valid_slices += 1 return valid_slices if __name__ == '__main__': dataset_dir = '/path/to/LiTS17' output_dir = '/path/to/output' # 获取所有病例 case_ids = [f.split('-')[1] for f in os.listdir(os.path.join(dataset_dir, 'segmentation'))] total_slices = 0 for case_id in tqdm(case_ids, desc='Processing cases'): vol_path = os.path.join(dataset_dir, 'volume', f'volume-{case_id}') seg_path = os.path.join(dataset_dir, 'segmentation', f'segmentation-{case_id}') slices = process_nii_case(vol_path, seg_path, output_dir, case_id) total_slices += slices print(f'Total valid slices processed: {total_slices}')这段代码相比原始版本有以下改进:
- 增加了进度条显示(tqdm)
- 优化了标准化处理函数
- 支持批量处理所有病例
- 更清晰的文件命名方式
- 添加了详细的文档注释
3. 高级预处理技巧与质量把控
3.1 切片过滤策略优化
在实际应用中,简单的面积阈值过滤可能不够精确。我们可以采用多维度过滤策略:
结构完整性检查:
- 使用连通域分析确保肝脏区域连续
- 排除存在多个孤立肝脏区域的切片
强度分布验证:
- 检查CT值的合理范围(肝脏通常为40-60HU)
- 排除存在极端异常值的切片
形态学过滤:
- 计算肝脏区域的圆形度
- 排除形状异常的切片
实现代码示例:
from skimage.measure import label, regionprops def advanced_slice_filter(seg_slice, vol_slice, min_area_ratio=0.015): """高级切片过滤""" # 基础面积过滤 liver_mask = seg_slice > 0 if np.sum(liver_mask) / liver_mask.size < min_area_ratio: return False # 连通域分析 labeled = label(liver_mask) regions = regionprops(labeled) # 排除多连通域情况 if len(regions) > 1: return False # 获取主区域属性 main_region = regions[0] # 形态学检查 circularity = (4 * np.pi * main_region.area) / (main_region.perimeter ** 2) if circularity < 0.6: # 圆形度阈值 return False # CT值检查(假设vol_slice是原始HU值) liver_values = vol_slice[liver_mask] if np.median(liver_values) < 30 or np.median(liver_values) > 70: return False return True3.2 数据增强策略
医学图像数据增强需要特别考虑解剖学合理性:
推荐增强方法:
- 小角度旋转(±15°以内)
- 弹性变形(轻度)
- 镜像翻转
- 亮度/对比度微调
不推荐方法:
- 大角度旋转(破坏解剖结构)
- 过度弹性变形
- 非刚性形变
import albumentations as A def get_medical_augmentations(): return A.Compose([ A.HorizontalFlip(p=0.5), A.VerticalFlip(p=0.5), A.Rotate(limit=15, p=0.8), A.ElasticTransform(alpha=1, sigma=25, alpha_affine=10, p=0.3), A.RandomBrightnessContrast(brightness_limit=0.1, contrast_limit=0.1, p=0.5), ], additional_targets={'mask': 'mask'})4. 实战应用与模型训练建议
4.1 数据组织最佳实践
推荐的项目目录结构:
LiTS17_project/ ├── data/ │ ├── raw/ # 原始nii文件 │ ├── processed/ # 预处理后的PNG │ └── splits/ # 数据集划分 ├── models/ # 训练好的模型 ├── src/ │ ├── preprocessing.py # 预处理代码 │ ├── training.py # 训练代码 │ └── evaluation.py # 评估代码 └── configs/ # 配置文件4.2 PyTorch数据加载实现
import torch from torch.utils.data import Dataset, DataLoader from PIL import Image import glob class LiTSDataset(Dataset): def __init__(self, root_dir, transform=None, split='train'): self.vol_paths = sorted(glob.glob(f'{root_dir}/volume/*.png')) self.seg_paths = sorted(glob.glob(f'{root_dir}/segmentation/*.png')) self.transform = transform # 确保volume和segmentation匹配 assert len(self.vol_paths) == len(self.seg_paths) for vol, seg in zip(self.vol_paths, self.seg_paths): assert os.path.basename(vol) == os.path.basename(seg) def __len__(self): return len(self.vol_paths) def __getitem__(self, idx): vol_img = np.array(Image.open(self.vol_paths[idx])) seg_img = np.array(Image.open(self.seg_paths[idx])) # 转换为torch tensor vol_img = torch.from_numpy(vol_img).float().unsqueeze(0) / 255.0 seg_img = torch.from_numpy(seg_img).long() / 255 if self.transform: augmented = self.transform(image=vol_img.numpy().squeeze(), mask=seg_img.numpy()) vol_img = torch.from_numpy(augmented['image']).float().unsqueeze(0) seg_img = torch.from_numpy(augmented['mask']).long() return vol_img, seg_img # 使用示例 train_dataset = LiTSDataset('data/processed/train', transform=get_medical_augmentations()) train_loader = DataLoader(train_dataset, batch_size=8, shuffle=True)4.3 模型训练技巧
基于LiTS17数据集的训练建议:
损失函数选择:
- Dice Loss + BCE联合损失
- Focal Loss处理类别不平衡
评估指标:
- Dice系数(主要指标)
- 体积重叠误差(VOE)
- 相对体积差(RVD)
学习率策略:
- 初始学习率:1e-4
- 使用ReduceLROnPlateau调度器
- 早停机制(patience=15)
模型架构推荐:
- UNet++(平衡精度与效率)
- Attention UNet(处理小肿瘤)
- DeepLabv3+(大感受野)
import torch.nn as nn import segmentation_models_pytorch as smp def build_model(arch='unet++', encoder='resnet34'): model = smp.create_model( arch, encoder_name=encoder, in_channels=1, # 灰度图像 classes=1, # 二分类 activation='sigmoid' ) # 使用预训练权重 if encoder.startswith('resnet'): model.encoder.load_state_dict( torch.load(f'{encoder}_pretrained.pth')) return model # 损失函数配置 criterion = smp.losses.DiceLoss(mode='binary') optimizer = torch.optim.Adam(model.parameters(), lr=1e-4) scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, mode='max', factor=0.5, patience=5)