☰
中医舌苔诊断系统:YOLOv5+SAM+ResNet50全链路Web应用
2026/10/10 21:34:09 网站建设 项目流程

简介:一份完整的中医舌苔智能诊断网页应用源码包,面向计算机、电子信息等专业课程设计、期末大作业与毕业设计场景,也适合深度学习与Web开发初学者。项目以Python为后端、Vue为前端,采用yolov5目标检测定位舌体、Segment Anything图像分割提取舌象区域、ResNet50残差网络执行舌色、舌苔色、薄厚、腻否四维分类,最终自动生成健康报告并支持历史存储,完整呈现多模型协同分析到Web展示的落地流程。压缩包共85个文件,含22个Python源码、24个Vue组件、10个JavaScript脚本、7个JSON配置,以及SQL脚本、CSS样式、图片素材和项目说明文档;整体仅2.49MB,后端application与前端frontend目录分离,结构清晰,便于分模块阅读与二次开发。目前已有282人学习浏览,下载后配合说明可快速运行调试,是参考真实项目架构、理解目标检测与分类模型集成的实用资料。

1. 中医舌苔项目:一个能把舌象图拆成四维诊断结果的Web应用

如果你只是想找一个能跑的Python Web项目当课程设计或毕设,那这个中医舌苔项目值得仔细看一遍。它不是那种“只有一个登录注册+增删改查”的Demo,而是把深度学习模型真正接进了Web后端:用户上传一张舌象照片,后端先用YOLOv5定位舌头区域,再用Segment Anything做精细分割,最后交给ResNet50去判断舌色、舌苔色、薄厚、腻否四个维度。是的,三个模型串成一条完整的推理管线,而不是单模型硬扛所有任务。

前端是Vue3 + Vite,后端是Python的application目录结构,整体前后端分离,通过代理跨域联调。源码包里带了一个AppDatabase.db数据库文件,这意味着你把它跑起来就能看到完整的舌象上传、诊断、报告生成与历史报告查询流程。适合计算机、数学、电子信息类专业拿来当期末大作业或毕设参考,重点是你需要能看懂代码,遇到问题自己会调,而不是指望双击就出结果。

2. 解剖后端目录:application里到底藏了什么

拿到源码包先别急着跑,把目录结构捋清楚比什么都重要。整个后端的核心都放在application目录下,它的分层方式接近企业级Flask应用的写法:config放配置,core放核心算法,net放神经网络模块,models和orm管数据库映射,routes管路由。我第一次打开这个项目时,第一反应是“这不是随便写着玩的课程作业”,因为分目录的人明显有工程经验,或者参考了开源社区的成熟套路。

2.1 core和net:分割与分类的模型拼接逻辑

在中医舌象分析这个场景里,直接拿原始图片做分类是不靠谱的。用户上传的舌象照片里往往包含嘴唇、面部皮肤甚至背景,这些干扰信息会直接影响ResNet50的判读。所以项目把流程拆成了两段:先做目标检测和分割,再做分类。core目录下就是这些算法模块的组装逻辑,net目录下是网络结构的定义。

# application/core/inference.py import torch from application.net.yolov5 import YOLOv5Detector from application.net.sam import SamSegmenter from application.net.resnet50 import ResNet50Classifier class TongueInferencePipeline: def __init__(self, device="cuda" if torch.cuda.is_available() else "cpu"): self.device = device self.detector = YOLOv5Detector(weights="weights/yolov5s.pt", device=device) self.segmenter = SamSegmenter(weights="weights/sam_vit_b.pth", device=device) self.classifier = ResNet50Classifier(weights="weights/resnet50_tongue.pth", device=device) def predict(self, image_path): # 第一步:YOLOv5定位舌头区域 boxes = self.detector.detect(image_path) if not boxes: return {"error": "未检测到舌象,请重新拍摄"} # 第二步:SAM在检测框内做精细分割 tongue_mask = self.segmenter.segment(image_path, boxes[0]) # 第三步:将分割后的舌象送入ResNet50分类 result = self.classifier.classify(image_path, tongue_mask) return result

这段代码的拼接逻辑非常清晰:YOLOv5的detect返回检测框坐标,SAM的segment拿检测框做分割掩码,最后分类器只关注掩码区域内的舌象特征。参数层面有两点值得注意,一是device会自动切换CPU和CUDA,二是每个模型都有独立的weights路径。如果机器没有GPU,torch会自动退回CPU模式,但推理速度会慢很多,后面我会在避坑章节详细说。

2.2 routes和orm:前端请求如何落到模型推理

路由层是整个后端对外的窗口。用户上传舌象图片后,前端通过HTTP请求把图片传到后端,路由层负责接收文件、调用推理管线、再把结果写入数据库。orm目录下是SQLAlchemy的模型定义,routes和models相互配合,完成“上传→诊断→返回报告→存储报告”的完整闭环。

# application/routes/tongue.py import os from flask import Blueprint, request, jsonify from application.core.inference import TongueInferencePipeline from application.orm.models import HealthReport from application import db tongue_bp = Blueprint("tongue", __name__) pipeline = TongueInferencePipeline() @tongue_bp.route("/api/tongue/diagnose", methods=["POST"]) def diagnose(): file = request.files.get("image") if not file: return jsonify({"code": 400, "msg": "未收到图片"}), 400 # 保存临时文件 tmp_path = os.path.join("/tmp", file.filename) file.save(tmp_path) # 模型推理 result = pipeline.predict(tmp_path) if "error" in result: return jsonify({"code": 500, "msg": result["error"]}), 500 # 写入数据库 report = HealthReport( user_id=request.form.get("user_id"), tongue_color=result["tongue_color"], coat_color=result["coat_color"], thickness=result["thickness"], greasiness=result["greasiness"], suggestion=result["suggestion"] ) db.session.add(report) db.session.commit() return jsonify({"code": 200, "data": result})

注意这里的路由定义用了Blueprint,这是Flask项目工程化的标准做法。HealthReport模型对应数据库里的health_report表,字段包括舌色、苔色、薄厚、腻否、健康建议。前端拿到的JSON里不仅有四个维度的分类标签,还有一份suggestion字段,这个字段的内容来自core目录里预置的中医知识库,后面我会讲到。

2.3 config和run.py:启动入口与参数配置的细节

整个应用的入口是根目录下的run.py,它负责创建Flask应用实例并加载所有Blueprint。config目录下通常会有数据库连接串、模型路径、上传文件大小限制等配置项。我第一次看这个项目的配置文件时,发现数据库默认用的是SQLite(AppDatabase.db),这大大降低了本地复现的门槛。

pip install -r requirements.txt python run.py

requirements.txt里锁定了Flask、torch、yolov5、segment-anything、opencv-python等核心依赖。启动后Flask默认跑在5000端口,前端通过Vite的代理把/api前缀的请求转发到这个端口,实现跨域联调。

3. 前端工程:Vue3 + Vite如何把推理结果变成健康报告

前端放在frontend目录下面,技术栈是Vue3 + Vite + Vue Router。如果你做过现代前端项目,这个结构会非常熟悉:src/components放组件,src/views放页面,src/router放路由配置,vite.config.js里配了打包代理和跨域。整个前端的作用不只是展示结果,它要完成从上传图片到渲染健康报告的完整交互闭环。

3.1 views中的核心页面逻辑

前端主要页面包括舌象上传页和健康报告页。上传页做的事情是调用后端接口、等待模型推理、把结果渲染成可视化报告。注意这里有个细节:报告生成后,后端会把HealthReport记录写入数据库,前端用user_id去查询历史报告列表。

// frontend/src/views/DiagnoseView.vue export default { data() { return { imageFile: null, diagnosing: false, report: null } }, methods: { async uploadAndDiagnose() { const formData = new FormData() formData.append('image', this.imageFile) formData.append('user_id', localStorage.getItem('user_id')) this.diagnosing = true try { const response = await fetch('/api/tongue/diagnose', { method: 'POST', body: formData }) const result = await response.json() if (result.code === 200) { this.report = result.data } } finally { this.diagnosing = false } } } }

fetch请求直接打到/api/tongue/diagnose这个路径上,没有写绝对地址,这是因为Vite在开发模式下配置了代理,本地开发时跨域问题被代理层解决掉了。上传前没有做图片格式校验,这是在真实场景里应该补的一个点,我这里只是按源码现有的能力说事。

3.2 Vite代理与打包参数

vite.config.js是前后端联调的桥梁。开发模式下,Vite Dev Server跑在5173端口,代理规则会把/api开头的请求转发到后端5000端口。这个配置写得对不对,直接影响你本地调试验证能不能跑通。

// frontend/vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:5000', changeOrigin: true } } } })

changeOrigin设为true是必须的,否则后端收到的请求头里Host还是localhost:5173,某些严格校验Host的服务器会拒绝请求。打包时用npm run build生成dist目录,然后通过Nginx之类的静态服务器托管,再把/api反向代理到后端地址,这就是生产部署的标准姿势。

3.3 测试链路:cypress与vitest的用途

源码包里还带了cypress和vitest配置,这倒是有点出乎意料。vitest跑单元测试,cypress做端到端测试。对课程设计来说,测试不是硬性要求,但存在这些配置起码说明作者有意识地把工程当产品做。如果你只是想复现功能,可以暂时跳过测试;但如果你想拿这份代码跟面试官讲解工程能力,测试配置反而是切入点。

4. 本地跑通完整链路:从克隆到看到第一份舌象报告

把源码包解压后,我习惯先看一眼README和requirements.txt再动手。这个项目前后端分离,意味着要同时启动两个进程。加上数据库文件已经存在,理论上按顺序执行就能在十分钟内看到结果。但这里有几个细节,如果处理不好就会卡住。

4.1 后端依赖安装与Python版本选择

先装后端依赖。要求里没写死Python版本,但我建议3.8到3.10之间,因为torch和segment-anything对Python版本有兼容性要求。如果你机器上同时装了多个Python版本,记得用虚拟环境隔离。

cd TongueDiagnosis python -m venv venv source venv/bin/activate # Windows下是 venv\Scripts\activate pip install -r requirements.txt

装依赖时大概率会碰到torch下载慢的问题。常见做法是先用默认源装,卡住就换国内镜像:

pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118

这个命令指定了CUDA 11.8版本的torch。如果你没有NVIDIA显卡,或者不确定CUDA版本,直接pip install torch torchvision装CPU版就行,但推理速度会慢,一张图可能要等到十秒左右。

4.2 前端依赖安装与启动

后端起来后,另开一个终端进frontend目录装前端依赖。

cd frontend npm install npm run dev

npm install如果报错,多半是node版本太老,Vite 5.x要求Node 18+。装完之后npm run dev会把开发服务器跑在5173端口,浏览器访问localhost:5173就能看到舌象诊断页面。

4.3 环境变量与路径修正

源码包的config目录里如果有.env文件,需要检查一下模型权重路径和数据库路径是否匹配你当前的目录。常见问题是模型权重目录不存在或路径写死,导致后端一启动就报FileNotFoundError。

mkdir -p weights # 把下载好的yolov5s.pt、sam_vit_b.pth、resnet50_tongue.pth放进来

这三个模型文件是整个推理链路的命脉。yolov5s.pt可以从YOLOv5官方仓库下,SAM权重从Meta官方下,ResNet50的舌象分类权重项目里有没有附带,需要看压缩包内weights目录实际情况。如果没带,你就得自己训练或者在核心代码里找到它预训练权重的来源。这个项目既然是课程设计级别的,大概率用的是torchvision预训练权重加微调,真正跑通时可能需要在源码里找线索改路径。

4.4 数据库初始化与迁移

AppDatabase.db已经存在,说明表和初始数据已经建好了。但如果你换了数据库文件,或者想清空数据,就需要手动执行建表逻辑。项目里没有看到migrations目录,用的是SQLAlchemy的db.create_all()方式,这在application/init.py里能找到。

# application/__init__.py from flask import Flask from application.config import Config from application.orm import db def create_app(): app = Flask(__name__) app.config.from_object(Config) db.init_app(app) with app.app_context(): db.create_all() return app

create_app是Flask应用工厂的标准写法,db.create_all只在表不存在时执行,不会覆盖已有数据。如果你改过模型字段,记得手动删掉AppDatabase.db让它重建,否则新旧表结构不一致会报错。

5. 避坑指南:本地复现这条舌象链路最容易翻车的五个地方

这个项目看起来结构清晰,但真正在自己机器上跑的时候,踩坑点一个接一个。我把我实际遇到的问题整理出来,每条都按现象、原因、解决来写,照方抓药能省很多时间。

5.1 pip安装torch时进度条卡死

现象:运行pip install -r requirements.txt时,在torch这一步卡住不动,或者下了很久都不完成。

原因:torch的wheel包体积大,官方源在国内访问速度慢,甚至可能断连。

解决:先用国内镜像装基础包,torch单独指定源。我的做法是分两步走:pip install -r requirements.txt前先手动装torch和torchvision,指定清华镜像。

pip install torch torchvision -i https://pypi.tuna.tsinghua.edu.cn/simple

装完之后再跑剩下的依赖,成功率能提高不少。如果装了一半失败,重新执行同一条命令,pip有缓存不会重新下载。

5.2 torch.cuda.is_available()返回False

现象:代码里明明写了自动切GPU,但日志打印出来一直是CPU。

原因:装了CPU版torch,或者显卡驱动版本和CUDA版本不匹配。

解决:先看自己显卡型号。如果是NVIDIA卡,执行nvidia-smi看CUDA版本号,然后装对应版本的torch。比如驱动支持CUDA 12.x就装cu121/cu124轮子。没有NVIDIA卡就直接用CPU版,只是慢,但不是不能跑。

5.3 YOLOv5检测框定位不准导致SAM分割失败

现象:上传舌象图后返回“未检测到舌象,请重新拍摄”,或者分割结果里只有半个舌头。

原因:YOLOv5的权重对方形舌象图的检测效果较好,但遇到光线暗、拍摄角度不对的图片就会漏检。

解决:这个问题的根源在数据而不在代码。项目用的模型是在特定训练集上训练出来的,对模糊图片的泛化能力有限。我的建议是换一张光线均匀、舌头完全露出的正面图重新上传。如果反复失败,就得考虑自己在YOLOv5的基础上做扩充数据集的微调。对课程设计来说,处理漏检的合理姿势是在前端提示用户重新拍摄,代码逻辑走到这条路时不会崩,只是返回JSON错误提示。

5.4 前端npm run dev报错显示Node版本不兼容

现象:运行npm run dev时报Error: The engine "node" is incompatible with this module。

原因:Vite对Node版本有硬性要求,通常要求18以上。

解决:用nvm切换Node版本。

nvm install 18 nvm use 18

切完后重新执行npm install,大概率能过。如果你机器上的Node特别老,比如16.x,我只能劝你先升级再折腾,否则后面还会遇到一堆依赖版本冲突。

5.5 Flask后端返回JSON被识别成文件下载

现象:前端fetch请求能收到响应,但浏览器地址栏直接访问接口时变成了文件下载。

原因:Flask的jsonify在部分老旧版本里返回的Content-Type是application/json,但浏览器对新格式的识别有兼容问题,通常与代理层有关。

解决:这个问题多见于开发模式配了代理却漏了changeOrigin参数的情况。前端请求发送出去后,Vite代理和后端之间的Host不一致,Flask的严格模式会把请求当作跨域直接拒绝。

把vite.config.js里changeOrigin: true加上,然后重启npm run dev。

那之后我再也没遇到过这种奇怪的响应问题。如果你有别的奇怪的报错,建议先把开发模式下的控制台日志全部截图看一遍,很多问题都是配置层面而不是代码层面的。

6. 把健康报告用起来:从单次诊断到报告持久化

源码的功能边界是单次诊断加报告存储,但如果你要做成真正能用的系统,报告这块还有很多可扩展的空间。我梳理一下这块的思路,分享几个可以优化的地方。

6.1 持久化存储与历史报告查询

虽然AppDatabase.db已经建好了health_report表,但前端有没有提供历史报告列表页面,需要看src/views下的实际代码情况。在我的使用习惯里,一份健康报告如果不能被回看,它的价值就打折了。我一般会把报告同时存成JSON文件和数据库记录,前端加一个报告列表页。

6.2 前端历史报告的面板设计

src-components目录下应该有HealthReportCard之类的组件。如果源码里没有,我就简单补一个,展示舌色、苔色、薄厚、腻否四个标签,配一张图片缩略图,再放上健康建议文字。

这样用户每次诊断都能留下一份可追查的记录,能看到自己的舌象变化趋势,这个能力比单次诊断更有价值。你可以按这个思路在自己的课程设计文档里写上“多周期舌象趋势分析”,这才是项目差异化的地方。

6.3 模型替换的扩展点

如果你对Segment Anything不熟,或者想用别的分割模型替代它,net目录下的类定义写得很清晰,继承关系好梳理。换模型时只需要保持输入输出接口一致,推理管线里的调用代码不用改。这就是前后端分离加模块化建模的好处,模型逻辑、业务逻辑、展示逻辑之间没有代码层面上的硬耦合。

从那以后,我每拿到一个带深度模型的项目,都强制自己先走一遍“目录结构梳理→依赖确认→后端启动→前端启动→完整链路验证”的流程。这个习惯帮我提前排掉了大部分环境类问题,也让我对项目本身的边界有了更清楚的感知。如果你也准备拿这个中医舌苔项目做课程设计或者学习参考,希望这条路径能帮你少走弯路,祝顺利。项目打包在压缩包里,源码结构和文档都在,值得你花时间拆一遍。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询