简介这份资源是面向计算机、人工智能、通信工程、自动化等专业学生与教师的YOLOv8目标检测实战项目聚焦智慧教室场景下的人数统计任务可作为毕业设计、课程设计或大作业的完整参考方案。压缩包共8个文件包含3个Python脚本、3个模型权重文件与2个说明文本整体约15.91MB脚本分别承担可视化界面、模型训练与视频检测等功能权重文件可直接加载推理说明文档则提供部署与使用指引。项目已完整跑通可输出核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图便于答辩时展示实验过程与效果。目前已有58人学习适合希望快速上手YOLOv8、需要一站式源码与数据集支撑的读者参考也可在此基础上修改扩展实现其他检测功能。1. 智慧教室人数统计从 YOLOv8 检测框到可视化界面的完整落地路径智慧教室人数统计这件事听起来像是把 YOLOv8 跑起来数框就完事了但真正做过的人都知道从模型推理到界面展示之间隔着一堆工程细节。我最近刚把一套基于 YOLOv8 的教室人数统计方案从零跑通包含数据集处理、模型训练、推理优化和可视化界面整个过程踩了不少坑也积累了一些可以复用的经验。这套方案的核心思路很直接用 YOLOv8 检测画面中的人体目标统计检测框数量作为人数再通过可视化界面实时展示结果。它适合做毕设、课程设计也适合作为目标检测入门到部署的练手项目。如果你手头有教室监控画面或者想用公开数据集快速验证这篇文章会按「数据准备 → 模型训练 → 推理优化 → 界面集成 → 避坑排查」的顺序把每个环节的参数和代码都摊开讲清楚。2. 数据集准备与 YOLOv8 训练环境搭建2.1 教室场景数据集从哪里来、怎么标教室人数统计的数据集有两个来源一是自己采集教室监控截图用 Labelme 或 LabelImg 标注二是用公开的人群检测数据集做迁移。自己采集的话建议覆盖不同时段上午、下午、傍晚、不同座位密度稀疏、中等、满座、不同遮挡情况前排遮挡后排、立柱遮挡。标注时只标一类person标注框尽量贴紧人体可见部分不要为了框全而把被遮挡的部分也画进去否则模型会学到错误的边界。用 Labelme 标注完成后需要转成 YOLO 格式。YOLO 的标签文件是.txt每行格式为class_id x_center y_center width height坐标都要归一化到 0~1。转换脚本如下import json import os from pathlib import Path def labelme_to_yolo(json_dir, output_dir, class_names): 将 Labelme 的 JSON 标注转换为 YOLO 格式的 txt 文件 json_dir: 存放 .json 文件的目录 output_dir: 输出 .txt 的目录 class_names: 类别名称列表如 [person] json_dir Path(json_dir) output_dir Path(output_dir) output_dir.mkdir(parentsTrue, exist_okTrue) for json_file in json_dir.glob(*.json): with open(json_file, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] lines [] for shape in data[shapes]: label shape[label] if label not in class_names: continue class_id class_names.index(label) points shape[points] xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # 归一化并计算中心点与宽高 x_center (x_min x_max) / 2.0 / img_w y_center (y_min y_max) / 2.0 / img_h width (x_max - x_min) / img_w height (y_max - y_min) / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) txt_path output_dir / (json_file.stem .txt) with open(txt_path, w) as f: f.write(\n.join(lines)) if __name__ __main__: labelme_to_yolo( json_dirdatasets/labelme_json, output_dirdatasets/labels, class_names[person] )这段代码的关键点在于坐标归一化x_center和y_center是框中心点除以图像宽高width和height是框的宽高除以图像宽高。归一化后的值必须在 0~1 之间如果出现负数或大于 1说明标注框超出了图像边界需要检查原始标注。转换完成后按 8:1:1 划分训练集、验证集和测试集目录结构如下datasets/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/然后创建data.yamlpath: ./datasets train: images/train val: images/val test: images/test nc: 1 names: [person]nc是类别数教室人数统计只有person一类所以设为 1。names的顺序必须和标注时的class_names一致否则训练出来的模型会把类别搞混。2.2 YOLOv8 环境搭建CPU 版本也能跑但要注意这些参数YOLOv8 的环境搭建有两种路线CPU 版本和 GPU 版本。如果只是做课程设计或者验证流程CPU 版本完全够用但训练速度会慢很多。我一般推荐用 Ubuntu 20.04 或者 Windows WSL2Python 版本选 3.8~3.10太高或太低都可能遇到依赖冲突。安装命令如下# 创建虚拟环境 python -m venv yolov8_env source yolov8_env/bin/activate # Windows 用 yolov8_env\Scripts\activate # 安装 PyTorchCPU 版本 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics pip install ultralytics # 验证安装 yolo checksyolo checks会输出当前环境的信息包括 PyTorch 版本、CUDA 是否可用、YOLOv8 版本等。如果 CUDA 显示不可用但你有 NVIDIA 显卡说明 PyTorch 装成了 CPU 版本需要卸载后重新安装对应 CUDA 版本的 PyTorch。CPU 版本训练时把workers设为 0 或 2避免多进程数据加载在 Windows 上出问题。训练命令yolo detect train \ datadatasets/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ workers4 \ device0 \ patience20 \ saveTrue \ projectruns/train \ nameclassroom_person参数说明modelyolov8n.pt用的是 nano 版本参数量最小适合 CPU 推理和边缘设备部署如果 GPU 显存充足可以换成yolov8s.pt或yolov8m.pt。imgsz640是输入图像尺寸教室场景如果人物较小可以提到 1280但显存占用会翻倍。batch16在 CPU 上可能内存不够改成 8 或 4。patience20表示 20 个 epoch 验证集指标不提升就早停避免过拟合。device0指定第一块 GPUCPU 训练改成devicecpu。训练完成后权重文件保存在runs/train/classroom_person/weights/best.pt。用这个权重做推理yolo detect predict \ modelruns/train/classroom_person/weights/best.pt \ sourcedatasets/images/test \ conf0.5 \ iou0.45 \ saveTrueconf0.5是置信度阈值低于这个值的检测框会被过滤掉iou0.45是 NMS 的 IoU 阈值用来合并重叠框。教室场景如果人挨得比较近IoU 可以调到 0.5~0.6避免把相邻的人合并成一个框。3. 人数统计逻辑与可视化界面集成3.1 从检测框到人数计数逻辑与去重策略YOLOv8 输出的是检测框列表每个框包含坐标、置信度和类别。人数统计最直接的做法就是统计person类别的框数量。但实际场景中会遇到两个问题一是同一帧画面中一个人可能被检测出多个框比如背包被误检成人二是视频流中同一人在连续帧中被重复计数。对于单帧图像直接用len(boxes)统计即可。对于视频流需要引入简单的跟踪逻辑。我一般用两种方案一种是基于 IoU 的帧间匹配另一种是调用 ByteTrack 或 BoT-SORT 跟踪器。YOLOv8 内置了跟踪功能命令如下yolo track \ modelruns/train/classroom_person/weights/best.pt \ sourceclassroom_video.mp4 \ trackerbytetrack.yaml \ conf0.5 \ saveTruetrackerbytetrack.yaml启用 ByteTrack 跟踪器它会为每个检测框分配一个 track ID统计人数时只需要统计唯一 ID 的数量。但 ByteTrack 在人群密集场景下 ID 切换比较频繁需要配合conf和iou调参。如果只是做课程设计单帧统计已经足够视频流统计可以用「每 N 帧统计一次取滑动平均」的方式降低抖动。Python 代码实现单帧统计from ultralytics import YOLO import cv2 model YOLO(runs/train/classroom_person/weights/best.pt) def count_person(image_path, conf_thres0.5): 统计单张图像中的人数 image_path: 图像路径 conf_thres: 置信度阈值 返回: 人数、标注后的图像 results model(image_path, confconf_thres)[0] person_count 0 img cv2.imread(image_path) for box in results.boxes: cls_id int(box.cls[0]) if cls_id 0: # person 类别 person_count 1 x1, y1, x2, y2 map(int, box.xyxy[0]) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, fperson {box.conf[0]:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.putText(img, fTotal: {person_count}, (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 255), 3) return person_count, img这段代码的逻辑是遍历所有检测框只统计cls_id 0的框并在图像上画出矩形框和置信度。最后在左上角显示总人数。conf_thres可以根据实际效果调整教室场景如果误检较多可以提到 0.6如果漏检较多降到 0.3~0.4。3.2 可视化界面用 Gradio 快速搭一个能演示的 Web 界面可视化界面是毕设和课程设计的加分项。我试过 PyQt、Streamlit 和 Gradio最后发现 Gradio 最适合快速搭建演示界面代码量少支持图片上传、视频上传和实时摄像头。安装命令pip install gradio界面代码import gradio as gr from ultralytics import YOLO import cv2 import numpy as np model YOLO(runs/train/classroom_person/weights/best.pt) def detect_image(image, conf_thres): Gradio 图片检测回调 image: numpy 数组来自 Gradio 上传 conf_thres: 置信度阈值 results model(image, confconf_thres)[0] person_count 0 img image.copy() for box in results.boxes: cls_id int(box.cls[0]) if cls_id 0: person_count 1 x1, y1, x2, y2 map(int, box.xyxy[0]) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, fCount: {person_count}, (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 3) return img, f当前人数{person_count} with gr.Blocks(title智慧教室人数统计) as demo: gr.Markdown(## 智慧教室人数统计系统) with gr.Row(): with gr.Column(): input_image gr.Image(label上传教室图片, typenumpy) conf_slider gr.Slider(0.1, 0.9, value0.5, label置信度阈值) btn gr.Button(开始检测) with gr.Column(): output_image gr.Image(label检测结果) output_text gr.Textbox(label统计结果) btn.click(fndetect_image, inputs[input_image, conf_slider], outputs[output_image, output_text]) demo.launch(server_name0.0.0.0, server_port7860)gr.Blocks是 Gradio 的块式布局比gr.Interface更灵活。gr.Image(typenumpy)把上传的图片转成 numpy 数组直接传给 YOLOv8 推理。conf_slider让用户实时调整置信度阈值方便演示不同参数下的效果。demo.launch(server_name0.0.0.0)让界面监听所有网卡局域网内其他设备也能访问。如果部署在服务器上记得开放 7860 端口。提示Gradio 默认只允许本地访问server_name0.0.0.0改成这个才能外部访问。如果遇到端口占用换一个端口即可。4. 避坑与常见问题排查4.1 训练 loss 不下降mAP 卡在 0.3 上不去现象训练了 50 个 epochbox_loss 和 cls_loss 都在波动但不下降验证集 mAP0.5 一直在 0.3 左右。原因最常见的原因是标注格式错误。YOLO 的标签必须是归一化后的class_id x_center y_center width height如果直接用了像素坐标模型学到的就是错误的位置信息。另一个原因是数据集中负样本太多或者标注框把整个画面都框进去了。解决用yolo detect train之前先跑一遍数据校验脚本检查标签文件是否有越界值。可以用labelImg或labelme重新检查标注。另外把data.yaml里的nc和names确认一遍类别数不对也会导致 loss 异常。4.2 CPU 推理速度太慢单帧要 2 秒以上现象用 CPU 跑 YOLOv8n单张 640x640 图像推理耗时 2 秒以上视频流完全卡顿。原因CPU 版本 PyTorch 默认用单线程推理没有启用 MKL 或 OpenMP 加速。另外模型输入尺寸太大也会拖慢速度。解决推理时把imgsz降到 320 或 416速度能提升 2~3 倍。另外用model.export(formatonnx)导出 ONNX 模型再用onnxruntime推理CPU 上速度比 PyTorch 快 30%~50%。如果还是慢考虑用 OpenVINO 做进一步优化。4.3 可视化界面图片上传后显示空白现象Gradio 界面上传图片后输出区域一片空白没有检测结果。原因Gradio 的gr.Image(typenumpy)返回的是 RGB 格式而 OpenCV 默认是 BGR 格式。如果直接把 numpy 数组传给cv2.rectangle和cv2.putText颜色会错乱但更常见的问题是图像通道数不对导致显示异常。解决在detect_image函数里加一行img cv2.cvtColor(image, cv2.COLOR_RGB2BGR)处理完再转回 RGB 返回。或者直接用 PIL 库画框避免 OpenCV 的通道问题。4.4 视频流统计人数跳变严重现象视频流中人数一会儿 15 一会儿 23波动很大。原因单帧检测本身有抖动加上没有做帧间平滑导致计数不稳定。解决引入滑动平均比如取最近 10 帧的计数中位数作为当前人数。或者用 ByteTrack 跟踪器统计唯一 track ID 的数量。如果场景中人流量不大还可以加一个「人数变化超过阈值才更新显示」的逻辑减少视觉抖动。4.5 模型把椅子、书包误检成人现象检测结果中出现了大量非人物的框置信度还不低。原因训练数据中负样本不足或者标注时把一些类似人物的物体也标成了 person。解决在训练集中加入包含椅子、书包、海报等干扰物的负样本图像不标注任何目标。另外推理时提高conf阈值到 0.6~0.7过滤掉低置信度的误检。如果误检集中在某个区域可以用 ROI 裁剪只对教室座位区域做检测。5. 进阶技巧用 ONNX 导出和 TensorRT 加速推理如果部署环境有 NVIDIA GPU把 YOLOv8 导出成 TensorRT 引擎能获得 3~5 倍的推理加速。导出命令yolo export modelruns/train/classroom_person/weights/best.pt formatengine halfTrue device0halfTrue启用 FP16 精度速度更快精度损失很小。导出后的.engine文件可以直接用YOLO(best.engine)加载推理。如果没有 GPU用 ONNX 导出也能在 CPU 上获得不错的加速yolo export modelruns/train/classroom_person/weights/best.pt formatonnx opset12 simplifyTrueopset12是 ONNX 算子集版本兼容性最好simplifyTrue会简化计算图减少冗余算子。导出后用onnxruntime推理import onnxruntime as ort import numpy as np import cv2 session ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) input_name session.get_inputs()[0].name def preprocess(image, input_size640): 图像预处理缩放、归一化、转 NCHW img cv2.resize(image, (input_size, input_size)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img img.astype(np.float32) / 255.0 img np.transpose(img, (2, 0, 1)) # HWC - CHW img np.expand_dims(img, axis0) # CHW - NCHW return img def infer(image_path): ONNX 推理并统计人数 img cv2.imread(image_path) input_tensor preprocess(img) outputs session.run(None, {input_name: input_tensor}) # outputs[0] 形状为 [1, 84, 8400]前 4 个是框坐标后面是类别分数 predictions outputs[0][0] person_count 0 conf_thres 0.5 for pred in predictions.T: scores pred[4:] class_id np.argmax(scores) confidence scores[class_id] if confidence conf_thres and class_id 0: person_count 1 return person_count if __name__ __main__: count infer(test_classroom.jpg) print(f检测到人数{count})这段代码的关键是理解 ONNX 输出格式YOLOv8 的输出形状是[1, 84, 8400]其中 84 4 个框坐标 80 个类别分数COCO 数据集8400 是候选框数量。教室人数统计只有 1 类所以输出形状是[1, 5, 8400]。遍历时取pred[4:]作为类别分数argmax得到类别 ID判断是否为person并累加。注意ONNX 推理时没有内置 NMS需要自己实现或调用cv2.dnn.NMSBoxes。如果直接统计所有超过置信度阈值的框会出现大量重叠框导致人数虚高。我自己的习惯是训练阶段用 PyTorch 验证效果部署阶段优先导出 ONNX 或 TensorRT推理代码里一定要加 NMS。另外教室场景的光照变化很大如果模型在某个时段效果明显下降不要急着重新训练先检查是不是摄像头曝光或白平衡变了调整摄像头参数往往比调模型更快。希望帮到你。本文还有配套的精品资源点击获取