YOLOv11鱼类行为识别与密度统计实战指南

发布时间:2026/9/29 12:35:35
YOLOv11鱼类行为识别与密度统计实战指南 简介面向智慧养殖与计算机视觉交叉领域的实践者这份PDF系统讲解了YOLOv11在鱼类行为识别与密度统计中的完整落地路径。文档共30页首先从传统养殖模式困境、水质与疾病防控难点切入引出智慧养殖对精准监测的迫切需求随后详细拆解YOLOv11的网络结构优化、多尺度特征融合改进与损失函数设计并给出基于PythonOpenCV的数据采集、图像标注与格式转换、数据集划分、模型训练及结果输出的可执行代码。实验部分还针对不同光照条件、不同养殖密度下的识别准确率与统计误差进行对比分析并延伸至物联网、大数据融合及渔业资源保护等应用场景。压缩包内含单个PDF文件大小1.95MB支持目录章节跳转与大纲定位文字图表完整。已有85人学习下载适合水产养殖从业者、算法工程师及目标检测初学者按需查阅。1. YOLOv11落地鱼类行为识别从监控画面到养殖决策把YOLOv11用到鱼类养殖这件事听起来像是拿大炮打蚊子但实际跑过一遍就知道它解决的正是水产行业最头疼的「看不见」问题。鱼在水里养殖户靠肉眼盯监控一天盯下来眼睛发花扎堆、浮头和摄食异常根本分不清等发现问题往往已经晚了。这份PDF把YOLOv11单阶段检测的思路完整落到鱼类场景一段视频进去鱼的位置、数量、游动状态都变成可量化的数据喂料和增氧决策跟着数据走。适合正在做水产智能化项目、想给传统鱼塘加视觉方案的工程师也适合刚接触YOLO系列、想找一个完整落地案例的研究者。它的核心价值不在模型本身而在于把行为识别和密度统计这两个养殖刚需用一套目标检测流程串了起来。2. 数据采集与预处理图像质量决定模型上限2.1 采集设备选型与方案设计先把摄像头选对否则后面全是补救。圆形池优先水流均匀、鱼群分布相对稳定方形池角落容易堆鱼后期统计密度会有偏差。分辨率建议上到4K实测1080p对单条小鱼的分辨率不够训练出的模型对小目标鱼苗基本是玄学。帧率至少30帧每秒低于这个数鱼游动轨迹在帧间会断裂后面光流特征算出来全是噪声。视角要覆盖整个养殖池实在覆盖不了就多装几个尽量避免监测盲区——鱼在盲区里的行为模型再好也看不到。采集频率是个平衡问题。文档建议每分钟1到5次这个量级合理。高频采集一天下来几个GB数据存储和处理成本兜不住低频又容易错过应激反应这类瞬时行为。我一般按养殖阶段调鱼苗期密度变化快用5次/分钟成鱼期观察摄食行为为主1到2次/分钟够用。采集时间安排在白天活跃期但注意别只采正午强光下的数据早晨和傍晚的光照差异如果不覆盖进训练集部署时早晚检测精度会明显掉。2.2 图像清洗与标注要点采集回来的原始图像至少三成是不能直接进训练集的。模糊、过曝、水面反光、鱼体被遮挡超过一半的都得先筛掉。清洗这块人工过一遍最靠谱别指望算法全自动。标注用LabelImg这类工具即可矩形框标鱼类别按你的行为研究粒度定义——如果只做密度统计一个「fish」类就够要做行为识别就得按游动、觅食、静止分别建类检测器输出类别后行为分类在后续处理里做。标注格式注意一下。LabelImg默认输出Pascal VOC的XML而YOLOv11训练走的是Ultralytics的txt格式每行类别序号 归一化中心x 归一化中心y 归一化宽 归一化高。中间需要做一次格式转换常见做法是写一个Python脚本解析XML里的object坐标除以图片宽高得到归一化值再写入同名txt。这个转换脚本值得认真写一次后面所有新采集的数据都要用它。2.3 数据增强与标注同步变换数据增强的核心目的不是凑数量是让模型见过更多「变体」同一池鱼在不同光照、不同角度下的样子。文档里给了一套OpenCV增强流程翻转、旋转、亮度调整思路没问题但有一个关键坑必须处理——增强图像的同时标注框必须跟着变换。import cv2 import numpy as np def augment_image_and_label(image, boxes): h, w image.shape[:2] # 随机水平翻转标注框x坐标同步翻转 if np.random.rand() 0.5: image cv2.flip(image, 1) boxes[:, [0, 2]] w - boxes[:, [2, 0]] # xmin/xmax对称映射 # 随机旋转标注框坐标按旋转矩阵重新投影 angle np.random.randint(-20, 20) M cv2.getRotationMatrix2D((w/2, h/2), angle, 1.0) image cv2.warpAffine(image, M, (w, h), borderValue(114, 114, 114)) corners np.array([ [boxes[:, 0], boxes[:, 1]], [boxes[:, 2], boxes[:, 1]], [boxes[:, 2], boxes[:, 3]], [boxes[:, 0], boxes[:, 3]] ]) # 四个角点映射到旋转后坐标 ones np.ones((len(boxes), 1)) rotated M np.concatenate([corners[:, i], ones], axis1).T boxes[:, 0] np.min(rotated[0].reshape(-1, 4), axis1) boxes[:, 1] np.min(rotated[1].reshape(-1, 4), axis1) boxes[:, 2] np.max(rotated[0].reshape(-1, 4), axis1) boxes[:, 3] np.max(rotated[1].reshape(-1, 4), axis1) return image, boxes逻辑说明这段代码把图像增强和标注变换捆在一起。翻转时把xmin和xmax做镜像映射旋转时先把框的四个角点投影到旋转矩阵再重新取最小外接矩形作为新框。注意旋转后图像四角会用灰色填充超出边界的框要过滤掉——我一般加一行判断框的新坐标如果超出图像范围就丢弃该样本避免标签错位。参数说明旋转角度范围±20度是经验值太大容易让鱼形变失真模型学着学着开始认「歪鱼」。亮度调整用np.add加一个-50到50的随机偏移即可幅度超过60会让高光区域直接过曝。除此之外我建议额外加一个高斯噪声增强模拟水质浑浊场景噪声强度σ取0.01左右对模型在低可见度水体里的鲁棒性提升明显。增强完成后数据集按7:2:1左右划分训练、验证、测试。划分时注意同一段视频的连续帧要放进同一个集合——否则相邻帧高度相似相当于测试集泄题评估结果虚高。按视频片段粒度切别按单帧随机切。3. 模型配置与训练从环境搭建到跑通第一个epoch3.1 环境搭建与CUDA版本匹配训练环境这块文档里给的是conda建环境加pip装依赖方向对但细节不够。实操中坑最多的是torch版本和CUDA不匹配GPU训练速度直接拉到CPU水平。我现在的标准做法# 用conda创建Python 3.10环境YOLOv11官方仓库已明确支持 conda create -n yolov11 python3.10 -y conda activate yolov11 # 先看本机CUDA版本再装对应torch别直接pip install torch nvcc --version pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python numpy matplotlib scikit-learn逻辑说明先查CUDA再装torch是为了避免版本漂移这一步能省掉大半环境翻车的时间。ultralytics库是YOLOv11的官方训练入口训练、验证、推理都用它的CLI比手写训练循环靠谱。sklearn是给后面行为分类用的第4章会用到。参数说明Python版本优先保证3.103.9或3.11也能跑但部分依赖在3.10上最稳。torch版本按CUDA选CUDA 11.8对应cu118后缀CUDA 12.x对应cu121或cu124。如果没有NVIDIA GPUCPU也能训练但一个epoch要慢几十倍纯研究小数据集可以正经做项目不建议。3.2 数据配置与模型配置文件修改YOLOv11的数据配置走一个YAML文件。# fish_dataset.yaml path: /data/fish_project train: images/train val: images/val test: images/test # 类别列表这里是密度统计场景的单一类别 nc: 1 names: 0: fish # 如果要做行为识别按粒度拆类别示意如下 # nc: 3 # names: # 0: swimming # 1: feeding # 2: resting逻辑说明path是数据集根目录train/val/test填相对于根目录的图片目录路径。nc是类别总数names字典每个编号对应一个类别名——类别名要和你标注时的名称完全一致。这个文件放在项目任意位置训练命令里用data参数指向它。参数说明如果你用的是yolov11n.yaml这种结构配置文件里面的nc会被数据配置覆盖不用重复改改yaml风险反而大。类别名不建议用中文Ultralytics日志和可视化对中文支持不稳定容易乱码全部用英文小写。3.3 训练启动与关键参数文档里的训练代码是结构示意实际跑训练直接用Ultralytics CLI干净利落# 预训练权重完整训练流程 yolo detect train \ modelyolov11n.pt \ datafish_dataset.yaml \ epochs150 \ batch16 \ imgsz640 \ lr00.001 \ patience30 \ device0 \ projectfish_runs \ namefish_density逻辑说明train子命令加载yolov11n.pt预训练权重COCO上训过的在你自己的鱼类数据上做迁移学习。相比从零训练迁移学习收敛速度快得多小数据集尤其明显几百张图也能训出可用的模型。epoch150是池子里的常规配置配合patience早停损失连续30个epoch不降就自动停止避免无效训练浪费时间。参数说明batch由显存决定16对应大约8GB显存12GB以上可以尝试32不够就降到8别硬撑。imgsz640是速度和精度的平衡点鱼苗这种小目标可以试896后面第6章会展开。lr0用0.001打底数据集大超过1万张可以调到0.01。device0是单卡训练多卡环境写0,1。训练起来后重点盯两组日志一个是训练集和验证集的loss曲线两者差距越拉越大就是过拟合需要加数据增强或调低epoch另一个是验证集的mAP0.5和mAP50-95前者反映了目标框和类别预测的综合正确率。鱼类这类目标形态相对规整mAP0.5能到0.8以上就算可用的模型了。4. 行为识别实现检测框之外的运动特征4.1 检测结果解析与行为特征维度模型跑起来只输出检测框还不够行为识别需要的是「鱼在干什么」这要从连续帧中提取运动特征来回答。检测结果解析的标准流程是这样from ultralytics import YOLO import numpy as np model YOLO(fish_runs/fish_density/weights/best.pt) results model.predict(test_video.mp4, conf0.35, saveFalse) # 逐帧解析检测结果 prev_positions None for r in results: boxes r.boxes.xyxy.cpu().numpy() # 每行的鱼框坐标 x1,y1,x2,y2 scores r.boxes.conf.cpu().numpy() classes r.boxes.cls.cpu().numpy() # 计算鱼的质心位置 current_positions np.array([ [(box[0] box[2]) / 2, (box[1] box[3]) / 2] for box in boxes ]) # 与上一帧的质心做匹配计算帧间位移 if prev_positions is not None: distances np.linalg.norm( current_positions[:, None, :] - prev_positions[None, :, :], axis2 ) min_indices np.argmin(distances, axis1) per_fish_speed distances[np.arange(len(distances)), min_indices] prev_positions current_positions逻辑说明逐帧读取检测结果用框的中心点表示鱼的位置。相邻两帧同一目标的质心位移就是该鱼的帧间速度。min_indices做的是最近邻匹配假设鱼在相邻两帧内不会跑出太远——这就是之前帧率不能太低的原因10帧每秒的视频鱼一快就匹配串号了。参数说明conf0.35是置信度阈值低于这个分数的检测框会被丢掉。这个值要现场调调低了容易把反光和水纹误检成鱼调高了漏检增多。我的习惯是先在验证集上各跑一遍0.3/0.35/0.4看哪个阈值下F1最高。4.2 行为特征速度拐点、轨迹形态与平稳度要在「游动、摄食、静止、应激」之间做区分最基本也最稳定的特征是速度、位移和轨迹形态。单帧的运动学指标只有有限几种速度陡增往往出现在摄食或应激中连续多帧位移接近零是静止摄食行为还有嘴巴张合和头部朝向水面这两个特征但这需要更高分辨率镜头普通全景监控给不到。常见做法是采集连续帧的速度窗口叠加统计量。比如记录每帧每条鱼的速度滑动取2秒内均值、方差、最大值和方向一致性作为行为分类输入。这个特征的物理意义比直接拿归一化像素值去喂模型更可靠而且对光照变化不敏感。文档里用光流法的Farneback算法适合整个画面都动的情况——比如把相机固定住、鱼群大面积游动时光流场能体现群体运动方向。但单个小目标的光流只有几个像素的残差噪声太大我一般只用它做群体活跃度判断不用于单鱼行为分析单鱼行为直接用检测框的质心轨迹更稳。4.3 行为分类向量化到标签拿到特征向量之后分类模型的选择要看训练数据规模和实时性要求。无人机直播里的鱼类行为分类最省事的做法还是传统机器学习。文档里用的SVM分类思路在我实际项目里效果不错不同行为对应的运动特征向量在特征空间里是线性可分的不需要深度时序模型。但如果你的应用场景是长时间连续行为序列分析比如自定义一个「异常行为」类别SVM就明显不够用了LSTM/GRU这类时序模型才接得住。用LSTM之前要先确认你的序列长度——太长的序列会让训练收敛变慢且容易过拟合建议窗口长度压在10到20帧之间。5. 密度统计与常见问题计数误差、重叠与光照干扰5.1 三种计数方式与误差来源密度统计的底层就是数鱼但数鱼的方式有讲究。最直接的取单帧检测框数量说实话效果一般因为鱼群密集时遮挡严重头部和尾部经常一起出现在一个框里单帧漏检率可能到两成。实际项目中我更倾向做帧间融合计数滑窗取30秒的视频逐帧检测后对所有帧的目标数量取中位数再用时间片之间的中位数做平滑这样能把单帧偶发错误压下去不少。如果鱼群太密、单张图已经数不清了就得换思路了——把画面切分成有序的多个小区域对每个区域单独推理再求和相当于用空间换取感受野精度虽然多花一次推理时间但重叠目标被挤在一个框里的概率大大降低。from ultralytics import YOLO import cv2 import numpy as np model YOLO(fish_runs/best.pt) cap cv2.VideoCapture(pond_video.mp4) frame_count 0 counts [] while True: ret, frame cap.read() if not ret: break frame_count 1 results model.predict(frame, conf0.35, verboseFalse) # 单帧检测到的目标数 n_det len(results[0].boxes) counts.append(n_det) # 每30帧做一次中位数统计输出稳定计数 if frame_count % 30 0: median_count int(np.median(counts[-30:])) # 再做一个时间窗口内的中位数平滑防止抖动脉冲 smooth_count int(np.median(counts)) print(f第{frame_count}帧当前稳定计数{smooth_count}最近30帧中位数{median_count}) cap.release()逻辑说明每帧检测一次把结果放进列表每30帧取一次中位数作为该时段的稳定计数。中位数对个别帧的突发误检不敏感——比如某帧水面反光误检了5条中位数基本不受影响但均值会被拉高。这就是计数值选median而不是mean的原因。参数说明30帧滑窗是经验值对应1秒30fps的观测窗口。窗口太短平滑效果差太长会把真实的密度变化也抹平。视频帧率如果是25fps建议窗口改为25帧。5.2 密度计算与场地标定方法有了稳定计数密度计算就简单了。文档里的公式是密度 总数 / 池面积比如10平方米池子检测到100条鱼密度就是10条/平方米。但这里的面积有个坑如果是俯视鱼群检测到的是水面集合密度而实际水体是立体的鱼能利用的是整个水体体积单位用条/立方米更合理。我一般会在项目初期就和养殖户确认统计口径——单纯养鱼和科研论文里习惯用条/平方米做表观密度生态研究和环境容量评估用条/立方米。如果拿到的是侧视摄像头画面建议直接用条/画面代替因为侧视「面积」的定义不统一硬换算反而误事。具体采用哪个口径要看你拿到的摄像头安装方式。5.3 系统评估指标与五个典型坑密度统计任务我用MAE和MSE做评估指标——和人工点数的真值对比偏差在5%以内算可靠。行为识别任务用准确率、召回率、F1但更推荐按行为类别分开看摄食和静止的行为特征差异大分类准确率高游动和应激的行为在速度曲线上有重叠准确率会互相拉低这不是模型烂是定义边界本身模糊。踩坑记录一连续多天的夜间监测数据密度统计值在凌晨2点左右突然跳高第二天白天又回落。原因是夜间光照突变摄像头自动曝光切换时水面反光被误检成鱼。解决方法是固定摄像头白平衡和曝光参数不打开自动模式加一盏红外补光灯让全天光照条件趋于一致。踩坑记录二高密度养殖池中误检漏检很常见一对紧挨的鱼被识别成了一条最终数量偏少。原因是在高密度场景下NMS把两个高度重叠的框合并了。解决方法是把NMS的IoU阈值从默认的0.45调到0.6保留更多重叠的候选框这是官方默认值直接改即可。如果还是不行就需要分区域推理。踩坑记录三数据增强时只增强了图片标签没同步旋转训练时模型看到的框和鱼的位置错位验证集上mAP直接崩掉。这个是很多新手最容易犯的错。解决方法就是第2章代码里的做法——图像和标签同步变换增强后的样本再随机抽查一遍确认框确实贴合目标区域。踩坑记录四单个养殖池模型迁移到另一个池子后误检率上升。原因是两个池子的水体颜色、深度、背景差异大模型没见过这个分布。解决方法是迁移部署前至少采集新场景200张图做增量训练把新场景的样本混合进原有训练集这个操作能显著降低误检率。踩坑记录五推理速度跟不上实时监控视频卡顿严重。原因是直接用了yolov11x大模型参数多、计算量大边缘设备跑不动。解决方法是先换yolov11n或s模型速度提升几倍推理前把图像缩放到640像素宽半精度float16开启速度还能再翻一倍。如果对速度有硬指标要求用TensorRT导出engine格式是较为彻底的方案官方仓库对部署支持比较完整值得用起来。6. 部署与优化技巧保存结果、小目标优化与长期监测习惯6.1 推理结果保存的两种姿势训练好模型之后评估阶段要保存结果但测试时保存的视频体积很大没必要每一帧都带框输出。我的做法是分两层验证阶段保存全部带标注的视频用于人工抽查和汇报展示——用ultralytics自带的saveTrue即可推理结果自动叠加边界框并保存在runs/detect目录取用如果是长时间监测改成定期截帧保存带框图片因为连续的检测框叠加视频几个小时就能把磁盘撑满按时间戳归档关键帧更合理。6.2 小目标鱼苗场景的优化方向如果是小鱼苗单个框只占图像几十个像素用640分辨率训练效果不会太好。我的优先处理顺序是先尝试训练和推理都提升到896分辨率代价是显存占用增加需要配合降低batch然后可以增加浅层特征融合权重——虽然YOLOv11已经做了多尺度融合但小目标分支的权重默认并不偏向鱼苗这种稀疏小目标微调这部分结构能带来收益如果这些都还不够再谈切片推理每次只推理原图四分之一区域把小目标放大成中目标来识别。第一步性价比最高优先推荐先试我一般不会上来就切片因为推理耗时增加明显。6.3 长期监测的验证习惯从那以后我每做完一套养殖检测流程都会强制走一遍「全天候验证」拿至少24小时的连续监控视频按2小时间隔抽取12个时间点每个时间点人工数鱼和模型统计做对比。白天4个点、夜间4个点、清晨傍晚各2个点覆盖全光照变化。这一轮跑下来模型真能用还是假能用就一目了然了。如果某台设备的统计偏差明显大于之前验证的平均水平我会直接调低该场景的置信度阈值再不行就重新增量训练直到偏差回到合理区间为止。这套验证习惯看着笨重但确实是能在上线前把模型问题和数据问题的根源提前暴露出来的最有效方法——否则到了投产阶段再发现夜间误检、阴天漏检就相当被动了。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询