ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于YOLO的血细胞识别与计数系统:源码、数据集与PyQt界面全解析

2026/9/28 16:43:12 拓冰建站 浏览量
基于YOLO的血细胞识别与计数系统:源码、数据集与PyQt界面全解析 简介这份资源面向医学图像处理与深度学习入门者提供一套基于YOLO模型的红细胞、白细胞、血小板识别与计数系统完整实现可用于临床辅助诊断研究或课程设计实践。压缩包共约2000个文件以txt标注数据为主辅以yaml配置、py源码、xml标注及css样式文件整体约276.9MB涵盖数据集、训练模型、用户界面程序与测试图片等模块。其中UIProgram提供可视化操作界面models存放可直接预测的模型文件datasets与save_data分别管理数据集和训练过程指标并集成wandb工具跟踪实验。已有85人学习下载。读者可获取从数据预处理、模型训练到界面交互的完整代码与数据理解YOLO在血细胞检测中的落地流程并借助现成模型快速验证计数效果适合作为医学AI项目的参考方案。1. 血细胞计数不再靠显微镜这套 YOLO 源码包能跑出什么结果手里有一批血涂片图像要数红细胞、白细胞、血小板人工在显微镜下一个个点一天下来眼睛发花计数板还得反复清洗。这是很多检验科和生物医学实验室的日常。这套基于 YOLO 的红细胞、白细胞、血小板识别与计数系统就是冲着这个场景来的Python 写的完整源码带数据集、带训练好的模型、带 PyQt 图形界面解压后配好环境就能跑推理非技术用户点几下也能出计数结果。它适合三类人一是做医学图像检测课题的学生需要一套能跑通、能改、能写进论文的基线二是想学 YOLO 目标检测落地的 Python 开发者这套代码把数据标注、训练、推理、UI 串成了完整链路三是实验室里想先验证自动化计数可行性的人拿现成模型跑一批图看准确率够不够用。项目正文里列出的UiMain.py、ui_sources.py、QssLoader.py、precess_bar.py这些文件说明界面层和推理逻辑是分开的改起来不会牵一发动全身。需要先说明一点血细胞里白细胞有内核形态和红细胞、血小板差异明显但红细胞之间粘连、血小板面积极小这两类才是检测精度的主要瓶颈。后面会围绕数据集格式、训练参数、推理阈值、UI 集成几个环节把能复现的步骤和容易翻车的地方讲清楚。2. 数据集与标注格式YOLO 训练前的第一道关2.1 三类目标的标注差异与类别定义这套系统的检测类别是三类红细胞、白细胞、血小板。在 YOLO 的标注体系里类别顺序一旦定下来就不能中途改否则训练时标签和模型输出对不上损失会一直震荡。常见做法是在datasets目录下放一个data.yaml里面写清楚类别数和类别名# datasets/data.yaml path: ./datasets # 数据集根目录 train: images/train # 训练集图像路径 val: images/val # 验证集图像路径 nc: 3 # 类别数量红细胞、白细胞、血小板 names: 0: RBC # 红细胞 Red Blood Cell 1: WBC # 白细胞 White Blood Cell 2: PLT # 血小板 Platelet这里nc: 3和names的索引必须严格对应标注文件里的类别编号。我见过有人把白细胞写成 0、红细胞写成 1结果训练完推理时把红细胞全标成白细胞计数直接反了。标注文件是 YOLO 格式的.txt每行一个目标格式为类别编号 中心x 中心y 宽 高坐标全部归一化到 0 到 1 之间。项目正文里出现的00096.txt、01095.txt就是这类标注文件和TestFiles里的图片一一对应。血小板的标注要特别小心。血小板在图像里可能只有几个像素宽标注框稍微大一点就会框进背景小一点又漏掉目标。我的经验是血小板标注框紧贴边缘即可不要为了“看起来完整”往外扩扩出去的背景像素会干扰回归。2.2 数据划分与预处理脚本数据集不能全部拿来训练必须留出验证集。常见做法是按 8:1:1 划分训练、验证、测试但血细胞图像如果来自同一张涂片的多个视野要按涂片来源划分不能随机打散否则同一张涂片的相似图像同时出现在训练和验证集里验证指标会虚高。下面是一个按文件列表划分的脚本import os import random import shutil # 原始图像和标注目录 img_dir datasets/all_images lbl_dir datasets/all_labels # 输出目录 for split in [train, val, test]: os.makedirs(fdatasets/images/{split}, exist_okTrue) os.makedirs(fdatasets/labels/{split}, exist_okTrue) # 收集所有标注文件对应的图像名 names [f[:-4] for f in os.listdir(lbl_dir) if f.endswith(.txt)] random.seed(42) # 固定随机种子保证可复现 random.shuffle(names) n len(names) n_train int(n * 0.8) n_val int(n * 0.1) splits { train: names[:n_train], val: names[n_train:n_train n_val], test: names[n_train n_val:] } for split, file_list in splits.items(): for name in file_list: # 复制图像 shutil.copy(f{img_dir}/{name}.jpg, fdatasets/images/{split}/{name}.jpg) # 复制标注 shutil.copy(f{lbl_dir}/{name}.txt, fdatasets/labels/{split}/{name}.txt) print(划分完成, {k: len(v) for k, v in splits.items()})random.seed(42)这行别省否则每次跑划分结果都不一样实验没法对比。n_train、n_val的比例可以按数据量调整数据少于 500 张时验证集至少留 50 张不然指标波动太大。复制而不是移动是为了保留原始数据标注改错了还能回头。2.3 标注质量自查训练前一定要抽查标注。写个小脚本把标注框画回图像上肉眼过一遍import cv2 def draw_boxes(img_path, lbl_path, save_path): img cv2.imread(img_path) h, w img.shape[:2] with open(lbl_path) as f: for line in f: cls, cx, cy, bw, bh map(float, line.split()) # 归一化坐标还原为像素坐标 x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) color [(0, 0, 255), (0, 255, 0), (255, 0, 0)][int(cls)] cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.imwrite(save_path, img) draw_boxes(datasets/images/train/00096.jpg, datasets/labels/train/00096.txt, check_00096.jpg)颜色按类别区分红细胞红、白细胞绿、血小板蓝。重点看三类问题框有没有漏标、有没有把背景框进去、血小板框是不是过大。这一步花十分钟能省后面几小时排查损失不下降的时间。3. 训练配置与损失函数让模型真正学会数细胞3.1 环境搭建与依赖安装项目用 Python 开发深度学习框架按正文描述是 PyTorch 或 TensorFlow 二选一从文件结构看更接近 PyTorch 生态。环境建议单独建虚拟环境避免和系统里的包打架# 创建虚拟环境 python -m venv venv_yolo # 激活Windows venv_yolo\Scripts\activate # 激活Linux / macOS source venv_yolo/bin/activate # 安装核心依赖 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pyqt5 wandbcu118对应 CUDA 11.8如果你的显卡驱动支持的 CUDA 版本不同去 PyTorch 官网查对应命令替换。没有独立显卡也能跑但训练会慢很多推理阶段 CPU 勉强够用。wandb是正文里提到的实验跟踪工具第一次运行会要求登录不想用可以在训练命令里加wandb0关掉。3.2 训练参数怎么设训练入口一般是一个train.py或者直接用 ultralytics 的命令行。关键参数如下表参数建议值说明epochs100~300血细胞数据集通常 150 轮左右收敛imgsz640输入尺寸血小板小目标可试 1024batch8~16按显存调显存不够就降lr00.01初始学习率太大损失震荡patience3030 轮没提升就早停省时间conf0.25推理置信度阈值训练时不用改对应命令yolo detect train \ datadatasets/data.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch8 \ lr00.01 \ patience30 \ projectsave_data \ nameblood_cell_v1modelyolov8n.pt是预训练权重用预训练模型比从零训练收敛快得多这是常识但总有人忘。projectsave_data把训练日志和权重存到save_data目录和正文里说的保存损失值、准确率对得上。name给实验起个名跑多次实验时不会互相覆盖。3.3 损失函数在学什么YOLO 的损失由三部分组成边界框回归损失、置信度损失、分类损失。边界框回归用 CIoU 或类似指标衡量预测框和真实框的重叠程度置信度损失判断这个格子里有没有目标分类损失判断是红细胞还是白细胞还是血小板。训练时看save_data里的损失曲线三条损失应该同步下降如果分类损失降不下去而框损失正常多半是类别标错了如果框损失一直高检查标注框坐标是不是没归一化。血细胞检测里血小板因为目标小回归损失占比低模型容易忽略。可以在数据增强里加 mosaic 和 mixup让模型多见小目标组合场景。但 mixup 对医学图像要慎用两张不同涂片的图像叠在一起语义上不太合理我一般只开 mosaic。3.4 训练过程监控与中断恢复训练中断是常事断电、显存溢出、误关终端都可能发生。ultralytics 默认会在save_data/blood_cell_v1/weights/下存last.pt和best.pt恢复训练用yolo detect train \ datadatasets/data.yaml \ modelsave_data/blood_cell_v1/weights/last.pt \ resumeTrueresumeTrue会从上次中断的轮次继续优化器状态也恢复。注意last.pt是最后一轮best.pt是验证指标最好的一轮推理用best.pt恢复训练用last.pt别搞混。如果wandb开着训练曲线会实时上传手机也能看但网络不稳时上传失败会拖慢训练本地跑建议关掉。4. 推理、计数与 UI 集成从模型输出到可用界面4.1 推理脚本与计数逻辑训练完拿到best.pt推理就是加载模型、读图、输出框和类别再按类别统计数量。核心代码from ultralytics import YOLO import cv2 model YOLO(save_data/blood_cell_v1/weights/best.pt) def count_cells(img_path, conf_thres0.25): results model(img_path, confconf_thres)[0] counts {RBC: 0, WBC: 0, PLT: 0} names results.names # {0: RBC, 1: WBC, 2: PLT} for box in results.boxes: cls_id int(box.cls[0]) counts[names[cls_id]] 1 # 把框画回图像方便核对 annotated results.plot() cv2.imwrite(result_annotated.jpg, annotated) return counts print(count_cells(TestFiles/sample_01.jpg))conf0.25是置信度阈值低于这个值的框不输出。血小板检测如果漏检多可以降到 0.15 试试但误检会上升需要权衡。results.plot()直接返回画好框的图像省得自己写绘制逻辑。计数结果是个字典红细胞、白细胞、血小板各多少一目了然。4.2 PyQt 界面文件结构解析正文里列出的UiMain.py、ui_sources.py、ui_sources_rc.py、QssLoader.py、precess_bar.py是典型的 PyQt 界面工程结构。ui_sources.py是 Qt Designer 生成的界面定义ui_sources_rc.py是资源文件编译结果图标、图片等QssLoader.py负责加载样式表precess_bar.py大概率是进度条控件UiMain.py是主窗口逻辑。这种拆分的好处是界面和业务逻辑分离改样式不动逻辑改逻辑不动界面。style.css是界面样式表控制按钮颜色、字体、布局间距。想换界面风格改这个文件就行不用碰 Python 代码。setup.py是打包脚本可以把整个项目打成可安装的包方便分发。4.3 把推理接进界面界面里点“选择图片”按钮后调用推理函数把计数结果显示在标签上同时把标注后的图像显示在界面上。关键是把推理放在子线程里否则界面会卡死from PyQt5.QtCore import QThread, pyqtSignal class InferThread(QThread): finished pyqtSignal(dict, str) # 计数结果, 标注图路径 def __init__(self, img_path): super().__init__() self.img_path img_path def run(self): counts count_cells(self.img_path) self.finished.emit(counts, result_annotated.jpg)QThread把推理放到后台线程finished信号带两个参数回传主线程。主线程收到信号后更新界面这样点按钮不会卡。precess_bar.py里的进度条可以在推理前显示“处理中”推理完隐藏用户体验会好很多。4.4 计数结果的验证方法自动计数准不准得和人工计数对比。挑 20 张测试图人工数一遍再跑一遍模型算误差图像编号人工 RBC模型 RBC误差人工 WBC模型 WBC误差00096312305-2.2%880%010952872963.1%65-16.7%红细胞误差通常在 5% 以内算可用白细胞因为数量少一个误差就是十几个百分点要看绝对误差。血小板如果误差超过 15%检查是不是置信度阈值设太高导致漏检。这套对比表也是写论文时最有说服力的材料。5. 避坑与排查血细胞检测里最容易翻车的五件事5.1 损失不下降标注格式错了现象训练开始后框损失一直在高位震荡几十轮都不降。原因标注文件坐标没归一化或者类别编号从 1 开始而不是 0。YOLO 要求类别从 0 开始坐标是 0 到 1 的小数。解决写脚本检查所有标注文件坐标大于 1 的说明没归一化类别编号出现 3 的说明多了一类。批量修正后再训练。5.2 验证指标虚高数据泄漏了现象验证集 mAP 到 0.95 以上但拿新图推理效果很差。原因同一张涂片的多个视野被随机分到了训练集和验证集模型见过相似图像。解决按涂片来源划分数据集同一涂片的图像只出现在一个 split 里。如果数据来源信息丢失至少按图像文件名前缀分组划分。5.3 血小板全漏检小目标没学好现象红细胞白细胞检测正常血小板一个都框不出来。原因输入尺寸 640 下血小板只有几个像素特征太弱。解决把imgsz提到 1024 或 1280同时在数据增强里开 mosaic让模型多见小目标。如果还不行考虑用切片推理把大图切成小块分别检测再合并。5.4 界面卡死推理在主线程跑了现象点“开始计数”后界面无响应进度条不动。原因推理函数直接在主线程调用耗时操作阻塞了事件循环。解决用QThread把推理放到子线程通过信号槽回传结果。precess_bar.py里的进度条要在推理前show()推理完hide()。5.5 换机器后模型加载失败现象在自己电脑上跑得好好的换台机器报错找不到模型或 CUDA 不可用。原因模型路径写成了绝对路径或者新机器没有 GPU。解决路径全部用相对路径基于项目根目录拼接。推理时先判断torch.cuda.is_available()没有 GPU 就自动切 CPU虽然慢但能跑。6. 进阶技巧把计数误差压到 5% 以内的三个习惯第一个习惯是推理后做一次形态学过滤。YOLO 输出的框偶尔会把两个粘连的红细胞框成一个或者把噪声框成血小板。对红细胞可以检查框的宽高比正常红细胞接近圆形宽高比超过 2 的框大概率是粘连可以拆开或标记出来人工复核。对血小板检查框内像素的平均亮度血小板染色后比背景亮亮度太低的框直接丢弃。这段过滤代码不长但能把误检压下去一截import numpy as np def filter_boxes(img, boxes, names): keep [] for box in boxes: cls_id int(box.cls[0]) x1, y1, x2, y2 map(int, box.xyxy[0]) w, h x2 - x1, y2 - y1 if names[cls_id] RBC: # 红细胞宽高比超过 2 视为粘连保留但标记 if w / max(h, 1) 2: keep.append(box) elif names[cls_id] PLT: # 血小板区域平均亮度太低则丢弃 roi img[y1:y2, x1:x2] if roi.size 0 and roi.mean() 80: keep.append(box) else: keep.append(box) return keeproi.mean() 80这个阈值不是固定的不同染色和曝光条件下要重新标定。拿几张典型图跑一下看血小板区域的平均亮度分布取一个能分开血小板和背景的值。第二个习惯是每批新数据先跑 10 张做人工比对。不要等全部跑完再看结果前 10 张就能暴露大部分问题阈值合不合适、有没有系统性漏检、计数偏差方向是偏大还是偏小。发现偏差及时调conf和过滤参数比跑完几百张再返工省时间。第三个习惯是保存每次推理的参数和结果。save_data目录不只存训练数据推理时的置信度阈值、过滤参数、计数结果也存一份用时间戳命名。后面写报告或者复现实验时能说清楚每个数字是怎么来的。我吃过这个亏一个月后回头找某次计数结果忘了当时阈值设的多少只能重跑。从那以后我每次跑推理都强制走一遍“前 10 张人工比对 参数存档”的流程再批量处理。这套源码包把训练、推理、界面都串好了剩下的就是按自己的数据把参数调到位。希望帮到你。本文还有配套的精品资源点击获取