ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

血细胞检测数据集VOC+YOLO双格式实战:YOLOv8训练与避坑指南

血细胞检测数据集VOC+YOLO双格式实战:YOLOv8训练与避坑指南 简介本资源为血细胞检测数据集面向医学图像识别、细胞分类与目标检测方向的研究者及算法工程师可用于训练和验证血细胞检测模型。数据集同时提供Pascal VOC与YOLO两种标注格式包含jpg图片及对应的xml、txt标注文件标注工具为labelImg采用矩形框方式标注。压缩包共约2000个文件以1999个xml标注文件和1个说明txt为主整体大小约66.75MB便于直接接入常见检测框架。标注类别共4类分别为Platelets、RBC、WBC与sickle cell总框数达46143个其中RBC框数最多为39206其余三类框数在两千上下类别分布差异明显适合用于类别不平衡场景下的实验分析。目前已有389人学习下载可为血细胞检测、医学辅助诊断等任务提供准确且合理的标注数据基础。1. 血细胞检测数据集2757 张 VOCYOLO 双格式到底能干什么手里拿到一个压缩包名字叫「血细胞检测数据集VOCYOLO格式2757张4类别.7z」第一反应通常是这玩意儿能直接喂给 YOLO 吗答案是能但得先搞清楚它长什么样。这个数据集的核心价值在于同时提供了 VOC 和 YOLO 两套标注格式2757 张图像覆盖 4 个血细胞类别意味着你不需要从零标注也不用写格式转换脚本就能直接开训。血细胞检测在显微镜图像分析里是个经典任务红细胞、白细胞、血小板这些目标的形态差异大、密集程度高正好用来验证 YOLO 系列模型在小目标密集场景下的表现。适合谁刚入门 YOLO 想找个真实医学图像练手的、做检验科辅助诊断原型验证的、以及需要快速搭一个血细胞计数 demo 的工程师。但别急着解压就train.pyVOC 和 YOLO 两套格式的目录结构、类别映射、坐标归一化方式都不一样搞混了就是白跑一晚上。2. 拆开压缩包VOC 与 YOLO 双格式的目录结构和类别映射2.1 VOC 格式的 XML 长什么样四个类别怎么对应VOC 格式的核心是每张图配一个 XML 文件里面记录了图像尺寸、目标类别和边界框的绝对坐标。血细胞数据集里常见的四个类别通常是 RBC红细胞、WBC白细胞、Platelets血小板有的还会细分出 Neutrophil中性粒细胞或 Lymphocyte淋巴细胞。拿到数据集先别写代码用tree或文件管理器看一眼目录# 典型 VOC 结构 BloodCell_VOC/ ├── JPEGImages/ # 2757 张 jpg/png ├── Annotations/ # 2757 个 xml ├── ImageSets/ │ └── Main/ # train.txt val.txt trainval.txt test.txt └── SegmentationClass/ # 如果有分割掩码会放这里检测任务可忽略打开一个 XML 看看annotation folderJPEGImages/folder filenameblood_0001.jpg/filename size width640/width height480/height depth3/depth /size object nameRBC/name bndbox xmin112/xmin ymin89/ymin xmax156/xmax ymax134/ymax /bndbox /object !-- 可能还有多个 object -- /annotation这里的关键信息name是类别字符串bndbox是绝对像素坐标。四个类别的名称必须和你的data.yaml里的names列表顺序一致否则训练出来的模型会把红细胞认成白细胞。我一般会先跑个脚本统计一下每个类别的实例数量看看有没有严重的长尾分布——血细胞数据里血小板往往比红细胞少很多如果比例超过 1:10就得考虑用copy_paste或者mosaic增强来平衡。2.2 YOLO 格式的 txt 文件与归一化坐标YOLO 格式就简洁多了每张图对应一个.txt每行一个目标class_id x_center y_center width height全部是归一化到 0~1 的相对值。同样的图YOLO 标注长这样0 0.209375 0.232292 0.068750 0.093750 1 0.543750 0.610417 0.112500 0.145833第一列0对应data.yaml里names[0]也就是 RBC。注意这里的坐标是相对于图像宽高的比例不是像素值。如果你从 VOC 转 YOLO转换公式是# VOC 绝对坐标转 YOLO 归一化 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height这个转换看起来简单但坑在于VOC 的xmax和ymax是包含边界的有些标注工具会写成xmax xmin width - 1差一个像素在 640 分辨率下可能影响不大但在小目标上就是几个点的 IoU 差距。我一般会写个校验脚本把转换后的 YOLO 框反算回像素坐标和原 XML 对比偏差超过 2 像素的就人工检查。2.3 用 Python 脚本一键完成 VOC 到 YOLO 的转换与校验虽然数据集号称双格式但实际拿到手经常发现 YOLO 格式的labels文件夹是空的或者类别映射对不上。自己写个转换脚本最稳妥import xml.etree.ElementTree as ET import os from pathlib import Path from PIL import Image # 类别映射根据你的 data.yaml 顺序调整 CLASS_MAP {RBC: 0, WBC: 1, Platelets: 2, Neutrophil: 3} def voc_to_yolo(xml_path, img_path, output_dir): tree ET.parse(xml_path) root tree.getroot() img Image.open(img_path) w, h img.size lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_MAP: continue cls_id CLASS_MAP[name] bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 裁剪到图像边界防止越界 xmin max(0, min(xmin, w)) ymin max(0, min(ymin, h)) xmax max(0, min(xmax, w)) ymax max(0, min(ymax, h)) x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) out_path Path(output_dir) / (Path(xml_path).stem .txt) with open(out_path, w) as f: f.write(\n.join(lines)) # 批量处理 voc_root Path(BloodCell_VOC) yolo_labels Path(BloodCell_YOLO/labels) yolo_labels.mkdir(parentsTrue, exist_okTrue) for xml_file in (voc_root / Annotations).glob(*.xml): img_name xml_file.stem .jpg img_path voc_root / JPEGImages / img_name if img_path.exists(): voc_to_yolo(xml_file, img_path, yolo_labels)这段脚本做了三件事解析 XML、裁剪越界框、归一化写入 txt。参数说明CLASS_MAP必须和你的data.yaml完全一致顺序错了模型就学歪了max(0, min(xmin, w))这行是防止标注框超出图像边界血细胞图像边缘经常有半个细胞被截断的情况不裁剪的话 YOLO 训练时会报NaN损失。转换完记得抽查几个 txt用labelImg或labelme打开看看框有没有偏移。3. 用 YOLOv8 训练血细胞检测模型从 data.yaml 到第一个 checkpoint3.1 环境搭建与 data.yaml 的四个必填字段训练环境我一般用 conda 建个干净的环境避免和系统里的 CUDA 版本打架conda create -n bloodcell python3.10 -y conda activate bloodcell pip install ultralytics opencv-python pillowUltralytics 的 YOLOv8 对 PyTorch 版本有要求pip install ultralytics会自动拉取兼容的 torch。装完跑yolo checks确认 CUDA 可用。接下来是data.yaml这是整个训练流程的入口path: /home/user/BloodCell_YOLO train: images/train val: images/val test: images/test nc: 4 names: [RBC, WBC, Platelets, Neutrophil]四个必填字段path是数据集根目录train/val/test是相对路径nc是类别数names是类别名列表。注意names的顺序必须和转换脚本里的CLASS_MAP一致否则标签全乱。我见过有人把nc写成 5 但names只有 4 个训练直接报IndexError。另外path最好用绝对路径相对路径在不同工作目录下跑容易找不到文件。3.2 训练命令与关键超参imgsz、batch、lr0 怎么定血细胞图像分辨率一般不高640 或 512 就够用。2757 张图不算大batch size 设 16 或 32 都行显存 8G 的话 batch16 比较稳yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience20 \ device0 \ projectbloodcell_runs \ nameexp1参数逐个说modelyolov8n.pt是 nano 版本参数量小、推理快适合先跑通流程如果 mAP 不够再换yolov8s.pt或yolov8m.pt。imgsz640是输入分辨率血细胞目标普遍偏小如果原始图像是 1024 以上可以试试imgsz1024但显存占用会翻倍。lr00.01是初始学习率YOLOv8 默认用 SGD 优化器这个值比较稳如果 loss 震荡厉害降到 0.001。patience20是早停耐心值20 个 epoch 验证集 mAP 不涨就停防止过拟合。lrf0.01是最终学习率因子和余弦退火配合让学习率从 0.01 降到 0.0001。训练过程中重点看三个指标box_loss应该稳步下降如果震荡或上升检查学习率mAP50是 IoU0.5 时的平均精度血细胞检测一般能到 0.85 以上mAP50-95更严格能到 0.6 就算不错。如果mAP50高但mAP50-95低说明框的位置不够准可以试试调box损失权重或加更多数据增强。3.3 训练日志怎么看loss 曲线与 mAP 的异常信号YOLOv8 训练完会在runs/detect/exp1/下生成results.csv和一堆曲线图。我习惯先看results.png重点看train/box_loss和val/box_loss的差距。如果训练 loss 一直降但验证 loss 从某个 epoch 开始涨那就是过拟合了解决办法是加dropout、减小模型规模或者增加数据增强。另一个常见异常是mAP50卡在 0.5 左右上不去大概率是类别不平衡——血小板样本太少模型倾向于把血小板预测成背景。这时候可以在data.yaml里加cls权重或者用copy_paste增强专门复制血小板实例。还有一个玄学问题有时候mAP50很高但实际推理时漏检严重。这通常是验证集和测试集分布不一致导致的比如验证集里红细胞多测试集里白细胞多。解决办法是重新划分数据集用sklearn的train_test_split按类别分层抽样保证每个子集的类别比例一致。4. 血细胞检测的避坑与排查从标注错位到小目标漏检4.1 标注框偏移或类别错乱现象、原因与解决现象训练完模型推理时框的位置明显偏了或者把红细胞标成白细胞。原因VOC 转 YOLO 时坐标归一化用错了图像尺寸或者CLASS_MAP和data.yaml的names顺序不一致。解决写个校验脚本随机抽 20 张图把 YOLO txt 反算成像素坐标画在原图上和 XML 的框对比。如果偏移是系统性的所有框都往一个方向偏检查img_width和img_height是不是从 XML 的size里读的而不是从 PIL 读的——有些数据集 XML 里的尺寸和实际图像尺寸不一致。4.2 小目标漏检严重血小板为什么总被忽略现象红细胞和白细胞检测正常但血小板几乎全漏。原因血小板在图像中占比极小YOLO 的默认 anchor 尺寸偏大小目标特征在深层特征图上被稀释了。解决三个方向——第一提高输入分辨率到 1024 或 1280让小目标在特征图上占更多像素第二用yolov8-p2版本P2 层专门检测小目标第三在data.yaml里加overlap_mask和copy_paste0.3通过复制粘贴增加小目标样本密度。我实测下来imgsz1024copy_paste0.3能把血小板 mAP 从 0.4 拉到 0.7 左右。4.3 训练 loss 变 NaN越界框与空标签的排查现象训练几个 epoch 后 loss 突然变成nan或者报AssertionError: img size mismatch。原因标注框越界xmax 图像宽度或者某张图的 txt 文件是空的但图像还在。解决在转换脚本里加边界裁剪已经写了另外遍历所有 txt如果文件大小为 0要么删掉对应图像要么在data.yaml里设置ignore_emptyTrue。还有一个隐蔽原因图像损坏用cv2.imread读出来是None训练时就会崩。批量检查一遍import cv2 from pathlib import Path for img_path in Path(images/train).glob(*.jpg): img cv2.imread(str(img_path)) if img is None: print(f损坏图像: {img_path})4.4 验证集 mAP 虚高数据泄露与重复图像的坑现象验证集 mAP 0.95但测试集只有 0.6。原因训练集和验证集有重复图像或者同一张图的不同增强版本被分到了两个集合。解决用imagedhash或md5对图像去重确保每张图只出现在一个集合里。血细胞数据集经常从同一个显微镜视野裁剪出多张图这些图高度相似随机划分很容易泄露。我一般按患者 ID 或视野 ID 划分而不是按图像随机分。4.5 推理速度不达标TensorRT 加速与分辨率权衡现象训练时 mAP 不错但部署到边缘设备上帧率只有个位数。原因PyTorch 模型没做推理优化或者输入分辨率设太高。解决导出 ONNX 再用 TensorRT 加速yolo export modelbest.pt formatengine halfTrue。在 T4 上YOLOv8n 640 分辨率 FP16 大概能跑 200 FPS 以上但血细胞检测如果为了小目标把imgsz提到 1280帧率会降到 50 左右。权衡方法是先保证 mAP再根据硬件选最小可接受分辨率。如果必须高分辨率用yolov8n而不是yolov8mnano 版本在 TensorRT 上优势明显。5. 把 2757 张图用到极致分层采样、类别平衡与推理后处理5.1 按类别分层划分训练集与验证集2757 张图如果随机划分很可能验证集里某个类别只有个位数实例导致 mAP 波动大。用sklearn的StratifiedKFold按主类别分层from sklearn.model_selection import StratifiedKFold import numpy as np from pathlib import Path # 假设每张图的主类别是实例最多的那个 image_labels [] # 需要先统计每张图的类别分布 for txt in Path(labels).glob(*.txt): with open(txt) as f: classes [int(line.split()[0]) for line in f if line.strip()] if classes: # 取众数作为主类别 image_labels.append(max(set(classes), keyclasses.count)) else: image_labels.append(-1) skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) for train_idx, val_idx in skf.split(range(len(image_labels)), image_labels): # 按索引划分文件 ...这样每个折里四个类别的比例和全集一致验证集 mAP 更可靠。如果某个类别实例太少比如 Neutrophil 只有 50 个可以考虑合并到 WBC 大类里或者用过采样生成合成样本。5.2 用 copy_paste 和 mosaic 增强小类别YOLOv8 内置了mosaic和copy_paste增强在data.yaml同级目录建个hyps.yaml覆盖默认超参# hyps.yaml mosaic: 1.0 copy_paste: 0.3 mixup: 0.1 degrees: 10.0 translate: 0.1 scale: 0.5 shear: 2.0 perspective: 0.0 flipud: 0.0 fliplr: 0.5 hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4copy_paste0.3表示 30% 的概率把一个小目标复制粘贴到另一张图上对血小板这种小类别特别有效。mosaic1.0是四图拼接增加背景多样性。注意mixup在医学图像上要慎用血细胞形态固定mixup 可能生成不存在的细胞形态反而降低精度。我一般把mixup设 0.1 以下或者直接关掉。5.3 推理后处理置信度阈值与 NMS 的调参技巧训练完导出模型后推理时的conf和iou阈值直接影响最终计数结果。血细胞检测中红细胞密集区域容易产生重叠框NMS 的iou设太高会保留重复框设太低会误删相邻细胞。我一般这样调from ultralytics import YOLO model YOLO(best.pt) results model.predict( sourcetest_images/, conf0.25, # 置信度阈值低于此值的框丢弃 iou0.45, # NMS IoU 阈值 imgsz1024, augmentTrue, # 测试时增强提升小目标召回 saveTrue )conf0.25是起点如果漏检多就降到 0.15误检多就升到 0.4。iou0.45对血细胞比较合适因为细胞之间通常有间隙不需要太激进的合并。augmentTrue会做多尺度翻转推理能提升 1~2 个点的 mAP但速度会慢一倍。如果做细胞计数建议在推理后再加一个基于面积的过滤血小板面积通常小于 200 像素红细胞在 500~2000 之间白细胞大于 2000按面积过滤能去掉大部分误检。5.4 从检测框到细胞计数面积过滤与去重逻辑检测模型输出的是框但检验科要的是计数。直接数框数量会有重复计数问题——同一个细胞被多个框覆盖。除了 NMS还可以用基于距离的去重如果两个框的中心点距离小于较小框宽度的 0.5 倍就合并为一个。代码不复杂import numpy as np def merge_boxes(boxes, scores, classes, dist_ratio0.5): 合并中心点过近的同类框 keep [] for i in range(len(boxes)): merged False for j in keep: if classes[i] ! classes[j]: continue # 计算中心点距离 cx1 (boxes[i][0] boxes[i][2]) / 2 cy1 (boxes[i][1] boxes[i][3]) / 2 cx2 (boxes[j][0] boxes[j][2]) / 2 cy2 (boxes[j][1] boxes[j][3]) / 2 dist np.sqrt((cx1-cx2)**2 (cy1-cy2)**2) min_w min(boxes[i][2]-boxes[i][0], boxes[j][2]-boxes[j][0]) if dist min_w * dist_ratio: merged True break if not merged: keep.append(i) return keep这个逻辑对红细胞密集区域特别有用能把计数误差从 15% 降到 5% 以内。最后把计数结果按类别汇总输出 CSV 或 JSON对接检验科的报告系统。6. 验证模型是否真的学会了混淆矩阵、PR 曲线与 Grad-CAM 热力图训练完不能只看 mAP 数字得确认模型关注的是细胞本身而不是背景伪影。YOLOv8 训练时会自动生成混淆矩阵和 PR 曲线在runs/detect/exp1/下找confusion_matrix.png和PR_curve.png。混淆矩阵看对角线如果 RBC 和 WBC 之间有明显交叉说明模型分不清这两种细胞——可能是染色差异不够或者标注时把边缘细胞标错了。PR 曲线看每个类别的曲线下面积血小板如果曲线明显低于其他类别就得回去补样本或调增强。更直观的方法是 Grad-CAM 热力图看模型推理时注意力落在哪里import torch import cv2 import numpy as np from ultralytics import YOLO model YOLO(best.pt) model.model.eval() # 取最后一层卷积 target_layer model.model.model[-2] def grad_cam(img_path): img cv2.imread(img_path) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) input_tensor torch.from_numpy(img_rgb).permute(2,0,1).float().unsqueeze(0) / 255.0 features [] def hook(module, input, output): features.append(output) target_layer.register_forward_hook(hook) output model.model(input_tensor) # 取最大置信度的检测框对应的特征 # 这里简化处理实际需要根据输出结构取梯度 ...热力图如果高亮区域集中在细胞核或细胞膜上说明模型学到了形态特征如果高亮在背景或载玻片边缘那就是过拟合了。我一般会抽 10 张验证集图像跑一遍肉眼确认热力图合理再部署。说个我自己的习惯每次训完模型先不急着看 mAP而是把验证集的前 20 张图推理结果拼成一张大图肉眼扫一遍。漏检、误检、框偏移这些问题看一眼比看十张曲线图都管用。血细胞检测这个方向数据质量比模型结构重要得多2757 张图如果标注干净YOLOv8n 就能跑出不错的效果标注有噪声的话换再大的模型也是白搭。希望帮到你。本文还有配套的精品资源点击获取
返回列表