简介:基于Python与YOLOv深度学习框架构建的舌象诊断系统,定位为高校毕业设计、期末大作业及课程设计的高分范本,主要面向计算机视觉与医学影像交叉方向的本科学习者。系统实现舌象图像的目标检测、特征识别与诊断结果输出,界面直观、操作简单,代码内附详细注释,便于理解与二次扩展。压缩包共包含184个文件,除Python源码外,还配有61张舌象图像样本、训练记录文本、JSON配置、界面UI文件、Markdown说明及Word版学习路线文档,资源包整体大小约42.67MB,目录结构清晰,按文档指引部署即可运行。目前已有390人学习下载,项目经严格调试,功能完整,能直接用于毕业设计展示或课程设计提交。资源内含数据集、完整工程代码与学习路线说明,可帮助读者系统掌握YOLOv模型在舌象诊断场景中的数据处理、模型训练和推理部署全流程,适合需要快速搭建完整项目的学习者。
1. 舌象诊断系统是什么:YOLOv目标检测为什么是这条毕设路线的核心
舌象诊断在中医里靠医生肉眼观察舌色、舌苔、舌形,一套流程高度依赖经验和环境光线。用Python+YOLOv做舌象诊断系统,本质是把“看舌头”这件事拆成两个可量化任务:先让目标检测模型在图片里定位舌头,再对定位到的舌区做颜色、苔质分类。它能把一张随手拍的舌头照片转成结构化诊断结果,是典型的“目标检测模型+业务规则”组合,适合作为高校毕设——因为数据能自己采、模型能本地训练、效果能直观演示。下文按“选型→数据集→训练→避坑→部署”这条线,给出一套可以照做的完整方案。
2. 舌象检测的技术栈与YOLOv选型:从舌体定位到舌色舌苔判断
2.1 舌象诊断要检测什么:目标定义与标注粒度
设计系统时,我遇到的第一个问题不是“模型选哪个”,而是“舌头这个目标怎么定义”:框选范围是只包含舌体,还是连同嘴唇一起框?舌色和舌苔的分类字段挂在框上,还是单独作为类别?这些决策直接决定标注工作量、模型收敛难度和最终诊断准确率。
按照中医舌诊的常见维度,把检测目标拆成三层:第一层是舌体检测,单类别,目标就是在图片里定位舌头区域,输出边界框;第二层是舌色分类,包括淡白、淡红、红、绛红、青紫等;第三层是舌苔分类,包括薄白、厚白、黄苔、灰黑苔、腻苔等。在YOLO的目标检测框架里,最简单的做法是让每一个框对应一个复合标签,比如类别直接写成“红舌+厚白苔”的组合形式。
实操中我一般用“单框多标签”思路:每张图只标一个舌头框,但类别字段记录该舌象的舌色和苔质组合,例如red_thickwhite,模型输出后由后处理脚本拆成舌色、舌苔两个结论。这个常见做法虽然会膨胀类别数,但标注时反而更快,也绕开了同一个框挂多个标签时工具不兼容的问题。
2.2 为什么选YOLOv8而不是其他目标检测框架
毕设场景里候选方案其实不少:Faster R-CNN精度高但慢,SSD快但小目标弱,YOLOv5成熟但生态老旧。在2025年这个节点上,YOLOv8的ultralytics库把训练、验证、导出、部署拧成了一套命令,最省心。它比YOLOv5多了anchor-free检测头和C2f结构,收敛速度在同等数据量下明显更稳,而且自带Mosaic和MixUp增强策略,对几百张的小数据集很友好——这一点对舌象这种难以大批量获取的训练数据来说非常关键。
另外,YOLOv8有 n/s/m/l/x 五个规格,毕设数据量通常只有几百到一千张,用yolov8s或yolov8m就够,不需要上l和x。s模型在GTX 1060这种老显卡上都能跑接近实时,训练一轮也就十几分钟。这条路线不需要分布式训练、不需要云端GPU,一台带NVIDIA显卡的Windows笔记本就能完成全流程,这也是它适合作为“高分毕设”工程底线的核心原因。
2.3 检测+分类的系统架构
这里要说明白:严格讲,舌象诊断最好做一个“先检测、再分类”的二阶段系统,而不是让YOLO直接把舌色、舌苔当多类别检测。理由很简单——同一张舌象图上,舌色和舌苔是同时存在的两个属性,而目标检测的类别是互斥的,一个框只能落一个类别,这跟诊断模型的语义天然冲突。
因此整体架构拆成两级:第一级用YOLOv8检测舌头区域,只训练一个类别tongue,输出边界框;第二级把舌框裁剪出来,用分类网络或颜色直方图配合规则阈值,对舌色、舌苔分别做判断。指标上也是分开看:检测看mAP@0.5,分类看准确率和F1。很多毕设翻车就是因为只在YOLO里加了十几个“舌质红”“舌苔白”的类别标签,模型面对互斥类别很难学出联合属性,最后mAP虚高但结果不可解释。
我建议第一级检测做精,第二级分类用规则或小分类网络兜底。这样答辩时既能讲检测流程,又能讲中医诊断逻辑,逻辑链是闭合的,评审老师追问任何一个环节都能接上。系统模块划分是:数据加载模块、YOLO检测模块、舌区裁剪模块、色彩特征提取模块、结果映射与报告生成模块,五个模块之间用标准接口连接,这也是源码组织结构的主线。
3. 构建舌象数据集:采集、标注与增强的实操流程
3.1 数据集来源与样本筛选方法
舌象数据集在公开领域并不多,最典型的是高校公开的舌诊图像库,但版权和授权需要自行确认;另外一些医学影像竞赛数据集里也附带舌象图。对这种毕设项目,我一般建议三条路并行:第一是去Kaggle、GitHub搜tongue dataset、tongue diagnosis,找到后确认license是否允许科研使用;第二是自己采集,找身边同学用手机在自然光下拍舌头,每人拍3到5张不同角度,凑300张并不难;第三是借助公开图片搜索做补充,但商用和发表要注意版权,这个要在文档说明里写明。
筛选样本时有个硬指标:凡是模糊、严重偏色、舌头占比小于整图10%的样本,直接淘汰。我收过的第一批图里,有将近三分之一是含脸部大特写的,舌框画出来太小,YOLO在小目标上本来就弱,这类样本只会拖低mAP。最后能进训练集的,应该是舌头清晰、面积合适、背景简洁的正面照,宁缺毋滥,因为舌象诊断的关键在于舌色和苔质的细节,图片质量直接决定后续分类准确率。
3.2 用LabelImg标注舌象:标注规则与格式转换
标注工具我用LabelImg,原因是YOLO训练格式它能直接导出,不需要额外转换脚本。安装命令在Windows下是:
pip install labelImg labelImg打开软件后,先在左侧“PascalVOC”切换成“YOLO”格式,因为后面的YOLOv8训练要的是txt标注文件。每张图片画一个框,类别填tongue。如果按“单框多标签”方案做复合类别,就把类别填成red_thickwhite这种组合命名。标注过程中要统一规则:框从舌根两侧边缘开始,贴近舌体,不包含嘴唇,舌头顶端距离框上边缘留2到3像素的余量。
一个框画完后,LabelImg会生成同名txt文件,比如tongue_001.txt,内容长这样:
0 0.5125 0.4467 0.3824 0.5172这串数字的含义是:类别索引0、边界框中心点x坐标、中心点y坐标、框宽度、框高度,全部归一化到0到1之间。注意YOLO格式是“中心点+宽高”,不是左上角加右下角,换用其他标注工具导出时最容易在这上面出错。标注完不要急着训练,随机抽20张,用脚本把txt还原画回图片上检查:
import cv2 def draw_yolo_box(img_path, txt_path): img = cv2.imread(img_path) h, w = img.shape[:2] with open(txt_path) as f: _, cx, cy, bw, bh = map(float, f.read().strip().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) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite('check_' + img_path.split('/')[-1], img)这段脚本读取YOLO格式标注并还原成可视化框,检查标注是否偏移或漏标。代码里cx * w算出框中心点的像素坐标,bw * w算出框宽,再反推左上角和右下角,画框显示。检查时重点看三处:框是否紧贴舌体边缘、有没有把嘴唇包进来太多、有没有出现框画到舌头一半就截断的情况。这一步做完再进训练,能省下后面大量排错时间。
3.3 数据增强与训练集划分
小数据集的训练离不开增强。YOLOv8自己带了Mosaic和MixUp,所以我在数据层面只做离线增强,按优先级排序:
| 增强操作 | 参数范围 | 适用场景 |
|---|---|---|
| 水平翻转 | p=0.5 | 舌象左右对称,天然适合 |
| 亮度调节 | -50 ~ +50 | 模拟不同光线条件下的拍摄 |
| 高斯模糊 | kernel=3 | 模拟轻微手抖 |
| 随机裁剪缩放 | 0.8~1.2倍 | 模拟远近不同的拍摄距离 |
增强后的文件夹结构必须严格按YOLO格式摆好,否则训练脚本找不到数据:
tongue_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/划分比例我用7:2:1,其中test单独留出来,这部分图片在训练和验证时完全不参与,专用于最终效果评估。一个常被忽略的细节是划分后要保证每个子集里的样本分布一致,不要出现train里全是没有舌苔的舌象、test里全是黄厚苔的情况。解决办法是按舌色、苔质分层抽样:先按类别分组,再每组按比例随机分,这样模型见过的类别分布才均匀。
4. 用YOLOv8从零训练舌象检测模型:关键参数与命令
4.1 环境搭建与依赖安装
开始训练之前,先把环境准备好。这里最省事的路径是创建独立虚拟环境,避免系统Python被深度学习依赖搞乱:
conda create -n tongue python=3.10 conda activate tongue pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pandas matplotlib labelimg这一段命令里,--index-url参数指定PyTorch的CUDA 11.8版本下载源,如果显卡驱动只支持CUDA 12,就把cu118换成cu121,重点是要让torch版本和显卡驱动匹配。装完跑一句验证:
python -c "import torch; print(torch.cuda.is_available())"输出True说明GPU可用,输出False基本都是驱动版本太老或torch装成了CPU版。我遇到过系统里装了老版本numpy,ultralytics加载时直接报module 'numpy' has no attribute 'bool'的问题,用pip install -U numpy升到最新版解决。环境搭建这部分看起来不起眼,但毕设答辩现场翻车最多的就是这里——现场设备没装对依赖,演示跑不起来,前面训练得再好也白搭。
4.2 准备数据配置文件与第一次训练
YOLOv8训练需要数据配置文件,指定类别和路径。在项目根目录建tongue.yaml:
path: C:/tongue_dataset train: images/train val: images/val test: images/test nc: 1 names: ['tongue']这个配置文件让ultralytics按指定路径寻找训练集和验证集。nc是类别数量1,names是类别名字列表,顺序必须和标注txt里类别索引一致。注意路径里的反斜杠最好写成双反斜杠或正斜杠,否则Windows上容易解析失败。
然后启动第一次训练,我建议直接拿s规格的小模型试跑:
yolo detect train data=tongue.yaml model=yolov8s.pt epochs=50 batch=16 imgsz=640model=yolov8s.pt会从官方仓库自动下载预训练权重,不需要手动找文件。epochs=50在300张数据量上大约需要40分钟,取决于显卡型号;batch=16在6G显存以下要减到8。跑起来看输出里的两类日志:box_loss和cls_loss每轮都在降,说明模型在学习;mAP50如果过了第10轮还没上0.3,基本就是数据或标注出问题了,不要继续跑完全程才发现。
4.3 调参策略:批次、学习率与预训练权重
YOLOv8里基础参数人人都能跑通,毕设的差距在调参上。影响舌象检测最明显的三个参数:
第一是imgsz。我试过512和640,512下mAP50掉了4个百分点,但速度快了近三分之一。舌象目标通常占画面20%到40%,不是小目标,640够用;如果数据里舌头占比小,直接上960,不要用640硬扛。
第二是batch。batch=32时loss曲线比batch=8平稳得多,但显存不够时宁可选小batch配小imgsz,也不要强行调大导致OOM。训练时注意看显存占用,接近上限就把batch减半。
第三是学习率。YOLOv8默认lr0=0.01,对50轮以内的短训练偏保守。我会用lr0=0.02配lrf=0.005,让学习率从0.02线性衰减到0.005。在舌象这种小数据集上,这个设置比默认值收敛更快:
yolo detect train data=tongue.yaml model=yolov8s.pt epochs=80 batch=16 imgsz=640 lr0=0.02 lrf=0.005跑的时候留意日志里的P和R两列:如果P高R低,说明模型画框偏保守,宁可漏检也不误检,调低置信度阈值或增加数据增强;如果R高P低,说明画框激进,推理时把conf阈值往上提。这套经验值在小数据集的迁移学习场景下比较通用,比直接套默认参数有效得多。
5. 舌象训练避坑:5 个高频 case 与排查路径
5.1 现象:loss降到很低但mAP只有0.3
训练过程中loss一路降到0.02,验证集mAP却始终在0.3附近徘徊。这个现象很隐蔽,loss收敛不代表检测得好——如果标注框偏大偏小,分类loss会随着过拟合降下来,但IoU计算的定位精度被框偏差压住。
原因是标注框质量参差不齐,尤其是第一批人工标注会有松有紧,模型学到的是一个“平均框”,永远贴不准边缘。解决方法是做一次标注质量复查,用归一化标准偏差检查同类框的尺寸分布,剔除宽高比和面积偏离均值超过30%的样本。同时把imgsz从640提高到960,模型看到更多舌头细节,框定位会更准。
5.2 现象:所有图片都检测不到舌头
训练正常结束,但推理时一张图都检测不到,置信度全部为0。先排查类别映射——这是最常见的原因,训练时的names顺序和推理脚本里的标签列表不一致,YOLO输出的类别索引对不上,推理结果自然为空。
另一个高发因素是训练集里负样本太多。从网上采集的图里如果有一半是“没有舌头的脸部照片”,YOLO会把这个目标学得过保守,推理时宁可不框。处理方法是删掉大部分负样本,保持负样本占比不超过10%,同时在数据配置里不做背景类的额外标注。
5.3 现象:训练集精度92%,测试集直接腰斩
典型的过拟合,但舌象场景有个特殊点:训练集和测试集的拍摄环境差异大。很多同学训练时用的图都在室内补光条件下拍摄,测试时拿手机随手拍的糊图来试,背景、色温完全不同,模型没见过这种数据分布。
解决路径有两条:第一是离线增强里加大亮度扰动和颜色扰动,模拟不同手机的白平衡差异;第二是正常拍照收集更多测试域样本加入训练。还有一个实用技巧——训练完成不要直接用最后一个epoch的权重,看验证集mAP曲线选次优epoch的权重,这类模型泛化能力反而更好,这招我用下来比各种正则化都有效。
5.4 现象:推理速度只有2 FPS
台式机RTX 3060跑yolov8m,推理每张图要400多毫秒,明显不正常。第一步排查是不是CPU推理——很多人装的是CPU版torch,CUDA不可用,速度自然只有这个水平。torch.cuda.is_available()返回False时,重装GPU版即可解决。
确认是GPU跑还慢,就检查推理尺寸。如果外部脚本把图片resize成3000x4000再喂给模型,YOLO会按原图尺寸做前处理,耗时翻好几倍。推理前统一imgsz=640,并开启半精度half=True,速度能拉上来两倍左右。对毕设演示场景,还可以用yolov8s替换yolov8m,精度掉得不多,速度翻倍。
5.5 现象:病理舌象类别的准确率接近0
舌象数据集中,正常舌象占了大半,像绛紫舌、灰黑苔这类病理样本只有十几张。训练出来的模型对少数类基本放弃,全预测成多数类。
解决方式是做少数类过采样:把绛紫舌的样本复制一份,配合不同的增强方式生成多个变体,让少数类在每轮训练中的占比提升。更彻底的办法是离线合成——对已有少数类样本做HSV色彩空间偏移,把正常舌象的色相向病理色方向调整,生成新样本。评估时不要只看整体mAP,单独画出每一类的PR曲线,少数类的PR曲线面积明显增长才说明改善有效。
6. 把模型做成诊断系统:部署、界面与验证
6.1 模型导出与推理封装
训练完的best.pt可以直接用于推理,也可以导出为onnx格式,用于论文里的性能对比实验。导出命令:
yolo export model=best.pt format=onnx imgsz=640毕设系统主流程直接用best.pt就够,推理封装成一个类,输入一张图片,输出裁剪好的舌区:
import cv2 import numpy as np from ultralytics import YOLO class TongueDetector: def __init__(self, weight_path='best.pt'): self.model = YOLO(weight_path) def predict(self, img): results = self.model.predict(img, conf=0.35, iou=0.5, imgsz=640)[0] boxes = results.boxes if len(boxes) == 0: return None box = boxes[0] x1, y1, x2, y2 = map(int, box.xyxy[0].tolist()) return img[y1:y2, x1:x2]推理类把检测和裁剪合成一步:加载训练好的权重,预测后取置信度最高的框,裁剪出舌区供后续分类模块使用。conf=0.35是经验值,调低会引入背景误检,调高会漏检,实际演示前先在自己的测试集上扫一遍阈值。
6.2 用PyQt5做桌面诊断界面
毕设演示最直观的载体是桌面程序。用PyQt5搭一个窗口,左边放图片预览,右边放诊断结果,核心逻辑:
from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, QVBoxLayout, QWidget, QFileDialog from PyQt5.QtGui import QPixmap class TongueApp(QWidget): def __init__(self): super().__init__() self.detector = TongueDetector('best.pt') self.init_ui() def init_ui(self): self.btn = QPushButton('选择舌象图片', self) self.img_label = QLabel(self) layout = QVBoxLayout(self) layout.addWidget(self.btn) layout.addWidget(self.img_label) self.btn.clicked.connect(self.open_image) def open_image(self): path, _ = QFileDialog.getOpenFileName(self, '选择图片', '', 'Image Files (*.jpg *.png)') if path: img = cv2.imread(path) tongue = self.detector.predict(img) if tongue is not None: cv2.imwrite('result.jpg', tongue) self.img_label.setPixmap(QPixmap('result.jpg'))界面逻辑很直接:按钮触发选图,选完调用检测器裁出舌区,再显示在界面上。实际扩展时要再加一个分类模块输出诊断文本,但骨架不用变。如果不会PyQt5,用Flask写个网页版也完全可以,核心是推理类的接口稳定。
6.3 效果不靠感觉验证:混淆矩阵与逐类指标
系统跑通后,最容易被答辩老师挑战的问题就是“你凭什么说这个系统好用”。只展示几张检测框是站不住的,要提供一份离线验证报告,里面至少包含三张图:混淆矩阵、归一化混淆矩阵、每一类别的PR曲线。这些图在训练时就会自动生成在runs/detect/train目录下,整理进文档说明即可。
另一条验证路径是人工抽检100张未参与训练的测试图,统计系统判定舌色舌苔与人工判读的一致率。这个数字比mAP更有说服力,因为它是业务层的准确率。我习惯把mAP数值和业务一致率分开写:mAP证明模型定位能力,一致率证明系统诊断有效性,两条线在论文和答辩里都讲得清楚。
关于舌象诊断检测模型,最后的质量反而靠“保守的置信度阈值+类别文本映射规则”撑起来。我踩过的教训是:一开始为了演示好看把conf拉到0.1,结果背景被狂框误检,后来直接改默认conf=0.35,误检少了,演示反而顺利。建议你也把验证脚本和原始预测结果一起打包进“实验记录”文件夹,下次复现和答辩举证都靠它。希望帮到你。
本文还有配套的精品资源,点击获取