ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

头颅侧位片蝶鞍语义分割数据集构建与训练实战

头颅侧位片蝶鞍语义分割数据集构建与训练实战 简介本资源是面向医学影像AI研究者与深度学习工程师的头颅侧位片蝶鞍区域语义分割专用数据集聚焦内分泌疾病辅助诊断场景解决蝶鞍小目标、低对比度、解剖边界模糊带来的分割建模难题。数据包共1558个文件含779张标注清晰的头颅侧位X光JPG图像及对应LabelMe生成的JSON标注文件完整覆盖蝶鞍轮廓与形态细节便于直接用于U-Net、SegFormer等模型训练与评估压缩包大小671.16MB结构规整开箱即用。目前已有123人学习下载适合开展医学图像分割算法验证、模型轻量化探索或临床可解释性研究。用户可直接加载数据进行数据增强、标签转换与训练 pipeline 搭建无需额外清洗或格式转换配套标注质量高、解剖一致性好显著降低高质量医疗分割数据集构建门槛。1. 头颅侧位片蝶鞍形态数据集为什么医生和AI工程师都在手动标注这张X光片在放射科日常工作中蝶鞍的形态变化是诊断垂体瘤、空蝶鞍综合征、颅内压增高或发育异常的关键影像学线索。但传统测量依赖医生肉眼勾画边界、手工记录前后径/深径/高径主观性强、耗时长、难以批量分析。这个标题指向一个具体落地场景用LabelMe对头颅侧位X光片中的蝶鞍区域进行像素级标注生成带掩码mask的语义分割数据集直接喂给DeepLabV3、SegFormer或Mask2Former等模型训练。它不是通用医学图像数据集而是聚焦“蝶鞍”这一解剖结构的细粒度分割任务——这意味着标注必须严格贴合鞍结节、鞍底、后床突等骨性边界的CT/MRI/X光灰度过渡区不能简单框选。适合两类人一是想快速验证分割模型在低对比度X光片上泛化能力的算法工程师二是需要构建垂体疾病辅助判读工具的临床科研团队。数据集开箱即用但真正决定模型效果的是LabelMe标注时的层厚控制、多边形拟合精度、以及后续数据增强策略。2. 用LabelMe标注蝶鞍掩码从安装到导出COCO格式的完整链路2.1 安装LabelMe 5.3.1并配置医学图像适配环境LabelMe官方推荐使用Python 3.8环境但头颅侧位片常为DICOM或高分辨率JPEG如2048×2048需避免默认缩放导致边缘模糊。我一般会创建独立虚拟环境并安装特定版本python -m venv labelme_env source labelme_env/bin/activate # Linux/macOS # labelme_env\Scripts\activate # Windows pip install labelme5.3.1 opencv-python-headless pydicom注意opencv-python-headless替代默认OpenCV避免GUI冲突pydicom用于后续处理原始DICOM文件若数据源为DICOM。安装后运行labelme --version确认输出5.3.1避免因版本错位导致JSON字段不兼容如旧版无imageData字段新版默认base64编码图像。2.2 标注蝶鞍区域的3个关键操作规范蝶鞍在侧位片中呈马鞍状透亮区边界由致密骨质构成但X光对比度低、易受投照角度影响。标注时必须遵循以下规范否则分割模型将学习错误边界多边形顶点密度每厘米骨性边界至少放置8个顶点。例如鞍底长度约2.5cm则需20个点禁止用矩形或粗略四边形替代。层级嵌套逻辑蝶鞍是一个单连通区域但需区分“鞍内腔隙”与“鞍背骨皮质”。实际标注中应先用polygon工具沿鞍结节→鞍底→后床突→鞍背→鞍结节闭合绘制外轮廓再用rectangle工具在鞍内标记“伪影干扰区”如金属牙冠重叠处后者在后续转换时设为ignore_label。快捷键效率组合CtrlZ撤销顶点 →Shift左键添加顶点 →CtrlD复制当前多边形到新图层 →W切换至“编辑多边形”模式微调顶点。标注100张图平均提速40%。2.3 导出为COCO格式解决LabelMe原生JSON与PyTorch DataLoader的兼容问题LabelMe默认导出.json含shapes数组但主流分割框架如MMSegmentation要求COCO格式的annotations字段。需用脚本转换核心是将每个polygon坐标映射为RLE编码# convert_labelme_to_coco.py import json, numpy as np, cv2 from pycocotools import mask as coco_mask def polygon_to_rle(polygon, img_h, img_w): # polygon: [[x1,y1], [x2,y2], ...] 归一化坐标需转为整数像素 rles coco_mask.frPyObjects([np.array(polygon).flatten().tolist()], img_h, img_w) return coco_mask.decode(rles[0]) # 主转换逻辑省略文件遍历部分 for json_file in json_files: with open(json_file) as f: data json.load(f) img_h, img_w data[imageHeight], data[imageWidth] for shape in data[shapes]: if shape[label] sella_turcica: rle polygon_to_rle(shape[points], img_h, img_w) # 构造COCO annotation字典含segmentation(RLE)、area、bbox等提示coco_mask.decode()返回的是H×W布尔矩阵需保存为.png掩码图时用cv2.imwrite(mask_path, rle.astype(np.uint8) * 255)。此步骤不可跳过——直接用LabelMe JSON训练会导致PyTorch的torchvision.transforms.ToTensor()将多边形坐标误读为噪声。参数推荐值说明min_area_ratio0.001过滤面积小于图像0.1%的误标碎片如噪点ignore_label255在训练时被nn.CrossEntropyLoss(ignore_index255)忽略class_names[background, sella_turcica]COCO格式要求category_id从1开始background固定为03. 训练语义分割模型以DeepLabV3 ResNet50为例的全流程配置3.1 数据集目录结构与配置文件修改要点MMSegmentation要求严格目录结构蝶鞍数据集需组织为data/sella/ ├── images/ │ ├── train/ │ │ ├── 001.jpg │ │ └── ... │ └── val/ ├── masks/ │ ├── train/ │ │ ├── 001.png # 0background, 1sella, 255ignore │ │ └── ... │ └── val/ └── annfile/ ├── train.txt # 每行001.jpg 001.png └── val.txt关键配置修改在configs/deeplabv3/deeplabv3_r50-d8_4xb4-40k_cityscapes-512x1024.py基础上调整# dataset settings dataset_type CustomDataset data_root data/sella/ train_pipeline [ dict(typeLoadImageFromFile), dict(typeLoadAnnotations, reduce_zero_labelFalse), # 蝶鞍标签为1不减0 dict(typeResize, scale(512, 512), keep_ratioTrue), # X光片需保持宽高比 dict(typeRandomFlip, prob0.5), dict(typePhotoMetricDistortion), # 增强X光片低对比度特性 dict(typePackSegInputs) ] train_dataloader dict( datasetdict( typedataset_type, data_rootdata_root, data_prefixdict(img_pathimages/train/, seg_map_pathmasks/train/), ann_fileannfile/train.txt, classes(background, sella_turcica), palette[[0,0,0], [0,255,0]] # 可视化时绿色显示蝶鞍 ) )注意reduce_zero_labelFalse必须显式声明否则标签1会被转为0导致所有蝶鞍区域被当作背景。这是新手最常踩的坑。3.2 针对X光片特性的3项模型参数优化头颅侧位片与自然图像差异巨大灰度集中于100-20016bit DICOM常截断为8bit、纹理单一、边界模糊。直接套用Cityscapes预训练权重效果差需针对性调整主干网络输入归一化将img_norm_cfg从ImageNet的mean[123.675, 116.28, 103.53]改为X光专用img_norm_cfg dict( mean[128.0, 128.0, 128.0], # 灰度图三通道均值设为128 std[32.0, 32.0, 32.0], # 标准差缩小增强低对比度响应 to_rgbFalse)损失函数加权蝶鞍区域仅占图像5%需用ClassBalancedLossloss_decodedict( typeCrossEntropyLoss, use_sigmoidFalse, loss_weight1.0, class_weight[0.1, 0.9]) # 背景权重0.1蝶鞍权重0.9学习率热身策略前500步线性warmup避免初始梯度爆炸param_scheduler [ dict( typeLinearLR, start_factor1e-6, by_epochFalse, begin0, end500), dict( typePolyLR, eta_min0.0, power0.9, begin500, end40000, by_epochFalse) ]3.3 训练日志关键指标解读与早停判断启动训练后重点关注mmseg输出的mIoU和mAcc但需结合医学意义解读指标正常范围异常信号医学含义mIoU0.65~0.780.55持续5个epoch模型未学会区分鞍底与邻近斜坡骨质sella_turcica_IoU0.72~0.850.92且background_IoU骤降过拟合蝶鞍局部纹理忽略整体解剖上下文val_loss波动±0.03连续上升0.1数据增强过度如GaussianBlur核过大破坏骨性边界当val_loss连续3个epoch上升且sella_turcica_IoU下降时立即触发早停。此时检查work_dirs/下的vis_data/目录——用visualize.py脚本生成预测图重点观察鞍背与后床突交界处是否出现“阶梯状”分割断裂如下图示意真实掩码 ██████████ ← 平滑鞍背曲线 预测掩码 ████ ██ ███ ← 顶点缺失导致锯齿若存在此类现象需回退到LabelMe重新标注该批次图像而非调参。4. 验证分割结果临床可用性DICOM元数据融合与三维重建接口4.1 将分割结果写回DICOM文件的元数据字段单纯输出PNG掩码无法对接PACS系统。需将二值掩码嵌入DICOM的Overlay Data0019,100A私有标签供放射科医生在工作站直接查看import pydicom from pydicom.dataset import Dataset def write_mask_to_dicom(dcm_path, mask_path, output_path): ds pydicom.dcmread(dcm_path) mask cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) # 缩放至DICOM原始尺寸非512x512 orig_h, orig_w ds.Rows, ds.Columns mask_resized cv2.resize(mask, (orig_w, orig_h), interpolationcv2.INTER_NEAREST) # 创建Overlay Data序列 overlay_ds Dataset() overlay_ds.OverlayRows orig_h overlay_ds.OverlayColumns orig_w overlay_ds.OverlayData mask_resized.tobytes() # 二进制存储 overlay_ds.OverlayBitsAllocated 1 overlay_ds.OverlayBitPosition 0 ds.OverlayData overlay_ds ds.save_as(output_path)提示OverlayData要求mask为1-bit故需mask_resized (mask_resized 128).astype(np.uint8)二值化。此操作使结果可被GE、Siemens等主流PACS识别为“叠加层”。4.2 生成蝶鞍三维点云用于手术导航单张侧位片分割结果可作为三维重建的约束条件。通过open3d库将掩码转为点云并沿Z轴堆叠多期扫描如术前/术后import open3d as o3d import numpy as np def mask_to_pointcloud(mask_2d, z_depth0.0, spacing0.5): y_coords, x_coords np.where(mask_2d 0) points np.column_stack([ x_coords * spacing, y_coords * spacing, np.full(len(x_coords), z_depth) ]) pcd o3d.geometry.PointCloud() pcd.points o3d.utility.Vector3dVector(points) return pcd # 合并多期点云 pcd_list [] for i, mask_path in enumerate(mask_paths): mask cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) pcd mask_to_pointcloud(mask, z_depthi*3.0) # 层厚3mm pcd_list.append(pcd) merged_pcd o3d.geometry.PointCloud() for pcd in pcd_list: merged_pcd pcd o3d.io.write_point_cloud(sella_3d.ply, merged_pcd)生成的.ply文件可导入3D Slicer用“Segment Editor”模块平滑表面最终导出STL供3D打印蝶鞍模型——这正是该数据集区别于通用分割数据集的核心价值从像素标注直达临床实体应用。5. 提升标注效率的3个实战技巧批量预标注与跨模态校验5.1 用YOLOv8-seg做初始框选减少LabelMe 70%顶点操作虽标题强调“手动标注”但完全手绘1000张图不现实。我采用半自动流程先用YOLOv8-seg在侧位片上检测蝶鞍粗略区域导出*.txtYOLO格式再用脚本转为LabelMe可识别的polygon# yolov8_to_labelme.py def yolov8_to_labelme(yolo_txt, img_w, img_h, output_json): with open(yolo_txt) as f: lines f.readlines() shapes [] for line in lines: parts list(map(float, line.strip().split())) cls_id, *poly_norm parts if cls_id ! 0: continue # 0蝶鞍 # YOLO多边形为归一化坐标转为像素坐标 poly_px [] for i in range(0, len(poly_norm), 2): x int(poly_norm[i] * img_w) y int(poly_norm[i1] * img_h) poly_px.extend([x, y]) shapes.append({ label: sella_turcica, points: [poly_px[i:i2] for i in range(0, len(poly_px), 2)], shape_type: polygon }) # 写入LabelMe JSON模板...注意YOLOv8-seg的初始框选IoU通常仅0.4~0.5必须人工在LabelMe中用W键进入编辑模式拖拽顶点贴合真实骨性边界。此技巧将单图标注时间从8分钟降至2.5分钟。5.2 基于DICOM头信息的跨模态一致性校验同一患者可能有侧位片冠状位MRI需确保分割结果解剖一致。利用DICOM的PatientID和StudyInstanceUID字段关联多模态数据# 校验脚本伪代码 for dcm_file in dicom_dir.glob(*.dcm): ds pydicom.dcmread(dcm_file) if ds.Modality CR: # X光 cr_id f{ds.PatientID}_{ds.StudyInstanceUID} # 查找同ID的MRI文件 mri_files list(mri_dir.glob(f*{ds.PatientID}*)) if mri_files: # 对MRI掩码做刚性配准计算蝶鞍中心距误差 error_mm register_and_compare(cr_mask, mri_mask) if error_mm 3.0: # 超过3mm报警 print(fWarning: {dcm_file.name} misalignment {error_mm:.1f}mm)此校验能发现投照角度偏差导致的蝶鞍变形避免将病理变异误标为正常形态。5.3 LabelMe标签模板复用建立蝶鞍解剖结构标准词典为统一不同标注员对“鞍底”的理解创建labelme_template.json{ labels: [ {name: sella_turcica, color: #00FF00}, {name: tuberculum_sellae, color: #FF0000, parent: sella_turcica}, {name: dorsum_sellae, color: #0000FF, parent: sella_turcica}, {name: anterior_clinoid_process, color: #FFFF00, parent: sella_turcica} ] }在LabelMe中加载该模板后标注员右键即可选择子结构导出JSON自动包含group_id字段。训练时可启用instance segmentation分支单独预测各子结构——这为后续垂体瘤侵犯范围分析提供基础。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进