ARTICLE DETAIL

资讯详情

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

YOLOv8教室人数统计实战:从数据标注到部署的完整指南

YOLOv8教室人数统计实战:从数据标注到部署的完整指南 简介一套基于YOLOv8的智慧教室人数统计应用面向计算机视觉、深度学习方向的毕业设计或课程设计人群提供可直接运行的完整项目源码、可视化界面、数据集与部署教程。包体共8个文件包含三个Python脚本分别承担模型训练、视频检测及可视化页面启动、三个PyTorch模型权重文件含yolov8n.pt、best.pt等以及两个说明文档压缩包约15.91MB适合直接部署并快速运行使用。项目源码已经完整测试并成功运行可输出核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果以及标签分布图便于答辩展示和效果复盘图表均由程序自动生成。目前已有59人学习下载适合需要快速落地深度学习目标检测项目的在校学生、老师或企业开发者参考使用。1. 教室人数统计用 YOLOv8 做不只为毕设也为落地踩坑教室人数统计这个需求看起来只是把 YOLOv8 跑起来数人头实际做一轮你会发现它不是单纯的目标检测而是数据、训练、部署、界面四条线的组合。论文里能找到的公开项目很多但大部分要么只给训练代码没有可视化界面要么数据集不完整要么部署教程写得像黑匣子你拿过来根本跑不通。这套基于 YOLOv8 的智慧教室人数统计应用覆盖了从数据集标注、模型训练到桌面界面和 Web 界面的完整链路适合毕设答辩也适合想第一次把检测模型推到实际场景的开发者。本文按我自己的实操顺序讲先整理数据再调参训练再部署界面最后是几个能让你少熬夜的排错思路。我会尽量说清楚每一步为什么这么做而不是给你一串黑命令。后端训练用 YOLOv8n 起步界面用 PyQt5 或 Flask 二选一取决于你要不要实时视频流。完整项目里通常附带已经标注好的教室场景数据集但建议你自己跑一遍标注流程因为训练出的模型要在你自己的教室场景里认得准公开数据集只是帮你验证流程通不通。接下来我们从数据准备开始这部分决定后面所有步骤是顺滑还是反复返工。2. 数据准备教室场景为什么要自己标数据才能不翻车2.1 公开人头数据集与自建数据集的取舍YOLOv8 训练自己的数据集第一步不是写代码是确认你的数据能表达教室人数统计这个任务。SCUT-HEAD、CrowdHuman 这类公开数据集标注质量好、样本量大但它们是通用行人/人头检测不是教室俯视/斜视场景。教室摄像头通常装在黑板正上方或教室后墙视角、光照、遮挡分布和街景行人数据集完全不一样直接用公开集训练出来的模型换到自己教室画面里漏检率会明显上升。我的建议是公开数据可以做预训练或辅助增强但核心数据必须从你目标教室实拍哪怕只采集几百张也要让验证集与测试集来自同一镜头视角。标注格式统一用 YOLO 的 txt 格式每行三个值类别 id、归一化后的中心 x、中心 y、宽 w、高 h。如果项目自带的数据集已经是这个格式你可以跳过标注但也要抽 20 张图把标签打开对照检查因为很多数据集下载回来存在类别 id 错位或坐标越界。自建数据流程建议是录 5 到 10 分钟视频按每秒 1 帧抽帧筛掉画面模糊和大量重复的帧剩下约 500 到 1000 张其中 80% 做训练10% 做验证10% 做测试。这样你的数据分布接近真实使用情况后面训练时 loss 曲线和 mAP 才有参考意义。注意如果项目源码里自带了数据集别急着跳过检查。用脚本统计每张图标注框的宽高分布和类别数量能提前发现标注错位。import os from collections import Counter label_dir datasets/classroom/labels/train widths, heights, classes [], [], [] for fname in os.listdir(label_dir): with open(os.path.join(label_dir, fname), r) as f: for line in f.readlines(): parts line.strip().split() if len(parts) ! 5: print(f格式异常: {fname}) continue cls, x, y, w, h int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if w 0 or h 0 or not (0 x 1 and 0 y 1): print(f坐标越界: {fname}) widths.append(w), heights.append(h), classes.append(cls) print(类别分布:, Counter(classes)) print(平均宽高:, sum(widths)/len(widths), sum(heights)/len(heights))这段脚本遍历标签目录把每行标签按 YOLO 格式的五个字段拆分检查宽度高度是否大于 0、归一化中心坐标是否在 0 到 1 区间内。类别分布用 Counter 统计可以一眼看出某个类别样本是否过少。平均宽高比值如果明显小于 0.1说明标注框太小模型学习难度大后续可以适当裁剪或提高输入分辨率。2.2 标注工具选择与 class 定义策略标注教室人头只有一个类别时建议类别名叫 person_head不要叫 person。这是因为教室场景里身体遮挡严重模型若能学会只盯头部统计人数更稳。如果你用的是 LabelImg 或 LabelMe导出时要选 YOLO 格式才会生成 txt 而不是 JSON。标注时框选位置有讲究框要从头顶到下巴不要包含整个上半身尤其俯视视角下身体会被桌椅分隔成碎片反而干扰特征学习。前额留一点 margin 即可框太大模型会学到太多背景。另一个容易被忽略的点是同一个视频抽帧出来的相邻帧太像随机划分训练集和验证集时验证集里可能出现训练帧的近亲导致验证 loss 虚低。手动把时间连续帧归到同一集合或者用视频片段维度划分。数据不干净后面 YOLOv8 训练自己的数据集做得再精细出来的模型也是看似准确、一换场景就崩这是教室场景最典型的坑。project/ ├── datasets/ │ ├── classroom/ │ │ ├── images/train │ │ ├── images/val │ │ ├── labels/train │ │ └── labels/val │ └── classroom.yaml目录结构上YOLOv8 默认按 images 和 labels 同级组织data.yaml 里写 train 和 val 路径。如果你拿到的是其他格式比如 VOC 或 COCO 的 XML/JSON需要先转换不要嫌麻烦后面避坑章节我会专门讲 VOC 转 YOLO 的边界问题。3. 模型训练从 YOLOv8n 到 mAP 达标的关键参数3.1 安装环境与最小训练命令YOLOv8 环境配置网上教程很多但最容易出问题的是依赖版本冲突尤其是 PyTorch 的 CUDA 版本和 torchvision 对不上。建议直接用官方提供的 requirements单独建一个 conda 环境Python 版本 3.9 或 3.10 最稳。GPU 显存只有 6G 左右的话先选 YOLOv8n 而不是 s 或 mn 是 nano参数量约 3.2M白天教室场景足够用。如果你的显卡是 GTX 1660 Ti 或同级epochs 从 100 起调batch size 设 16 左右不要一上来就追高分。conda create -n yolov8 python3.10 -y conda activate yolov8 pip install ultralytics torch torchvision --index-url https://download.pytorch.org/whl/cu118 yolo detect train datadatasets/classroom/classroom.yaml modelyolov8n.pt epochs100 imgsz640 batch16 device0这段命令先用 conda 建环境装 ultralytics 和带 CUDA 11.8 的 torch然后启动训练。data 参数指向 classroom.yamlmodel 用预训练权重 yolov8n.pt这样能在 ImageNet 预训练基础上做迁移学习收敛快很多。imgsz640 是输入分辨率教室场景人头小建议不要降到 320否则小目标直接消失。device0 表示第一块 GPU如果你的电脑只有 CPU就去掉 device 参数但训练时间会翻很多倍。训练完成后weights 目录下会出现 best.pt 和 last.pt后续部署用的是 best.pt。3.2 训练参数逐项拆解epochs 不是越大越好教室人头类别单一100 轮左右基本能收敛超过 200 轮容易过拟合。过拟合的信号是验证集 mAP 先升后降而训练 loss 还在一直降。batch size 受显存约束6G 显存建议 16 到 32如果出现 CUDA out of memory先降 batch而不是降 imgsz因为小目标检测对分辨率更敏感。optimizer 保持默认的 auto它内部会按 batch size 自动选 SGD 或 AdamW手动改成 SGD 加 momentum 也行但调参门槛高一些。重点说 imgsz这个参数经常被忽略。教室摄像头画面里一个人头可能只有 30x30 像素级别imgsz640 时网络会把 30 像素的小头映射到特征图上的十几个像素勉强能检测。imgsz320 时小头可能直接丢失。如果显卡跑得动建议 imgsz768 或 1024 再对比一轮mAP 会涨 3 到 5 个百分点代价是推理帧率下降这个取舍要在部署阶段想清楚。yolo detect train datadatasets/classroom/classroom.yaml modelyolov8n.pt epochs150 imgsz768 batch8 device0 patience20patience 是早停参数连续 20 轮验证 mAP 没提升就自动停止防止后 50 轮都在白跑。如果你的数据量少patience30 也可以但早停机制有时会 stop 在局部最优想拿最好结果就关掉早停把 best.pt 自己在每个 epoch 后都存一份。训练到一半想看模型质量可以随时中断best.pt 已经是目前最优。3.3 画损失函数曲线图判断模型健康度训练结束后程序会自动在 runs/detect/train 目录生成 results.png包含 box_loss、cls_loss、dfd_loss 和 mAP 曲线。但毕设里通常需要单独画一张曲线图放进论文这时可以用 ultralytics 的返回值自己画。判断模型健康度的标准训练 loss 和验证 loss 同步下降二者差距不大如果验证 loss 开始抬升就是过拟合。教室场景数据集如果只有几百张图过拟合非常常见表现为 mAP 在验证集上不错但实拍画面里漏检频繁。from ultralytics import YOLO import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) plt.figure(figsize(10, 6)) plt.plot(df[epoch], df[train/box_loss], labeltrain box loss) plt.plot(df[epoch], df[val/box_loss], labelval box loss) plt.xlabel(epoch) plt.ylabel(box loss) plt.title(YOLOv8 loss curve) plt.legend() plt.grid(True) plt.savefig(loss_curve.png, dpi300)results.csv 是训练过程的完整记录train/box_loss 是训练集框回归损失val/box_loss 是验证集框回归损失。判断标准是二者都下降且保持接近。如果 train 降但 val 不降甚至涨说明过拟合回到数据增强或降低模型复杂度。如果想单独画 cls_loss把列名换成 train/cls_loss 即可。4. 部署与可视化把模型封装成能用的界面4.1 使用 PyQt5 做桌面端实时统计界面模型训练完下一步是让老师或管理员能打开一个窗口看到视频画面和人数统计。PyQt5 是毕设项目里最常见的选择因为它能直接调用摄像头或视频文件在界面上画框并用 label 显示实时人数。核心逻辑是把 YOLOv8 推理封装成一个类用 QThread 子线程跑防止界面卡死。这个点很多教程里不讲但如果你在主线程里做推理视频一卡整个窗口就无响应答辩时非常尴尬。import cv2 from PyQt5.QtCore import QThread, pyqtSignal from ultralytics import YOLO class DetectThread(QThread): frame_signal pyqtSignal(object, int) def __init__(self, source0, model_pathbest.pt): super().__init__() self.cap cv2.VideoCapture(source) self.model YOLO(model_path) def run(self): while True: ret, frame self.cap.read() if not ret: break results self.model(frame, conf0.4, imgsz640, verboseFalse) count len(results[0].boxes) self.frame_signal.emit(results[0].plot(), count)这段代码把视频采集和模型推理放进子线程每帧读取后传入 YOLO 模型conf0.4 是置信度阈值低于 0.4 的检测框会被滤掉。教室场景里 threshold 通常取 0.35 到 0.5 之间太高漏检太低误检。verboseFalse 关掉推理日志否则控制台会被刷爆。results[0].plot() 返回画好框的图片通过信号发给主线程显示计数通过同一个信号传回去。用信号而不是直接改界面控件是 Qt 线程安全的惯例做法。4.2 使用 Flask 做 Web 端部署方案另一种常见做法是 Web 端部署尤其当学生或管理员想通过浏览器访问时。Flask RTSP 推流是经典组合但要注意 Flask 默认的开发服务器不适合并发部署时换个 waitress 或 gunicorn。如果你只是毕设演示Flask 自带的 app.run 也够用。教室场景里摄像头通常是 RTSP 流直接给 YOLOv8 传 RTSP 地址opencv 会去拉流但拉流超时或断流时需要重连。from flask import Flask, Response, render_template import cv2 from ultralytics import YOLO app Flask(__name__) model YOLO(best.pt) cap cv2.VideoCapture(rtsp://your_camera_ip:554/stream) def gen_frames(): while True: ret, frame cap.read() if not ret: cap.open(rtsp://your_camera_ip:554/stream) continue results model(frame, conf0.45, imgsz640, verboseFalse) annotated results[0].plot() ret, buffer cv2.imencode(.jpg, annotated) yield (b--frame\r\nContent-Type: image/jpeg\r\n\r\n buffer.tobytes() b\r\n) app.route(/video_feed) def video_feed(): return Response(gen_frames(), mimetypemultipart/x-mixed-replace; boundaryframe)这段代码的核心是一个生成器函数每一帧推理后按 JPEG 格式编码通过 multipart 协议推给浏览器。RTSP 断流后 cap.read() 会持续失败我在这里做了一个简单的重连cap.open 重新拉流。如果你只是测试本地视频文件把 VideoCapture 参数换成视频路径就行。Web 端相比 PyQt5 的优势是不需要每台电脑装 Python 环境但实时性受浏览器刷新帧率限制视频流延迟大约 1 到 2 秒。4.3 导出 ONNX 与部署到边缘设备如果你想把模型从训练机搬到边缘设备比如 RK3588 这类板子需要先把 PyTorch 权重导出为 ONNX。RK3588 的 NPU 对 ONNX 支持比直接跑 PyTorch 好很多通常配合 rknn-toolkit2 做模型转换。导出命令很简单但导出后要检查输入输出的算子兼容性PyTorch 版本和 ONNX opset 版本不一致时导出结果可能推理报错。yolo export modelbest.pt formatonnx dynamicFalse imgsz640 opset12export 后得到 best.onnx。dynamicFalse 表示输入尺寸固定为 640x640这样 NPU 不需要处理动态 shape推理速度更快。opset12 是兼容性较好的版本RK3588 的 rknn-toolkit2 对 opset 12 支持稳定。导出完成后建议先用 onnxruntime 推理一遍测试结果是否与 PyTorch 推理一致再上板子不然排查问题会非常痛苦。5. 避坑指南复现这套毕设最容易踩的六个坑5.1 现象训练时 loss 下降但 mAP 一直为零原因通常是数据集标注框和 YOLO 格式转换出错最常见的是类别 id 从 0 变成 1或归一化坐标写反。我的排错做法是用 cv2 把训练集前 20 张图画上框人工看一眼标注是否贴合人头。如果框的位置明显偏了就是标注转换脚本的问题而不是模型问题。VOC 转 YOLO 格式时尤其要小心VOC 的 bndbox 存的是 xmin、ymin、xmax、ymax而 YOLO 需要中心点和宽高换算公式为 x_center(xminxmax)/2/widthwidth 是图的宽不是框的宽。import cv2 img cv2.imread(datasets/classroom/images/train/0001.jpg) with open(datasets/classroom/labels/train/0001.txt) as f: for line in f.readlines(): cls, x, y, w, h map(float, line.strip().split()) x1 int((x - w / 2) * img.shape[1]) y1 int((y - h / 2) * img.shape[0]) x2 int((x w / 2) * img.shape[1]) y2 int((y h / 2) * img.shape[0]) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check_annotation.jpg, img)这个脚本读取一张图和它的标签把归一化坐标换算回像素坐标然后画框输出。如果框离人头中心太远问题不在模型在数据。把 20 张图都查一下比看任何训练指标都快。5.2 现象摄像头画面里漏检后排学生原因有两个方向。一是训练数据里后排人头样本太少模型没见过这个尺度。二是 imgsz 设置太小。教室后排的人头在 640 分辨率下往往只有 20 到 30 像素是典型小目标imgsz 提到 768 或 1024 能明显改善。另一个有效做法是训练时开启马赛克增强之外单独把后排区域做 cutout 扩大。如果你用的是项目自带数据集也要确认是否覆盖远距离样本不然部署后就是漏检。解法回到数据层面专门从后排视角多抽帧并适当把这类小目标样本复制增强 1 到 2 倍重新训练。这属于调数据而不是调代码也是我在这个项目里最花时间的部分。5.3 现象CPU 推理只有 2 帧界面卡成幻灯片原因很简单YOLOv8n 虽然轻量但 CPU 跑实时视频还是吃力。这里我给小白建议是降到 480p 分辨率再做推理或者把帧率限制在 10 FPS先保证界面不卡。如果想要流畅体验把模型导出 ONNX 之后用 OpenVINO 或 ONNX Runtime 推理比 PyTorch 直接跑快 2 到 4 倍。还有一种玄学方案是检测框轻微抖动相邻帧 jitter 明显时做平滑常见做法是维护一个队列输出人数取最近 5 帧的众数或均值单个跳变不会导致界面数字乱跳。from collections import deque import statistics count_history deque(maxlen5) def smooth_count(raw_count): count_history.append(raw_count) return statistics.median(count_history)这个中值平滑技巧特别适合人数统计不会因为单帧误检把数字从 42 跳到 47。它本身不改变模型只做后处理但对毕设演示的观感提升巨大。你如果要在论文里写可以说这是时序平滑模块。5.4 现象RK3588 上 ONNX 转换失败报 op 不支持原因通常是模型里出现了 RKNN-toolkit 不支持的层比如某些新版本的 DFL 或注意力机制。做法是先看日志里报哪个 op再在导出时加上简化算子选项。onnxsim 先简化计算图再转到 rknn。如果还不行就把模型改回 YOLOv8n 标准结构不要用自己改的 attention 头边缘部署优先保证结构兼容。5.5 现象忘记把推理代码封装成类项目一坨代码没法答辩很多毕设拿到源码能跑通但对项目结构说不清楚。解决方案把数据加载、模型推理、界面逻辑拆成三个模块训练、验证、推理入口分开。这样不光是代码好看调试也省心。我见过太多把摄像头读取、模型推理、绘图全部塞进一个 main.py 的同学来回折腾很难受。这个坑是工程性问题不是技术难点。5.6 现象数据集路径写错训练 10 轮后才发现没读数据把 data.yaml 里的路径写成绝对路径换机器就要改。建议项目根目录下用相对路径或者训练命令里统一用项目前缀拼接路径。YOLOv8 在找不到数据时不会立即报错而是训练一些轮次后在日志里提示WARNING data not found遇到这个提示直接 CtrlC 检查路径。注意以上坑都来自于我实际带别人复现同类项目的记录第 1 和第 3 条最隐蔽也最影响答辩结果优先级最高。6. 验证方法和进阶技巧不只是跑通还能说清为什么好把训练好的模型部署到界面上只是第一步。毕设答辩时被问到最多的几个问题是你如何验证模型泛化能力你统计的人数准确率是多少不同光照条件下模型会不会失效。如果你没有提前准备好验证脚本和量化数据现场演示就会变成看它能框人但经不起追问。这里给出一套我在挖数据集和调参过程中反复用的验证方法。先做离线视频回放验证。找一个没有被用作训练集的教室视频把模型跑完整段逐帧统计 Count 结果再与人工标注的每帧真实人数对比。这个对比就产生了漏检率和误检率两个指标。标注 500 帧的工作量大约 2 到 3 小时但对毕设论文来说非常值得因为你终于能用一条折线图表达模型在哪些时段、哪些位置出错。另一块可以做得更深的是 ROI 分区统计把教室画面按座位分区分别统计人数这样能看到不同区域的检测精度差异也方便后续做区域人数上限预警。对于光线变化建议在训练数据里加入亮度扰动OpenCV 里可以快速把原图增强或变暗后再参与训练YOLOv8 本身有 hsv 增强但教室场景里日光灯闪烁和傍晚昏暗是两类特殊光照单独增强更有效。模型训练] 最终的模型选择建议做 A/B 对比同一组验证视频分别跑 yolov8n 和 yolov8s统计两版的平均精度和推理时间。如果差距在 2% 以内就选能跑满 30 FPS 的那版教室人数统计是准实时任务流畅度优先。如果数据量充足、显卡允许再用 TTA测试时增强或集成推理验证一下上限但正式部署不推荐开 TTA耗时翻倍精度提升有限。我自己的习惯是每次训练完把训练命令和参数写进一个 yaml 文件存档记录 imgsz、epochs、patience、数据版本发生改动时容易回看这个习惯在我反复调整参数之后帮了大忙再也不会出现上次那版效果更好但忘了配了啥的情况。这条流程走完你对基于 YOLOv8 的智慧教室人数统计应用这个项目的理解就不是停留在能跑而是能讲清每个步骤背后的取舍。希望帮到你。 p a hrefhttps://download.csdn.net/download/m0_65481401/90615937 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
返回列表