简介:这份资源是一套基于Python的昆虫识别与数目统计完整项目源码,面向具备一定Python基础、希望入门计算机视觉与深度学习应用的开发者,以及生物学、农业和环境保护领域的科研辅助人员。项目围绕图像预处理、CNN分类、YOLO等目标检测算法、模型训练与优化、批量多线程处理、Pandas数据分析与Matplotlib可视化等环节展开,并配有a.txt日志记录与模块化项目结构,可帮助读者理解从数据到识别结果的完整链路。压缩包共164个文件,约14.59MB,包含97张jpg样本图、23个py脚本、13个npy数据文件、10个xml标注、9张png图表及csv、model、ui等类型,覆盖数据、代码、模型与界面资源。目前已有144人学习下载,适合作为课程设计、毕业设计或科研原型参考,便于快速复现昆虫识别与计数流程并在此基础上二次开发。
1. 从一堆散图到虫口统计:这个 Python 昆虫识别包到底能干什么
去年帮农科院的朋友处理一批诱捕灯拍回来的照片,两千多张,每张上面少则几只、多则几十只小飞虫。人工数?一个人盯屏幕数一天,眼睛花不说,数到后面误差越来越大。当时我就想,要是有个现成的 Python 脚本能自动把虫子框出来、顺便把数量统计了,能省多少事。后来翻到这个「基于python的昆虫识别和数目统计.zip」,解压一看,结构比想象中朴素——没有花哨的 Web 界面,就是几个 CSV、一批 fly 开头的样本图、一个.iml工程文件,外加一套围绕 OpenCV 和深度学习推理的识别统计逻辑。它解决的核心问题很明确:把昆虫图像批量喂进去,输出每张图里昆虫的种类判定和数量计数,结果落到 CSV 里方便后续用 Pandas 做分析。适合谁?做生物多样性监测、农业虫情测报、生态调查的从业者,以及想拿一个真实小项目练手计算机视觉的 Python 学习者。它不追求 SOTA 精度,胜在流程完整、依赖清晰、能跑通从图像预处理到结果落盘的全链路。
2. 拆开压缩包:文件结构、依赖与识别统计主链路
2.1 从文件清单反推项目骨架
拿到一个源码包,我习惯先ls一遍,从文件命名和类型判断作者的组织思路。这个包里的东西不多,但每个都有明确分工:
| 文件/目录 | 类型 | 作用推断 |
|---|---|---|
data.csv | 数据文件 | 训练集或全量数据的标注/索引,可能含图片路径与类别标签 |
datatest.csv | 数据文件 | 测试集索引,用于验证识别准确率 |
ques.csv | 数据文件 | 待识别图片清单或问题记录,可能是推理入口 |
Insect_Identification.iml | 工程配置 | IntelliJ/PyCharm 系 IDE 的模块文件,说明作者在 JetBrains 环境开发 |
fly1.jpg~fly17.jpg | 样本图像 | 果蝇等昆虫实拍图,用于演示识别与计数效果 |
a.txt(正文提及) | 日志文件 | 记录处理路径、识别结果、时间戳 |
没有requirements.txt,没有README,这是很多课程设计或小型科研项目的通病。但反过来看,依赖关系可以从代码 import 里反推。常见做法是:OpenCV 做图像预处理,TensorFlow/Keras 或 PyTorch 做分类/检测推理,Pandas 管数据,Matplotlib 出图。我一般会先建一个干净的虚拟环境,再按报错逐个补库,比盲目pip install一堆用不上的包要稳。
2.2 图像预处理:灰度化、二值化与噪声消除的参数门道
昆虫识别的前置步骤直接决定后续模型能不能用。这个项目里,预处理链路大概率是「读图 → 灰度 → 高斯模糊 → 自适应二值化 → 形态学去噪」。为什么不是简单阈值?因为诱捕灯照片背景明暗不均,固定阈值一刀切,虫子要么被背景吞掉,要么和噪点混在一起。
import cv2 import numpy as np def preprocess_insect_image(img_path): # 以灰度模式读取,省去后续转换 img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) if img is None: raise FileNotFoundError(f"读不到图:{img_path}") # 高斯模糊,核大小取奇数,5x5 适合 640x480 左右的虫图 blurred = cv2.GaussianBlur(img, (5, 5), 0) # 自适应阈值:邻域 11,常数 C 取 2,抑制背景噪声 binary = cv2.adaptiveThreshold( blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2 ) # 形态学开运算:先腐蚀后膨胀,去掉孤立小白点 kernel = np.ones((3, 3), np.uint8) cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=1) return cleaned这段代码里,ADAPTIVE_THRESH_GAUSSIAN_C比均值法更适合昆虫图,因为高斯加权能保留虫子边缘的细微变化。THRESH_BINARY_INV把虫子变成白色前景、背景变黑,方便后面做轮廓查找。11是邻域块大小,太小会把虫子身体切成碎片,太大又会让相邻虫子粘连。C=2是从计算出的阈值里减去的常数,用来微调灵敏度。形态学核用3x3是保守选择,虫子体型再小也能保住主体,同时干掉大部分椒盐噪声。如果原图分辨率很高(比如 4000x3000),核大小和邻域块都要相应放大,否则预处理完虫子就剩几个像素点了。
2.3 识别与计数:轮廓查找和深度学习推理怎么衔接
预处理完,计数环节通常走两条路。一条是传统 CV 路线:cv2.findContours找连通区域,按面积过滤掉太小或太大的轮廓,剩下的就算一只虫。另一条是深度学习路线:用训练好的分类模型对每个候选框做种类判定,再用检测模型输出位置和数量。这个项目既然提到了 CNN 和 YOLO/SSD,说明作者至少预留了深度学习接口。
def count_insects_by_contour(binary_img, min_area=50, max_area=5000): # RETR_EXTERNAL 只取最外层轮廓,避免虫子内部纹理被重复计数 contours, _ = cv2.findContours( binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) valid_contours = [] for cnt in contours: area = cv2.contourArea(cnt) # 面积过滤:太小是噪点,太大可能是多只粘连或非虫物体 if min_area < area < max_area: valid_contours.append(cnt) return len(valid_contours), valid_contoursmin_area=50和max_area=5000这两个参数没有万能值,得根据你的图像分辨率和虫子实际像素面积来调。我一般会先跑一张典型图,把轮廓面积打印出来看看分布,再定阈值。如果虫子经常粘连,RETR_EXTERNAL会把两只算成一只,这时候要么上分水岭算法,要么直接切到 YOLO 这类检测模型。深度学习推理部分,常见做法是用 Keras 加载.h5模型或 PyTorch 加载.pt权重,对每个候选区域做model.predict,取置信度最高的类别作为种类标签。批量处理时,把图片路径列表丢进循环,结果逐行写入 CSV,列名通常是image_path, insect_count, species, confidence。
2.4 批量处理与结果落盘:Pandas 和文件操作的配合
单张图跑通只是第一步,真正省时间的是批量。这个项目里ques.csv很可能就是待处理清单,data.csv和datatest.csv管训练测试划分。用 Pandas 读进来,遍历路径列,每处理一张就往结果列表里追加一条记录,最后to_csv一次性写出。
import pandas as pd import os def batch_process(csv_path, output_path): df = pd.read_csv(csv_path) # 假设 CSV 里有一列叫 image_path results = [] for idx, row in df.iterrows(): img_path = row['image_path'] if not os.path.exists(img_path): # 路径不存在就跳过,记一条日志,别让整个批次崩掉 results.append({'image_path': img_path, 'count': -1, 'note': 'file_missing'}) continue binary = preprocess_insect_image(img_path) count, _ = count_insects_by_contour(binary) results.append({'image_path': img_path, 'count': count, 'note': 'ok'}) pd.DataFrame(results).to_csv(output_path, index=False, encoding='utf-8-sig')encoding='utf-8-sig'是为了 Excel 打开不乱码,这个坑我踩过不止一次。os.path.exists做前置检查,避免某张图被误删导致整个脚本中断。结果 CSV 里保留note列,方便回溯哪些图处理失败、失败原因是什么。如果图片量大,可以把循环改成concurrent.futures.ThreadPoolExecutor做多线程,但要注意 OpenCV 的imread本身释放 GIL,多线程能吃到一部分并行红利,进程池则更稳但内存开销大。
3. 把识别结果用起来:CSV 分析、可视化与统计口径
3.1 从结果 CSV 到虫情报表
识别跑完,拿到一张带image_path、count、species的 CSV,这只是原材料。真正要给科研人员看的,是按时间、按地点、按物种聚合后的统计表。Pandas 的groupby在这里是主力。
import pandas as pd df = pd.read_csv('result.csv') # 按物种汇总数量和出现频次 species_stats = df.groupby('species').agg( total_count=('count', 'sum'), image_count=('count', 'size'), avg_per_image=('count', 'mean') ).reset_index() # 按图片路径里的日期字段分组,需要先从路径提取日期 df['date'] = df['image_path'].str.extract(r'(\d{4}-\d{2}-\d{2})') daily_stats = df.groupby('date')['count'].sum().reset_index() species_stats.to_csv('species_summary.csv', index=False) daily_stats.to_csv('daily_summary.csv', index=False)agg里可以同时挂多个统计函数,sum看总量,size看有多少张图拍到了该物种,mean看单图平均密度。日期提取用正则从路径里抠,前提是你的图片命名有规律。如果路径里没有日期,就得靠a.txt日志里的时间戳来关联,或者手动维护一张映射表。统计口径要提前和业务方对齐:一只虫出现在两张图里,算一次还是两次?按图片计数和按个体计数是两码事,这个必须在报表里写清楚。
3.2 可视化:种类分布图和数量趋势图
Matplotlib 和 Seaborn 出图,重点不是花哨,是让非技术背景的合作者一眼看懂。种类分布用横向柱状图,数量趋势用折线图,标注清楚单位和样本量。
import matplotlib.pyplot as plt import seaborn as sns # 设置中文字体,Windows 和 Linux 路径不同,按需改 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False fig, axes = plt.subplots(1, 2, figsize=(14, 5)) sns.barplot(data=species_stats, y='species', x='total_count', ax=axes[0]) axes[0].set_title('各物种累计数量') axes[0].set_xlabel('数量') axes[1].plot(daily_stats['date'], daily_stats['count'], marker='o') axes[1].set_title('每日昆虫数量趋势') axes[1].tick_params(axis='x', rotation=45) plt.tight_layout() plt.savefig('insect_report.png', dpi=150)SimHei是 Windows 自带黑体,Linux 上通常没有,得换成WenQuanYi Micro Hei或手动指定字体文件路径。dpi=150保证导出图在报告里不糊。如果横坐标日期太密集,rotation=45只是缓解,更好的做法是隔几个点显示一个标签,或者直接按周聚合。可视化不是终点,把图嵌进报告、配上统计口径说明,才算完整交付。
4. 避坑与排查:昆虫识别统计里最容易翻车的五个地方
4.1 现象:识别数量忽高忽低,同一张图跑两次结果不一样
原因:预处理里的自适应阈值对光照变化敏感,或者深度学习推理时没固定随机种子。OpenCV 的adaptiveThreshold本身是确定性的,但如果图像读取时用了IMREAD_COLOR再转灰度,不同色彩空间转换可能引入微小差异。更常见的是模型推理时dropout没关、model.eval()没调。
解决:推理前强制model.eval()和torch.no_grad()(PyTorch)或tf.keras.backend.set_learning_phase(0)(TF1.x)。图像统一用IMREAD_GRAYSCALE读。把预处理参数写进配置文件,别散落在代码各处。
4.2 现象:虫子粘连在一起,两只被数成一只
原因:轮廓查找基于连通区域,两只虫子身体接触时二值化后连成一片,findContours只返回一个外轮廓。
解决:轻量方案是加形态学腐蚀,把粘连处断开,但会牺牲小虫子的完整性。稳妥方案是上分水岭算法或直接换 YOLO 检测模型,让模型学边界框回归。如果坚持传统 CV,可以尝试距离变换 + 局部最大值找种子点,再分水岭分割。
4.3 现象:CSV 里中文物种名乱码,Excel 打开全是问号
原因:Pandasto_csv默认utf-8编码,Excel 在中文 Windows 上默认按 GBK 解析,不认 UTF-8 无 BOM。
解决:写文件时加encoding='utf-8-sig',让 Excel 识别 BOM 头。读文件时如果源文件是 GBK,用encoding='gbk'或chardet探测。别用gb2312,字符集太小,生僻字会丢。
4.4 现象:批量处理跑到一半报MemoryError或程序卡死
原因:一次性把所有图片读进内存,或者结果列表无限追加没落盘。高分辨率图单张就几十 MB,几百张下来内存直接爆。
解决:用生成器逐张读、逐张处理、逐张写。结果先写临时文件,全部跑完再合并。如果必须并行,用ProcessPoolExecutor并限制max_workers不超过 CPU 核数,每个进程处理完主动gc.collect()。
4.5 现象:模型在测试集上准确率很高,实际拍的新图一塌糊涂
原因:训练集和实际场景分布不一致。datatest.csv里的图和fly*.jpg可能来自同一批采集,光照、背景、拍摄角度都相似,模型学到了背景特征而不是虫子特征。
解决:做数据增强时加入随机裁剪、亮度抖动、背景替换。实际部署前,拿一批完全没参与训练的新图做盲测,看混淆矩阵里哪些类别被系统性误判。如果某类虫子召回率极低,优先补该类样本,而不是盲目加层加参数。
5. 进阶技巧:用置信度过滤和 NMS 把统计精度再提一档
传统轮廓计数有个硬伤:它不知道「这是不是虫」,只要面积在阈值内就计数。实际场景里,树叶碎屑、水滴、灰尘都可能落进面积区间。我的做法是在轮廓计数之后加一层分类置信度过滤——对每个候选轮廓裁剪出小图,送进训练好的 CNN 分类器,置信度低于 0.6 的直接丢弃。这样虽然多了一步推理,但误计数能压下去不少。
def filter_by_confidence(contours, img, model, threshold=0.6): kept = [] for cnt in contours: x, y, w, h = cv2.boundingRect(cnt) # 裁剪候选区域,留一点边距 roi = img[max(0, y-5):y+h+5, max(0, x-5):x+w+5] if roi.size == 0: continue roi_resized = cv2.resize(roi, (64, 64)) roi_norm = roi_resized.astype('float32') / 255.0 roi_input = roi_norm.reshape(1, 64, 64, 3) pred = model.predict(roi_input, verbose=0) confidence = float(np.max(pred)) if confidence >= threshold: kept.append((cnt, confidence)) return keptthreshold=0.6是保守起点,如果你的模型校准得好,可以提到 0.7 甚至 0.8。64x64的输入尺寸要和训练时一致,不一致的话模型性能会断崖式下跌。裁剪时留 5 像素边距是为了避免虫子触角、翅膀被切掉导致分类器认不出。
另一个技巧是 NMS(非极大值抑制)。当同一只虫被多个重叠轮廓框住时,NMS 按置信度排序,保留最高分框,抑制 IoU 超过阈值的冗余框。OpenCV 自带cv2.dnn.NMSBoxes,直接调就行,不用自己手写。
boxes = [[x, y, w, h] for (x, y, w, h) in bounding_boxes] scores = [conf for conf in confidences] indices = cv2.dnn.NMSBoxes(boxes, scores, score_threshold=0.5, nms_threshold=0.4) final_count = len(indices)nms_threshold=0.4意味着两个框重叠超过 40% 就认为是同一只虫。虫子密集时这个值可以调到 0.3,让抑制更激进;虫子稀疏时调到 0.5,避免把相邻两只误合并。这套组合拳打下来,统计结果比裸轮廓计数稳得多。
从那以后我每次拿到新的昆虫图像批次,都强制先跑一遍预处理参数扫描,把min_area、max_area、adaptiveThreshold的C值各试三档,看计数结果波动范围。波动超过 15% 就说明预处理没调稳,后面模型再准也是白搭。希望帮到你。
本文还有配套的精品资源,点击获取