简介:这份资源是一套基于Python的昆虫识别与数目统计完整项目源码,面向具备一定Python基础、希望入门计算机视觉与深度学习应用的开发者,以及生物学、农业和环境保护领域的科研人员。项目围绕图像预处理、CNN分类、YOLO等目标检测算法、模型训练与优化、批量多线程处理、Pandas数据分析与Matplotlib可视化等环节展开,并配有a.txt日志记录与模块化项目结构,可作为跨学科实战参考。压缩包共164个文件,约14.59MB,包含97张jpg样本图片、23个py脚本、13个npy数据文件、10个xml标注、9张png图表、3个csv数据表、3个model模型文件,以及ui界面、txt日志、md说明和pdf文档等,覆盖数据、代码、模型与文档全流程。目前已有144人学习下载,适合用来理解从数据准备到识别统计的完整实现思路,并借鉴其目录组织与排错方法。
1. 从一张野外监测照片说起:昆虫识别和数目统计到底在做什么
去年夏天帮一个做农林虫情监测的团队看代码,他们每天从野外回传几千张黄板照片,两个实习生从早看到晚,一天最多标两百张,眼睛都快看花了。他们想做的事很朴素:给一张昆虫照片,程序自动框出每一只虫、认出是什么虫、最后数出个数。这就是「基于python的昆虫识别和数目统计」要解决的问题,核心链路是目标检测加计数统计,不是单纯的图像分类。
它适合三类人:做农林植保、生态监测、仓储虫害的从业者;想拿一个完整视觉项目练手的学生和转行者;手里已经有一批标注图片、想快速跑通检测到计数全流程的工程师。读完你能拿到一条可复现的路径:环境怎么配、数据怎么组织、模型怎么选、数目怎么统计、坑在哪。下面按这条链路一层层拆开讲。
2. 环境搭建与数据准备:把python和标注数据先理顺
2.1 python环境与依赖库的最小可用配置
很多人卡在第一步不是算法,是环境。python安装教程、vscode python环境配置、pycharm配置python环境这些热搜词背后,是大量新手在装环境时翻车。我的建议是别在系统python上折腾,用conda建独立环境,版本锁在3.9或3.10,太新的版本某些视觉库轮子还没跟上。
# 创建独立环境,python版本锁3.10,避免依赖冲突 conda create -n insect python=3.10 -y conda activate insect # 安装核心依赖,torch按自己显卡的cuda版本去官网选对应命令 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python numpy pandas openpyxl这段命令的逻辑是:先隔离环境,再装深度学习框架和检测库。ultralytics是现在跑YOLO系列最省事的库,openpyxl是为了最后把统计结果写进excel,对应python写入excel这个高频需求。参数上,cu118要换成你机器实际的cuda版本,没有独显就用cpu版命令,速度会慢但能跑通。装完用python -c "import torch; print(torch.cuda.is_available())"验证,返回True说明显卡可用。
提示:如果
pip install卡住,换国内镜像源加-i参数,别硬等。
2.2 昆虫数据集的标注格式与目录组织
昆虫识别和通用目标检测最大的差别在数据:虫体小、密集、种类多、背景杂。数据来源一般是两种,自己拍加标,或者用公开的昆虫检测数据集。标注工具常见的是labelImg或Roboflow,导出成YOLO格式最省事,因为后面训练直接吃。
YOLO格式的目录结构必须严格,错一个层级训练就报找不到标签:
insect_dataset/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 ├── labels/ │ ├── train/ # 与train图片同名的.txt │ └── val/ └── data.yaml # 数据集配置文件每个txt里一行代表一只虫,格式是类别id 中心x 中心y 宽 高,后四个都是归一化到0到1的值。data.yaml写清楚路径和类别名:
path: ./insect_dataset train: images/train val: images/val nc: 8 # 类别数,按你实际标注的虫种改 names: ['locust', 'aphid', 'ladybug', 'beetle', 'moth', 'fly', 'ant', 'other']参数说明:nc是类别数量,必须和你标注时用的类别索引对得上,多一个少一个都会在训练时报维度错误。names顺序要和标注时的id一一对应,id从0开始。常见翻车是图片和标签文件名不一致,或者标签里出现负数、大于1的坐标,训练时loss直接nan。
2.3 数据增强与类别不平衡的处理
昆虫数据天然不平衡,某种虫可能几百张,稀有虫只有几十张。直接训练模型会偏向多数类。常见做法是在data.yaml同级配置里开增强,YOLO训练时自带mosaic、翻转、缩放。对稀有类,可以复制样本加随机旋转,但别过度,否则过拟合。
import cv2 import numpy as np import os # 对稀有类图片做旋转增强,扩充样本量 src_dir = "insect_dataset/images/train" save_dir = "insect_dataset/images/train_aug" os.makedirs(save_dir, exist_ok=True) for name in os.listdir(src_dir): img = cv2.imread(os.path.join(src_dir, name)) if img is None: continue # 90度、180度旋转,对应标签也要同步旋转,这里只演示图片 for angle in (90, 180): if angle == 90: aug = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) else: aug = cv2.rotate(img, cv2.ROTATE_180) cv2.imwrite(os.path.join(save_dir, f"aug{angle}_{name}"), aug)这段代码只做了图片旋转,逻辑说明必须补一句:旋转图片的同时,标签坐标也要做对应变换,否则图和框对不上,模型学出来全是错的。90度旋转时中心点坐标要按(x,y) -> (1-y, x)变换,宽高互换。这一步是血泪经验,很多人只转图不转标签,训练loss看着降,实际检测全是乱框。
3. 模型选型与训练:为什么小目标检测要单独调参
3.1 YOLO系列在昆虫小目标上的选型对比
昆虫检测的难点是小目标密集。YOLOv8n速度快但小目标召回一般,YOLOv8s或m更均衡,YOLOv11在同样精度下参数更省。选型要看你的部署端:如果是边缘设备实时跑,选n或s;如果是服务器离线批量处理,选m或l换精度。
| 模型 | 参数量 | 适合场景 | 小目标表现 |
|---|---|---|---|
| YOLOv8n | 最小 | 边缘实时 | 一般 |
| YOLOv8s | 小 | 平衡 | 较好 |
| YOLOv8m | 中 | 服务器 | 好 |
| YOLOv11s | 小 | 平衡 | 好 |
选型理由很直接:昆虫在整图里可能只占几十个像素,模型下采样太狠就丢了。输入分辨率从默认640提到960或1280,小目标召回会明显上升,代价是显存和耗时。我一般先用YOLOv8s加960分辨率跑一版基线,看召回够不够再决定换模型。
3.2 训练命令与关键参数怎么设
训练本身一行命令能跑,但参数决定成败。
# 从预训练权重开始训练,输入分辨率提到960 yolo detect train \ data=insect_dataset/data.yaml \ model=yolov8s.pt \ imgsz=960 \ epochs=150 \ batch=16 \ patience=30 \ lr0=0.01 \ cos_lr=True \ augment=True \ project=runs/insect \ name=exp1逻辑说明:model=yolov8s.pt用官方预训练权重做迁移学习,比从零训练收敛快得多。imgsz=960是给小目标的关键参数,别用默认640。batch=16按显存调,显存不够就降到8或4。patience=30是早停,30轮没提升就停,省时间。cos_lr=True用余弦退火学习率,后期收敛更稳。lr0=0.01是初始学习率,迁移学习一般0.01够用,太大容易震荡。
训练完看runs/insect/exp1/results.csv,重点看mAP50和mAP50-95,还有recall。昆虫检测里recall比precision更值得关注,漏检一只虫比多框一个假目标影响大,因为数目统计漏检直接导致计数偏少。
3.3 训练过程中的显存与过拟合排查
显存爆了是最常见的翻车。现象是CUDA out of memory。原因通常是batch或imgsz太大。解决是先把batch降到8,还爆就降imgsz到640,或者开梯度累积模拟大batch。别一上来就上大模型大分辨率,先跑通再调优。
过拟合的现象是训练loss一直降但验证mAP不涨甚至下降。原因是数据量不够或增强太弱。解决是加数据、开更强增强、加dropout或weight decay。昆虫数据如果只有几百张,过拟合几乎必然,这时候数据增强和迁移学习就是后悔药。
注意:训练日志里
box_loss和cls_loss要分开看,box_loss高说明框不准,cls_loss高说明分类分不清,对症下药。
4. 数目统计的实现:从检测框到最终计数
4.1 用检测结果做计数的基本逻辑
检测模型输出的是每只虫的框和类别,数目统计就是按类别统计框的数量。听起来简单,但密集场景下同一个虫可能被框两次,或者相邻虫的框重叠,直接数框会偏多。所以计数前要做后处理,核心是置信度过滤加NMS。
from ultralytics import YOLO import cv2 import pandas as pd model = YOLO("runs/insect/exp1/weights/best.pt") def count_insects(img_path, conf_thres=0.4, iou_thres=0.5): # 推理,conf和iou是过滤阈值 results = model.predict(img_path, conf=conf_thres, iou=iou_thres, imgsz=960) counts = {} for r in results: boxes = r.boxes for box in boxes: cls_id = int(box.cls[0]) name = model.names[cls_id] counts[name] = counts.get(name, 0) + 1 return counts result = count_insects("test.jpg") print(result)逻辑说明:conf=0.4是置信度阈值,低于它的框丢掉,太高会漏检,太低会误检,0.3到0.5之间调。iou=0.5是NMS的IoU阈值,控制重叠框合并,密集场景可以调到0.6让重叠框保留更多。model.names是类别id到名字的映射。这段代码按类别累加框数,就是最基础的计数。
4.2 密集场景下的重复计数与去重
密集场景直接数框会偏多,因为一只虫可能出多个框。除了NMS,还可以用框中心点距离做二次去重:两个框中心距离小于某个像素阈值且类别相同,就认为是同一只。
import numpy as np def dedup_boxes(boxes, centers, min_dist=15): # boxes: Nx4, centers: Nx2, 按中心距离去重 keep = [] used = np.zeros(len(centers), dtype=bool) for i in range(len(centers)): if used[i]: continue keep.append(i) for j in range(i + 1, len(centers)): if used[j]: continue dist = np.linalg.norm(centers[i] - centers[j]) if dist < min_dist: used[j] = True return keep逻辑说明:min_dist是判定同一只虫的中心距离阈值,按你图像里虫的实际像素大小设,虫越小这个值越小。这段是贪心去重,先保留一个,把离它太近的都标记掉。参数设大了会把相邻两只虫误合并,设小了去重不彻底,需要拿几张典型图试出来。
4.3 把统计结果写进excel和可视化输出
统计结果最终要给人看,写excel和画标注图是两个刚需。
import pandas as pd import cv2 # 假设result是{类别:数量},批量图片统计后汇总 rows = [] for img_name, counts in all_results.items(): row = {"image": img_name} row.update(counts) rows.append(row) df = pd.DataFrame(rows).fillna(0) df.to_excel("insect_count.xlsx", index=False) # 画框并保存,方便人工核对 results = model.predict("test.jpg", conf=0.4, imgsz=960) annotated = results[0].plot() cv2.imwrite("test_annotated.jpg", annotated)逻辑说明:fillna(0)是因为不同图片出现的虫种不同,缺失的补0,保证表格规整。results[0].plot()是ultralytics自带的画框方法,直接出带框带类别的图,人工核对时一眼能看出漏检误检。这一步别省,数目统计的准确性最终要靠人抽查验证。
5. 避坑与排查:昆虫计数项目里最容易翻车的五件事
5.1 现象:训练loss正常但检测全是乱框
原因:标签坐标没归一化,或者图片和标签文件名不匹配,或者旋转增强时标签没同步变换。解决:写个脚本抽查标签,确认所有坐标在0到1之间,确认每张图都有同名txt,增强时图和标签一起处理。
5.2 现象:小虫大量漏检
原因:输入分辨率太低,虫在特征图上只剩几个像素。解决:把imgsz从640提到960或1280,同时确认标注框没把虫标得太小。如果还漏,换更大的模型或加小目标检测头。
5.3 现象:同一只虫被数成好几只
原因:NMS阈值太低导致重叠框没合并,或者密集场景下框本身就有多个。解决:调高iou阈值到0.6,加中心点距离去重,必要时人工核对典型图调整阈值。
5.4 现象:某些虫种完全识别不出来
原因:类别不平衡,稀有类样本太少。解决:对稀有类做增强扩充,或者训练时给稀有类更高权重,再不行就合并相近类别先跑通。
5.5 现象:推理速度慢到没法批量处理
原因:模型太大或分辨率太高,或者没用GPU。解决:确认torch.cuda.is_available()为True,换YOLOv8n或s,分辨率按需降,批量推理时用model.predict传图片列表而不是循环单张。
6. 进阶技巧:把计数精度再往上提一档的验证方法
跑通全流程只是起点,真正决定这个方案值不值得投入的是计数精度能不能稳定。我一般会做三件事来验证和提升。
第一件是建立人工核对基线。从验证集里抽50张图,人工数一遍每类的真实数量,再和程序输出对比,算出每类的计数误差。误差超过10%的类别重点查,是漏检还是重复计数。这个基线不做,你永远不知道模型到底准不准。
第二件是分场景调阈值。白天和夜间、黄板和诱虫灯、单只和密集,这些场景的最优conf和iou阈值不一样。我的习惯是按场景分组,每组单独调一组阈值,而不是全图用一个值。下面这个表格是我常用的分组调参思路:
| 场景 | conf阈值 | iou阈值 | 说明 |
|---|---|---|---|
| 单只大虫 | 0.5 | 0.5 | 置信度可以高 |
| 密集小虫 | 0.3 | 0.6 | 宁可多留框再去重 |
| 夜间低对比 | 0.25 | 0.5 | 置信度压低防漏检 |
第三件是用计数结果反推模型问题。如果某类虫计数总是偏少,大概率是漏检,去看召回;如果总是偏多,大概率是重复框,去看NMS和去重逻辑。计数误差是检测质量的放大镜,比单看mAP更贴近实际需求。
# 按场景分组评估计数误差 def eval_count(gt_counts, pred_counts): errors = {} for cls in gt_counts: gt = gt_counts[cls] pred = pred_counts.get(cls, 0) if gt > 0: errors[cls] = abs(pred - gt) / gt return errors # gt_counts和pred_counts都是{类别:数量},逐图算完再平均这段评估代码逻辑很简单,但坚持跑才有价值。参数上没什么可调的,关键是gt_counts要人工数准,否则评估本身就没意义。
我自己踩过最深的坑是早期只看mAP就上线,结果实际计数偏差20%以上,因为mAP高不代表计数准,密集场景的重复框在mAP里被平均掉了。后来养成习惯,任何计数项目先做人工核对基线,再谈优化。这个习惯帮我省了太多返工。希望帮到你。
本文还有配套的精品资源,点击获取