news 2026/10/2 2:47:53

YOLOv5小麦麦穗检测全流程:从标注训练到Flask网页部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5小麦麦穗检测全流程:从标注训练到Flask网页部署

简介:本资源是一套面向计算机视觉初学者与农业AI应用开发者的毕业设计级项目,聚焦小麦麦穗的自动化检测需求,解决农业生产中作物生长状态评估、产量预估与智能监测等实际问题。压缩包共37个文件,含14个Python源码(覆盖YOLOv5模型加载、Flask后端服务、图像预处理与结果可视化)、12张测试/示例图像、7个配置类YAML文件(含模型结构、训练参数与数据路径)、以及HTML前端页面、CSS样式、README说明文档和requirements依赖清单,整体仅2.23MB,轻量易部署。已有96人学习下载,适合课程设计、毕设开发或农业AI入门实践。用户可直接运行Flask服务上传图片获取检测结果,源码采用模块化设计,结构清晰;配套详细使用说明涵盖环境配置、参数调优、异常处理及性能优化要点,同时提供完整训练—推理—Web集成全流程代码,具备良好的二次开发与场景迁移基础。

1. 这不是又一个“YOLOv5 + Flask”套壳项目:它真能跑通小麦麦穗检测全流程,从标注、训练、推理到网页端可视化,所有环节都留了可调试的断点

你搜“YOLOv5 Flask 小麦检测”,十页结果里八页是空壳 demo:模型权重缺失、Flask 路由写死、前端页面连图片上传按钮都点不动。而这份毕业设计资源,我拆开 zip 后第一眼就看到datasets/wheat_spike/下整齐放着 327 张带.xml标注的田间实拍图(非公开数据集裁剪),models/yolov5s_wheat_best.pt是真实训练收敛后的权重(val/box_loss=0.042),app.py里predict_image()函数明确调用torch.no_grad()+conf=0.35+iou=0.45三重控制——这不是教学模板,是有人在华北平原麦田边蹲了两周拍图、标框、调参后交出的完整工程包。它解决的是农业场景下最痛的三个落地卡点:小目标密集麦穗漏检、田间光照变化导致的误报、以及基层农技员不会装 CUDA 的部署难题。适合两类人:一是需要快速交付毕设答辩的本科生(含 Word 版《使用说明》和答辩 PPT 框架),二是想验证轻量级农作物检测 pipeline 的一线算法工程师(所有超参数、数据增强策略、Flask 响应头配置全开放)。


2. 从原始图像到检测模型:YOLOv5 训练小麦麦穗的四步闭环,每步都配可复现命令与参数依据

2.1 数据准备:为什么必须用 VOC 格式而非 COCO?327 张图的标注逻辑与边界处理

这份资源采用 PASCAL VOC 格式(.xml),而非更常见的 COCO JSON,原因很实际:小麦麦穗在田间呈簇状分布,单穗尺寸常小于 32×32 像素,VOC 的<bndbox>直接记录像素坐标,避免 COCO 中 segmentation 多边形在小目标上产生的浮点误差累积。所有标注均通过labelImg工具完成,关键约束有三点:

  • 最小尺寸过滤:剔除宽度 < 15px 或高度 < 12px 的 bbox(对应田间拍摄距离下不可信的噪点);
  • 重叠抑制:同一张图中 IOU > 0.8 的相邻麦穗框只保留置信度更高的一个(防止模型学习虚假密集伪标签);
  • 类别统一:全部标注为wheat_spike单类(不区分品种或成熟度,符合基层农技识别需求)。

数据集结构严格遵循 YOLOv5 官方要求:

datasets/ └── wheat_spike/ ├── Annotations/ # 327 个 .xml 文件,含 <xmin><ymin><xmax><ymax> ├── JPEGImages/ # 327 张 .jpg,命名与 xml 一一对应 ├── ImageSets/ │ └── Main/ │ ├── train.txt # 262 行,每行一个文件名(无后缀) │ ├── val.txt # 65 行 │ └── test.txt # 空文件(毕业设计未做测试集评估,但留了接口) └── labels/ # 转换后生成的 .txt,每行格式:class_id center_x center_y width height(归一化)

提示:convert_voc_to_yolo.py脚本中--img-size 1280参数必须与训练时--img 1280一致,否则归一化坐标错位。该脚本会自动检查 XML 中<size>与实际图像尺寸是否匹配,不匹配则报错退出——这是防止后期训练 loss 爆炸的关键守门员。

2.2 模型训练:为什么选 yolov5s 而非 yolov5m?超参数组合背后的田间推理权衡

资源包中models/yolov5s_wheat_best.pt是基于yolov5s.yaml修改后训练所得,核心修改点有三处(见models/yolov5s_wheat.yaml):

  • 输入尺寸:nc: 1(单类)、depth_multiple: 0.33、width_multiple: 0.50(轻量化);
  • Anchor 重聚类:用utils/autoanchor.py对小麦麦穗 bbox 长宽比重新聚类,生成 3 组 anchor([12,18, 24,36, 48,72]),比默认 anchor 在小目标召回率上提升 11.3%(见results/train/exp/labels_correlogram.jpg);
  • 损失函数加权:hyp.yaml中box: 0.05,cls: 0.5,obj: 1.0—— 因麦穗外观差异小,分类损失权重压低,目标存在性(obj)权重拉高,防止背景误判。

训练命令直贴可用(Windows / Linux 通用):

# 进入 yolov5 目录(需提前 git clone https://github.com/ultralytics/yolov5) python train.py \ --img 1280 \ --batch 8 \ --epochs 300 \ --data ../datasets/wheat_spike/wheat_spike.yaml \ --cfg models/yolov5s_wheat.yaml \ --weights '' \ --name wheat_spike_train \ --cache
  • --cache:强制启用内存缓存,避免 SSD 频繁读取导致训练卡顿(327 张图虽少,但 1280×1280 分辨率下 I/O 是瓶颈);
  • --epochs 300:早停阈值设为patience=50(见train.py第 421 行),实际在 epoch 217 收敛;
  • --batch 8:显存占用峰值约 5.2GB(RTX 3060 可跑),若用 2060 需降为 4 并开启--rect。

2.3 模型验证:不只是 mAP@0.5,重点看小目标召回率(AP_s)与田间误报率(FP per image)

验证阶段不能只看results/train/exp/val_batch0_labels.jpg里的框画得漂不漂亮。资源包中val_results_analysis.ipynb提供了三维度分析:

  1. 小目标专项 AP:按 bbox 面积分桶统计(<32², 32²–96², >96²),AP_s达 0.682(高于 baseline yolov5s 的 0.521);
  2. 误报溯源:对val/集中所有 FP 框提取 HOG 特征,聚类发现 67% 误报集中在秸秆阴影区域(提示后续可加阴影抑制模块);
  3. 速度-精度平衡:在 Jetson Nano 上实测1280×1280→640×640resize 后推理耗时 142ms,mAP@0.5 下降 0.019,但帧率从 3.2fps 提升至 5.8fps——这对无人机巡检足够。

关键验证命令(生成详细 PR 曲线):

python val.py \ --data ../datasets/wheat_spike/wheat_spike.yaml \ --weights ../models/yolov5s_wheat_best.pt \ --img 1280 \ --task val \ --name wheat_spike_val \ --plots # 自动生成 PR_curve.png、F1_curve.png 等

输出目录runs/val/wheat_spike_val/中metrics.txt记录了每类 AP、AR、F1-score,单类任务下F1-score=0.82是比 mAP 更贴近业务的指标(农技员更关心“找全了多少穗”,而非“框得多准”)。

2.4 模型导出:ONNX 与 TorchScript 双路径,为什么 Flask 选 ONNX 而非原生 PyTorch?

Flask 后端采用 ONNX Runtime 推理,而非直接加载.pt,原因有二:

  • 跨平台兼容性:ONNX 模型可在无 CUDA 环境(如树莓派 5)用 CPU 推理,且onnxruntime-gpu与onnxruntimeAPI 一致;
  • 内存可控性:PyTorch 加载.pt会预分配显存,而 ONNX Runtime 可通过sess_options.intra_op_thread_count = 2限制线程数,避免 Flask 多请求并发时显存 OOM。

导出命令(需先安装 onnx==1.12.0):

python export.py \ --weights ../models/yolov5s_wheat_best.pt \ --include onnx \ --imgsz 1280 \ --dynamic # 启用动态轴,适配不同尺寸输入

生成yolov5s_wheat_best.onnx后,用onnx.checker.check_model()验证有效性,并用onnx.shape_inference.infer_shapes()补全 shape 信息——这步漏掉会导致 Flask 加载时报InvalidArgumentError: Input size mismatch。


3. 从模型到网页:Flask 服务的三层架构设计,如何让农技员在 Chrome 里直接拖图检测

3.1 后端路由设计:为什么/detect不用 POST 传图,而坚持用 multipart/form-data?

资源包中app.py的/detect路由明确要求enctype="multipart/form-data",而非 base64 字符串或 JSON 传图,原因在于:

  • 大图容忍度:田间拍摄图常达 4000×3000 像素,base64 编码后体积膨胀 33%,Chrome 对 URL 长度有限制(约 2MB),而 form-data 无此限制;
  • 流式处理:Flask 的request.files['image']返回FileStorage对象,可直接save()到临时目录,避免内存中解码整图(12MB 图片解码后占 140MB RAM);
  • 错误反馈精准:若用户上传非图片文件,file.filename.rsplit('.', 1)[1].lower() not in {'png', 'jpg', 'jpeg'}可立即返回400 Bad Request,而非等到模型加载时报PIL.UnidentifiedImageError。

核心检测逻辑(精简版):

@app.route('/detect', methods=['POST']) def detect(): if 'image' not in request.files: return jsonify({'error': 'No image uploaded'}), 400 file = request.files['image'] if file.filename == '': return jsonify({'error': 'Empty filename'}), 400 # 保存临时文件(避免内存爆炸) temp_path = os.path.join('temp', secure_filename(file.filename)) file.save(temp_path) try: # ONNX 推理(含预处理、NMS、后处理) results = run_onnx_inference(temp_path, model_path='models/yolov5s_wheat_best.onnx') # 生成带框图并返回 base64 output_img_b64 = draw_boxes_on_image(temp_path, results) return jsonify({ 'success': True, 'detection_count': len(results), 'image_with_boxes': output_img_b64 }) except Exception as e: return jsonify({'error': str(e)}), 500 finally: if os.path.exists(temp_path): os.remove(temp_path) # 必须清理,否则磁盘爆满

3.2 前端交互:HTML 中的<input type="file">如何绑定到 Flask 的 multipart 解析?

templates/index.html中关键 HTML 结构:

<form id="uploadForm" enctype="multipart/form-data"> <input type="file" id="imageInput" name="image" accept="image/*" required> <button type="submit">上传检测</button> </form> <div id="result"></div>

配套 JavaScript(static/js/main.js)用FormData构造请求:

document.getElementById('uploadForm').addEventListener('submit', async function(e) { e.preventDefault(); const fileInput = document.getElementById('imageInput'); const formData = new FormData(); formData.append('image', fileInput.files[0]); // 关键:字段名必须与 Flask 中 request.files['image'] 一致 try { const res = await fetch('/detect', { method: 'POST', body: formData // 不设 Content-Type,浏览器自动设为 multipart/form-data + boundary }); const data = await res.json(); if (data.success) { document.getElementById('result').innerHTML = `<img src="data:image/png;base64,${data.image_with_boxes}" alt="检测结果">`; } else { alert('检测失败:' + data.error); } } catch (err) { alert('网络错误:' + err.message); } });

注意:fetch发送FormData时绝不能手动设置Content-Type,否则 boundary 丢失,Flask 解析失败报400 Bad Request。

3.3 响应优化:为什么返回 base64 图片而非 URL?如何控制响应大小不超 2MB?

返回 base64 而非http://localhost:5000/static/results/xxx.jpg,是为了规避 CORS 和静态文件权限问题(农技员可能用手机热点访问,IP 经常变)。但 base64 体积比原图大 33%,故必须压缩:

  • 预处理压缩:draw_boxes_on_image()中cv2.imencode('.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 85])将 JPEG 质量压至 85(肉眼无损,体积减 40%);
  • 尺寸限制:上传图 > 1920×1080 时,前端 JS 自动canvas.toDataURL('image/jpeg', 0.7)降采样,确保 base64 字符串 < 2MB(Chrome 对fetch响应体无硬限,但过大会卡 UI)。

验证方法:在浏览器开发者工具 Network 标签页查看/detect响应大小,正常应为 300–800KB(1280×720 输入图)。

3.4 部署适配:Windows 下如何绕过spawn问题启动多进程 Flask?

资源包run.bat中启动命令为:

set FLASK_APP=app.py set FLASK_ENV=production flask run --host=0.0.0.0 --port=5000 --no-debugger --no-reload

禁用--reload是关键——Windows 下 Flask 默认用spawn方式重启子进程,而 ONNX Runtime 初始化时会锁住 GPU 显存,导致重启时报CUDA initialization error。生产环境必须用--no-reload,靠nginx或supervisor管理进程生命周期。若需热更新,改用watchdog监控文件变化后os.kill(os.getpid(), signal.SIGTERM)主动退出,由外部进程管理器拉起新实例。


4. 避坑指南:小麦麦穗检测在 Flask 中的五个血泪现场,现象、原因、解决方案全列清

4.1 现象:上传图片后页面卡死 30 秒,最终返回 500 错误,日志显示OSError: [WinError 1455] 页面文件太小

  • 原因:Windows 默认页面文件(虚拟内存)仅 2GB,而 YOLOv5s ONNX 模型加载 + 1280×1280 图像预处理需峰值 3.8GB 内存,物理内存不足时触发页面文件耗尽。
  • 解决:
    1. 扩展页面文件:系统属性 → 高级 → 性能设置 → 高级 → 虚拟内存 → 自定义大小,初始大小设 4096MB,最大值 8192MB;
    2. 代码层降内存:在run_onnx_inference()开头添加torch.set_num_threads(2),限制 PyTorch 线程数;
    3. 预处理降分辨率:cv2.resize(img, (640, 640))替代 1280,mAP@0.5 仅降 0.008,但内存峰值降至 1.9GB。

4.2 现象:检测结果框位置偏移 50 像素,且所有框都向右下角偏

  • 原因:convert_voc_to_yolo.py脚本中未校验图像实际尺寸,某张图 XML 标注<size><width>1920</width><height>1080</height></size>,但实际 JPG 是 1280×720,导致归一化坐标计算错误。
  • 解决:
    1. 运行python utils/check_dataset_integrity.py --dataset-path datasets/wheat_spike/,自动扫描所有 XML 与 JPG 尺寸不匹配项;
    2. 手动修正 XML 中<size>值,或用PIL.Image.open().size读取 JPG 实际尺寸覆盖 XML;
    3. 在run_onnx_inference()中增加assert img.shape[:2] == (1280, 1280), 否则resize后坐标错乱。

4.3 现象:Flask 启动后访问http://127.0.0.1:5000显示空白页,Network 查看/static/css/style.css404

  • 原因:Flask 默认静态文件路径为./static,但资源包中static/目录被压缩进 zip 时因路径层级错误变成static\css\style.css(Windows 反斜杠),而 Flask 用/解析路径,找不到文件。
  • 解决:
    1. 解压 zip 后检查static/是否为顶层目录(不是【精选毕业设计】/static/);
    2. 若路径嵌套,将static/、templates/剪切到项目根目录(与app.py同级);
    3. 验证:print(app.static_folder)应输出绝对路径,末尾为/static。

4.4 现象:同一张图多次上传,第一次检测正确,第二次开始框变少且置信度暴跌

  • 原因:ONNX Runtime Session 复用时未重置状态,内部缓存了前次推理的中间 tensor,导致后续输入被污染。
  • 解决:
    1. 禁止 Session 复用:每次run_onnx_inference()都新建 Session(牺牲 120ms 启动时间,换稳定性);
    2. 或启用 Session 共享但强制清除:
      sess.run(None, {input_name: input_tensor}) # 推理 sess._sess.reset_session() # 清除内部状态(ONNX Runtime 1.12+ 支持)

4.5 现象:Linux 服务器部署后,/detect返回500 Internal Server Error,日志无报错

  • 原因:onnxruntime-gpu依赖 CUDA 驱动,但服务器仅安装了nvidia-driver-525,而onnxruntime-gpu==1.15.1要求驱动 ≥ 535,版本不匹配导致 silent fail。
  • 解决:
    1. 查驱动版本:nvidia-smi;
    2. 查 ONNX Runtime 兼容表(https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html);
    3. 降级 ONNX Runtime:pip install onnxruntime-gpu==1.14.1(支持驱动 515+);
    4. 或改用 CPU 版:pip uninstall onnxruntime-gpu && pip install onnxruntime,性能降 3.2 倍但稳定。

5. 进阶技巧:如何用这份资源快速迁移到新作物检测?三步替换法与两个必改参数

5.1 数据迁移:从小麦麦穗到水稻稻穗,只需改三处文件,无需重写代码

新作物检测的核心是标注一致性与数据增强适配,而非模型重训。以水稻稻穗为例(同样小目标、簇状分布):

  1. 替换标注文件:将datasets/wheat_spike/Annotations/全部替换为水稻.xml,确保<object><name>rice_ear</name></object>;
  2. 更新数据集配置:修改datasets/wheat_spike/wheat_spike.yaml中train: ../wheat_spike/images/train/→train: ../rice_ear/images/train/,nc: 1→nc: 1(仍单类),names: ['wheat_spike']→names: ['rice_ear'];
  3. 调整数据增强:编辑data/hyps/hyp.scratch-low.yaml,将hsv_h: 0.015(色相扰动)改为0.005(水稻颜色更稳定),mosaic: 1.0保持(应对田间密集分布)。

关键:convert_voc_to_yolo.py中--classes ['rice_ear']参数必须与 yaml 中names一致,否则生成的labels/*.txt里 class_id 为-1,训练时报IndexError: index -1 is out of bounds。

5.2 模型微调:冻结 backbone 微调 head,30 分钟内获得新作物检测能力

不必从头训练,用--weights加载原小麦模型,冻结 backbone 后微调 detection head:

python train.py \ --img 1280 \ --batch 8 \ --epochs 50 \ --data ../datasets/rice_ear/rice_ear.yaml \ --weights ../models/yolov5s_wheat_best.pt \ --cfg models/yolov5s_wheat.yaml \ --name rice_ear_finetune \ --freeze 10 # 冻结前 10 层(backbone),只训 neck + head
  • --freeze 10:查看models/yolov5s_wheat.yaml中backbone:部分共 10 层(从Focus到Conv),冻结后显存占用降 35%;
  • --epochs 50:早停patience=15,通常 22 轮收敛;
  • 效果:水稻稻穗 mAP@0.5 从随机初始化的 0.312 提升至 0.741,耗时 28 分钟(RTX 3060)。

5.3 Flask 服务增强:添加检测统计面板,用 Pandas 实时汇总每张图的穗数与密度

资源包未提供统计功能,但可 5 行代码接入:在app.py的/detect路由末尾插入:

# 新增统计逻辑 import pandas as pd from datetime import datetime stats_df = pd.DataFrame([{ 'timestamp': datetime.now().isoformat(), 'filename': file.filename, 'spike_count': len(results), 'image_width': img.shape[1], 'image_height': img.shape[0], 'density_per_m2': len(results) / (img.shape[0] * img.shape[1] * 0.0001) # 假设 1px=0.1mm }]) stats_df.to_csv('detection_stats.csv', mode='a', header=False, index=False)

再新建/stats路由读取 CSV 并返回 JSON:

@app.route('/stats') def get_stats(): try: df = pd.read_csv('detection_stats.csv', names=['timestamp','filename','spike_count','image_width','image_height','density_per_m2']) return jsonify(df.tail(10).to_dict('records')) # 返回最近 10 条 except FileNotFoundError: return jsonify([])

前端用setInterval(() => fetch('/stats').then(r => r.json()).then(renderStats), 5000)每 5 秒刷新统计面板——农技员一眼看出“今天巡检 12 块地,平均密度 128 穗/m²”。

从那以后我每次接手新作物检测项目,都先拿这份小麦资源当骨架:convert_voc_to_yolo.py校验数据、train.py冻结微调、app.py注入统计逻辑,三小时搭出可演示 demo。它不完美,但每个.py文件里都埋着可 debug 的 print、每个.yaml里都有注释说明参数意义——这才是毕业设计该有的样子,不是炫技的黑匣子,而是能掰开揉碎教人复现的后悔药。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 2:47:38

Windows图片自动上传百度网盘:官方同步空间与Python脚本方案

每天打开Windows电脑&#xff0c;总会看到一堆新产生的图片&#xff1a;手机相册导出的照片、截图工具抓的图、相机SD卡里的原片。这些图如果只躺在本地硬盘上&#xff0c;硬盘一坏&#xff0c;几年的记录全没了。我早就把备份这件事定成了刚需&#xff1a;自动上传到百度网盘。…

作者头像 李华
网站建设 2026/10/2 2:46:20

416×416脑部MRI肿瘤二分割数据集:从数据校验到可视化跑通

简介&#xff1a;本资源面向医学图像分割方向的初学者与算法实践者&#xff0c;提供一套416416分辨率的人脑MRI肿瘤二分类分割数据集及配套可视化脚本&#xff0c;可用于训练与验证U-Net等分割网络&#xff0c;帮助解决医学影像中前景标注获取困难的问题。压缩包共2000个文件&a…

作者头像 李华
网站建设 2026/10/2 2:46:17

Spring Boot集成MyBatis实战:从核心原理到排查方法

其实Spring和MyBatis这套组合&#xff0c;几乎是国内Java后端开发的“国民级配置”。凡是做业务系统、后台管理的朋友&#xff0c;大概率都跟它打过交道。为什么这样说&#xff1f;因为Spring负责把对象之间的依赖关系管起来&#xff0c;MyBatis负责把你跟数据库打交道这件事做…

作者头像 李华
网站建设 2026/10/2 2:45:49

SpringBoot+Vue车辆管理系统设计与实现:从零搭建到答辩通关指南

车辆管理系统这类题目&#xff0c;放在毕业设计里属于“经典永流传”的选择了。无论是计算机专业还是相关工科专业&#xff0c;每年选题库里基本都少不了它。要说原因也很直接&#xff1a;业务模型清晰&#xff08;无非是车辆信息、司机信息、用车申请这些&#xff09;、技术栈…

作者头像 李华
网站建设 2026/10/2 2:45:47

AI应用缓存优化实战:从单层Redis到三层架构,P95延迟降80%

去年年初我接手公司的一个AI客服项目&#xff0c;上线两周收到的用户反馈出奇一致&#xff1a;转圈太久了。团队第一反应和我一样——加缓存。做后端出身的人条件反射都是Redis&#xff0c;于是我们快速做了一个“推理结果缓存”&#xff1a;用户输入原样哈希&#xff0c;命中R…

作者头像 李华
网站建设 2026/10/2 2:45:45

YOLO交通标志数据集制作:3000张图VOC/COCO/YOLO格式转换与训练全流程

简介&#xff1a;本资源为面向目标检测初学者与算法工程师的YOLO交通标志识别数据集&#xff0c;聚焦真实道路场景下的标志检测任务&#xff0c;可服务于课程设计、毕业项目与模型训练实践。数据均经labelimg精细标注&#xff0c;标注框质量高&#xff0c;并同步提供voc(xml)、…

作者头像 李华