
简介乳腺癌细胞分割数据集面向医学图像分析与深度学习领域的研究者和初学者提供58张经HE染色的乳腺癌组织病理学图像及对应xml格式的真实标注文件可用于训练细胞分割模型并衔接后续的良性与恶性细胞分类任务。染色使细胞核等结构清晰可见但细胞密集、形态差异大分割本身具有典型挑战性适合作为语义分割、实例分割等算法的验证数据。压缩包共232个文件包含116张tif原图与116个xml标注整体约93.7MB文件命名包含benign/malignant及病例编号便于按类别划分训练集与测试集。目前已有270人学习下载可直接对接PyTorch、TensorFlow等框架完成数据加载与模型评测帮助使用者省去病理图像采集和手工标注成本快速开展乳腺癌细胞检测相关实验。1. 乳腺癌细胞分割图片数据集先搞懂它在解决什么问题病理科医生在显微镜下数癌细胞核一天看几百张图眼睛酸是小事漏掉几个不典型的核才是大事。乳腺癌细胞分割这个方向本质上就是让模型在 HE 染色的病理切片上把每个细胞核的边界自动勾出来。而“图片数据集”这四个字决定了这件事能不能落地——没有像素级标注的图片分割模型训得再好也只是论文里的演示。这个标题指向的不是某个单一文件包而是一整套“原始病理图 掩膜标注 训练划分”的组合资产服务的是语义分割、实例分割这类任务的训练与评测。适合正在做医学影像 AI、病理辅助诊断或者拿公开数据集打比赛的算法工程师知道它是什么、怎么建、怎么用、坑在哪比到处找“现成下载地址”更重要。2. 数据从哪来公开集选型与自建标注流程做细胞分割的数据集第一步永远不是写模型而是搞清楚你手上的数据到底是什么粒度。整张 WSI全切片扫描图和单细胞核标注是两回事组织区域分级和细胞级分割也是两回事。标题里“细胞分割”四个字直接把任务锁死在细胞核这一级这意味着标注成本高、标注标准敏感、模型输入输出都是像素级。想少走弯路先是选对数据来源再是规范自建流程最后是科学划分数据集这三件事决定了后面训练阶段是顺风还是逆风。2.1 先看标注粒度公开集选型的三个标准圈内常见的公开病理数据集里BACH、TNBC、MoNuSeg、NuInsSeg 这类名字经常被放在一起讨论但它们的标注粒度完全不同。BACH 偏组织学图像分级给的是整张图的类别TNBC 是乳腺癌肿瘤细胞核分割的经典选择直接给核掩膜MoNuSeg 覆盖多个器官的细胞核分割NuInsSeg 更偏小目标密集场景。选型时只看“文件名带不带 breast”远远不够我用三个标准过一遍第一标注粒度是不是“细胞核边界”。要做实例分割或语义分割的细胞任务必须选带像素级掩膜的数据集区域级标签再标准也用不上。第二染色风格和你待处理的病理图是否接近。不同医院、不同制片批次的 HE 染色差异很大公开集是浅染还是深染直接决定要不要额外做染色归一化。第三有没有配套的评测协议。很多公开集自带划分好的训练/验证/测试集和官方指标比如 F1、Dice、AJI跟着协议走你的模型分数才能和别人横向比较。关注点选型检查项常见误用标注粒度是否有细胞核级掩膜拿组织分级标签当细胞分割标签染色风格HE 深浅、核质比忽略染色差异导致跨集泛化差评测协议是否自带划分和指标自己随意 split分数不可比选公开集只是第一步。实际项目里公开集往往不够用——细胞形态、染色风格、癌与非癌比例都和你自己的临床场景有差距。这时候就得自建标注流程这也是数据资产真正值钱的地方。2.2 自建标注从 WSI 切 patch 到细胞级勾画病理科拿到的原始文件通常是 WSI一张几十亿像素的 svs/ndpi 文件。整张图喂给语义分割模型不现实显存放不下而且细胞核在 40 倍物镜下才清晰。自建数据集的第一个动作是按固定物理尺寸切 patch。常见做法是先用 OpenSlide 读 WSI把“目标倍数下对应的像素尺寸”算出来再按固定 stride 滑窗保存只保留细胞密度达标的 patch避免把大片空白背景也存进去。import openslide import cv2 import numpy as np from pathlib import Path slide_path case_001.svs out_dir Path(patches/case_001) out_dir.mkdir(parentsTrue, exist_okTrue) slide openslide.OpenSlide(slide_path) # 40x 物镜下 patch 尺寸为 512x512对应 0.25 微米/像素 target_mpp 0.25 native_mpp float(slide.properties[openslide.PROPERTY_NAME_MPP_X]) downsample native_mpp / target_mpp level slide.get_best_level_for_downsample(downsample) # 该 level 下实际放大倍数 scale slide.level_downsamples[level] patch_size 512 step 256 # 步长 256patch 之间 50% 重叠保留上下文 for y in range(0, slide.level_dimensions[level][1], step): for x in range(0, slide.level_dimensions[level][0], step): # 读入后缩放到 0.25 mpp 分辨率 img slide.read_region((int(x * scale), int(y * scale)), level, (patch_size, patch_size)) arr np.array(img.convert(RGB)) # 简单过滤细胞密集区域才保留背景占比超过 80% 则丢弃 gray cv2.cvtColor(arr, cv2.COLOR_RGB2GRAY) bg_ratio np.sum(gray 245) / (patch_size * patch_size) if bg_ratio 0.8: continue cv2.imwrite(str(out_dir / fpatch_{x}_{y}.png), arr)这段代码的逻辑有三个点要说明。第一get_best_level_for_downsample是根据目标物理分辨率选最合适的金字塔层级不要自己硬算 level避免读出模糊影像。第二read_region的坐标和尺寸必须以 level 0 为基准代码里把x * scale传进去就是保证坐标基准不错位。第三背景过滤用灰度阈值并非常规分割只用来在存储阶段省空间和减少无效标注。step 256这种重叠滑窗会在推理阶段造成重复预测训练阶段倒没问题能让模型在不同位置上见过同一细胞相当于隐式增强。切出 patch 后标注工具我在用 QuPath 和 Labelme 之间切换。QuPath 适合先跑一个自动检测初稿再人工修正Labelme 适合纯手动勾多边形。标注准则比工具更重要细胞核边界以核膜连续处为准核浆重叠的紧密接触细胞必须单独标实例不能图省事画一个合并多边形。做语义分割时可以接受“核粘连区域合并为同一前景”但一旦后面想切到实例分割那些合并标注全部要返工。2.3 数据集划分按患者划分而不是按 patch 划分这是自建数据集最容易被忽略、却又最影响模型可信度的一步。很多人随手用 sklearn 的train_test_split把几千张 patch 随机划分结果同一个患者的上百个 patch 同时出现在训练集和验证集里。由于同一患者的切片在染色、组织背景、细胞形态上高度相似验证集分数会虚高等到换一家医院的数据测试立刻翻车。import pandas as pd from sklearn.model_selection import train_test_split df pd.DataFrame({ patch_path: [patches/case_001/patch_0_0.png, patches/case_001/patch_0_256.png, patches/case_002/...], patient_id: [case_001, case_001, case_002] }) # 以患者为粒度划分同一患者的所有 patch 全部进同一集合 unique_patients df[patient_id].unique() train_patients, val_patients train_test_split( unique_patients, test_size0.2, random_state42 ) train_df df[df[patient_id].isin(train_patients)] val_df df[df[patient_id].isin(val_patients)] print(ftrain patches: {len(train_df)}, val patches: {len(val_df)})这里的参数只有一个random_state但它的作用不只是可复现。种子固定后后续增删 patch 或更换预处理流程划分边界不会漂移实验结果才能直接对比。test_size 0.2是惯例值但病理场景我更建议按“患者数”而不是“patch 数”来配比如果总共只有 10 个患者至少留 3 个做验证哪怕这样验证 patch 数量变少。数据量实在不够时宁可用 K 折交叉验证按患者分组也别用随机划分来刷一个虚高的分数。3. 把标注变成模型能吃的数据格式转换与预处理标注完成后你手上是一堆 JSON 多边形而模型需要的是和原图尺寸对齐的掩膜图。这一步的转换质量直接决定后面语义分割训练是快速收敛还是反复炸 loss。这里有三件必做的事多边形转掩膜、染色归一化、数据增强。前两件事做不好是硬伤第三件事做过头是玄学但都能在早期埋下肉眼可见的隐患。3.1 从 polygon 到单通道掩膜两个格式坑Labelme 导出的 JSON 里每个细胞的轮廓是一串多边形顶点。转换掩膜时最常见的两个坑一是顶点坐标对应的图形尺寸和原图不一致二是多边形没有按顺序排列导致填充区域错乱。前者一般是因为 QuPath 导出时坐标基准在 40 倍而原图存的是 20 倍后者则要检查顶点排序是否逆时针连续。import json import cv2 import numpy as np def json_to_mask(json_path, img_h512, img_w512): with open(json_path, r, encodingutf-8) as f: data json.load(f) # 语义分割掩膜前景为 1背景为 0 semantic_mask np.zeros((img_h, img_w), dtypenp.uint8) # 实例分割掩膜每个细胞一个递增 id从 1 开始 instance_mask np.zeros((img_h, img_w), dtypenp.int32) for idx, shape in enumerate(data[shapes], start1): points np.array(shape[points], dtypenp.float32) # 关键坐标可能带小数必须转成 int 且检查是否在图像范围内 points np.round(points).astype(np.int32) points[:, 0] np.clip(points[:, 0], 0, img_w - 1) points[:, 1] np.clip(points[:, 1], 0, img_h - 1) cv2.fillPoly(semantic_mask, [points], color1) cv2.fillPoly(instance_mask, [points], coloridx) # 检查掩膜与原图是否对齐打印细胞数量与前景占比 n_cells instance_mask.max() fg_ratio semantic_mask.mean() print(fjson: {json_path}, cells{n_cells}, fg_ratio{fg_ratio:.3f}) return semantic_mask, instance_mask这段代码保留了两份掩膜语义分割只用 0/1 前景实例分割用递增 id。参数里最值得留意的是np.clip它把越界顶点拉回图像边界防止fillPoly因为顶点自交或越界画出错误的形状。每次转换后打印fg_ratio是一个习惯细胞核在 512x512 patch 里的面积占比一般在 5%–30%如果某张图突然显示 0.5% 或 80%几乎可以肯定是原图和标注对不上或者坐标基准错位趁早回查标注比等训练完再排查省时间。3.2 染色归一化跨院泛化的第一道关HE 染色的深浅在每家医院都不一样同一家医院不同批次的玻片也有肉眼可见的色差。模型在深紫色的数据集上训练拿到浅粉色切片上推理IoU 能掉十几个点。语义分割模型对颜色分布极其敏感所以染色归一化不是锦上添花而是细胞分割数据集预处理的标准动作。常见做法是 Macenko 或 Reinhard 算法前者通过颜色反卷积分解染色矩阵后者直接把目标图的均值和标准差匹配上来。import cv2 import numpy as np def reihard_stain_norm(src_img, target_mean, target_std): Reinhard 染色归一化把 src 的颜色分布匹配到 target 的分布 lab_src cv2.cvtColor(src_img, cv2.COLOR_RGB2LAB).astype(np.float32) lab_target_mean np.array(target_mean, dtypenp.float32) lab_target_std np.array(target_std, dtypenp.float32) for c in range(3): ch lab_src[:, :, c] ch - ch.mean() ch * (lab_target_std[c] / (ch.std() 1e-6)) ch lab_target_mean[c] lab_src[:, :, c] np.clip(ch, 0, 255) return cv2.cvtColor(lab_src.astype(np.uint8), cv2.COLOR_LAB2RGB) # 目标统计量从一张“理想染色”的参考图里算出来 ref_img cv2.imread(reference_stain.png) ref_lab cv2.cvtColor(ref_img, cv2.COLOR_BGR2LAB).astype(np.float32) target_mean ref_lab.mean(axis(0, 1)) target_std ref_lab.std(axis(0, 1)) norm_img reihard_stain_norm(cv2.imread(case_patch.png), target_mean, target_std)这段实现里有两个参数是必须人工盯住的target_mean和target_std必须来自同一张参考图且参考图要选染色标准、核边界清晰的切片1e-6是防御性加项防止某通道标准差为零时除零。注意测试阶段也要用同一份参考图统计量不能训练用 A 图、测试又换 B 图否则数据分布又变了。Macenko 比 Reinhard 更稳健因为它在颜色反卷积空间操作但实现也更长工程上先用 Reinhard 跑通流程效果不够再换 Macenko。3.3 数据增强哪些有效、哪些是玄学细胞分割任务里增强策略和其他自然图像任务不太一样。旋转、翻转、弹性形变是确定有效的因为细胞核在切片里方向随机组织在制片过程中也会有形变而色彩抖动对 HE 图像的增强效果受染色归一化影响做过头直接失真。albumentations 是顺手且易用的选择参数设置上有几个值得记住的边界。import albumentations as A train_transform A.Compose([ A.RandomRotate90(p0.5), A.HorizontalFlip(p0.5), A.VerticalFlip(p0.5), A.ElasticTransform( alpha1.0, sigma50.0, alpha_affine15, p0.3, border_modecv2.BORDER_REFLECT_101 ), A.RandomBrightnessContrast(brightness_limit0.1, contrast_limit0.1, p0.3), A.RandomGamma(gamma_limit(80, 120), p0.2), ], additional_targets{mask: mask})这里RandomRotate90只做 90 度的整数倍旋转避免了插值带来的边界模糊ElasticTransform是细胞分割中最值得开的增强alpha 控制变形幅度1.0 是安全值调太大会把细胞核扯成不自然的形状亮度对比度限制在 0.1 以内因为 HE 染色本身对比度变化剧烈再加强度容易毁掉核质边界信息。border_mode用BORDER_REFLECT_101比默认补零好不会在 patch 边缘产生黑色伪影干扰模型。增强不要什么都加该舍弃的场景我会直接写进代码注释高斯模糊、光学畸变、粗粒度的 Cutout 对细胞核这类毫米级小目标都是负优化。4. 让语义分割模型在数据集上跑起来YOLOv8 与 nnU-Net 两条路线数据和预处理就绪后选模型成了下一个决策点。细胞分割界没有“最好”的模型只有最顺手的路线。YOLOv8 自带实例分割分支适合工程落地和快速验证nnU-Net 是医学影像语义分割的标配自动化程度高适合追求指标的学术验证。这两条路线的取舍逻辑我会按从业者的视角讲清楚。4.1 算法选型语义分割还是实例分割标题里的“细胞分割图片数据集”可以喂给两种模型语义分割输出像素级前景/背景实例分割还要区分每个细胞。绝大多数病理场景需要实例信息——细胞核的数量、密度、形态学特征都依赖实例边界。但语义分割也不该直接否定它更适合后续做“区域分割”或“先分割再后处理”的流程。模型路线输出形式后处理成本对细胞粘连的适应度工程化程度U-Net / nnU-Net逐像素类别需分水岭等实例拆分一般中YOLOv8-seg轮廓掩膜 实例 id低自带 NMS好高Cellpose向量场 掩膜中好中我的经验是纯粹做研究和指标对比优先 nnU-Net做产业落地或远程会诊系统优先 YOLOv8-seg因为它自带实例 id 和置信度后处理链路短。Cellpose 是细胞分割专用模型内置尺寸模型但它的训练范式比较特殊和标准分割数据集的组织方式不太一样这里不展开。4.2 用 YOLOv8 训练自己的数据集最小命令集YOLOv8-seg 训练细胞分割数据集需要把掩膜转成 YOLO 格式的文本标注。常见做法是遍历每张掩膜图用cv2.findContours提取每个实例的外轮廓把归一化坐标写进 txt 文件。转换脚本逻辑不复杂但有个细节轮廓顶点过多会导致训练时数据加载变慢可以先用cv2.approxPolyDP做轮廓简化。# 组织数据目录 # datasets/breast_cell/ # train/images/ train/labels/ # val/images/ val/labels/ # breast_cell.yaml # breast_cell.yaml 内容 # train: datasets/breast_cell/train/images # val: datasets/breast_cell/val/images # nc: 1 # names: [cell]然后用下面的命令训练yolo tasksegment modetrain \ modelyolov8s-seg.pt \ datadatasets/breast_cell/breast_cell.yaml \ imgsz512 \ epochs100 \ batch8 \ device0 \ patience20 \ projectruns/breast_seg \ nameexp1这几个参数值得仔细说。imgsz512对应 2.2 节切 patch 的尺寸不要在 512 的 patch 上强行训练 640小目标反而会被放大。batch8是 12G 显存的安全值显存不够时优先关patience而不是降batch因为早停被关掉后模型可能过拟合到后段。device0指定单卡多卡用0,1即可但数据量不大时多卡收益有限。训练完成后用下面的命令验证yolo tasksegment modeval \ modelruns/breast_seg/exp1/weights/best.pt \ datadatasets/breast_cell/breast_cell.yaml \ imgsz512YOLOv8 自带的 mask 指标会输出 mAP50-95 和 mask precision/recall。细胞分割场景里mAP50 比 mAP50-95 更接近真实感受因为细胞边界本身的标注误差就存在过严的 IoU 阈值会低估模型能力。建议以 mAP50 和 mask recall 为主mAP50-95 作为辅助参考。4.3 nnU-Net 路线三步命令与自动配置如果目标是发论文或参加医学影像算法比赛nnU-Net 是更省心的选择。它的核心逻辑是自动根据数据集的 spacing、图像尺寸和显存来制定训练计划不需要手动调 batch、lr 这些超参。先把数据集按它的要求组织imagesTr、labelsTr、imagesTs 目录图像命名带 case 编号标签为 png/ni 格式。# 1. 规划与预处理自动分析数据集并生成训练配置 nnUNetv2_plan_and_preprocess -d DATASET_ID -c 2d # 2. 训练fold 0 用于快速验证全量训练用 all nnUNetv2_train DATASET_ID 2d fold0 --np 4 # 3. 推理指定训练好的模型和输入输出目录 nnUNetv2_predict -i /path/to/input_images \ -o /path/to/predictions \ -d DATASET_ID -c 2d -f fold0-c 2d表示 2D 训练因为细胞分割的病理图天然是二维平面结构用 3D 模式反而把 z 维度强行拉进来增加开销。--np 4是数据加载进程数数值不要超过 CPU 核数的一半否则预处理瓶颈会挡住 GPU 训练。nnU-Net 会自动做归一化和数据增强在病理分割任务上往往能比手工调参的 U-Net 高出 3–5 个点 Dice代价是训练时间更长、配置灵活性更低。先跑一遍 fold0 确认 pipeline 没报错再启动全量训练这是省时间的习惯。5. 细胞分割数据集训练的五大坑从掩膜错位到染色偏差细胞分割数据集的训练过程翻车点比自然图像任务多且隐蔽。下面这五条每一条我都踩过有的让 loss 一路不降有的让验证集指标虚高到不敢相信。按“现象 → 原因 → 解决”写清楚算是给后来者的一份排雷手册。5.1 掩膜与原图尺寸对不上模型 loss 不降现象训练 loss 在第一个 epoch 就卡在 0.6 上下不动训练集上预测结果和标注明显错位差几个像素到几十个像素不等。原因最常见的是 WSI 切 patch 时read_region的坐标参考和标注导出的坐标参考不一致QuPath 标注在 40 倍下进行而切图用了 level 1 又没做坐标换算。解决统一以 patch 图像保存的物理分辨率为基准重新确认比例因子在转换脚本里打印掩膜和原图的尺寸及前景分布肉眼抽查三到五张图确认边缘对齐。def assert_mask_alignment(image_path, mask_path, tolerance_px2): img cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) mask cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE) if img.shape ! mask.shape: raise ValueError(fshape mismatch: {img.shape} vs {mask.shape}) # 取细胞核密度最高的区域做边缘对比 edges_img cv2.Canny(img, 30, 100) edges_mask cv2.Canny(mask, 30, 100) overlap np.sum((edges_img 0) (edges_mask 0)) union np.sum((edges_img 0) | (edges_mask 0)) if union 0 and (overlap / union) 0.5: print(fWARNING: {image_path} alignment suspicious)这段脚本的核心是tolerance_px边界容忍度细胞核边缘本身就有标注者之间的人为差异2–3 像素内都算正常低于 0.5 的边界重合率就该回查。每次转换完标注跑一遍能拦下大部分错位问题比等到训练完再排查省几十个小时。5.2 细胞像素占比太少模型全预测背景现象Dice 在验证集上看起来还行但预测图几乎全黑只有零星几个白点。原因细胞核在 512x512 的图像里占比普遍只有 5%–15%如果 loss 函数没有做类别均衡模型学到的“最优解”就是把所有像素预测为背景。解决用带权重的 Dice loss 或 Focal loss同时配合 2.2 节的背景过滤把前景占比过低的 patch 在标注阶段就删掉。import torch import torch.nn.functional as F def weighted_dice_loss(pred, target, smooth1.0): # pred: [B, 1, H, W] 概率图target: [B, 1, H, W] 二值掩膜 pred torch.sigmoid(pred) intersection (pred * target).sum(dim(2, 3)) union pred.sum(dim(2, 3)) target.sum(dim(2, 3)) dice (2 * intersection smooth) / (union smooth) # 对前景占比小的样本加大权重 foreground_ratio target.mean(dim(2, 3)) weight torch.where(foreground_ratio 0.1, 1.5, 1.0).unsqueeze(1) return (1 - dice) * weight.mean()smooth设为 1.0 是数值稳定性考虑不建议完全置 0。weight的 1.5 倍是我在细胞分割上多次试出的平衡值如果你用 U-Net 类模型直接在损失函数里这样加权即可不用改网络结构。如果换用 YOLOv8-seg 路线它的 loss 自带前景权重一般不需要额外处理。5.3 polygon 自交或不闭合转 mask 时轮廓消失现象标注的细胞在 JSON 里看是完整的转出来的掩膜却缺了一个角甚至整个实例没填充。原因多边形顶点不严格按照顺/逆时针排列有自相交或顶点之间存在微小缝隙fillPoly在处理异常轮廓时直接跳过。解决在转换脚本里加一步shapely.make_valid修复几何再重新提取外环顶点。from shapely.geometry import Polygon from shapely.validation import make_valid def repair_polygon(points): polygon Polygon(points) if not polygon.is_valid: polygon make_valid(polygon) # 取最大外环多边形排除内部孔洞干扰 repaired polygon.convex_hull coords list(repaired.exterior.coords) return np.array(coords, dtypenp.int32)make_valid是 shapely 里的修复入口它会自动处理自交和坐标折叠但会引入凸包化导致原本凹陷的细胞边界被拉平。所以这条修复只作为兜底转换后要逐张抽查掩膜和原图的贴合度。真正干净的方案是在标注阶段要求标注员闭合多边形只有少数历史数据需要靠这种脚本挽救。5.4 在公开集训练很好在自己的数据上翻车现象在 TNBC 上能到 0.85 Dice一换自己医院的切片直接掉到 0.6。原因染色风格、切片厚度、核形态分布都不一样这是典型的域漂移。解决先用 3.2 节的染色归一化把公开集和自有数据拉到同一颜色空间再在训练时把公开集数据按一定比例混合进来如果数据量允许用自己的数据做微调是见效最快的方式。# 假设公开集在 public/自有数据在 private/ # 混合训练时按 1:1 比例交替采样 # 每个 epoch 内一半 batch 来自 public一半来自 private python train.py --public-dir datasets/public \ --private-dir datasets/private \ --mix-ratio 0.5mix-ratio是混合训练时的关键参数。0.5 对半混合适合私有数据量不足的情况私有数据量充足时升到 0.8 以上。同时注意两张图不要来自同一个患者避免域内数据高度重合造成过拟合。5.5 相邻细胞融合成一个实例评估分数上不去现象预测结果里紧密接触的细胞核被模型画成一个整体实例 id 数量比实际少很多AJIAggregated Jaccard Index分数只有 0.5 出头。原因语义分割 loss 只看像素级重叠不看实例边界两个细胞核的膜在低分辨率下贴在一起模型分不开。解决切换到实例分割模型或在原有预测上做分水岭后处理。import cv2 def split_touching_cells(binary_mask, min_distance3): # 距离变换后做分水岭把粘连的细胞核切开 dist cv2.distanceTransform(binary_mask, cv2.DIST_L2, 5) _, markers cv2.connectedComponents(binary_mask) markers markers.astype(np.int32) # 给背景留标记否则分水岭会把整个边界都当盆地 markers[binary_mask 0] 0 ws cv2.watershed(cv2.cvtColor(binary_mask * 255, cv2.COLOR_GRAY2BGR), markers) return wsmin_distance是分水岭切割的最小细胞间距设得越小越能在密集区域切出实例但也越容易把单个细胞一切两半。这个参数需要根据标注数据的平均核直径来调细胞核直径 10–15 像素时3 是安全的起点。分水岭是语义分割输出的常见补救方案绕不开但也不是万能的——细胞核膜完全重叠时靠后处理永远不如换个实例分割模型。6. 评估与进阶别只看 Dice细胞级指标才是关键细胞分割的评估比自然图像分割更苛刻。Dice 只反映像素级重叠两个预测实例合并成一个时Dice 可能只降几个点但临床上这种错误不可接受。业内更看重 AJI 和 PQPanoptic Quality这类实例级指标。AJI 对“合并错误”的惩罚极其严厉两个核被预测成一个整个实例的匹配就失败分数直接掉一截。def compute_aji(pred_mask, gt_mask): Aggregated Jaccard Index实例级重合度 pred_ids np.unique(pred_mask)[1:] gt_ids np.unique(gt_mask)[1:] matched_preds set() total_inter, total_union 0, 0 for g in gt_ids: gt_area gt_mask g best_iou, best_pred 0, None for p in pred_ids: if p in matched_preds: continue inter np.sum(gt_area (pred_mask p)) union np.sum(gt_area | (pred_mask p)) iou inter / union if union 0 else 0 if iou best_iou: best_iou, best_pred iou, p if best_pred is not None and best_iou 0.5: matched_preds.add(best_pred) total_inter np.sum(gt_area (pred_mask best_pred)) total_union np.sum(gt_area | (pred_mask best_pred)) else: total_union np.sum(gt_area) unmatched_pred np.sum(np.isin(pred_mask, list(set(pred_ids) - matched_preds))) total_union unmatched_pred return total_inter / total_union if total_union 0 else 0iou 0.5是匹配阈值和 YOLO 的 mAP50 对齐是病理分割领域默认选择。AJI 的实现细节在各论文中略有差异但核心思想一致未匹配的真值实例和预测实例都要计入分母只有匹配成功的交集计入分子因此合并错误和分裂错误都会被严重惩罚。进阶方向上我的建议是伪标签半监督。细胞分割的瓶颈几乎都在标注量用训练好的模型在高置信度区域生成伪标签再把伪标签和真实标注混合训练第二轮通常能在不增加标注成本的情况下提升 2–3 个点 AJI。操作时要控制伪标签占比建议不超过 30%且只保留模型预测中实例内部置信度大于 0.9 的细胞核。另一个进阶方向是 WSI 全片推理直接对整个 WSI 预测时要注意 patch 之间重叠区域的预测不一致用重叠区域平均或几何平均融合比简单拼接更稳。我第一次做乳腺癌细胞分割时只盯 Dice模型跑出 0.9 就以为能上了直到病理医生指出“这个细胞被分成了两半这个和旁边那个又没分开”。后来换成 AJI 和 PQ 做验收模型才真正变得可用。指标选得对不对决定了你整个 pipeline 值不值得信。希望帮到你。本文还有配套的精品资源点击获取