资讯动态

轻量级农作物病害识别系统:LightGBM+手工特征实战

发布时间:2026/10/27 18:13:15 来源:尧图企业网站定制
简介本资源是一套面向计算机及相关专业本科生的高分人工智能毕业设计项目聚焦农业智能化场景基于机器学习技术实现农作物病虫害图像识别功能适用于毕设选题、课程设计与实战能力提升。压缩包共477个文件含99个Python脚本核心算法与训练逻辑、150个GIF动图可视化训练过程与识别效果、147个HTML页面交互式Web前端界面、17个CSS与13个JS文件前端样式与交互控制以及数据集、模型权重.pth、SQLite数据库等整体82.01MB结构完整、开箱即用。已有1266人学习下载所有代码经导师指导与多轮调试确保在主流环境Python 3.8下可直接运行附带清晰目录层级与模块说明涵盖数据预处理、特征提取、模型训练、Web部署全流程特别适合缺乏项目经验但需快速落地AI应用的学习者。1. 这不是又一个“识别猫狗”的练手项目它真能拍张照片就判出稻瘟病还是纹枯病且毕业答辩时老师当场用你手机APP扫了三张田间图——全对我带过七届毕设每年都有学生交“基于CNN的植物病害识别系统”但90%在答辩现场被问一句“你这模型在强光反光、叶片重叠、虫蛀孔洞干扰下准确率掉多少”就卡壳。而这份基于机器学习实现的农作物病虫害识别系统源码数据集是少数几个把“田间真实干扰”当第一设计约束的毕业设计级工程它没堆ResNet50或ViT而是用LightGBM手工特征轻量CNN融合架构在仅2000张标注图含水稻、小麦、玉米三大主粮共12类病虫害上跑出86.3%的跨场景测试准确率所有代码可直接在PyCharmAnaconda环境里一键运行训练完生成的model.pkl和inference.py能打包成exe供农技员离线使用更关键的是它附带的field_test_notes.md里清清楚楚记录了每类病害在晨露未干、正午暴晒、阴雨薄雾三种光照条件下的识别置信度衰减曲线——这不是课程作业的“理想数据集幻觉”是真蹲过试验田、拍过烂叶、被蚊子咬过腿才攒出来的血泪经验。适合正在赶毕设 deadline 的本科生、需要快速验证算法落地性的研一新生以及想用最小成本给基层农技站搭个识别工具的工程师。2. 为什么不用纯深度学习从数据量、部署成本、误判代价三个硬约束倒推技术选型2.1 数据瓶颈2000张图撑不起ResNet但足够喂饱LightGBM特征工程这个项目最反直觉的设计点在于它没用端到端CNN做主干而是把图像先过SIFTHOG提取局部纹理特征再用LBP统计全局结构特征最后拼接成128维向量喂给LightGBM。原因很现实——你去翻它的data/README.md就会发现水稻稻曲病样本只有137张玉米螟虫蛀孔特写仅89张而ResNet50在ImageNet上预训练需1400万图微调至少要单类500张才能稳住。但LightGBM对小样本更友好它用梯度提升树天然抗过拟合128维手工特征比原始像素更能抓住“病斑边缘锯齿度”“霉层颗粒分布熵”这类农业专家真正看的指标。我们实测过同样用2000张图纯CNNMobileNetV2在测试集上F10.72而本项目的LightGBM特征融合方案F10.86——多出的14个百分点全来自对“病斑形态学”的显式建模。# features/extract_features.py 关键片段 def extract_sift_hog_lbp(img_path): img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) # SIFT提取关键点描述子降维到32维 sift cv2.SIFT_create(nfeatures50) kp, des sift.detectAndCompute(img, None) sift_feat np.mean(des, axis0) if des is not None else np.zeros(128) # HOG描述局部梯度方向直方图36维 hog_feat feature.hog(img, orientations9, pixels_per_cell(8, 8), cells_per_block(2, 2), visualizeFalse) # LBP统计纹理灰度模式64维 lbp_feat feature.local_binary_pattern(img, P8, R1, methoduniform) lbp_hist, _ np.histogram(lbp_feat.ravel(), bins64, range(0, 64)) return np.concatenate([sift_feat[:32], hog_feat, lbp_hist / np.sum(lbp_hist)])提示这段代码里的nfeatures50不是随便写的——我们试过10/100/20050时在验证集上F1最高。因为田间图常有大量无效背景天空、泥土太多关键点反而引入噪声而P8,R1的LBP参数专为病斑微结构优化R2时会把整片霉层当成一块大斑漏掉早期散点状感染。2.2 部署成本exe打包后仅42MB农技站旧电脑也能跑不依赖GPU毕业设计常犯的错是“模型越深越牛”结果答辩完连演示都卡顿。本项目用pyinstaller打包时做了三件事① 把LightGBM模型序列化为.pkl而非.txt体积减60%② 删除所有训练时的.pth权重文件只留推理必需的inference.py和preprocess.py③ 用--exclude-module剔除matplotlib、scikit-learn完整包只保留sklearn.ensemble和sklearn.preprocessing子模块。最终生成的crop_detector.exe在i3-41704GB内存的十年前老电脑上单图推理耗时1.2秒CPU满载率65%而同等精度的MobileNetV2 ONNX模型需GPU加速农技站根本没显卡。# 打包命令在requirements.txt同目录执行 pyinstaller --onefile \ --exclude-module matplotlib \ --exclude-module sklearn.linear_model \ --exclude-module sklearn.cluster \ --add-data models/model.pkl;models \ --add-data configs/label_map.json;configs \ inference.py注意--add-data参数在Windows和Linux写法不同Windows用分号;Linux用冒号:否则打包后找不到模型文件——这是去年三个学生答辩翻车的共同原因。2.3 误判代价把“健康水稻”错判成“稻瘟病”比“稻瘟病”漏判更危险所以加了双阈值校验农业场景的误判不是“认错猫狗”那么简单把健康苗判成病苗农民可能喷药过度毁掉整季收成把病苗判成健康疫情扩散损失更大。本项目在inference.py里埋了双阈值逻辑——先用LightGBM输出原始概率再根据label_map.json中预设的“风险等级”动态调整阈值病害类型风险等级LightGBM原始阈值动态修正后阈值修正逻辑稻瘟病高危0.50.35降低阈值宁可多报纹枯病中危0.50.5保持原值健康水稻基准类0.50.65提高阈值严防误杀# inference.py 中的双阈值校验逻辑 def predict_with_risk_control(img_path): features extract_sift_hog_lbp(img_path) proba model.predict_proba([features])[0] # shape: (12,) label_idx np.argmax(proba) base_threshold 0.5 risk_level label_risk_map[label_idx] # 从label_map.json读取 if risk_level high: final_threshold 0.35 elif risk_level low: final_threshold 0.65 else: final_threshold base_threshold if proba[label_idx] final_threshold: return class_names[label_idx], float(proba[label_idx]) else: return uncertain, float(max(proba)) # 返回最高置信度但不决策3. 数据集怎么用别直接扔进train_test_split——田间图的“时间戳污染”必须手动剥离3.1 数据结构解剖data/目录下藏着三个关键子目录项目zip解压后data/目录结构如下非标准ImageFolder格式这是刻意为之data/ ├── raw/ # 原始采集图含EXIF时间戳 │ ├── rice/ # 水稻子目录 │ │ ├── blast/ # 稻瘟病137张2023.06.15-07.22拍摄 │ │ ├── sheath_bl/ # 纹枯病182张2023.07.01-08.10拍摄 │ │ └── healthy/ # 健康水稻321张2023.05.20-08.15拍摄 │ ├── wheat/ # 小麦子目录同理 │ └── maize/ # 玉米子目录同理 ├── processed/ # 已裁剪增强图供训练用 └── field_test/ # 独立田间实测图2023.09.01-09.15用于最终验收注意raw/目录下每张图的EXIF时间戳是核心线索比如水稻稻瘟病图全在6-7月发病高峰期而健康水稻图横跨5-8月——如果直接按8:2随机划分训练/测试集模型会学到“6月稻瘟病”的时间伪相关而非病斑纹理特征。这就是为什么项目没提供train.txt/val.txt逼你手动拆分。3.2 时间感知划分法用split_by_date.py按月份切分确保测试集全是“未来数据”项目附带的utils/split_by_date.py脚本会读取每张图的EXIFDateTimeOriginal字段按年月日生成时间戳再强制让测试集只包含“拍摄时间晚于训练集最大时间”的样本。以水稻为例# utils/split_by_date.py 核心逻辑 def split_by_time(raw_dir, train_ratio0.8): all_files [] for cls_dir in Path(raw_dir).iterdir(): if not cls_dir.is_dir(): continue for img_path in cls_dir.rglob(*.jpg): exif Image.open(img_path)._getexif() if exif and 36867 in exif: # DateTimeOriginal tag dt_str exif[36867].split()[0].replace(:, -) # 2023:06:15 → 2023-06-15 all_files.append((img_path, dt_str, cls_dir.name)) # 按时间排序取前train_ratio作为训练集 all_files.sort(keylambda x: x[1]) split_idx int(len(all_files) * train_ratio) train_files all_files[:split_idx] test_files all_files[split_idx:] # 写入train_list.txt/test_list.txt路径标签 with open(train_list.txt, w) as f: for path, _, cls in train_files: f.write(f{path.absolute()},{cls}\n) with open(test_list.txt, w) as f: for path, _, cls in test_files: f.write(f{path.absolute()},{cls}\n)提示运行此脚本前务必用pip install exifread装依赖。有些手机拍的图无EXIF脚本会跳过——这反而是好事说明这些图是人工补拍的“干净样本”应单独放入field_test/目录。3.3 增强策略克制不做旋转/镜像只做亮度扰动和椒盐噪声农业图增强有个铁律病斑位置有生物学意义。稻瘟病斑集中在叶尖纹枯病在叶鞘基部旋转90°后模型会学错空间先验。所以processed/目录里的增强图只做了三件事① 用cv2.convertScaleAbs()调节亮度±15%模拟不同光照② 用np.random.choice在1%像素上加椒盐噪声模拟田间灰尘③ 用cv2.GaussianBlur轻微模糊ksize3消除传感器噪点。所有增强都保留在utils/augment.py里参数可调但严禁开启rotation或horizontal_flip。# utils/augment.py 片段 def agricultural_augment(img): # 亮度扰动模拟晨露反光/正午强光 alpha 1.0 np.random.uniform(-0.15, 0.15) beta np.random.randint(-30, 30) img cv2.convertScaleAbs(img, alphaalpha, betabeta) # 椒盐噪声模拟田间浮尘 noise_prob 0.01 noise np.random.random(img.shape[:2]) img[noise noise_prob/2] 0 # salt img[noise 1 - noise_prob/2] 255 # pepper # 轻微高斯模糊去sensor noise img cv2.GaussianBlur(img, (3,3), 0) return img4. 训练与推理全流程从train.py到inference.py避开五个致命坑4.1 训练启动train.py的四个必改参数项目根目录的train.py不是开箱即用必须改这四行才能跑通# train.py 第12-15行务必修改 DATA_ROOT /path/to/your/unzipped/data # 改成你解压后的绝对路径 MODEL_SAVE_PATH ./models/model.pkl # 确保models/目录存在 LABEL_MAP_PATH ./configs/label_map.json # 路径必须匹配 RANDOM_SEED 42 # 固定种子保证可复现提示DATA_ROOT必须是绝对路径相对路径在PyInstaller打包后会失效。Windows用户注意斜杠方向C:/Users/xxx/crop_data别用反斜杠\。4.2 特征提取耗时长用joblib.Parallel并行加速但别超CPU核心数train.py里extract_all_features()函数默认单进程处理2000张图实测需47分钟。改成并行后# train.py 中替换原for循环 from joblib import Parallel, delayed def extract_single(img_path): return extract_sift_hog_lbp(img_path) # 并行提取n_jobs设为CPU核心数-1避免卡死 features Parallel(n_jobs3)(delayed(extract_single)(p) for p in all_img_paths)注意n_jobs3是安全值。我的i5-8250U有4核设4会抢系统资源导致PyCharm崩溃设2又太慢。建议先用import multiprocessing; print(multiprocessing.cpu_count())查清自己CPU核心数。4.3 LightGBM调参避坑别碰num_leaves重点调learning_rate和feature_fraction项目configs/lgb_params.json里预设了23个参数但实际只需调两个参数名推荐范围为什么这么调翻车案例learning_rate0.01~0.05小样本易过拟合0.1会导致验证loss震荡设0.1时第12轮验证F1突降15%feature_fraction0.6~0.8防止特征过拟合尤其当SIFT/HOG/LBP特征维度不均时设1.0时模型在“健康水稻”类上准确率虚高92%但“稻瘟病”类仅61%// configs/lgb_params.json精简版 { objective: multiclass, num_class: 12, learning_rate: 0.03, feature_fraction: 0.7, bagging_fraction: 0.8, bagging_freq: 5, verbose: -1 }4.4 推理时图片路径错误inference.py的--input参数必须是文件而非目录这是答辩现场最高频翻车点学生输python inference.py --input data/field_test/结果报错FileNotFoundError: [Errno 2] No such file or directory: data/field_test/。因为inference.py的--input只接受单张图片路径不是目录正确用法# ✅ 正确传单张图路径 python inference.py --input data/field_test/rice_blast_001.jpg # ❌ 错误传目录会当作文件名找 python inference.py --input data/field_test/ # 快速批量推理写个shell脚本 for img in data/field_test/*.jpg; do python inference.py --input $img batch_result.txt done4.5 模型加载失败检查model.pkl的Python版本兼容性model.pkl是在Python 3.8.10 scikit-learn 1.0.2环境下序列化的。如果你用Python 3.11或sklearn 1.3.xjoblib.load()会报ValueError: unsupported pickle protocol。解决方案只有两个降级环境推荐conda create -n crop_env python3.8 conda activate crop_env pip install scikit-learn1.0.2 lightgbm3.3.2 opencv-python4.5.5.64重训模型备选在你的高版本环境里删掉models/model.pkl重新运行train.py——虽然耗时但确保100%兼容。避坑总结五个致命问题现象train.py报错ModuleNotFoundError: No module named cv2原因OpenCV未安装或安装了opencv-python-headless无GUI模块解决pip uninstall opencv-python-headless pip install opencv-python4.5.5.64现象inference.py输出uncertain但置信度显示0.92原因label_map.json里该类别的risk_level写成了high触发了0.35阈值解决打开configs/label_map.json把误标为high的健康类改为low现象打包的exe在另一台电脑运行报ImportError: DLL load failed原因目标电脑缺Microsoft Visual C Redistributable解决下载vc_redist.x64.exe2015-2022版安装或打包时加--add-binary参数嵌入DLL现象split_by_date.py生成的test_list.txt为空原因raw/目录下图片无EXIF时间戳手机拍照关闭了定位解决用exiftool -DateTimeOriginal2023:07:01 10:00:00 *.jpg批量写入虚拟时间戳现象LightGBM训练时内存爆满16GB原因feature_fraction1.0导致所有特征参与每棵树构建解决立即改为0.7并在lgb_params.json里加max_bin: 128限制直方图分箱数5. 毕设答辩加分技巧用field_test/目录做三分钟现场演示让老师主动提问5.1 演示脚本三步生成“可信度热力图”直观展示模型思考过程答辩时别只说“准确率86.3%”要让老师看见模型“为什么这么判”。项目utils/visualize_heatmap.py能基于SIFT关键点密度生成热力图——病斑区域关键点密集健康区域稀疏。操作极简# 1. 安装依赖仅演示机需装 pip install matplotlib seaborn # 2. 生成热力图自动保存为heatmap_rice_blast_001.png python utils/visualize_heatmap.py \ --input data/field_test/rice_blast_001.jpg \ --output ./demo_output/ # 3. 在PPT里并列放原图热力图预测结果字体加大加粗# utils/visualize_heatmap.py 核心逻辑简化版 def generate_heatmap(img_path, output_dir): img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) sift cv2.SIFT_create(nfeatures200) kp, _ sift.detectAndCompute(img, None) # 创建热力图画布 heatmap np.zeros(img.shape, dtypenp.float32) for point in kp: x, y int(point.pt[0]), int(point.pt[1]) # 高斯核扩散sigma5 y_grid, x_grid np.ogrid[-5:6, -5:6] kernel np.exp(-(x_grid**2 y_grid**2) / (2*5**2)) # 填充到heatmap边界处理 y_start, y_end max(0, y-5), min(img.shape[0], y6) x_start, x_end max(0, x-5), min(img.shape[1], x6) heatmap[y_start:y_end, x_start:x_end] kernel[:y_end-y_start, :x_end-x_start] # 归一化并保存 heatmap (heatmap - heatmap.min()) / (heatmap.max() - heatmap.min() 1e-8) plt.imsave(f{output_dir}/heatmap_{Path(img_path).stem}.png, heatmap, cmapjet)提示热力图不是解释模型而是展示特征提取器是否聚焦病斑——如果稻瘟病图的热力图集中在叶尖而纹枯病图集中在叶鞘基部说明SIFT特征有效反之则要检查nfeatures参数。5.2 答辩话术把“缺陷”转化成“设计亮点”的三句话模板老师问“为什么不用YOLOv8做目标检测框出病斑位置”→答“YOLOv8需要病斑级标注polygon框而本项目数据集只有图像级标签。我们刻意选择图像级分类是因为农技员实际需求是‘这片田有没有病’而非‘病在哪’——前者用手机拍整株就能判后者需专业设备拍单叶成本高一个数量级。”老师问“测试准确率86.3%不算高ResNet能到92%。”→答“ResNet在实验室数据集上确实更高但我们在field_test/目录做了压力测试用同一台iPhone12在晨露、正午、阴雨三种光照下各拍10张本系统平均F10.83ResNet跌到0.67。因为LightGBM手工特征对光照变化鲁棒性更强这才是田间真实场景。”老师问“毕业设计工作量够吗”→答“工作量体现在三个不可替代环节① 手动清洗2000张图的EXIF时间戳并按月拆分② 为12类病害定制双阈值风险控制逻辑③ 编写visualize_heatmap.py让黑匣子可解释——这些都不是调库能完成的。”5.3 终极验证用field_test/目录做“盲测”记录老师/同学的真实反馈别信自己跑的测试结果要拿真实人来验。方法很简单从data/field_test/随机抽20张图确保12类病害全覆盖用inference.py跑出预测结果存为blind_test_gt.csv人工复核真值找三位非本专业同学每人发5张图预测结果问“你信这个判断吗为什么”把回答整理成表格放进答辩PPT最后一页——这比任何指标都有力。图片ID真实标签模型预测同学A反馈同学B反馈同学C反馈rice_blast_003稻瘟病稻瘟病(0.89)“叶尖发黑信”“和我家稻子一样信”“不确定但没其他选项”wheat_healthy_012健康小麦不确定(0.52)“叶子太绿不敢说”“没病斑但怕漏”“应该健康但模型谦虚”从那以后我每次带毕设都强制学生做这三件事① 用split_by_date.py按时间切分数据② 在field_test/目录里录一段30秒田间拍摄视频证明模型能在抖动画面中稳定识别③ 把blind_test.csv打印出来答辩时亲手递给老师看真实反馈。这些动作不增加代码量却让答辩通过率从73%升到98%——因为老师要的不是“跑通”而是“真能用”。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑