简介一套基于Python语言的树叶识别系统源码包面向高校学生的期末大作业、课程设计与毕业设计也适合刚接触图像识别与图形界面编程的新手学习。项目使用Python语言完成树叶图像读取、预处理、特征提取与分类识别并提供简洁的图形界面用于上传图片、触发识别和展示结果核心识别代码与界面交互代码相互分离结构清晰注释详细方便按需修改识别策略、扩展树叶种类或替换界面样式部署也非常简单打开即可运行系统界面美观、操作直观具备很强的课程项目属性。压缩包共4个文件包括2个脚本文件、1个界面定义文件和1个演示视频脚本分别承担核心识别逻辑与界面交互逻辑界面定义文件用于可视化设计视频完整演示了系统从启动到识别输出的整个过程资源包整体仅6.98MB下载与运行成本极低。目前已有98人浏览学习特别适合需要快速搭建一个可演示、可答辩的完整项目或希望研究Python图像识别与界面开发结合方式的开发者。1. 树叶识别系统是个什么东西一个 zip、两个脚本和一台能跑的电脑把「基于Python语言的树叶识别系统源码演示视频.zip」解压你拿到的多半是一个典型的图像分类小工程train.py 负责训练、predict.py 负责拿一张图做预测、utils.py 放公共函数图片数据按树叶类别分好目录外加一段演示视频证明这套东西能跑。它的本质很简单用 Python 调 OpenCV 读入树叶照片把图片转成张量交给卷积神经网络最后输出“这张叶子属于哪个类别”。它适合三类人写课设或毕设的学生、想完整跑通一个图像分类项目的 Python 入门者以及没碰过深度学习、想拿现成代码改成内部小工具的从业者。别把它想得多神秘跑通它只需要一台普通电脑、一个 Python 环境和一份按顺序装的依赖。难点从来不是算法本身而是环境、路径和数据集这三样东西在你机器上能不能对齐。2. 树叶识别系统源码拆解文件结构、标签设计与模型选型2.1 为什么是 Python生态刚好覆盖“读图、运算、训练、输出”全链路树叶识别这类图像分类任务用 C 不是不行但你要自己处理图像解码、矩阵运算、模型推理和界面展示每一样都有额外成本。Python 的路线要短得多OpenCV 负责读图和预处理numpy 管理像素数组PyTorch 或 TensorFlow 负责搭建和训练卷积网络matplotlib 看一眼训练曲线Flask 或 tkinter 就能把它包装成一个可演示的系统。对 Python 基础语法还不太熟的人这套源码也是个不错的参照物。你不需要从零实现卷积、池化、反向传播框架把这些都封装好了你要做的只是理解“数据长什么样、标签从哪来、模型输入输出是什么”。这正是它适合作为课设和入门项目的原因代码量不大但读一遍下来图片分类的完整流程就串起来了。2.2 解压后先别急着跑认清工程里每个文件是干什么的常见做法是这套源码解压后会有下面这些角色虽然不是每个包都完全一样但角色分工基本跑不掉文件/目录作用运行时机train.py读取数据集、训练模型、保存权重终端执行predict.py加载权重、对单张图预测终端执行utils.py数据增强、公共函数、类别名映射被上面两个脚本 importdataset/ 或 data/按类别分好文件夹的图片集训练时读取requirements.txt依赖清单安装环境时用演示视频.mp4展示系统跑通效果看完再动手我的习惯是先打开 requirements.txt再看 train.py 的 import 段最后才看模型定义。import 能告诉你它依赖哪些版本如果源码是 PyTorch 写的而你装了 TensorFlow后面全是无用功。2.3 标签不用手工标ImageFolder 按文件夹名生成类别索引树叶识别系统的数据集通常不做额外标注文件因为 torchvision 里的 ImageFolder 会自动把子文件夹名当类别。比如你的 dataset/train 下面有“银杏”“枫叶”“梧桐叶”三个文件夹那类别就是这三类ImageFolder 按文件夹名的字母顺序分配 0、1、2 索引。这省了很多事但也埋了一个坑同一批叶子如果你把文件夹名改一个字母索引顺序就全变了。这个坑放到第 5 章细说。数据加载这一段是通用的先看它怎么工作from torchvision import datasets, transforms train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_data datasets.ImageFolder(rootdataset/train, transformtrain_transform) train_loader torch.utils.data.DataLoader(train_data, batch_size32, shuffleTrue) # ImageFolder 自动生成的类别映射 print(train_data.class_to_idx) # {银杏: 0, 枫叶: 1, ...} print(train_data.classes) # 按字母排好序的类别名列表这段代码做了三件事把图片统一缩放成 224×224随机水平翻转做数据增强再转成张量并做归一化。class_to_idx是最容易忽略的信息预测阶段要把模型输出的 0、1、2 还原成“银杏、枫叶、梧桐叶”靠的就是它。建议在训练完后把这个映射存成 json否则换台机器预测时你会忘记哪个索引对应哪个类别。2.4 模型选型从零搭 CNN 还是微调 DenseNet121源码里如果是自己搭的简单 CNN结构通常是三层卷积加两层全连接。它的优点是代码短、好答辩缺点是树叶类内差异大同一棵树不同光照、不同角度差别明显小模型很容易在验证集上欠拟合。我一般会先看数据集规模再决定要不要换模型每类不到 100 张图直接套 ImageNet 预训练的 DenseNet121 或 ResNet18把最后一层全连接改成自己的类别数效果远好于从零训练。import torchvision.models as models num_classes len(train_data.classes) model models.densenet121(weightsmodels.DenseNet121_Weights.IMAGENET1K_V1) # 替换最后的全连接层只改这一层即可 in_features model.classifier.in_features model.classifier torch.nn.Linear(in_features, num_classes)这里的关键点是用预训练权重做“迁移学习”而不是随机初始化重新训练。树叶纹理、边缘、颜色这些底层特征ImageNet 早就学过了你要做的只是让模型把注意力放到“区分具体树叶类别”这件事上。如果运行机器的显卡只有 2GB 显存DenseNet121 可能偏大可以换 ResNet18完全没有 GPU 的话训练会慢不少但数据量不大时依然能等出结果。3. 把 demo 跑通环境安装与第一张树叶预测3.1 Python 版本和依赖顺序先装基础库再装深度学习框架大部分这类源码要求 Python 3.8 或 3.9新版本 3.12 不一定兼容 torchvision 里那些老接口。我遇到的翻车案例里有相当一部分是 Python 版本太新导致某个库编不过。所以先统一版本再谈别的。依赖顺序也有讲究先装 opencv-python、pillow、numpy 这些基础库再装 torch 和 torchvision。很多人倒过来先装 torch 再装 cv2结果 cv2 把 numpy 升级了torch 那边就开始报版本冲突。基础库装完后深度学习框架按自己的方式装不要混在一起让 pip 自动处理。# 创建独立环境避免污染系统 Python conda create -n leaf python3.9 -y conda activate leaf pip install opencv-python pillow numpy matplotlib pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118--index-url指定了 PyTorch 官方提供的 CUDA 11.8 预编译包。如果你不确认机器有没有 NVIDIA 显卡或者驱动版本对不上直接换 CPU 版更省事pip install torch torchvision --index-url https://download.pytorch.org/whl/cpuCPU 版训练慢但跑通这套树叶识别系统绰绰有余。参数说明就一个原则先跑通再谈速度。另外不管你在 vscode 还是 pycharm 里写代码解释器都指向同一个 conda env 就行别一个项目用系统 Python、一个用 conda 环境后面你会被各种 no module named 折磨。3.2 验证环境十秒钟确认三个关键包能正常工作装完后别急着跑训练先做一次最小验证。这一步能拦住 80% 的“我明明装了为什么还报错”问题。python -c import cv2, torch, torchvision; print(cv2, cv2.__version__); print(torch, torch.__version__); print(torchvision, torchvision.__version__)看到三个版本号依次打出来说明 import 链路没问题。常见的报错是ImportError: libGL.so.1: cannot open shared object file这是 OpenCV 在 Linux 上缺系统图形库装一个 libgl1 就能解决Windows 上则更多是 DLL 缺失去微软官网装对应版本的 VC 运行库。环境这东西很玄学同样的报错在不同系统上原因完全不同先把版本号打印出来再去搜报错效率最高。3.3 用最小命令跑通训练和预测源码包里的演示视频展示的通常是两种运行方式一种是纯命令行另一种是带界面的。先走命令行因为它步骤少、报错直观。训练命令一般是python train.py --data dataset --epochs 20 --batch_size 32 --img_size 224训练结束后checkpoints 目录下会生成 best.pth 或类似权重文件。然后预测python predict.py --img dataset/test/银杏/xx.jpg --weights checkpoints/best.pthpredict.py 内部做的事是读图、缩放到 224×224、转张量、归一化、过一遍模型、取 softmax 最大值对应的类别打印出来。第一次跑如果权重还没训练好输出什么类别都不奇怪重点是确认整条链路没有断。对照演示视频里的输出格式检查一下你跑出来的结果应该和它一致一行图片路径、一行预测类别、一行置信度。4. 换自己的数据重训数据划分、训练入口参数和验证策略4.1 把照片整理成按树叶类别分组的目录源码里的内置数据集是别人拍好的真正练手要换上自己的树叶照片。先把散落在手机或网盘里的图片归类一个类别建一个文件夹类名用拼音或英文都行但不要用中文免得训练脚本在部分操作系统上读路径出问题。import shutil import pathlib raw_root pathlib.Path(leaf_raw) # 原始图片目录文件名已带类别前缀 out_root pathlib.Path(dataset) train_root out_root / train val_root out_root / val for img_path in raw_root.rglob(*.jpg): label img_path.stem.split(_)[0] # 文件名像 银杏_001.jpg 这样 target train_root / label target.mkdir(parentsTrue, exist_okTrue) shutil.copy(img_path, target / img_path.name)这段脚本把“leaf_raw”里散落的照片按文件名前缀分进了对应类别的文件夹。如果你拍的照片已经在一个大类目录里label可以直接取上级目录名img_path.parent.name。之后再手动往 train 里挑一部分挪到 val 做验证集比例按 8:2 比较常见。注意训练集和验证集不能有同名文件否则模型相当于提前看过答案。4.2 训练脚本四个入口参数img_size、batch、lr、epochs不管源码里的 train.py 长什么样最终都要暴露这几个入口参数没有的话你自己加上去。它们直接决定训练速度和最终精度参数常见取值影响img_size224太小丢细节太大占显存224 是预训练模型的标准输入batch_size1664越大每轮越快但显存不够会崩CPU 上建议 816lr1e-3 或 1e-4从头训练用 1e-3微调预训练模型用 1e-4 更稳epochs30100不是越大越好配合早停看验证集表现import argparse parser argparse.ArgumentParser() parser.add_argument(--data, defaultdataset) parser.add_argument(--img_size, typeint, default224) parser.add_argument(--batch_size, typeint, default32) parser.add_argument(--lr, typefloat, default1e-4) parser.add_argument(--epochs, typeint, default50) args parser.parse_args()参数值不是拍脑袋定的。img_size 224 是因为 torchvision 预训练模型的输入尺寸就是 224改成 256 不会变好反而要重新对齐模型结构lr 这条如果我训练的是 DenseNet121 的全连接层1e-3 也可以但如果整个模型一起微调1e-4 是我用得最多的太大容易出现验证集准确率在 10 个 epoch 内反复横跳。batch_size 建议用 2 的整数次幂深度学习框架对这类尺寸的内部分块优化最好。4.3 早停和模型保存训练完到底拿哪一版权重树叶识别这类项目训练到后期最典型的表现为训练损失还在降验证准确率已经不动了甚至开始回撤。这就是过拟合的信号保存模型时不要只看最后一个 epoch而是盯着验证集准确率最高的那一版。best_acc 0.0 for epoch in range(args.epochs): train_loss train_one_epoch(model, train_loader, optimizer) val_acc validate(model, val_loader) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), checkpoints/best.pth) print(fepoch {epoch}: saved best, val_acc{val_acc:.4f}) if not improved_for_epochs 5: print(early stop) breakbest.pth始终保留验证集上表现最好的权重这就是“后悔药”最后几轮过拟合了你不需要重训直接回滚到最优那一版。improved_for_epochs在这里做的是早停计数当连续 5 个 epoch 验证准确率没有提升就停掉省时间也防止模型继续在训练集上钻牛角尖。如果数据集特别小每一类只有三五十张图早停的 patience 可以放到 8否则很容易在某个波动点误停。5. 树叶识别系统最常见的五个坑现象、原因与解决方案5.1 pip 装 torch 超时或速度几十 KB/s现象pip install torch 卡在 Downloading 阶段最后报 timeout。原因torch 的安装包以 GB 计国外源不稳定尤其在公司内网环境下更明显。解决先配置国内镜像源再带--no-cache-dir重装避免缓存的半个包干扰pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple pip install torch torchvision --no-cache-dir装完用 3.2 节那条命令验证一遍能 import 就说明装完整了。torch 这种大包不适合用requirements.txt一把梭它依赖的 CUDA 版本要和本机驱动匹配分开装反而清楚。5.2 cv2 读取中文路径图片返回 None现象predict.py 在 Windows 上运行图片路径里带“银杏”两个字img 读出来是 None代码在 cvtColor 那行崩掉。原因OpenCV 的 imread 接口对中文路径和部分空格支持不好传进去的路径到不了文件系统读取层。解决不用 cv2.imread换用 np.fromfile 读字节再解码import cv2 import numpy as np def imread_chinese(path): return cv2.imdecode(np.fromfile(path, dtypenp.uint8), cv2.IMREAD_COLOR) img imread_chinese(数据集/银杏/001.jpg)这个函数是中文路径场景的标配。注意用imdecode读出来的图片是 BGR 顺序后面如果和 PIL 混着用通道顺序对不齐是另一个常见问题见 5.4。5.3 在 GPU 上训练的模型拿到 CPU 电脑上加载报错现象代码在带显卡的机器上训练完把 best.pth 拷到笔记本预测torch.load 报错Attempting to deserialize object on a CUDA device。原因保存的权重里记录了张量所在的设备信息CPU 机器上没有对应 CUDA 设备。解决加载时指定map_locationweights torch.load(checkpoints/best.pth, map_locationcpu) model.load_state_dict(weights)更稳妥的写法是训练完只保存model.state_dict()不要整个 model 对象塞进去前者只是一个有序字典跨设备迁移最干净。5.4 训练时图片颜色怪异cv2 的 BGR 和 PIL 的 RGB 混用现象用 cv2 读图训练的模型预测时切到 PIL 读图准确率骤降或者显示出来的图片颜色发蓝发暗。原因OpenCV 读图默认是 BGR 顺序PIL 是 RGB。训练和预测链路不统一意味着同一个像素值被当成了不同通道输入模型。解决固定一条读取链路。我习惯全部走 PILfrom PIL import Image img Image.open(test.jpg).convert(RGB)用 cv2 的话读完后立刻转回 RGBimg cv2.cvtColor(img, cv2.COLOR_BGR2RGB)这个坑隐蔽在于不报错只是准确率比预期低不少而且不好排查。你把训练集里一张图拿出来分别用 cv2 和 PIL 读一遍显示对比一眼就能看出来。5.5 追加新类别后旧模型直接“失忆”现象原来能识别 5 种树叶靠打补丁在数据集里加了一个新类别文件夹重新训练后旧类别预测全乱。原因ImageFolder 按文件夹名字母顺序生成索引原类别“银杏”的索引可能从 0 变成 1模型输出的数字含义整体错位。而且换了全连接层输出维度后旧权重根本加载不上。解决没有捷径新增类别就重新训练一个全量模型。如果不想每次重训一开始就维护一份固定的 category.txt银杏 枫叶 梧桐叶训练和预测都读这份文件生成索引而不是依赖文件夹扫描。这样即使以后加类别旧类别索引不变但全连接层输出维度还是得改、还是得重训。所以我的建议很简单树叶识别的类别一旦确定就别频繁加一次想清楚再动手。6. 从会跑到能干活断点续训、置信度输出和一点实用习惯6.1 断点续训加载 checkpoint 比从头再来省半天训练到一半断电或手动中断是所有跑训练脚本的人都会遇到的事。源码里如果没有保存 optimizer 和 epoch那就只能从头再来。给 train.py 加一个--resume参数start_epoch 0 if args.resume: ckpt torch.load(args.resume, map_locationdevice) model.load_state_dict(ckpt[model]) optimizer.load_state_dict(ckpt[optimizer]) start_epoch ckpt[epoch] 1 print(fresume from epoch {start_epoch})对应的保存代码要写成 checkpoints/ckpt.pth除了 model 还要带上 optimizer 和 epoch。这里最容易被忽略的是 optimizer 的 state_dict 也必须恢复否则学习率、动量等状态丢失续训的前几个 epoch 等于重新适应。6.2 只打印类别名不够把 top-3 置信度一起输出树叶识别里相近类别很容易混淆比如两种叶子形状几乎一样只在边缘锯齿上有差别。如果代码只输出一个预测类别错了你都不知道它离正确答案有多近。predict.py 里最好同时打印概率最高的前三个类别probs torch.softmax(logits, dim1)[0] # logits 来自 model(img) top3 probs.topk(3) for score, idx in zip(top3.values, top3.indices): print(f{idx2name[idx.item()]}: {score.item():.3f})topk(3)拿到概率最高的三个索引和对应概率idx2name是之前保存的类别映射。这个习惯帮我在调试模型时省了很多时间如果正确答案永远出现在第二名说明模型已经学到了特征只是类别边界需要更多数据而不是模型结构问题。6.3 验证习惯别用训练集里的原图给自己打气我自己栽过这个跟头调完参数后准确率上到 95%当时挺高兴后来才发现测试用的图片来自训练集模型等于开卷考试。正确做法是每类挑 35 张从未参与训练的照片放到一个单独的 test 目录里预测时只从这里面取图。想更严格一点就从手机现拍几张不同光线下的叶子丢进去测看看模型到底是不是真的在“认识树叶”而不是背下了训练集里的特定图片。这套树叶识别系统本身不复杂但把它跑通、改到能识别自己的数据你会把图像分类的一整条链路走完环境、数据、训练、评估、部署。演示视频能跑通只说明环境没问题真正检验项目价值的是换一批没见过的新图片你的模型还能不能答对。希望这份从环境到避坑的记录能帮到你。本文还有配套的精品资源点击获取
