尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Python神经网络CNN舌诊系统源码拆解:从图像预处理到食疗方案全链路

发布时间:2026/9/26 1:56:44

资讯中心
01
ARTICLE

Python神经网络CNN舌诊系统源码拆解:从图像预处理到食疗方案全链路

Python神经网络CNN舌诊系统源码拆解:从图像预处理到食疗方案全链路
简介这份资源是一套基于Python与CNN卷积神经网络实现的舌诊系统完整源码面向计算机相关专业的在校学生、高校教师及开发者可用于毕业设计、课程设计或深度学习入门实践。项目通过图像识别技术诊断舌头症状并给出相应食疗方案涵盖舌体检测、齿痕检测与颜色检测等核心模块配有已训练好的模型文件便于直接运行与二次开发。压缩包共26个文件以py源码、keras模型、pyc缓存、xml配置及json数据为主整体约225.85MB目录结构清晰包含说明文档与配置脚本方便按模块查阅。目前已有95人学习下载。读者可获得一套可运行的舌诊识别方案、模型权重与食疗建议数据既能作为毕设课设直接使用也能在此基础上理解CNN图像分类流程、调试思路与项目组织方式适合爱钻研的学习者深入拓展。1. 舌诊系统源码拆解从一张舌头照片到食疗方案中间到底跑了什么拿到「基于python神经网络CNN实现的舌诊系统源码」这个标题时我第一反应不是去翻代码而是先想清楚一件事一张手机拍的舌头照片要经过多少步才能变成一条「湿热体质建议薏米赤小豆」的输出。这个链路里CNN 只负责其中一段前面有图像预处理后面有症状映射和食疗规则库中间还夹着模型文件的加载和推理。很多人以为下载一个 zip 解压跑起来就完事了实际上跑不起来的原因八成不在 CNN 本身而在环境版本、模型文件路径、输入尺寸对不上这些「脏活」上。这套东西适合两类人一类是做课程设计的学生需要一套能跑通、能演示、能写进报告里的完整流程另一类是想快速验证「图像分类 规则映射」这个组合拳的开发者拿舌诊当练手场景。它不解决临床诊断问题也不承诺任何医学准确性本质是一个 CNN 多分类器加上一张症状到食疗的映射表。把这句话记住后面所有选型和排错都会顺很多。下面我按实际复现顺序把环境、数据、模型、推理、映射、避坑一条条拆开讲。2. 环境与依赖把 CNN 舌诊系统在本地跑起来的最小步骤2.1 为什么这类课程设计项目最容易死在环境上舌诊系统源码通常是一个几年前的 TensorFlow 或 PyTorch 项目作者当时用的版本和你现在装的最新版之间可能隔着好几个大版本。CNN 的层定义、模型保存格式、甚至ImageDataGenerator的参数名都改过。我见过最常见的翻车现场是pip install tensorflow装了个 2.x代码里写的是tf.placeholder直接报 AttributeError。所以第一步不是急着python train.py而是先确认项目用的是哪个框架、哪个大版本。判断方法很简单打开源码目录看 import 语句。如果是import tensorflow as tf且出现tf.keras大概率是 TF 2.x如果出现tf.Session()或tf.placeholder那是 TF 1.x 的写法需要装tensorflow1.15或者用tf.compat.v1兼容模式。如果是import torch看模型保存的是.pth还是.pt以及有没有torch.load的map_location参数。这一步花五分钟能省后面两小时。提示不要在一个已经装了其他深度学习项目的环境里直接跑用 conda 或 venv 建独立环境这是后悔药。2.2 建环境、装依赖、验证版本的具体命令我一般会先建一个 Python 3.8 的环境因为这个版本对 TF 1.15 和 PyTorch 1.x 都比较友好。命令如下# 创建独立环境Python 3.8 兼容性最好 conda create -n tongue_cnn python3.8 -y conda activate tongue_cnn # 如果项目是 TF 1.x 写法 pip install tensorflow1.15.0 numpy1.19.5 opencv-python4.5.5.64 pillow8.4.0 # 如果项目是 TF 2.x 写法 pip install tensorflow2.6.0 numpy1.19.5 opencv-python4.5.5.64 pillow8.4.0 # 如果项目是 PyTorch pip install torch1.10.0 torchvision0.11.0 numpy1.19.5 opencv-python4.5.5.64 pillow8.4.0装完之后不要急着跑主程序先验证import tensorflow as tf print(tf.__version__) # 如果输出 1.15.x 或 2.6.x说明框架版本对了 import cv2 print(cv2.__version__) # 确认 OpenCV 能正常 import后面图像预处理要用参数说明numpy锁在 1.19.5 是因为 TF 1.15 对 numpy 1.20 有兼容问题会报np.object已弃用的错。opencv-python用 4.5.x 是因为部分老代码里用了cv2.findContours的旧返回值格式4.6 之后改了。这些版本号不是随便写的是我踩过坑之后固定下来的组合。2.3 模型文件加载路径、格式和输入尺寸三件事课程设计项目一般会附带一个.h5或.pth模型文件。加载之前先确认三件事文件路径对不对、模型结构和保存时是否一致、输入尺寸是多少。路径问题最常见代码里写的是model load_model(tongue_model.h5)但文件实际在models/子目录下直接报找不到文件。import tensorflow as tf from tensorflow.keras.models import load_model # 加载模型注意路径要用实际存放位置 model load_model(models/tongue_model.h5) # 打印模型结构确认输入层尺寸 model.summary() # 重点看第一层 Input 的 shape比如 (None, 224, 224, 3) # 这个 224x224 就是后面预处理必须对齐的尺寸如果model.summary()报错说层不识别大概率是自定义层没有注册。有些项目会在模型里用自定义的注意力层或损失函数加载时需要custom_objects参数。这时候去源码里搜class定义把自定义层传进去model load_model(models/tongue_model.h5, custom_objects{MyAttention: MyAttention})输入尺寸这一步必须确认因为后面图像预处理要按这个尺寸做 resize。如果模型输入是 224x224你喂 256x256 的图轻则报维度错误重则模型输出全是乱码。我一般会在加载后立刻打印model.input_shape把这个值记下来后面预处理直接引用。3. 数据与预处理舌头照片怎么变成 CNN 能吃的张量3.1 舌诊图像的特殊性颜色比形状更重要舌诊和普通物体分类不一样的地方在于舌头照片的关键信息在颜色和纹理上而不是轮廓形状。舌质红还是淡、舌苔白还是黄、有没有齿痕这些是分类依据。所以预处理阶段颜色空间的转换比灰度化更重要。很多课程设计代码直接cv2.cvtColor(img, cv2.COLOR_BGR2RGB)然后归一化就完事了这没错但如果数据集本身光照不一致模型学到的可能是光照而不是舌色。我一般会加一步白平衡或者直方图均衡化让不同光照下的舌头颜色更一致。常见做法是用 CLAHE限制对比度自适应直方图均衡化在 LAB 空间的 L 通道上做然后合并回去。这样不会破坏颜色信息但能减少光照差异。import cv2 import numpy as np def preprocess_tongue_image(img_path, target_size(224, 224)): # 读取图像 img cv2.imread(img_path) # BGR 转 RGB因为模型训练时用的是 RGB img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转 LAB 空间做 CLAHE只处理亮度通道 lab cv2.cvtColor(img, cv2.COLOR_RGB2LAB) clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) lab[:, :, 0] clahe.apply(lab[:, :, 0]) img cv2.cvtColor(lab, cv2.COLOR_LAB2RGB) # resize 到模型输入尺寸 img cv2.resize(img, target_size) # 归一化到 0-1 img img.astype(np.float32) / 255.0 # 增加 batch 维度 img np.expand_dims(img, axis0) return img参数说明clipLimit2.0控制对比度增强程度太高会让舌苔纹理失真太低没效果2.0 是常用值。tileGridSize(8,8)是分块大小图像小的时候可以调成 (4,4)。target_size必须和模型输入一致前面model.input_shape打印出来是多少就填多少。3.2 数据集划分与标签映射别让标签对不上号课程设计项目的数据集一般按类别放在不同文件夹里比如dataset/red_tongue/、dataset/pale_tongue/这样。用ImageDataGenerator或torchvision.datasets.ImageFolder加载时类别顺序是按文件夹名字母序排的。这里有个隐蔽的坑训练时类别顺序是[pale, red, yellow]推理时你手动写了个[red, pale, yellow]模型输出索引 0 你当成红色实际是 pale结果全错。解决办法是训练和推理用同一套标签映射。我一般会在训练脚本里把class_indices存成 jsonimport json from tensorflow.keras.preprocessing.image import ImageDataGenerator datagen ImageDataGenerator(rescale1./255, validation_split0.2) train_gen datagen.flow_from_directory( dataset/, target_size(224, 224), batch_size32, class_modecategorical, subsettraining ) # 保存类别索引映射 with open(class_indices.json, w) as f: json.dump(train_gen.class_indices, f) print(train_gen.class_indices) # 输出类似 {pale: 0, red: 1, yellow: 2}推理时加载这个 json用{v: k for k, v in indices.items()}反转成索引到类别名的映射这样就不会错位。这个文件虽小但少了它后面食疗方案会张冠李戴。3.3 数据增强舌诊场景下哪些增强能用哪些不能用数据增强是 CNN 训练标配但舌诊有它的特殊性。水平翻转可以用舌头左右翻转不影响类别。旋转小角度可以用±15 度以内合理。但颜色抖动要慎用因为颜色本身就是分类依据你把红色舌头的饱和度调低可能就变成淡白舌了。我一般只做轻微的亮度调整不做色相偏移。train_datagen ImageDataGenerator( rescale1./255, rotation_range15, # 小角度旋转 width_shift_range0.1, # 轻微平移 height_shift_range0.1, horizontal_flipTrue, # 水平翻转 brightness_range[0.9, 1.1], # 只做轻微亮度调整 validation_split0.2 )brightness_range控制在 0.9 到 1.1 之间超过这个范围颜色失真明显。rotation_range不要超过 20舌头旋转太多不符合实际拍摄场景。这些参数不是拍脑袋定的是我对比过增强前后验证集准确率之后固定下来的。4. CNN 模型结构与推理从卷积层到症状输出的完整链路4.1 课程设计里常见的 CNN 结构长什么样这类项目的 CNN 结构一般不会太复杂常见的是三到四个卷积块每个块是 Conv2D MaxPooling2D最后接 GlobalAveragePooling 或 Flatten再接两三个全连接层输出层用 softmax类别数等于舌诊类别数。输入尺寸 224x224x3 居多也有用 128x128 的。我见过一个典型结构是这样的from tensorflow.keras import layers, models def build_tongue_cnn(num_classes3): model models.Sequential([ # 第一个卷积块 layers.Conv2D(32, (3, 3), activationrelu, input_shape(224, 224, 3)), layers.MaxPooling2D((2, 2)), # 第二个卷积块 layers.Conv2D(64, (3, 3), activationrelu), layers.MaxPooling2D((2, 2)), # 第三个卷积块 layers.Conv2D(128, (3, 3), activationrelu), layers.MaxPooling2D((2, 2)), # 第四个卷积块 layers.Conv2D(128, (3, 3), activationrelu), layers.MaxPooling2D((2, 2)), # 分类头 layers.Flatten(), layers.Dropout(0.5), layers.Dense(256, activationrelu), layers.Dense(num_classes, activationsoftmax) ]) return model model build_tongue_cnn(num_classes3) model.compile(optimizeradam, losscategorical_crossentropy, metrics[accuracy])参数说明卷积核数量从 32 开始逐块翻倍是常见做法目的是让浅层学边缘纹理、深层学语义特征。Dropout(0.5)放在 Flatten 之后是为了防止全连接层过拟合舌诊数据集通常不大过拟合是主要风险。num_classes要和你的标签数一致改这个值的时候记得同步改输出层。4.2 推理脚本怎么写单张图片到类别概率训练完之后推理脚本是实际用的时候跑得最多的。我一般会写一个独立的predict.py接收图片路径输出类别和置信度。关键点是预处理要和训练时完全一致包括 resize 尺寸、归一化方式、颜色空间。import json import numpy as np import cv2 from tensorflow.keras.models import load_model # 加载模型和标签映射 model load_model(models/tongue_model.h5) with open(class_indices.json, r) as f: class_indices json.load(f) idx_to_class {v: k for k, v in class_indices.items()} def predict_single(img_path): # 预处理和训练时保持一致 img cv2.imread(img_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (224, 224)) img img.astype(np.float32) / 255.0 img np.expand_dims(img, axis0) # 推理 preds model.predict(img) pred_idx np.argmax(preds[0]) pred_class idx_to_class[pred_idx] confidence float(preds[0][pred_idx]) return pred_class, confidence, preds[0] result predict_single(test.jpg) print(f类别: {result[0]}, 置信度: {result[1]:.4f})这里有个细节model.predict返回的是 batch 维度的概率数组取preds[0]才是单张图的概率。置信度低于 0.6 的时候我一般会提示「结果不确定」因为舌诊场景下低置信度往往意味着图像质量差或者处于两个类别的边界。4.3 症状到食疗方案的映射规则表怎么设计CNN 输出的是舌象类别比如「红舌黄苔」但用户要的是食疗方案。中间需要一张映射表把舌象类别映射到症状描述和食疗建议。这张表通常是硬编码的字典或者 json 文件结构大概是food_therapy { red_yellow: { symptom: 舌质红舌苔黄腻常见于湿热体质, advice: 建议食用薏米、赤小豆、冬瓜等利湿食物少食辛辣油腻, avoid: 辛辣、油炸、酒类 }, pale_white: { symptom: 舌质淡舌苔薄白常见于气血不足, advice: 建议食用红枣、桂圆、山药等温补食物, avoid: 生冷、寒凉食物 }, red_thin: { symptom: 舌质红舌体瘦薄常见于阴虚, advice: 建议食用银耳、百合、枸杞等滋阴食物, avoid: 辛辣、温燥食物 } } def get_therapy(pred_class): return food_therapy.get(pred_class, { symptom: 未识别, advice: 建议咨询专业医师, avoid: 无 })这张表的设计要注意两点一是类别名要和模型输出的类别名完全对应二是get方法要有默认返回值防止模型输出一个映射表里没有的类别导致 KeyError。实际项目中我还会加一个置信度阈值低于阈值时直接返回「建议重新拍摄或咨询医师」不硬给方案。5. 避坑与排查舌诊 CNN 项目跑不起来时先看这五条5.1 现象模型加载报「Unknown layer: XXX」原因模型保存时用了自定义层或自定义损失函数加载时没有注册。课程设计项目里常见的是自定义的注意力层、Focal Loss 或者 SE 模块。解决去源码里搜class XXX的定义把类定义复制到推理脚本里加载时用custom_objects传进去。如果找不到定义说明模型文件和源码不匹配需要重新训练或者找作者要完整的模型定义文件。5.2 现象推理结果全是同一个类别置信度还很高原因预处理和训练时不一致。最常见的是训练用了rescale1./255推理时忘了除 255或者训练时是 RGB推理时用了 BGR或者 resize 尺寸不对模型输入 224 你喂了 256。解决把训练时的预处理代码原封不动复制到推理脚本里不要凭记忆重写。我一般会把预处理封装成一个函数训练和推理都调同一个函数从根源上避免不一致。5.3 现象训练准确率很高但实际拍舌头照片识别全错原因数据集偏差。课程设计用的数据集可能是网上爬的或者实验室固定光源拍的颜色分布和手机实拍差距很大。模型学到的是数据集的光照特征不是舌象特征。解决在预处理里加白平衡或 CLAHE减少光照影响。如果条件允许用手机在多种光照下拍几十张测试图看看哪些光照下错得最多针对性调整。实在不行就在报告里说明适用范围别硬吹。5.4 现象cv2.imread返回 None后面全部报错原因图片路径含中文或空格OpenCV 在 Windows 下对中文路径支持不好。解决用cv2.imdecode(np.fromfile(path, dtypenp.uint8), cv2.IMREAD_COLOR)替代cv2.imread或者把图片路径改成全英文。这个坑在课程设计里出现频率极高因为很多人的数据集文件夹名是中文的。5.5 现象食疗方案输出和舌象对不上原因标签映射错位。训练时class_indices是{pale: 0, red: 1, yellow: 2}推理时手动写了个[red, pale, yellow]索引 0 被当成红色实际是 pale。解决训练完立刻把class_indices存成 json推理时加载同一个 json 并反转。不要手动维护类别列表这是血泪经验。6. 进阶技巧用置信度阈值和 Top-2 输出让舌诊系统更可用前面讲的都是跑通这一章讲怎么让结果更靠谱。CNN 分类器有个特点即使输入一张完全无关的图片softmax 也会输出一个概率分布而且往往有一个类别概率偏高。舌诊场景下用户拍的可能不是舌头或者拍糊了这时候硬给一个食疗方案是不负责任的。我一般会加两个机制置信度阈值和 Top-2 输出。置信度阈值的意思是最高概率低于某个值比如 0.6时不输出具体方案而是提示「图像不清晰或不在识别范围内建议重新拍摄」。这个阈值怎么定拿一批测试图跑一遍看正确分类的样本置信度分布取一个能过滤掉大部分错误样本的值。我试过 0.5 到 0.7 之间0.6 是个比较平衡的点。Top-2 输出是同时给出概率最高的两个类别让用户看到「红舌黄苔 0.55淡白舌 0.30」这样的结果。这样即使第一判断错了第二判断也能提供参考。实现很简单def predict_with_top2(img_path, threshold0.6): img preprocess_tongue_image(img_path) preds model.predict(img)[0] # 取概率最高的两个索引 top2_idx np.argsort(preds)[-2:][::-1] results [] for idx in top2_idx: results.append({ class: idx_to_class[idx], prob: float(preds[idx]) }) # 判断是否低于阈值 if results[0][prob] threshold: return { status: uncertain, message: 图像不清晰或不在识别范围内建议重新拍摄, top2: results } return { status: ok, primary: results[0], secondary: results[1], therapy: get_therapy(results[0][class]) }这个函数返回的结构比单纯给一个类别有用得多。实际用的时候前端可以根据status字段决定是展示方案还是提示重拍。threshold参数可以根据实际测试调整数据质量高就调低数据杂就调高。还有一个技巧是测试时增强TTA。对同一张图做几个轻微变换比如水平翻转、小角度旋转分别推理后取平均概率。这样能提升 1 到 3 个百分点的准确率代价是推理时间翻几倍。课程设计里如果追求指标好看可以加上实际部署时看情况实时性要求高就不加。def predict_with_tta(img_path): img cv2.imread(img_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (224, 224)) img img.astype(np.float32) / 255.0 # 原图 preds model.predict(np.expand_dims(img, axis0)) # 水平翻转 img_flip np.fliplr(img) preds model.predict(np.expand_dims(img_flip, axis0)) # 平均 preds preds / 2.0 return preds[0]TTA 的变换方式要和训练时的数据增强一致训练时用了水平翻转TTA 就用水平翻转训练时没用旋转TTA 也别加旋转否则分布对不上反而掉点。最后说一个我自己的习惯每次跑通一个课程设计项目我都会把环境版本、模型输入尺寸、类别映射这三个信息写在一个README里和代码放在一起。过两个月再回头看没有这个 README 就得重新踩一遍坑。舌诊系统这类项目代码本身不复杂复杂的是环境、数据和映射这些外围细节。把外围理顺了CNN 那部分反而是最省心的。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。