ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python昆虫识别与计数实战:从图像预处理到CSV统计

Python昆虫识别与计数实战:从图像预处理到CSV统计 简介这份资源是一套基于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, iterations1) return cleaned这段代码里ADAPTIVE_THRESH_GAUSSIAN_C比均值法更适合昆虫图因为高斯加权能保留虫子边缘的细微变化。THRESH_BINARY_INV把虫子变成白色前景、背景变黑方便后面做轮廓查找。11是邻域块大小太小会把虫子身体切成碎片太大又会让相邻虫子粘连。C2是从计算出的阈值里减去的常数用来微调灵敏度。形态学核用3x3是保守选择虫子体型再小也能保住主体同时干掉大部分椒盐噪声。如果原图分辨率很高比如 4000x3000核大小和邻域块都要相应放大否则预处理完虫子就剩几个像素点了。2.3 识别与计数轮廓查找和深度学习推理怎么衔接预处理完计数环节通常走两条路。一条是传统 CV 路线cv2.findContours找连通区域按面积过滤掉太小或太大的轮廓剩下的就算一只虫。另一条是深度学习路线用训练好的分类模型对每个候选框做种类判定再用检测模型输出位置和数量。这个项目既然提到了 CNN 和 YOLO/SSD说明作者至少预留了深度学习接口。def count_insects_by_contour(binary_img, min_area50, max_area5000): # 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_area50和max_area5000这两个参数没有万能值得根据你的图像分辨率和虫子实际像素面积来调。我一般会先跑一张典型图把轮廓面积打印出来看看分布再定阈值。如果虫子经常粘连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, indexFalse, encodingutf-8-sig)encodingutf-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, indexFalse) daily_stats.to_csv(daily_summary.csv, indexFalse)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(dataspecies_stats, yspecies, xtotal_count, axaxes[0]) axes[0].set_title(各物种累计数量) axes[0].set_xlabel(数量) axes[1].plot(daily_stats[date], daily_stats[count], markero) axes[1].set_title(每日昆虫数量趋势) axes[1].tick_params(axisx, rotation45) plt.tight_layout() plt.savefig(insect_report.png, dpi150)SimHei是 Windows 自带黑体Linux 上通常没有得换成WenQuanYi Micro Hei或手动指定字体文件路径。dpi150保证导出图在报告里不糊。如果横坐标日期太密集rotation45只是缓解更好的做法是隔几个点显示一个标签或者直接按周聚合。可视化不是终点把图嵌进报告、配上统计口径说明才算完整交付。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。解决写文件时加encodingutf-8-sig让 Excel 识别 BOM 头。读文件时如果源文件是 GBK用encodinggbk或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, threshold0.6): kept [] for cnt in contours: x, y, w, h cv2.boundingRect(cnt) # 裁剪候选区域留一点边距 roi img[max(0, y-5):yh5, max(0, x-5):xw5] 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, verbose0) confidence float(np.max(pred)) if confidence threshold: kept.append((cnt, confidence)) return keptthreshold0.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_threshold0.5, nms_threshold0.4) final_count len(indices)nms_threshold0.4意味着两个框重叠超过 40% 就认为是同一只虫。虫子密集时这个值可以调到 0.3让抑制更激进虫子稀疏时调到 0.5避免把相邻两只误合并。这套组合拳打下来统计结果比裸轮廓计数稳得多。从那以后我每次拿到新的昆虫图像批次都强制先跑一遍预处理参数扫描把min_area、max_area、adaptiveThreshold的C值各试三档看计数结果波动范围。波动超过 15% 就说明预处理没调稳后面模型再准也是白搭。希望帮到你。本文还有配套的精品资源点击获取
返回列表