YOLOv5+Flask小麦麦穗检测:毕设级项目全流程实战解析

发布时间:2026/8/31 16:45:20
YOLOv5+Flask小麦麦穗检测:毕设级项目全流程实战解析 简介本资源是一套面向计算机视觉方向本科毕业设计与农业AI应用开发者的完整实践方案聚焦小麦麦穗智能检测这一典型农业场景融合YOLOv5目标检测模型与Flask轻量级Web服务解决田间图像中麦穗定位、计数与可视化反馈的实际问题。压缩包共37个文件2.23MB含14个Python核心模块如app.py、detect_utils.py、wbf_utils.py、7个配置类YAML文件含模型结构与训练参数、12张实测JPG图像、1个HTML前端页面、1个CSS样式文件及README.md等文档整体采用模块化分层设计便于理解推理流程、调试部署逻辑与二次集成。目前已有95人学习下载提供从数据预处理、YOLOv5模型训练、Flask接口封装到前后端联调的全流程源码与详细使用说明涵盖异常处理、参数调优与性能优化要点是深入掌握CV落地工程化实践的高价值参考项目。 做毕业设计选了农业视觉方向的同学应该都绕不开目标检测这块。YOLOv5加Flask实现小麦麦穗检测这套组合在近两年的毕设题目里出现频率非常高原因也简单一是YOLOv5在检测精度和部署灵活性上比较均衡适合本科毕设的体量二是Flask能快速把模型包装成一个网页应用演示效果好答辩时不用只对着终端输出讲打开浏览器上传一张图就能看到检测框说服力直接拉满。我把整套东西完整跑了一遍从环境搭建、数据处理、模型训练到Flask接口封装中间踩了不少坑也搞清楚了很多细节。这篇就以“毕设级小麦麦穗检测项目”为线索把整个链路拆开讲透重点说清楚每一步为什么要这么做、参数怎么调、哪些环节最容易出问题以及项目中“使用说明”里没写出来的那些经验。1. 项目整体设计与技术选型为什么是YOLOv5加Flask拿到这个毕设题目第一反应不能是“直接开训”而是要先想清楚系统架构。一个完整的小麦麦穗检测项目实际上由三块组成目标检测算法、模型训练/推理脚本、Web展示层。三者的技术选型决定了整个毕设的工作量和答辩深度。1.1 检测模型选型的得与失目标检测算法可选的方向很多从传统的Haar、HOG加SVM到两阶段的Faster R-CNN再到单阶段的SSD和YOLO系列。Faster R-CNN精度高但推理速度慢训练一轮的时间也长在普通学生电脑上跑起来很难受SSD速度尚可但小目标检测能力一般而小麦麦穗在田间图像里恰恰有很多小目标——麦穗在远景图像里可能只有几十个像素大小这直接影响了检测器的选型倾向。YOLOv5正好卡在一个舒服的位置。它用CSPDarknet作为骨干网络配合PANet做特征融合在COCO数据集上表现出不错的精度同时推理速度能跑到100FPS以上看显卡型号。更关键的是Ultralytics官方把训练、验证、导出流程整理得特别完善一个train.py就能跑完整轮训练一个detect.py就能做推理对毕设项目来说这意味着你不需要从零手写训练循环可以把精力放在数据处理和系统整合上。这个取舍在毕设场景里非常重要。你要清楚毕设不是发论文目标不是刷SOTA精度而是把一个完整项目做扎实。YOLOv5能让你花更少的时间在底层工程上把更多时间留给前端交互、接口设计、实验对比这些更“看得见”的工作。1.2 Web框架选型Flask为什么够用Web框架方面最常见的对比对象就是Flask和Django。Django功能强大自带Admin后台、ORM、表单校验但这也意味着它的学习成本和项目体积都不小。而Flask是一个轻量级框架核心代码非常精简路由、请求处理、模板渲染这些基础功能都具备对于“上传一张图、返回检测结果”这样的单功能应用来说Flask几十行代码就能搞定而且代码逻辑一目了然答辩时也容易讲清楚。有人可能会问为什么不直接用前端JavaScript调用ONNX模型或者TensorFlow.js在浏览器里跑推理理论上可以但这对前端功底要求很高模型转换和前端推理库的调优也容易出问题。而Flask的做法是前端只管上传图片和展示结果后端负责加载模型、做推理、返回标注图片职责分离很清晰。这种模式也是真实工业项目里最常见的架构之一做毕设的收获会更大。1.3 项目解压后的结构认知拿到压缩包后首先应该建立对项目整体结构的认知。一个规范的YOLOv5Flask项目一般不会把训练代码和Web代码混在一起而是分成几个明确的功能模块。正常的目录结构是这样project_root/ ├── app.py # Flask主程序 ├── models/ # 存放训练好的权重 │ └── best.pt ├── yolov5/ # YOLOv5官方源码目录 │ ├── train.py │ ├── detect.py │ └── ... ├── data/ # 数据集与配置文件 │ ├── mw.yaml │ └── images/ ├── templates/ # Flask前端页面 │ └── index.html ├── static/ │ ├── uploads/ # 上传的原始图片 │ └── results/ # 标注后的结果图 └── requirements.txt第一次拿到手先别急着跑把每个目录打开看一眼弄清楚哪些是官方代码、哪些是用户自己写的这对后续修改和答辩讲解非常重要。很多同学一上来就跑app.py结果报错找不到yolov5模块其实就是没理解相对路径的引用关系。2. 环境准备搭好一套能跑起来的PyTorch环境环境配置是很多毕设项目的第一个坑而且这个坑往往不是技术难度问题而是版本不匹配问题。YOLOv5对PyTorch版本有要求Flask对Python版本有要求如果一开始没规划好后面会遇到一堆莫名其妙的报错。2.1 Python与PyTorch版本的匹配策略YOLOv5官方在2023年左右的版本要求Python 3.8以上推荐3.10左右。PyTorch方面如果使用GPU训练需要根据显卡驱动版本选择合适的CUDA版本再安装对应的PyTorch。这里最容易踩的坑是直接pip install torch装的是CPU版本训练速度慢到怀疑人生或者装了一个太新的CUDA版本显卡驱动不支持。我的建议是先用nvidia-smi查一下自己显卡驱动的CUDA版本上限然后去PyTorch官网选择对应的安装命令。比如驱动支持CUDA 11.8就装cu118的版本不要盲目追求最新。如果机器没有独立显卡那就老老实实装CPU版PyTorch训练会很慢但也不是不能用后面会讲到CPU机器怎么调整策略。Python版本管理建议用conda创建一个独立环境不要污染系统Pythonconda create -n wheat python3.10 -y conda activate wheat这样即使环境装坏了删掉重来也就一条命令的成本。2.2 YOLOv5官方依赖安装的坑YOLOv5源码里自带一个requirements.txt里面列了所有依赖项。理论上直接pip install -r requirements.txt就能装好但实际执行时经常遇到几个问题一是torch和torchvision会自动装成最新版这会覆盖你已经安装好的GPU版本。解决办法是先用pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118装好GPU版再安装requirements.txt里的其他依赖但安装时要用--no-deps跳过torch相关项或者先手动删掉requirements.txt里torch那几行再装。二是opencv-python在部分Python版本下会编译失败或出现依赖冲突。如果装的是opencv-python-headless注意它和完整版不能共存否则会报libGL.so.1相关的错误。三是很多国内网络环境访问GitHub或PyTorch官方源下载模型权重时很慢或者直接超时。权重文件下载时可以手动到官方模型仓库下载然后放到项目指定目录比如YOLOv5会自动下载yolov5s.pt到当前目录如果卡住了可以手动下载后放进对应路径。我实际跑通一份版本组合是这样的Python 3.10 torch 2.0.1cu118 torchvision 0.15.1cu118 opencv-python 4.8.0.74 Flask 3.0.0。这套组合整体稳定没有遇到明显的冲突问题。2.3 没有GPU时怎么应对训练环节本科毕设的机器配置参差不齐用CPU训练YOLOv5不是不可以但要有心理准备。一张640x640的图像在CPU上调参训练一个epoch可能要跑几分钟到十几分钟100个epoch基本是天文数字。我的建议是分情况处理。如果只是想做推理演示那就直接用官方预训练权重微调实际上麦穗检测这类场景微调也能出效果如果是训练自己的小麦麦穗数据集可以采取三种策略一是降低图像分辨率到320或者416训练速度会明显提升二是缩小模型规模用YOLOv5s甚至YOLOv5n替代YOLOv5m三是租用云GPU训练成本不高比自己电脑熬几天强多了。另外不管用什么设备训练之前一定要先跑一个极小的迭代比如--epochs 3 --batch-size 2验证整个流程能走通再正式开训。这个习惯可以省下无数排查时间。3. 数据准备与模型训练复现小麦麦穗检测的核心环境装好之后就到了整个项目最有技术含量的部分——数据与训练。麦穗检测和通用物体检测不太一样小目标多、背景复杂农田、光照、遮挡训练细节直接影响最终效果。我在这部分踩的坑最多也最值得详细讲。3.1 CDataset的获取与标注格式转换做任何检测项目数据是第一生产力。小麦麦穗检测的开源数据集最有名的是GWHDGlobal Wheat Head Detection数据集在Kaggle上可以找到里面包含了来自多个国家的田间小麦图像标注的是麦穗的包围框。这个数据集也是很多毕设项目的直接数据来源。但拿到原始数据后不能直接开训因为YOLOv5需要的是YOLO格式的txt标注文件每行格式是“类别id 中心点x归一化 中心点y归一化 宽度w归一化 高度h归一化”。而COCO格式是JSON文件Pascal VOC格式是XML文件。如果原始数据是COCO格式就需要写一个转换脚本import json import os # coco标注转yolo格式 def coco_to_yolo(coco_json, img_dir, output_dir): with open(coco_json, r) as f: coco_data json.load(f) # 构建图像id到文件名映射 img_map {img[id]: img[file_name] for img in coco_data[images]} img_size_map {img[id]: (img[width], img[height]) for img in coco_data[images]} # 按图像分组标注 anns_by_img {} for ann in coco_data[annotations]: img_id ann[image_id] if img_id not in anns_by_img: anns_by_img[img_id] [] anns_by_img[img_id].append(ann) # 转换并写入txt for img_id, anns in anns_by_img.items(): img_w, img_h img_size_map[img_id] txt_path os.path.join(output_dir, img_map[img_id].replace(.jpg, .txt)) with open(txt_path, w) as f: for ann in anns: x, y, w, h ann[bbox] # coco的bbox是左上角坐标和宽高需要转成中心点格式 cx (x w / 2) / img_w cy (y h / 2) / img_h nw w / img_w nh h / img_h f.write(f0 {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}\n)这个转换逻辑很简单但有一个细节很容易被忽略COCO的bbox和YOLO的bbox坐标定义不一样前者是“左上角x、左上角y、宽、高”后者是“中心点x、中心点y、宽、高”转换的时候一定要先加半个宽高算出中心点。我见过很多同学没搞清楚这个区别标注文件全错了训练出来的模型预测框位置完全不对。3.2 数据配置yaml的写法YOLOv5训练时需要指定一个数据配置文件告诉它训练集、验证集路径类别数量和类别名称。小麦麦穗检测只有一个类别所以配置非常简单。但路径这块特别容易踩坑YOLOv5对路径的处理比较“直白”一般是相对于yaml文件所在目录的路径写成绝对路径最稳妥。配置文件内容如下# data/mw.yaml train: data/images/train val: data/images/val nc: 1 names: [wheat]注意train和val参数指向的是包含图片的目录而不是单个文件列表。YOLOv5会自动读取目录下所有图片并根据同名原则找到对应的txt标注文件。如果你的数据目录里有子文件夹YOLOv5也能递归扫描但建议保持目录结构的简洁。还要注意图片和标注文件必须同名且在同一层目录下。如果标注文件在另一个目录YOLOv5是不认识的除非你用/path/to/annotations这样的方式在yaml里指定但YOLOv5原生并不支持标注目录和图像目录分离。所以准备数据时把图片和txt放在一起是最省心的方式。3.3 训练参数怎么定从超参数到图像尺寸YOLOv5的训练参数直接影响模型效果和训练时间有几个关键参数需要重点理解。首先是--img即训练时的输入图像尺寸。YOLOv5默认是640但麦穗数据集里很多图像是1024x1024或者更大。直接缩放到640会丢失很多小目标细节所以在显存允许的情况下建议用--img 640或者--img 800。但如果你的显存不够比如只有6GB就得退回640甚至512不然会爆显存。这是一个权衡图像越大小目标检测效果越好但训练时间和显存消耗也越大。其次是--batch-size。常规经验是在显存允许范围内尽量设大一点因为batch size太小会导致训练不稳定但太大的batch在单卡上又可能直接OOM。6GB显存跑YOLOv5s、640分辨率batch-size可以设16如果是YOLOv5m设8比较保险。具体的显存占用可以用nvidia-smi实时监控。然后是--epochs。这个数值没有绝对标准但麦穗检测这种二分类小目标任务一般300个epoch以内就够了。我习惯用早停策略YOLOv5自带--patience参数比如设置--patience 20如果连续20个epoch验证集mAP没有提升就自动停止训练。这样既节省时间也能防止过拟合。最后是预训练权重--weights。强烈建议使用yolov5s.pt做预训练而不是从零训练。从零训练不仅收敛慢而且最终效果往往不如微调。麦穗的形态和通用物体特征在某些层面是共通的预训练权重能提供一个很好的初始状态。一个完整的训练命令参考如下python train.py \ --data data/mw.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 200 \ --patience 20 \ --project runs/train \ --name wheat_experiment3.4 训练完成后的模型评估怎么看训练结束后YOLOv5会在runs/train/wheat_experiment/下生成一系列文件其中最重要的是weights/best.pt和weights/last.pt。best.pt是在验证集上mAP最高的权重last.pt是最后一个epoch的权重。做毕设部署时一定要用best.pt别用错了。评估指标方面YOLOv5训练过程中会打印每个epoch的precision、recall、mAP0.5、mAP0.5:0.95等指标。对于麦穗检测我主要看mAP0.5因为在农业场景下IoU阈值0.5已经能很好地衡量检测框的位置准确性。如果mAP0.5能达到0.9以上说明模型在验证集上表现优秀如果只有0.6-0.7可能是数据量不足、标注质量不高或者学习率设置不合适。训练完成后用val.py做一次正式验证python val.py \ --data data/mw.yaml \ --weights runs/train/wheat_experiment/weights/best.pt \ --img 640这个命令会输出详细的评估报告并且把验证集上的预测结果可视化保存下来方便你直观判断模型哪里做得好、哪里做得不好。4. Flask Web服务实现把模型封装成可访问的接口训练出模型只是毕设的第一步真正让项目“看起来完整”的关键是Web服务。这部分的代码量并不大但要注意的细节非常多尤其是模型的加载时机和并发访问时的线程安全问题。4.1 Flask应用结构设计与路由规划Flask应用的主程序可以很简单但要对项目的可扩展性有所考虑。我的建议是不要在单个文件里塞太多逻辑至少把模型加载部分和图像处理部分抽出来。一个合理的Flask应用结构如下web_app/ ├── app.py ├── detector.py # 模型加载与推理封装 ├── templates/ │ └── index.html └── static/ ├── uploads/ └── results/detector.py里面封装一个麦穗检测器类负责加载模型、执行推理、返回结果。app.py只负责Flask路由和页面渲染。这样分层的好处是如果你以后想换一个检测模型或者换一种推理方式只需要修改detector.py不需要动Web层的代码。路由规划方面核心就两个首页路由GET /渲染上传页面以及上传接口POST /detect处理图片并返回结果。4.2 推理代码的封装思路推理封装是整条链路里最核心的一段代码直接决定上传图片后能否正确返回检测框。这里最推荐的方式是用torch.hub加载本地YOLOv5源码。加载本地仓库的好处是离线可用不依赖网络而且这个代码结构和训练时用的源码完全一致不容易出现版本冲突。# detector.py import torch import cv2 import numpy as np import os class WheatDetector: def __init__(self, weights_path, devicecpu): # 加载本地yolov5仓库 self.model torch.hub.load( ./yolov5, # 本地yolov5源码目录 custom, # 自定义模型 pathweights_path, # best.pt的路径 sourcelocal, # 从本地加载 force_reloadFalse ) self.model.conf 0.25 # 置信度阈值 self.model.iou 0.5 # NMS的IoU阈值 self.model.device device def predict(self, image_path): # 推理 results self.model(image_path, size640) # 获取结果中的信息 detections results.pandas().xyxy[0] # 绘制标注框 img cv2.imread(image_path) for _, row in detections.iterrows(): x1, y1, x2, y2 int(row[xmin]), int(row[ymin]), int(row[xmax]), int(row[ymax]) conf row[confidence] label fwheat {conf:.2f} cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) return img, detections这里有几个关键点要特别说明。第一个是torch.hub.load的force_reload参数第一次运行如果设为True它会重新加载模型并检查是否有缓存更新这在调试阶段很有用但每次启动都重新加载会比较慢。生产环境或者演示时建议设为False启动时间会缩短很多。第二个是推理时默认的size640这个要和训练时的--img保持一致否则检测效果会受到影响。如果你训练时用了800推理时也用800这样模型看到的图像尺度和训练时是一致的。第三个是模型加载时机。千万别在每次请求时都加载一次模型那会导致前端等很久。应该把模型的加载放在Flask应用启动时通过全局变量或者应用上下文持有。我的做法是直接实例化一个全局的WheatDetector对象只加载一次。4.3 Flask主程序与前端交互逻辑app.py的代码结构很清晰接收上传文件、保存图片、调用检测器推理、把结果图通过模板渲染展示。这里有一个小细节为了让浏览器能显示结果图需要把结果图保存到static目录下然后用URL路径传递给前端模板而不是直接把图片字节流塞给前端。# app.py import os import uuid from flask import Flask, request, render_template, jsonify from detector import WheatDetector app Flask(__name__) app.config[MAX_CONTENT_LENGTH] 16 * 1024 * 1024 # 限制上传大小16MB # 全局实例化检测器只在应用启动时加载一次 detector WheatDetector(weights_pathmodels/best.pt, devicecpu) UPLOAD_FOLDER static/uploads RESULT_FOLDER static/results os.makedirs(UPLOAD_FOLDER, exist_okTrue) os.makedirs(RESULT_FOLDER, exist_okTrue) app.route(/) def index(): return render_template(index.html) app.route(/detect, methods[POST]) def detect(): if file not in request.files: return jsonify({error: 没有上传文件}), 400 file request.files[file] if file.filename : return jsonify({error: 文件名为空}), 400 # 生成唯一文件名避免覆盖 ext file.filename.rsplit(., 1)[-1].lower() if ext not in [jpg, jpeg, png, bmp]: return jsonify({error: 不支持的图片格式}), 400 upload_name f{uuid.uuid4().hex}.{ext} upload_path os.path.join(UPLOAD_FOLDER, upload_name) file.save(upload_path) # 推理 result_img, detections detector.predict(upload_path) # 保存结果图 result_name fresult_{upload_name} result_path os.path.join(RESULT_FOLDER, result_name) cv2.imwrite(result_path, result_img) # 统计信息 count len(detections) result_data detections[[xmin, ymin, xmax, ymax, confidence]].to_dict(records) return render_template( index.html, original_imagefuploads/{upload_name}, result_imagefresults/{result_name}, countcount, detectionsresult_data ) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)前端页面index.html只需要一个文件上传控件、一个提交按钮和一个图片展示区域。用原生HTML加简单的条件判断就够了不需要引入前端框架。当Flask通过render_template传入检测结果后页面里用Jinja2模板渲染出结果即可。这里体现了Flask的好处模板和后端逻辑的整合非常自然不需要单独写前端API调用代码。4.4 上线前必须处理的安全与并发问题很多毕设项目做到能跑就收工了但如果你想让项目更完整这些问题值得提前考虑。首先是文件上传安全。一定要校验文件扩展名并且把上传文件存储到独立目录不要和源码混在一起。更安全一点的做法是不信任原始文件名用UUID重新生成文件名。我在上面的代码里就是这么做的既避免用户在文件名里做文章也防止中文文件名引起的编码问题。其次是图片大小限制。app.config[MAX_CONTENT_LENGTH]限制了请求体大小防止有人传一个几百MB的大图把服务拖垮。这个限制要根据你的服务器内存来定我设置16MB是够用的。然后是并发时的线程安全问题。Flask开发服务器默认是单进程多线程每个请求会在一个独立线程中执行。如果检测器中的模型是只读推理一般不会有问题但如果你在多个线程中同时调用同一个模型的forward方法理论上可能出现不确定行为。最稳妥的做法是给预测函数加一个线程锁import threading # 在WheatDetector初始化时 self.lock threading.Lock() # 在predict方法中 def predict(self, image_path): with self.lock: results self.model(image_path, size640)这样虽然牺牲了并发能力但保证了稳定性。如果你的并发要求更高可以考虑用gunicorn加多个worker进程每个进程单独加载一份模型但这属于生产级部署的范畴毕设够用就行。5. 常见问题排查与实测经验整个项目跑下来我整理了遇到频率最高的一批问题做成一个速查清单。如果你在复现过程中卡住了优先对照这个表排查。5.1 环境与依赖报错排雷表报错信息原因解决方法ModuleNotFoundError: No module named torchPyTorch未安装按显卡版本安装对应PyTorchAttributeError: NoneType object has no attribute shape读取图片失败很多情况下是中文路径问题确保所有路径不含中文用英文路径重命名CUDA out of memory显存不够减小batch-size或img尺寸或换小模型TypeError: __init__() got an unexpected keyword argument deviceYOLOv5版本和PyTorch版本不匹配核对YOLOv5源码版本要求升级或降级PyTorchFileNotFoundError: [Errno 2] No such file or directory: best.pt权重路径错误检查模型文件是否存在于指定路径用绝对路径最保险Host is unreachable或模型下载超时自动下载权重/数据集时网络不通手动下载权重放到对应目录或用国内镜像源Flask启动后浏览器无法访问监听地址不对app.run(host0.0.0.0)不要只写127.0.0.1前端上传图片后页面转圈模型推理太慢或报错未捕获终端查看Flask运行日志确认是否进入detect路由其中中文路径问题在国内用户里出现频率极高。Windows下默认用户名可能是中文YOLOv5的某些模块在读取路径时对中文支持不好报错还很隐晦。最省事的方案就是项目放英文纯路径下比如D:\wheat_det\不要放在桌面或中文目录里。5.2 检测效果不好的调优手段如果模型训练完了但在实际图片上检测效果不理想常见表现是漏检多、框不准、把背景误检成麦穗。这个问题要从几个方向排查。第一个方向是数据质量。检查标注框是否贴紧了麦穗边界是不是很多框框得过大或过小。麦穗在图像中可能是细长条标注框如果框成一整片模型学到的是“框出大片区域”而不是“框出单个麦穗”效果自然差。如果数据集里小目标占比高考虑使用YOLOv5的--multi-scale参数做多尺度训练让模型在不同尺度上都见过麦穗。第二个方向是推理参数。调试时可以把detector.model.conf调低到0.1看看是不是置信度阈值设得太高导致漏检如果出现大量低置信度误检再把阈值调回0.25或者更高。model.iou影响NMS的去重强度麦穗这类密集目标IoU阈值可以调高到0.6减少相邻麦穗被合并成一个框的情况。第三个方向是图像预处理。如果原始图像光照极不均匀直接训练和推理的效果都会打折。可以考虑在推理前先做一下直方图均衡化或者归一化处理。但要注意训练时如果没做这个处理推理时临时加上也不一定更好保持训练和推理的一致性才是关键。遇到过最典型的一个案例训练时用的是原始大图推理时Flask代码里用了cv2.resize把图片压到320后传给模型结果检测效果大幅下降。后来排查了半天才发现是推理尺寸和训练尺寸不一致。这个坑提醒大家修改任何代码之前先检查输入图像的尺寸是否符合模型预期。5.3 项目扩展的实际方向做完整套系统之后如果你想让毕设更有亮点有几个扩展方向可以参考。第一是把模型换成更新的YOLOv8或者YOLOv10对比检测效果和推理速度。这个方向操作起来不算难因为Ultralytics的API设计得很统一改一改detector.py里面的加载方式就能跑通答辩时还能多讲一个“模型选型实验对比”的模块。第二是做一个“批量检测”功能支持用户一次性上传多张图片后台用一个任务队列逐个处理。这个功能在真实农业场景中很实用比如检测一整块地的多张航拍照片。实现上可以用Falsk加Celery但毕设级别的话用线程池和简单的任务列表就足够了。第三是加入统计报表功能检测完之后按检测数量、置信度分布生成统计图表。前端可以用ECharts画图后端只需要把检测结果保存到JSON或者SQLite里。我个人的建议是选一个方向做深不要贪多。毕设答辩最怕的是“什么都有但什么都不深”与其做五个功能都很粗糙不如把一个功能做到极致把其中的技术细节讲清楚。6. 实测操作过程还原从零复现一遍完整项目为了让你能按图索骥地把项目跑通我把整套复现的操作步骤从头到尾写一遍。这里以一台8GB显存的Windows电脑为例操作步骤在Linux和Mac上基本一致只是安装命令会有些差异。6.1 推理演示的快速复现步骤如果你暂时没有训练计划只是想把整个项目跑起来看看效果可以按以下步骤操作。第一步安装Miniconda如果没装的话然后创建并激活虚拟环境。Python版本建议3.10。第二步安装PyTorch。假设你的GPU驱动支持CUDA 11.8执行pip install torch2.0.1 torchvision0.15.1 --index-url https://download.pytorch.org/whl/cu118没有GPU就执行pip install torch2.0.1 torchvision0.15.1第三步在项目根目录下安装其余依赖pip install -r requirements.txt如果requirements里包含torch建议先手动把torch相关行删掉或者安装时加--no-deps。第四步确认models/best.pt文件存在于指定目录。如果项目没有提供权重可以用你自己训练好的或者下载一个开源的麦穗检测权重。第五步启动Flask应用python app.py看到* Running on http://0.0.0.0:5000就说明启动成功了。浏览器打开http://127.0.0.1:5000上传一张包含麦穗的图片等待几秒钟就能看到检测结果和麦穗数量。6.2 用自己的数据训练麦穗模型的完整流程如果你手头有自己的小麦图片数据集或者从GWHD数据集取了一部分想从头训练一个属于自己的模型操作步骤如下。第一步把数据集整理成YOLOv5要求的格式。参考前面的转换脚本将标注转换成YOLO格式的txt确保每张图片都有对应的txt文件且两者放在同一目录下。然后把数据集按比例划分成训练集和验证集常见比例是8:2或者9:1。第二步创建数据配置文件data/mw.yaml内容参考前面提到的配置。第三步从官方仓库下载预训练权重yolov5s.pt放到项目根目录。第四步执行训练命令python train.py \ --data data/mw.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 200 \ --patience 20 \ --name wheat_custom训练过程中可以用TensorBoard查看loss曲线、mAP变化YOLOv5默认会在runs/train/wheat_custom/下生成训练日志。第五步训练完成后用val.py验证模型效果然后修改detector.py里的weights_path指向自己训练好的best.pt重启Flask应用即可。这套流程看起来很常规但每个环节都有需要注意的细节。数据划分时要保证训练集和验证集不来自同一张图像的不同裁剪区域避免数据泄漏训练集和验证集的类别分布要尽量一致防止验证集里出现训练集没见过的形态特征。7. 实测中的性能调优和体验优化项目跑通之后性能优化是一个很实际的话题。尤其是把模型部署为Web服务后用户上传图片的等待时间直接决定演示体验。我实测下来一个YOLOv5s模型在CPU上推理一张640x640的图片大约需要500到1000毫秒在GPU上则只需要30到60毫秒。这个差异在答辩演示现场会非常明显。7.1 模型推理加速的几个实用手段如果你是在本地笔记本上用CPU做演示推荐几个加速手段。第一个是使用TensorRT或者ONNX Runtime替换PyTorch原生推理。YOLOv5官方提供了export.py脚本可以把模型导出为ONNX格式。python export.py --weights models/best.pt --include onnx导出后在detector.py中使用onnxruntime进行推理代码逻辑略有变化但推理速度在CPU上通常能提升1.5到2倍。不过ONNX的部署代码需要自己写前处理和后处理工程量比直接用PyTorch大不少。如果是为了答辩演示可能不值得折腾但如果你想在论文里写一笔“部署优化”这个是值得做的。第二个是切换推理尺寸。如果对精度要求不是特别苛刻把推理时的size从640降到480推理速度会明显加快。但要注意验证一下检测效果没有明显下降。第三个是确保推理时不执行不必要的计算。比如用YOLOv5自带的detect.py时它会输出很多调试信息在Flask内调用时把日志级别调高避免不必要的IO操作拖慢速度。7.2 前端交互的体验优化性能优化不只是后端推理速度前端体验也很重要。我见过很多毕设项目上传图片后没有任何反馈用户以为卡死了其实是在等待推理结果。最简单的优化是在前端加一个loading状态form iduploadForm enctypemultipart/form-data input typefile namefile acceptimage/* required button typesubmit开始检测/button /form div idloading styledisplay:none;检测中请稍候.../div script document.getElementById(uploadForm).addEventListener(submit, function() { document.getElementById(loading).style.display block; }); /script这个改动成本极低但对演示体验的提升非常明显。另一个体验优化是显示检测到的麦穗数量统计。前端页面在展示结果图片的同时把count变量显示出来比如“检测到 128 个麦穗”。这个数据在Flask的render_template中已经传过去了前端直接展示就行。8. 最后再分享一点项目实操心得做这个小麦麦穗检测项目我最大的感受是毕设项目的难点往往不在算法本身而在于把算法工程的每一个细节都做扎实。很多人以为模型训练完就万事大吉了实际上从权重文件到用户能访问的Web服务中间隔着一层又一层容易被忽视的细节。我在实际调试中花时间最多的不是YOLOv5反而是Flask和前端之间的数据传递。比如结果图路径的拼接、静态文件目录的注册、上传文件名的编码问题这些看似简单的地方一旦出错报错信息往往又不太直观。我的建议是遇到问题先看Flask的终端日志把异常信息完整贴出来再结合报错关键字去查比盲目改代码高效得多。另外做这种项目一定要养成“小步快跑”的习惯。不要等全部代码写完再启动测试而是每完成一个功能就启动一次Flask手动上传一张图验证。比如先实现“上传图片并保存”再实现“加载模型并推理”最后实现“结果展示”。每一步都验证通过再往下走这样即使出问题范围也限定在最近改动的几行代码里。关于模型这部分如果你是用自己的数据集训练的记得保留训练过程中的输出文件。答辩时老师很可能会问训练集的规模、标注方式、最终精度这些数据跑一遍val.py就都有了。提前准备好这些数字比临时翻训练日志要从容得多。还想多说一句系统的最终效果很大程度上取决于标注质量而不是模型复杂度。如果你发现训练了很久模型效果依然一般先回头看数据集里有没有标注错误、漏标或者框不贴合的情况。我一直觉得数据质量才是这个项目的真正上限模型只是把数据里的信息表达出来而已。本文还有配套的精品资源点击获取