☰
YOLOv8实战:智慧校园人脸识别与公路车辆检测落地
2026/9/28 12:20:09 网站建设 项目流程

简介:这是一份基于YOLOv8的智慧校园人脸识别与公路汽车检测综合项目,面向计算机视觉入门与进阶学习者,适合毕业设计、课程设计、大作业或工程实训。项目实现校园门口学生出入场景的人脸识别:采用YOLOv8模型检测并跟踪人脸,通过dlib特征提取模型生成128维特征,与预存学生特征比对,识别成功为绿色,未识别为红色,并统计人数;同时内置公路汽车检测模块。配套代码与模型权重齐全,包含完整运行脚本、预训练模型、测试视频、人脸样本图片和说明文档,便于直接复现识别与检测效果,也支持二次开发。资源包共34个文件,压缩后约334MB,以脚本、模型、图片、视频为主,可参考其数据组织与检测流程迁移至其他目标检测任务。已有459人学习浏览,适合初学者作为完整实践案例,也适合进阶者优化算法。

1. 先想清楚:YOLOv8怎么同时接住人脸识别和汽车检测

基于YOLOv8的智慧校园人脸识别和公路汽车检测,听起来是两个项目,其实是一条技术主线:用同一个检测框架覆盖两类差异极大的视觉目标。智慧校园场景里,门口门禁机要认出刷卡人脸,校外公路要统计进出车辆,这两个需求经常同时出现在一份招标方案里。YOLOv8的价值在于,人脸检测和车辆检测本质上都是目标检测,数据标注、训练、部署流程可以完全复用;人脸识别只需把检测框喂给特征提取模型,车辆检测则直接做跟踪和计数。这个方案适合正在做毕业设计、校安集成或边缘部署的工程师,尤其是想用一套代码同时交付两个场景的人。下面从环境搭建、数据集处理、训练调参、RK3588部署到避坑,按我实际落地的顺序展开。

2. 环境与数据:Ubuntu 20.04 CPU版跑通,再用Labelme数据喂给YOLOv8

很多新手上来就装CUDA版PyTorch,光在驱动问题上就能卡一天。我的做法是先在Ubuntu 20.04上把CPU版环境跑通,确认YOLOv8能正常出框,再做数据工程。别小看这一步,它能帮你在没有GPU的机器上完成标注质量抽检和推理验证,正式训练再丢到有卡的机器上。

2.1 Ubuntu 20.04搭建YOLOv8环境CPU版本的最小命令

先创建独立Python虚拟环境,避免弄乱系统Python。YOLOv8的核心包是ultralytics,环境搭建比YOLOv5时代简单许多:

mkdir -p ~/yolov8-project && cd ~/yolov8-project python3 -m venv yolov8-env source yolov8-env/bin/activate pip install --upgrade pip pip install ultralytics

这段命令的逻辑是:python3 -m venv yolov8-env创建一个隔离的Python环境,source yolov8-env/bin/activate激活它,pip install ultralytics会顺带装上当前平台匹配的torch/torchvision CPU版。如果你的机器没有NVIDIA显卡,这个安装方式就是最省事的;如果有显卡,需要先装好驱动和CUDA,再从PyTorch官网选择对应CUDA版本的安装命令,而不是直接pip install torch,否则会装成CPU版而不自知。

装完后用一条预测命令验证环境:

yolo predict model=yolov8n.pt source=bus.jpg

第一次运行会自动下载yolov8n.pt预训练权重,并下载一张示例图片bus.jpg进行推理。看到输出里出现几个检测框坐标和类别名,环境就算通了。CPU推理一张普通图片约几百毫秒到一两秒,做验证够用,做实时视频流就不太现实。

这里还经常有人问GTX1660Ti能不能跑YOLOv8。能跑,但显存6GB要克制一点:模型选yolov8n,imgsz设640以下,batch不超过8,训练慢一点但能出结果。

2.2 两个场景的数据集怎么凑:公开集、自采和标注

人脸检测的公开数据集最常见的是WIDER Face、FDDB,但标注格式不一样,WIDER Face给的是txt格式的人脸框坐标,需要先转成YOLO格式。车辆检测常用的有UA-DETRAC、BDD100K,也可以自己抽帧标一套。实际项目里我更推荐先用公开集做预训练,再用现场摄像头抽几百帧人工标注,用来消除场景偏差:校园门口的光线、公路摄像头的俯视角度,和公开数据差别不小。

人脸识别门禁系统要注意一件事:YOLOv8在检测阶段只需要一个face类,识别谁是谁是后面特征比对的事。公路车辆检测则根据需求定义类别,常见的是car、bus、truck、motorcycle。类别不要一开始就定太多,每加一个类,标注成本和漏检率都可能上涨。

还有一个尺度问题:公路场景的远处车辆可能只有16x16像素,训练输入尺寸尽量用640起步,有条件用1280,同时依赖Mosaic增强把小目标拼进大图。人脸检测同样要注意,门禁机上人脸占画面比例不大,输入尺寸不能压得太低。

2.3 用Labelme标注并转成YOLO格式:转换脚本与坐标边界坑

Labelme生成的是多边形点集JSON,YOLO需要的是归一化的中心点和宽高。这个转换看起来简单,实际有三个坑:一是部分JSON里imageWidth/imageHeight字段缺失;二是多边形点集顺序不固定;三是坐标刚好贴边时归一化值可能越界。我常用这样一个转换脚本:

import json import os import cv2 import glob class_names = ['face'] # 车辆检测改成 ['car', 'bus', 'truck'] def convert_labelme_to_yolo(json_path, out_dir): with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) # 优先从文件系统读图,避免JSON缺少宽高字段 img_path = os.path.join(os.path.dirname(json_path), data['imagePath']) img = cv2.imread(img_path) if img is None: print('图片读取失败:', img_path) return img_h, img_w = img.shape[:2] base_name = os.path.splitext(os.path.basename(json_path))[0] out_txt = os.path.join(out_dir, base_name + '.txt') 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) # 过滤无效框 if x_max <= x_min or y_max <= y_min: print('跳过无效框:', json_path, label) continue x_center = (x_min + x_max) / 2.0 / img_w y_center = (y_min + y_max) / 2.0 / img_h box_w = (x_max - x_min) / img_w box_h = (y_max - y_min) / img_h # 边界裁剪,防止训练时坐标越界 x_center = min(max(x_center, 0.0), 1.0) y_center = min(max(y_center, 0.0), 1.0) box_w = min(max(box_w, 0.0), 1.0) box_h = min(max(box_h, 0.0), 1.0) lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}") if lines: with open(out_txt, 'w') as f: f.write('\n'.join(lines) + '\n') for json_path in glob.glob('labelme_data/*.json'): convert_labelme_to_yolo(json_path, 'yolo_labels')

逻辑说明:脚本用cv2读取原图拿宽高,而不是依赖JSON里的字段,兼容性更好。然后遍历每个标注形状,取多边形外接矩形,再转成YOLO格式。坐标裁剪和无效框过滤是为了防止训练时出现NaN或loss跳变。

参数说明:class_names的顺序就是类别ID顺序,必须和后面data.yaml里的names保持一致。如果转换后txt数量对不上图片数量,多半是imagePath是绝对路径但文件被移动过,优先检查路径。另外要注意,Labelme里如果是旋转框或分割掩膜,这个脚本只取外接矩形;车辆如果非要旋转框,得用YOLOv8-OBB,那是另一套流程。

转换完成后按这个目录结构放好:

datasets/face/ images/train/xxx.jpg images/val/xxx.jpg labels/train/xxx.txt labels/val/xxx.txt

对应的face.yaml:

path: datasets/face train: images/train val: images/val nc: 1 names: ['face']

注意path是到datasets/face的路径,train和val是相对这个路径的图片目录,YOLOv8会自动到同样的labels目录找txt。

3. 训练自己的数据集:读懂结构、参数和损失函数曲线

数据集准备好后,训练不是敲一条命令那么简单。很多人直接拿默认参数训练,效果差又不知道改哪里。我建议先花十分钟理解YOLOv8的网络结构图和关键参数,再动手。

3.1 YOLOv8网络结构图:检测头为什么对这两个任务都友好

YOLOv8的结构可以拆成三块:Backbone、Neck和Head。Backbone用CSPDarknet提取多尺度特征;Neck是PAN-FPN,把高层语义和低层细节融合;Head采用解耦头,分类和回归分成两个分支。相比YOLOv5,YOLOv8改成anchor-free,省去一堆anchor超参,对密集小目标更友好。人脸和车辆检测都是目标检测,网络结构不需要改,改nc类别数和数据就行。

对人脸识别门禁而言,YOLOv8只负责“脸在哪”,不负责“是谁”。如果直接把每个学生设成一个类别,类别会随人员变动无限膨胀,不现实。正确做法是YOLOv8出框后,再送人脸特征模型做身份比对。但如果只是实验室里做班级小规模识别,也可以训练一个YOLOv8分类模型,输入裁剪后的人脸,输出学生ID,模型小且能跑通。检测和识别分离的设计,加人、换人时不需要重新训练检测模型。

3.2 YOLOv8训练自己的数据集:train命令与参数含义

第一次训练建议用yolov8n作为预训练权重,文件小、收敛快。下面是一个可跑的train命令:

yolo train model=yolov8n.pt data=datasets/face.yaml \ epochs=100 imgsz=640 batch=16 device=0 \ workers=4 optimizer=AdamW lr0=0.001 \ close_mosaic=10 cos_lr=True

参数说明:model指定预训练权重,yolov8n.pt会自动下载;data指向自己的data.yaml;epochs给100,配合早停够用;imgsz=640是训练输入尺寸,公路场景小目标多可以提至960或1280,但显存占用会成倍上涨;batch=16是每批图片数,显存不够就降;workers控制数据加载进程数,CPU环境设2-4;optimizer=AdamW收敛更稳,SGD调好了也强但更依赖学习率;lr0=0.001是AdamW常见起点;close_mosaic=10表示最后10个epoch关闭Mosaic增强,因为Mosaic合成的图和真实场景差距太大,最后几轮关掉能减少分布偏移;cos_lr=True用余弦退火,后期loss下探更平稳。

如果是GTX1660Ti这类6GB显卡,imgsz=640、batch=8、yolov8n能跑,但每个epoch时间会偏长。CPU版我也试过训练,一个epoch能跑到二十分钟,所以我只把CPU版用于环境验证和推理,正式训练还是另找GPU。训练结束后,runs/detect/train/weights/下会生成best.pt和last.pt,部署时默认用best.pt。

3.3 用损失函数曲线判断训练状态:结果文件results.csv

YOLOv8训练过程不会实时弹出图表,所有指标都写进runs/detect/train/results.csv。我习惯在训练途中就画图看状态,而不是等结束。画图脚本不长:

import pandas as pd import matplotlib.pyplot as plt results = pd.read_csv('runs/detect/train/results.csv') results.columns = [c.strip() for c in results.columns] fig, axes = plt.subplots(2, 2, figsize=(12, 8)) axes[0, 0].plot(results['epoch'], results['train/box_loss'], label='train box') axes[0, 0].plot(results['epoch'], results['val/box_loss'], label='val box') axes[0, 0].set_title('box loss') axes[0, 0].legend() axes[0, 1].plot(results['epoch'], results['train/cls_loss'], label='train cls') axes[0, 1].plot(results['epoch'], results['val/cls_loss'], label='val cls') axes[0, 1].set_title('cls loss') axes[0, 1].legend() axes[1, 0].plot(results['epoch'], results['train/dfl_loss'], label='train dfl') axes[1, 0].plot(results['epoch'], results['val/dfl_loss'], label='val dfl') axes[1, 0].set_title('dfl loss') axes[1, 0].legend() axes[1, 1].plot(results['epoch'], results['metrics/mAP50(B)']) axes[1, 1].set_title('mAP50') plt.tight_layout() plt.savefig('loss_curve.png')

这段代码先读results.csv,清理列名空格,再分别画出box_loss、cls_loss、dfl_loss和mAP50。判断标准很简单:训练loss和验证loss同步下降是小幅波动是正常的;如果验证loss先降后升、mAP50同步回落,就是过拟合,回退到验证loss最低的epoch权重即可。如果训练loss一开始就降不下去,先查标注格式;如果出现nan,立刻停,查数据和学习率。

3.4 从检测框到“认人”:人脸识别门禁系统的接法

人脸检测模型训练好后,门禁系统不能只画框。我的标准接法是:YOLOv8输出人脸框,先用OpenCV裁剪出人脸图,再交给轻量特征模型提取向量,最后与注册库算余弦相似度。裁剪这一步直接决定识别率:

import cv2 from ultralytics import YOLO detector = YOLO('runs/detect/train/weights/best.pt') def crop_aligned_face(frame, box): x1, y1, x2, y2 = map(int, box) w = x2 - x1 h = y2 - y1 pad_w = int(w * 0.1) pad_h = int(h * 0.15) x1 = max(0, x1 - pad_w) y1 = max(0, y1 - pad_h) x2 = min(frame.shape[1], x2 + pad_w) y2 = min(frame.shape[0], y2 + pad_h) face = frame[y1:y2, x1:x2] face = cv2.resize(face, (112, 112)) return face results = detector(frame)[0] for box in results.boxes.xyxy.cpu().numpy().tolist(): face = crop_aligned_face(frame, box) # 把face送进特征模型得到embedding

逻辑说明:YOLOv8默认框刚好包住五官,直接裁剪会损失额头和下颚;这里按框宽高的10%和15%向外扩,再缩放到112x112,与常见人脸识别模型输入对齐。如果识别效果仍不理想,可以做关键点对齐,但需要额外标注眼睛、嘴角,工程量翻倍。门禁策略上还要加阈值判定:余弦相似度大于0.6放行,小于0.4拒绝,中间区间做人脸质量筛选,避免把模糊脸拿去比对。

4. 部署到门禁机和公路摄像头:RK3588转换与检测落地

训练出来的模型最终要跑在设备上。人脸门禁和公路车辆检测的部署形态、算力选型和优化方向不一样,不能一刀切。

4.1 部署形态和算力选型

人脸识别门禁机一般是一台带屏幕和摄像头的嵌入式设备,常见算力平台是RK3588,带6TOPs NPU,可以跑一个yolov8n加一个轻量人脸特征模型。公路车辆检测如果是多路摄像头,通常放在机房GPU服务器或高性能边缘盒子上,跑yolov8s/m甚至yolov8l,再接跟踪算法。一个简化的选型表:

场景路数建议模型建议平台帧率目标
人脸识别门禁1YOLOv8n + 特征模型RK3588 / RV110610-20 FPS
公路车辆检测4-8YOLOv8s/m带GPU服务器或RK3588集群单路15-25 FPS

这张表的核心是:门禁是单人近距离场景,模型越小越好;公路是远距离多目标场景,模型容量和输入尺寸都不能太低。

4.2 RK3588部署YOLOv8:从PyTorch权重到板端推理

RK3588部署YOLOv8是目前常见的边缘方案。流程分为三步:导出ONNX,用RKNN-Toolkit2转rknn格式,板端用RKNNLite加载推理。

先导出ONNX:

yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640 opset=12

这条命令生成best.onnx,同时保留检测头的原始输出。注意:如果导出时加nms=True,会把NMS也导进去,板端代码简单但兼容性差,我更倾向导出不带NMS的版本,自己在板端做解码。

接着转RKNN:

from rknn.api import RKNN rknn = RKNN(verbose=True) # mean/std要与训练预处理一致,YOLOv8默认是除以255 rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rk3588') ret = rknn.load_onnx(model='best.onnx') if ret != 0: print('load onnx failed') exit(1) # do_quantization=True 做INT8量化,加速明显 ret = rknn.build(do_quantization=True, dataset='calib_dataset.txt') if ret != 0: print('build rknn failed') exit(1) rknn.export_rknn('best.rknn')

逻辑说明:RKNN的mean_values和std_values是在模拟训练时的归一化。YOLOv8官方推理会把像素除以255,所以这里std填255、mean填0。calib_dataset.txt每行写一张jpg图片路径,建议从验证集挑50-100张,覆盖白天、黑夜、正脸、侧脸,量化效果才稳。转换后如果精度下降明显,先看校准图多样性,或者把do_quantization改为False跑FP16再评估。

板端推理代码:

from rknnlite.api import RKNNLite rknn_lite = RKNNLite() rknn_lite.load_rknn('best.rknn') rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0) # 输入前把BGR转RGB,并resize到640 img = cv2.cvtColor(cv2.imread('test.jpg'), cv2.COLOR_BGR2RGB) img = cv2.resize(img, (640, 640)) outputs = rknn_lite.inference(inputs=[img])

说明:RKNNLite推理得到的是多个feature map,还需要在板端做YOLOv8解码,包括置信度计算、bbox解码和NMS。这段后处理不短,建议先用项目里验证过的解码脚本,不要轻视。core_mask可以指定NPU核心,多路并发时各分一核。

4.3 公路汽车检测的落地技巧:跟踪、计数与参数调节

公路场景检测跑通容易,跑好要调细节。第一,推理参数不能照搬默认值:远距离小目标置信度天然偏低,conf_thres一般从0.25降到0.2;如果公交车和卡车经常重叠,把iou_thres从0.45降到0.4,减少NMS误删。第二,要做车辆计数时,单帧检测不够,通常用ByteTrack或BoT-SORT把检测框关联成轨迹,再在画面上设虚拟线,用track id跨线计数。ultralytics封装了跟踪推理:

yolo track model=runs/detect/train/weights/best.pt source=road.mp4 \ conf=0.2 iou=0.45 imgsz=1280 persist=True

这条命令会启用跟踪模式,persist=True表示跨帧保留track id,imgsz=1280能明显提升小目标召回,但推理时间变长,要实测。如果视频里没有需要跟踪的目标,也可以不加persist。第三,固定机位的公路监控可以加ROI区域,把栏杆、树枝框起来屏蔽,减少误检。第四,训练数据里必须混入雨雾和夜晚样本,否则夜间漏检会非常严重。

5. 避坑指南:训练、转换、识别三个环节的翻车实录

这里整理了几条我实际踩过、也帮别人排过的坑,按“现象→原因→解决”写清楚,可以直接对照排查。

5.1 训练Loss突然变NaN,模型权重直接报废

现象:训练到三十轮左右,控制台输出train/box_loss变成nan,后面所有loss全变nan,best.pt不再更新。原因:大概率是标注数据里有非法值,比如txt里类别ID超出nc范围、x_center是负数、width为0;另一种可能是学习率过高导致梯度爆炸。解决:先写脚本扫描所有标注txt,发现超出[0,1]的坐标或不存在类别ID就打印文件名;数据没问题就把lr0从0.01降到0.001,或把optimizer从SGD换成AdamW。我有一次就是这个原因,最后发现是Labelme转换脚本对空标注文件处理不当,生成了无内容的txt,被YOLOv8当成无目标图干扰了loss计算。所以空txt要么删掉,要么确保图片真的有目标。

5.2 CPU版训练慢到怀疑人生

现象:Ubuntu 20.04 CPU版在笔记本上训练yolov8n,一个epoch跑了近二十分钟,100轮要一天半。原因:CPU训练本来就不适合大卷模型,batch设置过大还会频繁交换内存,workers=0则数据加载拖后腿。解决:如果只是毕设或验证,把imgsz降到416、batch设为4、workers设4,模型用yolov8n.pt,并在命令行显式加device=cpu。但节省时间的有效方式还是租一张入门级GPU,GTX1660Ti级别就能跑yolov8n-640。我的原则是CPU只做环境冒烟和推理,训练一律上GPU,不硬扛。

5.3 ONNX转RKNN后检测框整体偏移

现象:板端RKNN推理能看到框,但框位置明显偏,大小也不对;PC上用ONNX Runtime跑同一张图却正常。原因:预处理不一致,最常见是BGR和RGB通道顺序搞反,或归一化参数和训练时不匹配;另一个被忽视的原因是量化校准图太少,只有十几张,误差在特征图上叠加。解决:固定通道顺序,转换前先用onnxruntime验证ONNX输出和PyTorch输出一致;校准图至少凑50张,并覆盖不同亮度、远近。如果偏移仍很大,改回do_quantization=False跑FP16,确认是量化损失还是代码问题。

5.4 门禁识别误识率高:把学生A认成学生B

现象:人脸识别门禁用YOLOv8检测框裁剪后直接送特征模型,现场错报率超10%。原因:检测框只包住五官,裁剪丢了下巴和发际线,送到识别模型的人脸不规整;另外YOLOv8的框在人左右晃动时会突然缩放,导致特征向量不稳定。解决:裁剪时按比例扩边,前面3.4节已经给过代码;更严格的做法是用关键点对齐,让两眼保持水平并统一尺度。放行阈值和告警阈值之间留一个“待复核”区间,能省掉大量误识投诉。这个坑在门禁系统设计里最容易翻车,最后往往不是模型问题,而是图像预处理流程太简陋。

5.5 公路夜间和雨天漏检严重,白天好用晚上摆烂

现象:车辆检测模型白天mAP50有0.92,一到傍晚或下雨天,漏检率翻倍。原因:训练集里干净场景占绝大多数,模型没见过低光照、反光和雨丝干扰;夜间尾灯让特征分布和白天完全不同。解决:标注阶段一次性加入夜间和雨天抽帧,训练时打开MixUp、HSV和gamma增强,模拟不同光照;如果现场相机有强光抑制,也可以先做图像预处理再送模型。这个坑属于数据场景覆盖,不是网络结构问题,补数据重训比调参数有效得多。

6. 上线前最后一步:用验证脚本和量化测试留下后悔药

模型部署不是训练完就结束,交付前我会做两件事:固定测试集基线、量化前后对比。否则现场出了问题,你连“以前是不是更好”都无法回答。

6.1 用yolo val和自定义脚本记录模型基线

最小验证命令是这样:

yolo val model=runs/detect/train/weights/best.pt data=datasets/face.yaml split=test

split=test表示用data.yaml里test对应的目录;如果没配test,默认用val。这里有一个很容易忽略的点:验证时的conf和iou参数会影响mAP,保存基线时必须固定这些参数。我见过有人换了权重说效果变好,实际只是conf从0.3改成了0.2。

我只依赖mAP还不够,会额外用脚本统计误检率。运行yolo val不会告诉你“哪张图把垃圾桶当成了人”。简单做法是对固定测试集逐张推理,记录假正例数和总框数比值,这个指标比mAP更接近现场观感。

量化对比同样要走这个流程:INT8量化后的rknn权重在板端和PC端跑同一个测试集,对比每张图的框坐标和置信度。掉点小于2%-3%可以接受,超过5%就换混合量化方案,或者只量化部分层。我的一个习惯是:任何模型替换前,先把旧权重在同一天同一测试集上的结果导出成txt存档;新权重来了直接对比,不一致的地方用现场截图一张张过。测试命令和参数都写进项目README,防止三个月后自己都忘了当初怎么跑的。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询