简介项目基于Python机器视觉实现害虫种类识别与数量检测面向毕业设计、课程设计及农业害虫智能监测场景重点解决害虫分类与计数难题帮助开发者快速掌握图像处理到模型落地的完整流程。压缩包内共163个文件涵盖97张jpg昆虫图像样本、23个py源码脚本、13个npy特征数组、10个xml标注、3个训练好的模型文件以及csv结果表等总大小约14.58MB目录结构清晰便于快速定位数据、源码、模型与文档。已有171人学习下载包内除数据集和可直接运行的训练/预测脚本外还附带UI交互界面、环境配置说明与项目文档支持二次开发。通过该案例可系统学习OpenCV图像预处理、SIFT/ORB特征提取、SVM/CNN模型构建与评估等关键知识点理论与实践兼顾是农业视觉识别方向颇有价值的参考项目。1. 害虫种类及数量检测的选题背景与经典技术路线害虫种类及数量检测是农业信息化毕设和课设里最常见的机器视觉选题之一。粘虫板图像背景相对干净、目标形态差异大是入门级检测管线最合适的练习场。标题里的“种类及数量”实为两个任务数量靠前景分割与连通域分析解决种类靠特征提取与分类器解决两端串成一条完整检测管线。常见实现是 Python 加 OpenCV 走经典机器视觉路线HSV 分割前景、开闭运算去噪与缝合、连通域统计计数、再对每个目标提取形状与颜色特征送 SVM 分类。这套方案不需要 GPU几百张样本就能跑通答辩时每一帧的处理逻辑都能现场讲清适合手里只有 CPU、又要求过程可解释的课设和毕设场景。下面按这条管线逐层展开。2. 用开闭运算与连通域分析分离害虫前景2.1 采集端三个硬约束背景、光照、拍摄距离不少项目做废在第一步拿到什么图就调什么参数最后每张图一套阈值。课程设计阶段采集条件其实是可控的应该先在数据侧立三个约束代码才有稳定的输入。第一背景统一。用粘虫板或纯色底板让前景和背景存在稳定的色差后续 inRange 的 HSV 范围才能一套参数覆盖整个数据集。第二光照均匀。强光直射会在虫体表面形成反光反光区域 S 值骤降很容易被当成背景滤掉均匀背光或阴天散射光最稳。第三固定拍摄距离。同一批样本保持物距一致面积、周长这类绝对特征才有跨样本可比性换一个拍摄高度面积阈值就要重调。这三条不是代码问题但它们决定了后面所有分割参数的有效范围。数据侧不收敛参数侧调得再好也是白费。2.2 RGB 转 HSV用色相分量剥离绿色背景RGB 下绿色叶片和深色虫体在亮度通道上经常重叠直接对 BGR 三通道分别设阈值很容易把暗部叶片和亮色虫体混在一起。HSV 把色相 H 和亮度 V 拆开绿色背景的 H 稳定落在 35~85OpenCV 的 H 范围是 0~180于是背景分割就变成一次范围判断。import cv2 import numpy as np img cv2.imread(trap_1.jpg) hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 绿色背景的 H 范围S、V 下界用于压制阴影和灰暗噪点 bg_mask cv2.inRange(hsv, (35, 40, 40), (85, 255, 255)) fg_mask cv2.bitwise_not(bg_mask)代码逻辑先用 inRange 圈出绿色背景像素再取反得到前景掩膜。参数说明H 上下界只锁颜色范围S 下界调高能滤掉发灰的阴影V 下界调高能滤掉过暗区域背景若是黄色粘虫板改成提取 H 20~35 的区间即可原理一致。容易踩的坑是在 RGB 下做三通道分别阈值那种阈值对光照极其敏感换个时间拍就失效。2.3 开闭运算参数原理核形状、核大小与迭代次数分割得到的前景掩膜通常带两类噪声背景残留的小亮点以及虫体内部的小黑洞复眼、翅纹反光。开运算是先腐蚀后膨胀主要清掉前者闭运算是先膨胀后腐蚀主要补上后者。两步连用就是标准的掩膜去噪流程也是“机器视觉开闭运算参数”里最常被问到的部分。kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) fg_mask cv2.morphologyEx(fg_mask, cv2.MORPH_OPEN, kernel, iterations2) fg_mask cv2.morphologyEx(fg_mask, cv2.MORPH_CLOSE, kernel, iterations3)参数说明核优先选椭圆而不是矩形昆虫边缘是弧形矩形核容易把触角腐蚀掉核大小 3~7 够用目标只有十来像素时 5×5 开运算也可能把整只虫吞掉。iterations 与核大小在效果上近似于一个更大的结构元所以更快的调法是把 iterations 固定先定核大小。判断标准开运算后背景噪点消失但虫体面积没有肉眼可见缩小闭运算后翅面空洞闭合且相邻虫体没有被连成一片。2.4 connectedComponentsWithStats连通域数量就是计数地基掩膜清理干净后计数问题退化为连通域统计。OpenCV 的 connectedComponentsWithStats 一次调用同时给出数量、标签图、外接矩形和面积比 findContours 后手动数轮廓更省事。num_labels, labels, stats, centroids cv2.connectedComponentsWithStats( fg_mask, connectivity8 ) boxes [] for i in range(1, num_labels): # 0 号标签是背景跳过 x, y, w, h, area stats[i] if area 50: # 面积过滤去掉灰尘类噪点 continue boxes.append((x, y, w, h, area)) cv2.rectangle(img, (x, y), (x w, y h), (0, 0, 255), 2) print(初检目标数:, len(boxes))参数说明connectivity8 把斜对角相邻像素也归入同一目标虫体分叉的触角不容易被切断area 阈值 50 是 1080p 图上的起点值分辨率翻倍时面积阈值也要按比例放大。注意这一步的计数只是前景块数量多只虫粘连时会算成一块这个欠分割问题放到第 5 章单独处理。3. 特征工程决定种类识别的天花板模型只是放大器3.1 单靠面积分不开面积、宽高比、Hu 矩与颜色统计一起上种类识别最常踩的坑是只拿面积当特征。菜青虫和大型甲虫面积可能重叠蛾子展翅后的面积甚至超过甲虫单一维度根本分不开。实践里要按特征组组合使用特征组计算方式主要区分能力面积、周长stats 的 area、cv2.arcLength体型档次周长/面积比、宽高比由前两项派生细长型蛾、蚊与圆胖型甲虫Hu 矩 1~4cv2.HuMoments整体形状对平移旋转缩放不敏感HSV 均值、标准差前景区域统计颜色近似类、翅面花纹差异Hu 矩单独说一句前两个矩描述重心分布与主轴方向高阶矩对噪声很敏感课程设计样本量就几百张全用 7 个矩反而容易过拟合取前 4 个更稳。特征提取的对象是第 2 章连通域统计得到的每个独立目标逐块提取后再统一进分类器。3.2 特征提取代码与归一化def extract_features(mask, hsv_img): moments cv2.moments(mask) hu cv2.HuMoments(moments).flatten()[:4] # Hu 矩数值跨度极大取对数压缩到可比较量级 hu -np.sign(hu) * np.log10(np.abs(hu) 1e-10) contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnt contours[0] area moments[m00] perimeter cv2.arcLength(cnt, True) x, y, w, h cv2.boundingRect(cnt) fg hsv_img[mask 0] color_feat [fg[:, i].mean() for i in range(3)] color_feat [fg[:, i].std() for i in range(3)] feat [area, perimeter, perimeter / (area 1e-6), w / (h 1e-6)] feat hu.tolist() color_feat return np.array(feat, dtypenp.float32)逻辑说明传入的 mask 必须是单个目标的二值图hsv_img 是整图用 mask 0 做索引只统计该目标内部的像素标准差描述翅面颜色的起伏能区分纯色翅和花纹翅。RETR_EXTERNAL 只取最外层轮廓避免翅面花纹被当成多个轮廓。取对数是因为 Hu 矩经常是 10 的负几十次方不压缩就没法和其他特征同框比较。特征之间量级差异极大面积是几千、Hu 矩是几、颜色均值是几十不归一化直接训练SVM 的决策面会被面积主导from sklearn.preprocessing import StandardScaler scaler StandardScaler() X scaler.fit_transform(X) # X 形状(样本数, 特征维数)标准化按列把每个特征变成均值 0、方差 1让所有特征对模型的贡献处于同一量级。在加新特征之前先确认这一步已做再谈分类效果。3.3 分类器选型小样本下 SVM 比深度学习更稳几百个样本、三五种害虫这是课程设计最常见的配置。这个量级下 RBF 核 SVM 和随机森林都够用我一般先跑 SVM因为 SVM 对特征质量反馈最敏感调参时能明显感觉到哪个特征在起作用而随机森林对异常值更鲁棒适合特征还没洗干净时兜底。from sklearn.model_selection import train_test_split from sklearn.svm import SVC X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) clf SVC(kernelrbf, C10, gammascale, class_weightbalanced) clf.fit(X_train, y_train) print(测试集准确率: %.3f % clf.score(X_test, y_test))参数说明gammascale 让核宽度随特征维度自动缩放C10 控制分类边界的松紧越大越容易过拟合class_weightbalanced 按类别频率反比加权避免某类样本过少被压制。如果准确率低于 0.8先别急着换模型回到特征侧看误判样本到底长什么样通常问题出在特征刻画不足而不是模型容量不够。4. 端到端检测代码从单张图片到批量统计4.1 最小可运行脚本分割、计数、分类一次跑通把前两章的模块拼起来就是完整检测流程。环境只需要 Python 3.9 以上、opencv-python 和 scikit-learn 三件套开发环境用 VSCode 或 PyCharm 配好解释器即可全程不需要 GPU。def detect(img, model, scaler): hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) bg cv2.inRange(hsv, (35, 40, 40), (85, 255, 255)) fg cv2.bitwise_not(bg) kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) fg cv2.morphologyEx(fg, cv2.MORPH_OPEN, kernel, iterations2) fg cv2.morphologyEx(fg, cv2.MORPH_CLOSE, kernel, iterations3) num, labels, stats, _ cv2.connectedComponentsWithStats(fg, 8) results [] for i in range(1, num): x, y, w, h, area stats[i] if area 50: continue obj_mask (labels i).astype(np.uint8) * 255 feat extract_features(obj_mask, hsv) feat scaler.transform([feat]) label model.predict(feat)[0] results.append((label, x, y, w, h, area)) return results逻辑说明每个连通域先生成独立的二值掩膜 obj_mask再传给 extract_features 提取特征scaler 和 model 是第 3 章训练好的对象这里直接复用。返回结果同时带类别标签、框位置和面积后续画框、统计都只需遍历这个列表。注意 obj_mask 必须由 labels i 生成不能用整张 fg 掩膜代替否则颜色特征会把其他目标也算进去。4.2 参数快速调节表先分割、再形态学、最后面积过滤参数位置默认值调大影响调小影响H 上界inRange85黄色叶片混入前景绿色背景滤不净S 下界inRange40滤掉反光与灰暗区阴影误检变多V 下界inRange40暗色虫体被丢弃深色背景残留开运算核大小morphologyEx5×5小目标被吞噪点残留闭运算迭代次数morphologyEx3邻近目标粘连翅面空洞不闭合面积阈值循环过滤50小虫漏检灰尘算目标调参顺序有讲究先调分割把背景块数压到最低再调形态学目标是让每只害虫恰好对应一个连通域面积阈值最后调它只负责兜底。每改一个参数就在 10 张图上验证一遍确认不是只修好了一张图。4.3 批量处理与 CSV 导出课程设计展示时通常要处理一个目录下的几十张图并把每张图的类别数量汇总成表。用 glob 读取全部图片结果写入 CSVimport glob import csv rows [] for path in sorted(glob.glob(images/*.jpg)): img cv2.imread(path) for label, x, y, w, h, area in detect(img, model, scaler): rows.append([path, label, x, y, w, h, int(area)]) with open(result.csv, w, newline) as f: writer csv.writer(f) writer.writerow([image, label, x, y, w, h, area]) writer.writerows(rows)逻辑说明每一行代表一个检测目标包含来源图片、类别、框位置和像素面积后续统计用 pandas 读入后按 image 和 label 分组计数即可。参数说明sorted 保证处理顺序稳定csv 写入时 newline 是避免 Windows 下多出空行。走到这一步“数量”就不再是调试脚本的打印值而是可交付的统计文件。5. 精度验证与三处高频坑5.1 用混淆矩阵验收别靠肉眼挑图分类效果要用混淆矩阵验收不要盯着几张画了框的图说“看起来不错”。训练完成后直接在测试集上输出from sklearn.metrics import confusion_matrix, classification_report pred clf.predict(X_test) print(confusion_matrix(y_test, pred)) print(classification_report(y_test, pred, target_namesclass_names))混淆矩阵对角线是正确分类数量非对角线就是两类互相打架的位置。如果某两类反复互认说明特征没有把它们分开正确动作是回第 3 章补特征或换色板而不是简单加数据硬顶。5.2 三类高频坑的处理粘连是计数误差的第一来源。多只虫挤在一起时连通域合并成一块计数偏低。拍摄时铺匀样本是最省事的解法代码层面可以做分水岭但会引入新的分割参数课设阶段性价比不高。常用做法是按面积估算统计每类目标的平均连通域面积用大块连通域面积除以对应类别的平均面积得到近似数量。阴影和反光会让掩膜比真实虫体大一圈面积特征随之失真。处理方式是调高 inRange 的 S、V 下界或者直接换均匀背光拍摄代码调参解决不了光源本身的缺陷。图像边缘截断的目标只有半个身体面积和 Hu 矩全部失效检测时裁掉边缘 20 像素内的连通域单独列为“未识别”比硬分一个类别更诚实。5.3 计数评价指标与线性修正数量检测的效果建议用平均绝对误差MAE评价MAE 等于检测数与人工计数之差的绝对值的平均。只报告分类准确率会漏掉计数上的系统性偏差。如果 MAE 稳定偏大且以粘连为主可以做一次线性标定取 10 张已人工计数的样本用预测总数除以人工总数得到修正系数乘到后续检测结果上。这个技巧通常能把 20% 左右的计数误差压到 10% 以内代价是逐目标框的准确性会下降所以只适用于总量统计不适合逐目标计数报告。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?