ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

基于YOLOv8的舌象诊断系统:从Python环境搭建到推理部署的毕设方案

基于YOLOv8的舌象诊断系统:从Python环境搭建到推理部署的毕设方案 简介这份资源面向计算机、人工智能及中医药信息化方向的本科生与研究生提供一套可直接用于毕业设计、期末大作业或课程设计的舌象诊断系统完整方案。项目以Python为开发语言结合YOLO系列深度学习目标检测算法实现对舌象图像的自动识别与分类诊断帮助缺乏实战经验的学生快速搭建可运行、可演示的智能诊断应用。压缩包共184个文件约42.67MB包含54个py源码文件、61张jpg样本图片、40个pyc编译文件以及14个txt说明、7个json配置、2个ui界面文件、2个md文档和1份docx学习路线另附ttf字体与png图标等辅助素材覆盖数据、代码、界面与文档全链路。目前已有390人学习下载。代码注释详尽新手也能看懂部署简单下载后即可运行配套数据集与文档说明可支撑模型训练、功能扩展与论文撰写界面美观、操作便捷具有较高的实际应用与参考价值。1. 舌象诊断系统落地从 Python 环境到 YOLOv8 推理一套能跑通的毕设方案很多同学做毕设时卡在第一步舌象图片拍了一堆却不知道怎么让模型自动框出舌体、判断舌质颜色和舌苔厚薄。这个标题讲的就是用 Python 加 YOLO 系列深度学习模型搭一套从数据标注到推理输出的舌象诊断系统。它解决的核心问题是把中医舌诊里“看舌质、看舌苔”的经验判断转成可复现的目标检测与分类流程。适合有 Python 基础、正在找毕设方向或想入门医学图像检测的开发者。整套方案不依赖昂贵设备普通笔记本加摄像头就能跑通最小闭环源码和数据集结构我会在中间章节拆开讲清楚。2. 舌象诊断系统的技术选型为什么是 YOLO 而不是分类网络2.1 舌象诊断的任务拆解与 YOLO 的匹配逻辑舌象诊断在算法层面可以拆成两个子任务一是舌体区域检测从人脸或口腔图像里把舌头框出来二是舌质舌苔分类对框出的区域判断颜色偏淡、偏红、苔白、苔黄等类别。很多同学一上来就想用 ResNet 做整图分类结果背景里的嘴唇、牙齿、光线全在干扰模型准确率卡在 60% 上不去。常见做法是先用目标检测把舌体裁出来再送进分类网络这样分类器的输入干净指标能明显提升。YOLO 系列适合这个场景的原因有三点。第一单阶段检测速度快摄像头实时预览没有明显延迟毕设演示时体验好。第二YOLOv8 的 API 设计统一训练、验证、导出 ONNX 都是几行代码新手不用在配置环境上耗太久。第三社区里舌象相关的标注数据虽然不多但 YOLO 对小数据集做过增强策略几百张图也能训出可用模型。我一般会建议选 YOLOv8n 或 YOLOv8sn 版在 CPU 上推理也能到 10 FPS 左右s 版精度更高适合有 GPU 的环境。这里要区分一个常见误区YOLO 负责的是“舌头在哪里”不是“舌头是什么证型”。证型判断需要结合舌质、舌苔、甚至问诊信息单靠一个检测模型输出类别是不够的。所以系统架构上YOLO 做定位后面接一个轻量分类头或独立分类模型才是完整链路。2.2 环境搭建与依赖安装的实操命令先确认 Python 版本YOLOv8 要求 Python 3.8 以上我习惯用 3.10 或 3.11兼容性好。安装命令如下# 创建虚拟环境避免污染系统包 python -m venv tongue_env # Windows 激活 tongue_env\Scripts\activate # Linux/Mac 激活 source tongue_env/bin/activate # 安装 ultralytics它会自动拉取 torch 等依赖 pip install ultralytics opencv-python pillow matplotlib安装完成后验证import ultralytics from ultralytics import YOLO print(ultralytics.__version__) # 加载预训练模型第一次会自动下载权重 model YOLO(yolov8n.pt) print(model.names)逻辑说明ultralytics包封装了训练、推理、导出全流程YOLO(yolov8n.pt)会加载 COCO 预训练权重用于后续迁移学习。参数上如果网络下载慢可以手动下载权重放到当前目录再把路径改成./yolov8n.pt。opencv-python用于读取摄像头和图像预处理pillow用于可视化标注结果。注意不要混用pip install yolov5和ultralytics两个包的 API 不兼容容易在model.train()时报参数错误。如果之前装过旧版先pip uninstall yolov5再装 ultralytics。2.3 数据集目录结构与标注格式转换YOLO 训练要求的数据集结构是固定的tongue_dataset/ ├── images/ │ ├── train/ │ │ ├── 001.jpg │ │ └── ... │ └── val/ │ ├── 002.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 001.txt │ │ └── ... │ └── val/ │ ├── 002.txt │ └── ... └── tongue.yaml每个.txt文件对应一张图每行格式为class_id x_center y_center width height坐标都是归一化到 0 到 1 之间的浮点数。如果你用 LabelImg 标注导出时选 YOLO 格式即可。如果手头是 VOC 的 XML可以用下面脚本转换import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, img_w, img_h, class_map): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in class_map: continue cls_id class_map[cls_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) # 归一化并转为中心点加宽高 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return lines class_map {tongue: 0, white_coating: 1, yellow_coating: 2} # 遍历 xml 目录写入对应 labels 目录参数说明class_map里舌体、白苔、黄苔分别给 0、1、2类别顺序要和tongue.yaml里的names一致否则训练时标签错位模型会把白苔学成舌体。img_w和img_h必须用原图尺寸不能用缩放后的尺寸否则框会偏移。3. 训练舌象检测模型配置文件、超参与训练过程监控3.1 tongue.yaml 的写法与类别数设置在数据集根目录新建tongue.yamlpath: ./tongue_dataset train: images/train val: images/val names: 0: tongue 1: white_coating 2: yellow_coatingpath是数据集根目录train和val是相对路径。names的键必须从 0 开始连续类别数就是 3。如果只检测舌体把names改成只有0: tongue同时标注文件里只保留 class_id 为 0 的行。常见错误是path写成绝对路径后换电脑跑不起来建议用相对路径训练时在项目根目录执行命令。另外val目录不能和train完全一样否则验证指标虚高答辩时被问到泛化能力会翻车。3.2 启动训练与关键超参含义训练命令yolo detect train \ datatongue.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/tongue \ nameexp1逻辑说明data指向配置文件model指定预训练权重epochs是训练轮数imgsz是输入尺寸batch是批大小lr0是初始学习率patience是早停耐心值project和name决定输出目录。参数怎么调显存小于 6GB 时把batch降到 8 或 4imgsz降到 416数据量少于 500 张时epochs可以设 150 到 200但patience设 30 防止过拟合lr0默认 0.01 对迁移学习偏大如果 loss 震荡明显改成 0.001 再试。训练过程中重点看mAP50和mAP50-95前者到 0.85 以上基本可用后者反映框的精准度舌体检测一般能到 0.6 左右。3.3 用 TensorBoard 和验证集排查过拟合训练启动后在另一个终端运行tensorboard --logdir runs/tongue浏览器打开对应地址看train/box_loss和val/box_loss曲线。如果训练 loss 持续下降但验证 loss 在 20 轮后开始上升说明过拟合。解决办法增加数据增强参数比如在训练命令里加degrees10 fliplr0.5 hsv_h0.015 hsv_s0.7分别控制旋转、水平翻转、色调和饱和度扰动。舌象图像对颜色敏感hsv_h不要超过 0.02否则白苔可能被增强成黄苔模型学乱。验证集评估命令yolo detect val modelruns/tongue/exp1/weights/best.pt datatongue.yaml输出里看每个类别的P、R、mAP50。如果white_coating的召回率明显低于tongue说明白苔样本太少需要补标或对白苔类别做复制增强。4. 推理与舌象诊断输出从检测框到舌质舌苔判断4.1 单张图片推理与结果解析from ultralytics import YOLO import cv2 model YOLO(runs/tongue/exp1/weights/best.pt) results model(test_tongue.jpg, conf0.4, iou0.5) for r in results: boxes r.boxes for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(f类别: {model.names[cls_id]}, 置信度: {conf:.2f}, 坐标: {xyxy}) # 保存带框图片 r.save(output.jpg)逻辑说明conf0.4是置信度阈值低于 0.4 的框不输出iou0.5是 NMS 的 IoU 阈值控制重叠框合并。舌象检测里舌体和舌苔可能重叠iou设 0.5 到 0.6 比较合适太低会漏掉被舌体框覆盖的舌苔框。参数说明如果演示时发现框太多把conf提到 0.5如果漏检严重降到 0.3 再观察。model.names返回类别名映射直接用于输出诊断文本。4.2 摄像头实时推理与舌象区域裁剪import cv2 from ultralytics import YOLO model YOLO(runs/tongue/exp1/weights/best.pt) cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break results model(frame, conf0.4, verboseFalse) annotated results[0].plot() cv2.imshow(Tongue Diagnosis, annotated) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()逻辑说明results[0].plot()直接在原图上画框和类别标签适合演示。verboseFalse关闭每帧日志避免终端刷屏。如果要进一步做舌质分类可以在循环里把舌体框裁出来for box in results[0].boxes: if model.names[int(box.cls[0])] tongue: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) tongue_roi frame[y1:y2, x1:x2] # 送进分类模型或做颜色直方图分析注意摄像头白平衡会影响舌色判断建议在代码里加一句cap.set(cv2.CAP_PROP_AUTO_WB, 0)关闭自动白平衡或者固定光源否则同一舌头在不同光线下可能被判成不同类别。4.3 舌质舌苔分类的轻量方案如果毕设要求输出“舌淡红、苔薄白”这类结论可以在检测框基础上加一个分类网络。最简单的方式是用颜色矩加 SVMimport cv2 import numpy as np from sklearn.svm import SVC def extract_color_features(roi): hsv cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) features [] for i in range(3): channel hsv[:, :, i] features.extend([ channel.mean(), channel.std(), np.percentile(channel, 25), np.percentile(channel, 75) ]) return features # 训练时收集特征和标签 X, y [], [] for img_path, label in dataset: roi cv2.imread(img_path) X.append(extract_color_features(roi)) y.append(label) clf SVC(kernelrbf, C1.0) clf.fit(X, y)参数说明C越大分类越严格容易过拟合舌象数据少时用C0.5到1.0。HSV 的 H 通道对颜色最敏感S 和 V 辅助判断苔的厚薄。这个方案不需要 GPU适合算力有限的毕设环境但准确率上限不如端到端 CNN适合作为对比实验或快速原型。5. 舌象诊断系统避坑标注、训练、推理里的 5 个翻车现场5.1 标注框把牙齿嘴唇框进去现象训练后模型把嘴唇也框成舌体演示时满屏都是框。原因标注时为了省事框拉得太大把周围组织都包进去了。解决重新标注舌体框紧贴舌缘留 2 到 3 像素边距即可。如果已经训了一版可以用yolo detect predict输出预测框人工挑出误检图补进训练集再训一轮。5.2 类别不平衡导致白苔漏检现象验证集里白苔的 mAP 只有 0.3黄苔却有 0.8。原因白苔样本只有几十张黄苔有几百张模型偏向多数类。解决对白苔图片做离线增强旋转、翻转、调亮度各生成一份或者训练时加cls0.5提高分类损失权重。更直接的办法是补标白苔数据至少凑到 150 张以上。5.3 学习率过大导致 loss 变 NaN现象训练到第 5 轮box_loss 突然变成 nan终端报梯度爆炸。原因lr00.01对随机初始化的检测头偏大加上 batch 太小梯度不稳定。解决把lr0降到 0.001加warmup_epochs3让学习率从低到高预热同时把batch提到 16 或 32。如果显存不够用accumulate2模拟大 batch。5.4 推理时框重叠严重现象一张图里同一个舌头出现三四个框置信度都差不多。原因NMS 的iou阈值设太高比如 0.7重叠框没被合并。解决把iou降到 0.5 或 0.45同时适当提高conf到 0.5。如果舌体和舌苔本来就重叠不要用太低的iou否则舌苔框会被舌体框吞掉建议分开两个模型分别检测。5.5 换电脑后路径报错现象在自己电脑上跑得好好的拷到同学电脑上datatongue.yaml报找不到文件。原因tongue.yaml里写了绝对路径或者path指向的目录没一起拷贝。解决统一用相对路径把数据集和代码放在同一级目录运行时先cd到项目根目录。另外runs目录里的权重文件路径也要检查best.pt默认在runs/tongue/exp1/weights/下换机器后要么一起拷要么重新训练。6. 把舌象诊断系统跑成可演示的毕设导出 ONNX 与阈值调优技巧答辩现场最怕的是环境崩了所以最后一章讲怎么把模型导出成 ONNX用 CPU 也能稳定推理再给一套阈值调优的实操方法。导出 ONNXyolo export modelruns/tongue/exp1/weights/best.pt formatonnx imgsz640 opset12导出后得到best.onnx用onnxruntime推理import onnxruntime as ort import numpy as np import cv2 session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name img cv2.imread(test_tongue.jpg) img cv2.resize(img, (640, 640)) img img[:, :, ::-1].transpose(2, 0, 1) # BGR 转 RGB 再转 CHW img np.expand_dims(img, axis0).astype(np.float32) / 255.0 outputs session.run(None, {input_name: img}) # outputs[0] 形状为 [1, 7, 8400]7 是 4 个框坐标加 3 个类别分数参数说明opset12兼容性好imgsz640要和训练时一致否则框坐标会错位。ONNX 推理不依赖 PyTorch在只有 CPU 的答辩机上也能跑速度大约是 PyTorch 的 1.5 到 2 倍。阈值调优我一般用网格搜索在验证集上跑不同conf和iou组合记录 F1 分数选最高的那组。舌象检测里conf在 0.35 到 0.5 之间iou在 0.45 到 0.55 之间通常能找到比默认值更好的点。如果答辩要求实时演示把imgsz降到 416帧率能翻倍精度掉 2 到 3 个点现场看不太出来。血泪经验不要等到答辩前一天才导出 ONNX有些自定义算子在不同 opset 下行为不一致提前一周跑通留出改代码的时间。另外演示前把摄像头白平衡和曝光锁死用同一套光源否则舌色判断的结论会飘。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

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