深度学习驱动的舌苔识别检测系统设计与实现

发布时间:2026/9/14 23:48:24
深度学习驱动的舌苔识别检测系统设计与实现 简介一套基于深度学习的舌苔识别检测鉴定系统完整项目适合计算机视觉方向毕业设计、课程实践或工程入门。资料内含可运行Python源码、带GUI交互界面、已训练模型权重、论文报告与运行截图覆盖从数据预处理、模型训练到界面部署的主要流程项目难度适中源码经本地编译验证评审分达95分以上能帮助学习者快速理解舌苔图像分类与检测的完整实现路径。压缩包共110个文件整体约105.46MB核心包括26个py源码文件、10个pyc编译文件、6个pth权重文件、5个json配置、2个ui界面文件及2个docx文档另配jpg/png运行截图、txt/md说明与TensorBoard训练日志文件类型覆盖代码、模型、文档和演示素材目录结构清晰便于按模块查阅。目前已有186人学习下载适合需要直接运行演示、参考论文结构或复现深度学习教育项目的读者入手。1. 舌苔识别检测先定任务边界再选模型一份舌象照片丢进模型很多现成方案只输出「苔厚腻 0.93」这样的分类概率却不能告诉你在图像哪个区域、范围多大。而标题里的「检测」决定了技术路线系统要有目标框要有类别还要经得起「为什么用目标检测而不是分类器」这一问。很多人把分类当检测做最后界面只显示一个数字评审一眼就能看出边界没想清楚。这套基于深度学习的舌苔识别检测系统主体是 Python 写的训练与推理链路外层套一个 GUI 界面把模型权重、检测框和鉴定文本整合到同一个窗口里。适合正在做深度学习课程大作业、Python 毕设题或者想把手头舌象数据快速做成可演示原型的人。下面按「算法选型 → 训练参数 → GUI 集成 → 打包验证」四个部分展开新手能照着复现老手可以直接替换成自己的数据和模型。2. 舌苔识别检测的算法选型与图像预处理2.1 检测与分类任务如何影响深度模型选型舌苔分析通常包含两层目标先确定舌体或苔质在图像上的位置再判断其状态属于哪一类。这两层目标如果拆开看就分别是目标检测和图像分类如果合在一个网络里就是带定位分支的检测模型。很多 Python 源码包把「识别」「检测」「鉴定」混着写拿到手第一件事是看推理结果里有没有 boxes。没有 boxes说明它本质上只是一个分类器只能回答「整体像什么」回答不了「在哪里」。从深度学习的目标检测方法出发候选方案集中在两阶段和单阶段两类。两阶段检测器以 Faster R-CNN 为代表先由 RPN 提出候选区域再对区域做分类和边界回归定位精度高但推理链路长单阶段检测器以 YOLO、SSD 为代表直接在特征图上回归边界框和类别训练和推理路径短。对带 GUI 舌苔识别检测系统来说YOLO 系更容易落地接口简单、推理耗时低、资料多PyTorch 生态下的 ultralytics 库把训练、验证、导出封装得比较完整代码改动面积小。模型检测头设计与舌苔任务的适配点更合适的场景YOLOv5/v8anchor / anchor-free推理快GUI 集成资料多本机演示、毕设系统Faster R-CNNRPN RoI Pooling小目标苔质区域更稳精度优先、不要求实时EfficientDetBiFPN 多尺度融合参数效率高显存占用低设备性能受限选型时还要想清楚一个容易被忽略的问题舌苔的细粒度分类例如薄、厚、腻、腐实际上可以通过多个检测类别来表达。常见做法是给每个类别一个独立 label例如 0 表示薄白苔1 表示白厚腻2 表示黄腻。这样模型在输出边界框的同时输出类别界面直接展示「坐标、类别、置信度」论文报告里也能写清 mAP、精确率、召回率的评估口径。2.2 预训练权重与迁移学习的作用舌苔图像与 ImageNet 里的自然图像差异很大但网络前几层学习的边缘、纹理等底层特征仍然通用。直接随机初始化训练舌苔数据量通常撑不起收敛也慢。常见做法是加载 COCO 预训练权重只把最后的检测头类别数改成自己的类别数。迁移学习在训练阶段的实际表现是 loss 下降更快前 10 到 20 个 epoch 尤其明显。如果数据集只有几百张预训练权重的作用比调任何超参数都大。2.3 舌体区域过滤与数据增强光照是舌象照片最不稳定的因素。一个有效的预处理步骤是训练前对图像做 HSV 色彩空间过滤把过暗或过亮的背景压掉减少模型对非舌体区域的注意力。下面的函数用 OpenCV 提取舌体近似区域可以作为训练前的 mask 参考也可以用于 GUI 里预览输入图。import cv2 import numpy as np def extract_tongue_mask(img): hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 舌体以红、粉、淡黄为主H 覆盖红黄范围S 取中等值V 保持明亮 lower np.array([0, 20, 60]) upper np.array([180, 180, 255]) mask cv2.inRange(hsv, lower, upper) # 闭运算填掉舌苔纹理造成的空洞保留整体轮廓 kernel np.ones((5, 5), np.uint8) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) return mask这里的 H 范围 0 到 180 是 OpenCV 中 HSV 的标准表示覆盖红色到黄色的全部色相S 上限设 180 是为了避开过分鲜艳的人工背景V 下限 60 过滤掉暗部阴影。如果采集环境偏黄可以把 S 上限放宽到 220如果偏冷色可以把 V 下限提高到 90。注意预处理只用于辅助不应把 mask 直接替换原图送进检测器否则会丢失舌体边缘的上下文信息。训练阶段的数据增强一般通过库自带参数完成例如 YOLO 训练配置里的 hsv_h、hsv_s、hsv_v 和 flipud。舌象数据不建议做上下翻转因为舌尖和舌根的方向在医学描述里有明确语义翻转后标注框对应关系会混乱。左右翻转则可以放心开不影响苔质对称性判断。3. Python 源码里的训练通路与必调参数3.1 标注格式、目录组织与 class 定义拿到源码包后先不要急着跑入口文件而是检查数据集目录长什么样。舌苔检测的标注格式常见有两种labelImg 导出的 XMLPascal VOC 格式和 YOLO 格式的 txt 文件。YOLO 格式的每一行是class_id x_center y_center width height坐标值都归一化到 0 到 1 之间。目录结构一般是这样data/ ├── images/ │ ├── train/ # 训练原图 │ └── val/ # 验证原图 ├── labels/ │ ├── train/ # 每张图对应的 txt 标注 │ └── val/ └── config.yaml # 类别数、类别名、路径config.yaml 里最核心的是 nc 和 names。例如识别三分类舌苔状态就写nc: 3names 按训练数据的类别索引顺序排列。类别顺序一旦定下整个训练到推理过程都不能改否则 GUI 里显示的名称和实际框会错位。建议从一开始就把 names 固定下来放进一个单独的模块文件里训练配置和 GUI 显示共用这个常量。path: data train: images/train val: images/val nc: 3 names: 0: thin_white 1: thick_greasy 2: yellow_greasy3.2 训练主流程与 epoch、batch_size、lr 的调法训练脚本本身不复杂核心是对model.train()传参。下面的片段以 YOLOv8 为例完整覆盖加载预训练权重、指定数据集、启动训练三个动作。from ultralytics import YOLO model YOLO(yolov8s.pt) # s 是 small 规格兼顾速度和精度 model.train( datadata/config.yaml, epochs80, # 预训练权重下 60100 都可接受 imgsz640, # 输入分辨率舌苔纹理细节依赖它 batch8, # 按显存大小调整常见 4、8、16 lr01e-3, # 初始学习率loss 发散就先降一半 device0, # 0 表示第一张 GPUCPU 环境写 cpu )imgsz640是整个训练里最值得优先保证的参数。舌苔的薄厚纹理属于中等尺度特征分辨率降到 416 会明显损失细节显存不够时优先降 batch 而不是降分辨率。batch8配合 640 分辨率在 8GB 显存内基本可行如果训练中途报 CUDA out of memory把 batch 改成 4 或 2。lr01e-3是 YOLO 系比较稳的起点如果看到 loss 在前 5 个 epoch 不降反升检查数据标注有没有空标签文件再把学习率降到 5e-4。epoch 数量不必盲目拉大。有 COCO 预训练的情况下80 个 epoch 对数百张舌苔数据足够如果验证集 mAP 在最后 20 个 epoch 没有提升说明已经收敛。训练结束后项目里会生成runs/detect/目录weights/best.pt就是论文报告里可以引用指标的最终模型。3.3 模型导出从 PyTorch 权重到 ONNX 推理文件训练完成后best.pt 是 PyTorch 格式。如果 GUI 端只用推理常见做法是把它导出成 ONNX后续换设备或去掉 torch 的部分编译依赖都更方便。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) model.export( formatonnx, dynamicTrue, # 允许输入尺寸动态变化 imgsz640, # 和训练分辨率保持一致 opset12, # 保持默认不用刻意改高 )dynamicTrue表示输入图像的宽高可以变化代价是部分运行时算子会变慢如果 GUI 里固定把图片缩放到 640 再送模型可以设成 False 得到更小的模型文件。导出后最好用 ONNX Runtime 或库自带的 predict 接口做一次验证确认输出 shape 仍是[1, num_detections, 6]最后一维对应x1, y1, x2, y2, confidence, class_id。这是 GUI 画框时最需要的数据格式。注意如果训练和推理不是同一台机器导出的 ONNX 要连同 config.yaml 里 names 顺序一起拷贝不要只拷权重文件。4. 将训练好的模型接入 PyQt5 GUI4.1 GUI 与推理模块的分层设计PyQt5 集成模型时最容易踩的坑是「按钮一点就卡住」。原因往往是把模型加载和推理都塞进按钮回调里加载权重一次几百毫秒检测一次几十毫秒界面主线程被阻塞后整个窗口就会变成无响应状态。常见做法是把 GUI 和推理分成两层模型封装成独立的 Detector 类进程内只初始化一次推理操作放到子线程或者至少用 QTimer 防止按钮连点触发连续推理图像读取、坐标画框、结果显示三件事分开写方便单独替换模型文件。这类分层的另一个好处是论文报告里能画清模块图数据输入层、模型推理层、结果展示层各占一块答辩时更好解释。4.2 推理接口的 Python 实现推理类只暴露一个detect()方法GUI 不直接碰 YOLO 对象。from ultralytics import YOLO class TongueDetector: def __init__(self, weights_path, conf0.35, imgsz640): self.model YOLO(weights_path) self.conf conf # 低于该置信度的框被过滤 self.imgsz imgsz # 推理分辨率和训练保持一致 def detect(self, image_path): results self.model.predict( sourceimage_path, confself.conf, imgszself.imgsz, verboseFalse, # 不打印逐帧日志避免刷屏 ) return results[0]conf的取值直接影响界面体验。设 0.5 会让框很少漏检多设 0.25 会看到大量低质量框。对舌苔这类目标相对集中的图像0.35 到 0.4 是合理区间。verboseFalse可以避免在 GUI 控制台里输出大量无关预测信息。4.3 PyQt5 界面中的图片选择与结果画框下面的代码是一个最小可用的 PyQt5 调用片段包含选择图片、执行检测、绘制边界框三个动作。import sys import cv2 import numpy as np from PyQt5.QtWidgets import ( QApplication, QMainWindow, QLabel, QPushButton, QFileDialog, QVBoxLayout, QWidget, ) from PyQt5.QtGui import QPixmap, QImage from tongue_detector import TongueDetector class MainWindow(QMainWindow): def __init__(self): super().__init__() self.detector TongueDetector(weights/best.pt) self.btn QPushButton(选择舌象图片并识别) self.btn.clicked.connect(self.run_detect) self.image_label QLabel(未加载图片) layout QVBoxLayout() layout.addWidget(self.btn) layout.addWidget(self.image_label) container QWidget() container.setLayout(layout) self.setCentralWidget(container) def run_detect(self): path, _ QFileDialog.getOpenFileName( self, 选择图片, , Images (*.png *.jpg *.jpeg)) if not path: return result self.detector.detect(path) img cv2.imread(path) for box in result.boxes: x1, y1, x2, y2 map(int, box.xyxy[0]) cls_id int(box.cls[0]) conf float(box.conf[0]) label f{result.names[cls_id]} {conf:.2f} cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, label, (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) self.show_image(img) def show_image(self, img): rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape qimg QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888) self.image_label.setPixmap(QPixmap.fromImage(qimg).scaledToWidth(640))这段代码的关键在box.xyxy[0]它给出的是左上角和右下角整数坐标cv2.rectangle 直接按这个坐标画框。result.names是模型配置里的类别名映射这里直接复用了训练时的 names不会出现显示错位。QImage(rgb.data, w, h, ch * w, ...)中的ch * w是每行字节数由于 RGB 图像每像素 3 字节这一步必须写对否则图片会出现斜纹错位。4.4 把检测结果转成鉴定文本标题里「鉴定」两个字落到界面就是检测结果的文本化。检测完成后再加一层规则映射把类别 ID 转成可读的中文结论比直接把英文 category 丢给用户友好得多。CLASS_TO_TEXT { 0: 薄白苔, 1: 白厚腻苔, 2: 黄腻苔, } def build_report(boxes, names): lines [] for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) text CLASS_TO_TEXT.get(cls_id, names[cls_id]) lines.append(f{text}置信度 {conf:.2f}) return \n.join(lines) if lines else 未检测到明确舌苔区域build_report的输入直接复用前一步的result.boxes和result.names。这里要注意模型输出的类别 ID 是训练时定义的索引不是自动排序的结果如果训练时 0 表示黄腻苔这里 0 也必须是黄腻苔。我把映射单独放到一个常量字典里就是为了让训练配置和界面展示之间只存在一个维护点。5. 舌苔识别检测系统的打包运行与截图验证5.1 用 requirements 固定 Python 依赖整套 Python 源码跑通后别人拿到手最常见的失败原因不是模型问题而是依赖版本不一致。先运行一次pip freeze requirements.txt把 torch、ultralytics、opencv-python、PyQt5 的版本固定下来。如果目标机器没有 GPU把 requirements 里的torch版本改成对应的 CPU 版否则下载的 CUDA 依赖会把几 GB 空间填满。5.2 主窗口闪退与界面无响应的定位方法闪退高发在三个位置模型文件路径不对、依赖缺失、图像转换时数组长度出错。逐个排查比反复重启程序有效。模型文件缺失时在TongueDetector.__init__里先判断文件是否存在不存在则弹出错误对话框界面无响应时先看控制台有没有输出再确认按钮回调里是否做了耗时操作。给按钮回调加一行 print 标记进度的习惯能省下大量定位时间。5.3 在 GUI 内部实现运行截图要生成符合验收要求的运行截图不需要额外打开截图工具。PyQt5 的窗口对象可以直接抓取自身内容保存成 PNG这样截图坐标、分辨率、画面内容都可复现。def capture_window(pixmap_pathrun_result.png): screen QApplication.primaryScreen() window QApplication.activeWindow() pixmap screen.grabWindow(window.winId()) pixmap.save(pixmap_path)grabWindow抓取的是窗口本身的绘制结果不依赖外部屏幕位置即使窗口被遮挡也能正常保存。实际使用时把capture_window绑定到某个快捷键或识别按钮的末尾程序就会在每次检测完成后自动保留一张带检测框、置信度和鉴定文本的运行截图。这类脚本运行方式对批量验证不同阈值下的界面效果也很实用。本文还有配套的精品资源点击获取