YOLOv8+PyQt5密集人群人体检测计数系统设计与实现

发布时间:2026/9/6 6:51:50
YOLOv8+PyQt5密集人群人体检测计数系统设计与实现 YOLOv8PyQt5 密集人群人体检测识别计数系统设计与实现这次我们来看一个非常典型的深度学习落地场景在商场、景区、地铁站这类人流密集区域如何用 YOLOv8 配合 PyQt5 搭建一套人体检测与计数系统。这类系统在智能安防、客流统计、区域拥挤预警里应用很多难点主要集中在三块——模型能不能在小目标密集场景下保持精度、视频流能不能实时处理、以及检测结果能不能在桌面端应用里直观展示。下面我直接把这套系统的设计思路、训练流程、PyQt5 界面实现和性能优化方案拆开写清楚。如果你正准备做毕业设计、课程项目或者想在公司内部快速搭一个人体计数 Demo这篇文章可以直接照着做。1. 核心能力速览先看这套系统的关键能力能力项说明检测模型YOLOv8支持 nano/small/medium/large/x 多版本界面框架PyQt5支持实时视频显示、检测结果叠加、计数统计核心功能人体检测、目标计数、视频/图片/摄像头输入、检测结果导出硬件要求训练推荐 NVIDIA GPU纯推理可 CPU有 GPU 速度更快显存占用与模型大小、输入分辨率直接相关需按实际配置测试支持平台Windows / Linux启动方式Python 命令行启动 PyQt5 应用API 能力可封装为检测接口供其他程序调用批量任务支持批量图片检测和视频逐帧检测适合场景客流统计、拥挤预警、安防监控、毕设/课设项目YOLOv8 是 Ultralytics 团队在 2023 年初发布的目标检测框架相比 YOLOv5 最大的变化是采用了 Anchor-Free 检测头并且把分类、检测、分割、姿态估计统一到了一个框架里。配合 PyQt5 做桌面界面不需要浏览器直接打包成 exe 就能在 Windows 上跑这对安防领域的实际部署来说非常方便。2. 适用场景与使用边界这套系统能解决的问题总结下来是三类静态图片中的人体检测与计数。比如上传一张商场入口的抓拍图系统自动标出每个人并显示总数。视频文件或实时摄像头中的人体检测与计数。比如接入监控摄像头 RTSP 流实时统计画面里的人数变化。批量图片的处理。比如一次性处理一整天抓拍的几百张图片输出每张图片的人数和检测结果图。适合的人群也很明确。做毕设和课设的学生、需要快速验证目标检测方案的开发者、以及安防或零售行业想做客流统计的技术人员都可以直接用这套方案。使用边界同样需要说清楚。这类系统基于视觉模型对遮挡严重的密集场景检测精度会下降光线暗、雨天、低分辨率摄像头都会影响效果。另外在公共区域部署时务必获得监控设备和数据使用的合法授权不能私自采集人脸或行人的可识别信息用于其他用途涉及隐私数据要脱敏处理。模型本身只输出人体位置和数量不识别身份但采集端合规问题仍然需要自己把关。3. 环境准备与前置条件先确认环境。这套系统核心依赖是 PyTorch、Ultralytics YOLOv8、OpenCV 和 PyQt5。硬件方面训练推荐至少一块 NVIDIA GPU最好是 6GB 以上显存。纯推理的话CPU 也能跑但视频流实时性和高分辨率图片的处理速度会差很多。如果你手里只有 GTX 1660 Ti、RTX 3060、RTX 4060 这类显卡只要显存超过 6GB跑 YOLOv8s 或 YOLOv8m 没有问题。软件环境建议如下我这里列的是推荐配置实际以你本机为基础操作系统Windows 10/11 或 Ubuntu 20.04/22.04Python3.8 到 3.11 均可推荐 3.10CUDA11.8 或 12.1需匹配 PyTorch 版本主要依赖ultralytics、torch、torchvision、opencv-python、PyQt5、numpy安装 PyTorch 前先确认显卡驱动和 CUDA 版本。推荐直接用 pip 安装命令如下# 先安装 PyTorchCUDA 12.1 版本示例 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 # 再安装其余依赖 pip install ultralytics opencv-python PyQt5 numpy如果在国内网络环境安装较慢可以换用清华镜像源pip install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple pip install ultralytics opencv-python PyQt5 numpy -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后用下面这段代码验证环境是否正常import torch import ultralytics import cv2 import PyQt5 print(PyTorch:, torch.__version__) print(CUDA available:, torch.cuda.is_available()) print(Ultralytics:, ultralytics.__version__) print(OpenCV:, cv2.__version__) print(PyQt5:, PyQt5.QtCore.PYQT_VERSION_STR)如果torch.cuda.is_available()返回 False说明 PyTorch 的 CUDA 版本和驱动不匹配需要重新安装对应版本的 PyTorch。CPU 环境也能继续做推理测试只是速度会慢。4. 数据处理与模型训练这套系统的核心之一是训练一个人体检测模型。如果你不想自己训练可以直接用 YOLOv8 自带的预训练权重yolov8n.pt和yolov8s.pt模型里本身就包含 person 这个类别。但你如果想要针对自己场景做更高精度的检测用自己的数据集微调是必须的。4.1 数据集准备人体检测数据集最常见的公开选项是 COCO 数据集中的 person 类别或者 CrowdHuman 这类密集人群数据集。自己采集数据时要注意覆盖不同角度、不同光照、不同密度场景。数据集目录结构建议如下dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/标注格式采用 YOLO 格式每个图片对应一个同名的 txt 文件每行代表一个目标class_id x_center y_center width height坐标值都是归一化到 0~1 的浮点数。比如0 0.514648 0.479492 0.235352 0.772461标注工具可以用 LabelImg 或者 LabelStudio导出成 YOLO 格式即可。4.2 模型配置文件在项目目录下创建person-detection.yaml内容如下path: ./dataset train: images/train val: images/val test: images/test names: 0: person这个文件告诉 YOLOv8 数据集的路径和类别名称。4.3 训练命令YOLOv8 训练入口非常简单一条命令就能启动yolo detect train dataperson-detection.yaml modelyolov8s.pt epochs100 imgsz640 batch16 device0参数含义data数据集配置文件的路径。model预训练权重路径也可以用yolov8s.yaml从头训练但用预训练权重收敛更快。epochs训练轮数小数据集建议 100 轮起步。imgsz输入图像分辨率推荐 640 或 768。密集小目标场景可以适当提升到 960但显存占用会增大。batch批大小根据显存调整。8GB 显存跑 YOLOv8s 建议 batch 不超过 32。device0表示第一张 GPUcpu表示 CPU 训练。训练过程中日志会打印每个 epoch 的 mAP、precision、recall 等指标。训练结束后最优权重保存在runs/detect/train/weights/best.pt。如果你只是想快速验证整套系统流程先不要训练直接用官方预训练权重测试yolo predict modelyolov8s.pt sourcetest.jpg5. PyQt5 系统设计与界面实现现在进入系统核心部分。这套人体检测识别计数系统采用 PyQt5 作为桌面界面框架整体结构分为三层层级组件职责界面层PyQt5 主窗口显示视频画面、按钮控制、计数结果展示逻辑层检测线程、计数逻辑调用 YOLOv8 模型推理统计人数数据层输入源管理支持图片文件、视频文件、摄像头、RTSP 流界面布局参考如下左侧视频/图片显示区域。右侧检测结果信息区包括总人数、当前帧人数、检测耗时。底部操作按钮包括打开图片、打开视频、打开摄像头、停止检测、导出结果。顶部菜单模型选择、置信度阈值设置、IOU 阈值设置。这里直接给出主窗口的核心代码框架import sys import cv2 import numpy as np from PyQt5.QtCore import QThread, pyqtSignal, Qt from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import ( QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QFileDialog, QSpinBox, QDoubleSpinBox, QMessageBox ) from ultralytics import YOLO class DetectThread(QThread): 检测线程避免在 UI 线程中执行推理导致界面卡顿 frame_signal pyqtSignal(object, int, float) # 处理后的帧、人数、耗时 def __init__(self): super().__init__() self.model None self.cap None self.running False self.conf_thres 0.25 self.iou_thres 0.45 def load_model(self, model_path): self.model YOLO(model_path) def set_video_source(self, source): source 可以是图片路径、视频路径、摄像头索引或 RTSP 地址 if isinstance(source, int): self.cap cv2.VideoCapture(source) else: self.cap cv2.VideoCapture(source) def run(self): self.running True while self.running: ret, frame self.cap.read() if not ret: break results self.model(frame, confself.conf_thres, iouself.iou_thres, verboseFalse) annotated_frame results[0].plot() person_count 0 for box in results[0].boxes: cls int(box.cls[0]) if cls 0: # person person_count 1 self.frame_signal.emit(annotated_frame, person_count, results[0].speed) def stop(self): self.running False检测线程通过frame_signal把处理后的帧、人数和推理耗时发回主界面主界面再渲染到 QLabel 上。关键点在于推理绝对不能放在主界面线程里做否则视频流一帧要卡几百毫秒界面直接假死。主窗口类核心代码如下class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(YOLOv8 人体检测识别计数系统) self.setMinimumSize(1280, 720) # 主布局 self.video_label QLabel(请打开视频或摄像头) self.video_label.setAlignment(Qt.AlignCenter) self.video_label.setStyleSheet(background-color: #1e1e1e; color: white;) self.count_label QLabel(当前人数: 0) self.time_label QLabel(检测耗时: 0 ms) self.open_image_btn QPushButton(打开图片) self.open_video_btn QPushButton(打开视频) self.open_camera_btn QPushButton(打开摄像头) self.stop_btn QPushButton(停止检测) self.export_btn QPushButton(导出结果) # 参数控制 self.conf_spin QDoubleSpinBox() self.conf_spin.setRange(0.05, 0.95) self.conf_spin.setValue(0.25) self.conf_spin.setSingleStep(0.05) self.iou_spin QDoubleSpinBox() self.iou_spin.setRange(0.05, 0.95) self.iou_spin.setValue(0.45) self.iou_spin.setSingleStep(0.05) # 检测线程 self.detect_thread DetectThread() self.detect_thread.frame_signal.connect(self.update_frame) self._init_ui() def _init_ui(self): central_widget QWidget() self.setCentralWidget(central_widget) layout QHBoxLayout(central_widget) # 左侧视频区域 left_layout QVBoxLayout() left_layout.addWidget(self.video_label) layout.addLayout(left_layout, stretch4) # 右侧控制区域 right_layout QVBoxLayout() right_layout.addWidget(self.count_label) right_layout.addWidget(self.time_label) right_layout.addWidget(self.open_image_btn) right_layout.addWidget(self.open_video_btn) right_layout.addWidget(self.open_camera_btn) right_layout.addWidget(self.stop_btn) right_layout.addWidget(self.export_btn) right_layout.addWidget(QLabel(置信度阈值:)) right_layout.addWidget(self.conf_spin) right_layout.addWidget(QLabel(IOU 阈值:)) right_layout.addWidget(self.iou_spin) right_layout.addStretch() layout.addLayout(right_layout, stretch1) # 信号连接 self.open_image_btn.clicked.connect(self.open_image) self.open_video_btn.clicked.connect(self.open_video) self.open_camera_btn.clicked.connect(self.open_camera) self.stop_btn.clicked.connect(self.stop_detection) self.export_btn.clicked.connect(self.export_results) def update_frame(self, frame, count, speed): 将 OpenCV BGR 帧转换为 QImage 并显示 rgb_frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb_frame.shape bytes_per_line ch * w q_img QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888) pixmap QPixmap.fromImage(q_img) pixmap pixmap.scaled( self.video_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation ) self.video_label.setPixmap(pixmap) self.count_label.setText(f当前人数: {count}) self.time_label.setText(f检测耗时: {speed[inference]:.1f} ms) def open_image(self): path, _ QFileDialog.getOpenFileName( self, 选择图片, , 图片文件 (*.jpg *.png *.bmp) ) if path: self.detect_thread.requestInterruption() frame cv2.imread(path) results self.detect_thread.model(frame, confself.conf_spin.value(), iouself.iou_spin.value()) annotated results[0].plot() person_count sum(1 for box in results[0].boxes if int(box.cls[0]) 0) self.update_frame(annotated, person_count, results[0].speed)图中代码是简化的主窗口。实际使用时建议把模型路径、输入源、线程状态管理写得更严谨比如打开摄像头前先停止当前检测线程再重新创建。6. 功能测试与效果验证6.1 图片检测测试测试目的验证模型能否正确识别图片中的行人并统计数量。操作步骤启动系统点击打开图片。选择一张人流较多的图片。观察右侧人数统计和检测耗时。预期结果画面中出现的人体都被矩形框标出右侧显示的人数和图片中实际人数基本一致。判断标准检测出的人数误差是否在可接受范围内。如果漏检严重降低置信度阈值如果误检多提高置信度阈值。6.2 视频文件检测测试测试目的验证系统在连续帧上的稳定性和计数变化。操作步骤点击打开视频选择一个 mp4 文件。观察视频播放过程中人数统计是否实时更新。注意画面是否卡顿。预期结果视频流畅播放每帧画面中的人数随着行人进出而变化。判断标准视频处理帧率能否达到 15 FPS 以上。如果帧率太低考虑换更小的模型yolov8n或降低输入分辨率。6.3 摄像头实时检测测试测试目的验证摄像头输入源的实时性。操作步骤点击打开摄像头。在摄像头前走动观察检测框是否跟随人体移动。记录检测耗时。预期结果系统能以准实时的速度对摄像头画面进行检测。判断标准从画面变化到检测框更新的延迟是否在可接受范围内。CPU 推理延迟会明显高于 GPU。6.4 密集场景测试这是这套系统的核心功能验证。选择一张人流非常密集的图片比如地铁通道、景区门口观察小目标漏检情况。远处的人体很小模型能否识别出来。人群互相遮挡时能否区分不同个体。计数结果与人工计数的差距。如果密集场景效果差优先在更高分辨率下推理imgsz960或imgsz1280或者使用 YOLOv8m/YOLOv8l 等更大的模型。7. 接口 API 与批量任务如果你想把检测能力集成到其他系统里可以单独封装一个检测服务。这里给两个方案。7.1 Python 函数封装最简单的 API 就是一个函数from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) def detect_persons(image_path, conf_thres0.25, iou_thres0.45): results model(image_path, confconf_thres, iouiou_thres, verboseFalse) boxes results[0].boxes persons [] for box in boxes: cls_id int(box.cls[0]) if cls_id 0: x1, y1, x2, y2 box.xyxy[0].tolist() conf float(box.conf[0]) persons.append({ bbox: [x1, y1, x2, y2], confidence: conf }) return {total: len(persons), persons: persons}调用result detect_persons(crowd.jpg) print(总人数:, result[total])7.2 HTTP 接口服务用 FastAPI 把检测封装成 HTTP 服务方便跨语言调用from fastapi import FastAPI, UploadFile, File import cv2 import numpy as np from ultralytics import YOLO app FastAPI() model YOLO(runs/detect/train/weights/best.pt) app.post(/detect) async def detect(file: UploadFile File(...)): contents await file.read() nparr np.frombuffer(contents, np.uint8) img cv2.imdecode(nparr, cv2.IMREAD_COLOR) results model(img, verboseFalse) persons [] for box in results[0].boxes: if int(box.cls[0]) 0: persons.append({ bbox: [float(v) for v in box.xyxy[0]], confidence: float(box.conf[0]) }) return {total: len(persons), persons: persons}启动服务uvicorn api_server:app --host 0.0.0.0 --port 8000测试请求curl -X POST -F filetest.jpg http://127.0.0.1:8000/detect7.3 批量图片检测批量处理任务建议加上进度记录和失败重试逻辑。核心思路遍历目录下所有图片逐张推理结果保存到输出目录并按原文件名命名同时生成一个 CSV 汇总表import csv import os from pathlib import Path from ultralytics import YOLO model YOLO(yolov8s.pt) input_dir Path(./test_images) output_dir Path(./output_images) output_dir.mkdir(exist_okTrue) results_csv [] for img_path in input_dir.glob(*.jpg): try: results model(img_path, conf0.25, verboseFalse) annotated results[0].plot() output_path output_dir / img_path.name cv2.imwrite(str(output_path), annotated) person_count sum(1 for box in results[0].boxes if int(box.cls[0]) 0) results_csv.append([img_path.name, person_count]) print(f{img_path.name}: {person_count} people) except Exception as e: print(f{img_path.name}: FAILED - {e}) with open(batch_results.csv, w, newline) as f: writer csv.writer(f) writer.writerow([image, person_count]) writer.writerows(results_csv)批量任务做得再完善一点可以加多线程并行推理或者将任务拆分成多个批次交给 GPU 排队处理。8. 资源占用与性能观察资源占用是实际部署时最需要关心的部分。虽然没有统一的数字可以套用但可以给出观察方法和调优方向。8.1 显存占用怎么看训练和推理时用 NVIDIA 官方工具查看nvidia-smiLinux 上还可以用实时监控watch -n 1 nvidia-smiWindows 上 Task Manager 的 GPU 页签也能看到显存占用。8.2 模型大小对性能的影响模型参数量推理速度精度适用环境YOLOv8n最小最快较低嵌入式设备、实时性要求高YOLOv8s小较快中上消费级 GPU、通用场景YOLOv8m中中等较高高精度需求YOLOv8l/x大较慢高服务器端离线处理选择建议实时视频流优先 yolov8s 或 yolov8n密集小目标图片优先 yolov8m 以上并结合 imgsz960 甚至 1280。8.3 推理性能关键参数输入分辨率直接决定推理速度和显存占用。640 和 1280 相比推理时间可能差 3~4 倍。batch size 对单张图片推理没有意义但对批量任务影响很大。GPU 在 batch 越大时效率越高。CPU 推理时输入分辨率对速度的影响更大建议密集场景先用预处理裁切而不是整张大图直接推理。8.4 PyQt5 界面性能优化视频流界面卡顿有两个常见原因一是推理线程耗时太长二是 QImage 转换和 QPixmap 缩放消耗过多 CPU。优化方向推理线程只负责推理和画框不负责界面缩放。显示帧进行降采样。比如推理分辨率是 1280显示时缩放到 720p。使用QLabel.setPixmap前先判断画面尺寸是否有变化避免重复缩放。帧率控制不是每帧都要做检测可以隔帧检测界面显示原帧和最近一次检测结果。9. 常见问题与排查方法实际部署中遇到最多的问题集中在这几类问题现象可能原因排查方式解决方案安装 ultralytics 失败Python 版本过高或依赖冲突查看 pip 报错日志使用 Python 3.9~3.11重新创建虚拟环境CUDA available 为 FalsePyTorch 版本和驱动不匹配运行nvidia-smi查看驱动版本安装匹配 CUDA 版本的 PyTorch训练时显存不足batch 太大或 imgsz 太大查看 nvidia-smi 显存占用降低 batch 或 imgsz开启梯度累积检测漏检严重置信度阈值过高、模型过小、输入分辨率不足查看置信度分布降低 conf 到 0.15换大模型提高 imgsz误检多置信度阈值太低、场景复杂查看误检框置信度提高 conf增加负样本训练视频流卡顿推理耗时太长、显示刷新阻塞统计每帧推理耗时换小模型、降分辨率、隔帧检测PyQt5 界面打不开依赖缺失或 Python 环境问题终端运行看报错重装 PyQt5检查 QT_QPA_PLATFORM摄像头打不开摄像头索引错误、权限不足用 cv2.VideoCapture 单独测试检查设备索引Windows 可尝试 0/1/2批量任务中途失败单张图片损坏、内存溢出查看异常日志增加 try/except逐张保存结果模型文件缺失权重路径错误或未下载完成检查路径和文件大小重新下载权重确认路径正确重点说两个常见坑第一个是 PyTorch 和 CUDA 版本不匹配。很多用户装完 PyTorch 后发现torch.cuda.is_available()是 False。解决方法是先查驱动支持的 CUDA 版本再安装对应 PyTorch。最新的 NVIDIA 驱动一般能兼容 CUDA 12.x 系列直接安装 cu121 或 cu124 的 PyTorch 通常没问题。第二个是 PyQt5 界面闪退或空白。这个问题在高 DPI 缩放和多显示器场景下很常见。可以在主程序入口加上import os os.environ[QT_AUTO_SCREEN_SCALE_FACTOR] 1如果画面闪烁或显示异常可以尝试调整QImage.Format或检查bytes_per_line是否计算正确。10. 最佳实践与使用建议这套系统从原型到真实可用中间还有很多细节。结合我的经验给几条工程化建议。第一第一次跑通流程用最小配置。先拿一张图片用官方预训练模型跑通 PyQt5 界面再逐步加视频、摄像头、自定义模型。一步到位容易出问题排查起来也麻烦。第二模型、数据集、输出结果分目录管理。推荐目录结构project/ ├── models/ # 训练好的权重文件 ├── dataset/ # 训练数据集 ├── images/ # 测试图片 ├── videos/ # 测试视频 ├── runs/ # YOLOv8 训练和推理日志 ├── ui/ # PyQt5 界面代码 └── main.py # 主程序入口第三批量任务一定要加日志。每一张图片的处理状态、结果、耗时都记录到日志文件里。处理几千张图片时没有日志根本没法定位是哪些图片出了问题。第四接口服务要限制访问范围。如果封装成 HTTP API至少设置--host 127.0.0.1只允许本机访问或者加上简单的 Token 验证。直接暴露在公网非常危险。第五涉及人脸、声音、版权素材时必须确认授权。这套系统检测的是行人整体不涉及人脸识别但在公共区域部署仍然需要符合当地监控和数据管理规定。不要在未经授权的情况下采集和存储可识别个人的数据。第六发布或商用前要做效果复核。训练集上的指标再好也不代表现场效果没问题。拿一段真实场景的视频跑一遍统计误检率、漏检率再决定是否上线。11. 总结与下一步这套基于 YOLOv8 PyQt5 的人体检测系统最值得尝试的点是它把模型训练、桌面界面和实际业务场景串在了一条完整的链路上。从数据集准备、模型微调到 PyQt5 界面开发、视频流检测、批量任务处理每一步都是可落地的。最先要验证的功能是图片检测和视频检测的基础流程。用官方预训练模型跑通后再换自己的数据集微调。最容易踩的坑是环境配置阶段的 CUDA 版本不匹配以及 PyQt5 界面渲染的性能瓶颈。后续可以继续扩展的方向也很多接入更复杂的跟踪算法如 ByteTrack 或 DeepSORT 实现跨帧人数统计和轨迹绘制增加 ROI 区域设定只统计指定区域的人群密度接入 RTSP 流做多路摄像头并发处理或者把 PyQt5 界面替换成 Web 界面统一管理多台检测设备。这套系统的价值不只是演示目标检测而是提供了一个完整的多功能桌面应用开发模板值得你动手试一试。