简介:这是基于YOLOv8的人脸检测项目实战资源,面向毕业设计、期末大作业、课程设计等场景,也适合希望快速上手目标检测的初学者。代码覆盖数据加载、模型构建、训练评估、推理导出与可视化等完整环节,关键位置附有注释,降低了阅读门槛。压缩包共19个文件,以14个Python脚本为核心,另有2个shell脚本用于快捷启动训练、1个pt格式的预训练权重、1份Markdown说明文档及辅助配置,整体大小约11.79MB,便于快速下载和部署。目前已有426人进行学习下载,项目经过严格调试,可保证在常见环境下稳定运行。系统前后端代码齐全,界面简洁、操作方便,下载后简单配置即可用于项目演示、毕业答辩或二次开发,实用性和完成度都比较高,能够有效节省从零搭建的时间。
1. 基于 YOLOv8 的人脸检测项目:下载即用不等于开箱即用
人脸检测是计算机视觉里最常拿来练手的任务,而 YOLOv8 凭借简洁的 API 和不错的推理速度,成了很多人做项目的首选。「基于 YOLOv8 的人脸检测项目(下载即用)」通常会打包好权重、数据集脚本和推理入口,看着解压就能跑。可真按 README 操作一遍,环境版本冲突、数据集路径失效、权重和源码不匹配,一个都躲不开。我会把整条链路拆开讲:先配好 YOLOv8 环境跑通第一张图,再用自己的数据集训练并看懂损失函数曲线图,最后把模型部署到 rk3588 这类边缘设备上。适合手里只有一张 GTX1660Ti 也敢动手的开发者,新手按步骤走,熟手直接看避坑记录。
2. 搭建 YOLOv8 环境与跑通最小检测:版本搭配、目录结构与首跑命令
下载即用的项目,九成问题出在环境而不是代码。项目包里通常只有一个 requirements.txt,但里面的依赖版本是作者在他自己的机器上测过的,换一台电脑,显卡驱动不一样,可能就装不上或者跑不起来。我从不建议把依赖直接装进全局环境,先建一个干净的虚拟环境,把 Python、PyTorch、CUDA 三者的版本关系锁死,再谈跑通。这也对应很多人搜的 yolov8 环境配置——配置本身不难,难的是版本之间互相咬合的关系。
2.1 环境版本搭配:Python、PyTorch 与 CUDA 的匹配关系
ultralytics 是 YOLOv8 的官方实现包,训练、验证、导出、推理都走同一个 API。它对 Python 版本要求不算苛刻,3.8 到 3.10 都能用,但我一般固定 Python 3.10:老版本在 Windows 上配 OpenCV 容易出幺蛾子,太新的版本又可能遇到某个依赖没有预编译 wheel。PyTorch 的版本选择要看显卡驱动支持到哪个 CUDA。以常见的 GTX1660Ti 为例,显存只有 6GB,算力 7.5,CUDA 11.8 的驱动完全够用,没必要追 cu121,新版本带来的收益抵不过环境兼容性的麻烦。
# 1. 创建独立虚拟环境,Python 3.10 对 ultralytics 8.x 兼容性最好 conda create -n yoloface python=3.10 -y conda activate yoloface # 2. 安装 PyTorch,1660Ti 用 CUDA 11.8 版本最稳 pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 # 3. 安装 ultralytics,会自动带上 opencv-python、numpy 等依赖 pip install ultralytics # 4. 验证 GPU 是否真的可用 python -c "import torch; print(torch.cuda.is_available())"这里有两个值得注意的点。第一,torch 和 torchvision 的版本必须成对出现,不能只装其中一个,否则导入 ultralytics 时报错基本跑不掉。第二,最后一步如果输出 False,先别急着重装,用 nvidia-smi 看驱动版本,大概率是驱动太老,升级驱动比在 conda 里折腾 CUDA 更省事。装完环境后跑一次yolo --version确认命令行入口正常,再进项目目录继续。
2.2 项目目录结构:权重、数据与脚本的对应关系
下载即用项目解压后的结构通常是这样:weights 目录放训练好的 pt 权重,dataset 目录放图片与标签,根目录有几个 Python 脚本负责推理和训练,外加一个 data.yaml 描述数据路径和类别。拿到手的第一件事不是急着跑,而是确认这个权重到底怎么来的。
from ultralytics import YOLO # 加载权重,同时验证它和当前 ultralytics 版本是否匹配 model = YOLO("weights/best.pt") print(model.names) # 若是人脸模型,应输出 {0: 'face'}如果这一步输出的是 COCO 的 80 个类别名,说明权重是官方预训练模型,不是人脸专用;如果直接抛 KeyError 或提示 zip 文件损坏,说明权重可能来自旧版 YOLOv5 或者被改过结构。常见做法是把项目里的 requirements.txt 和当前 ultralytics 版本对一下,版本差太多就升降级 ultralytics 来适配权重,而不是自己去改权重文件。
2.3 首跑最小命令:一张图验证整条链路
环境通了、权重没问题,就可以跑最小推理。ultralytics 命令行本身就够用,不用先读懂项目里的 Python 脚本。
# 单张图片验证,conf 是置信度阈值,device 指定 GPU 编号 yolo detect predict model=weights/best.pt source=test.jpg conf=0.5 device=0 # 也可以一次跑整个目录,结果统一输出到 runs/detect/predict/ yolo detect predict model=weights/best.pt source=./test_images/ conf=0.5 device=0跑完去 runs/detect/predict 看结果图。框能画出来并且置信度在 0.5 以上,说明整条链路通了。conf 首跑建议设 0.5,确认模型质量后再调低。很多项目自带的权重是在特定场景(比如办公室监控视角)上训练的,换到自拍视角可能 0.5 阈值下什么都框不出来,降到 0.25 再试往往就有框了。另外要注意,如果你机器上没装 GPU 版 torch,命令行会静默切到 CPU,速度慢十倍但不会报错,看到帧率异常时先确认 device 参数到底有没有生效。
3. 训练自己的 YOLOv8 人脸检测模型:数据集、参数与损失函数曲线
下载即用的权重看起来能用,但换到自己的摄像头角度、光照和清晰度,效果往往打对折。人脸检测任务的场景相关性极强,办公室训练出来的模型放到商场闸机口就是另一回事。要真正落地,必须用自己的数据重新训练。这一章把 yolov8 训练自己的数据集这件事拆开,讲数据准备、配置文件和训练调参。
3.1 人脸数据集准备:WIDER FACE 还是自采标注
如果只是验证流程,用公开数据集最省事。WIDER FACE 是人脸检测最常用的基准,三万多张图、接近四十万个人脸框,覆盖各种尺度、遮挡和密集场景,缺点是标注质量参差,直接训练会有不少噪声。我的做法是拿它先跑通流程,再混入自己场景的数据做最终训练。做特定项目时自采数据才是主力,标注工具用 labelImg 就能导出 YOLO 格式,一个周末标一两千张人脸,对单类检测来说足够出初版。
这里顺便说一个方向:把类别从 face 换成 helmet,同样的流程就是安全帽检测项目,这也是很多人问的 yolov8 改进思路。先别急着改网络结构,把数据管好、参数调对,效果通常比换模块来得快。
3.2 标签格式与 data.yaml:YOLO 人脸数据的组织方式
YOLO 格式的标签是每张图对应一个同名 txt 文件,每一行一个目标,格式为类别编号 x_center y_center width height,坐标全部归一化到 0 到 1。人脸只有一个类别,所以每行开头都是 0。
0 0.5123 0.4428 0.1562 0.1987如果是从 VOC 的 XML 标注转过来的,最常见的错误是忘了归一化,直接把像素坐标写进去。正确算法是把矩形中心点除以图片宽高,宽高也分别除以图片宽高。我常用下面这个函数处理:
def voc_to_yolo(xmin, ymin, xmax, ymax, img_w, img_h): x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h return x_center, y_center, width, height数据放好后写 data.yaml。注意 path 字段用相对路径,ultralytics 会以 data.yaml 所在目录为基准拼接 train 和 val。写绝对路径的话换一台机器就要改,相对路径能直接跟着项目走。
# data.yaml:路径相对于本文件所在目录 path: ./dataset train: images/train val: images/val nc: 1 names: 0: face3.3 训练启动参数与损失函数曲线解读
训练命令本身很短,参数选择影响很大。单类人脸检测我从 yolov8n 开始迁移学习,这个模型的网络结构轻、速度快,1660Ti 上完全跑得动;要更高精度可以换 yolov8m,但显存和训练时间都要翻倍。
# 从 yolov8n 预训练权重开始,迁移学习收敛快 yolo detect train model=yolov8n.pt data=data.yaml epochs=100 imgsz=640 batch=8 device=0batch 在 1660Ti 上建议 8,6GB 显存跑 640 输入尺寸时 16 很容易 OOM。imgsz 保持 640,和推理时一致,否则后面量化后精度会更难看。训练结束后结果在 runs/detect/train,weights 里有 best.pt 和 last.pt,还有一张现成的损失函数曲线图 results.png。
解读 yolov8 的损失函数曲线时,别只盯着训练集那一侧。单类检测时 cls_loss 会很快掉到接近 0,真正要盯的是 box_loss 和 val 侧 mAP50。如果 train loss 一路降、val loss 在某个 epoch 后开始回升,就是过拟合了,这时候不是继续加 epochs,而是回退到 best.pt。想自己画图的话,注意 results.csv 里的列名带斜杠,比如train/box_loss、metrics/mAP50-95。
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/detect/train/results.csv") fig, ax = plt.subplots(1, 2, figsize=(12, 4)) ax[0].plot(df["epoch"], df["train/box_loss"], label="train") ax[0].plot(df["epoch"], df["val/box_loss"], label="val") ax[0].set_title("box loss") ax[1].plot(df["epoch"], df["metrics/mAP50-95"]) ax[1].set_title("mAP50-95") plt.show()画出来的曲线如果抖得厉害,优先看 batch 大小,或者数据里人脸框数量差异是不是太大。数据侧加一些多尺度增强,训练侧把 patience 设到 30,基本能压住抖动。
4. 把 YOLOv8 部署到 RK3588:模型导出、量化与板端推理
训练出来的 pt 权重只能在 PC 上由 ultralytics 加载跑,rk3588 这类边缘设备的 NPU 不认识它。最近很多人问 yolov8 部署到 rk3588 的流程,核心就是 pt 转 ONNX,再转 RKNN,最后在板端用 rknnlite 推理。每一步都有格式和参数的讲究,走错一步后面全部白搭。
4.1 模型导出为 ONNX:固定输入尺寸与算子版本
导出这一步看似一行命令,实际决定后面能否顺利转 RKNN。我建议固定输入尺寸 640,不要用动态尺寸:动态 dim 在转 RKNN 时经常触发不支持的算子,而且板端推理一张图本来就不需要变尺寸。opset 用 12 左右,太高或太低都可能导致 RKNN 工具链解析失败。
# 导出 ONNX,imgsz 必须与训练时一致 yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640 opset=12导出后的 onnx 输出是一个大张量。输入 640 时,YOLOv8 检测头在 80×80、40×40、20×20 三个尺度上输出候选框,加起来共 8400 个,每个候选框带 4 个坐标和 1 个类别得分(单类情况),所以输出形状一般是 [1, 8400, 5] 或转置后的 [1, 5, 8400]。注意 NMS 不在模型里,后处理必须在板端自己做。
4.2 用 RKNN-Toolkit2 转成 rknn 模型并量化
转换流程在 PC 上做,板端只负责加载运行。先装 RKNN-Toolkit2,这个工具链对 numpy 版本很敏感,装完最好不要随便升级。
# PC 端安装 RKNN-Toolkit2,注意它依赖的 numpy 版本 pip install rknn-toolkit2from rknn.api import RKNN rknn = RKNN() # mean/std 要与训练时预处理一致,统一按 /255 归一化 rknn.config(target_platform="rk3588", mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]]) rknn.load_onnx(model="best.onnx") # int8 量化必须给校准图片,dataset.txt 每行一个图片路径 rknn.build(do_quantization=True, dataset="dataset.txt") rknn.export_rknn("face.rknn")转换后拿到 face.rknn,复制到板端。板端推理用 rknnlite,代码很短:
from rknnlite.api import RKNNLite rknn_lite = RKNNLite() rknn_lite.load_rknn("face.rknn") # 默认跑三核 NPU,如果有其他任务占资源可以只开一个核 rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0_1_2)4.3 板端预处理与后处理:letterbox 和 NMS 必须自己做
RKNN 推理只负责出张量,图片缩放、归一化和结果解码全部要手写。人脸检测里图片缩放要用 letterbox 保持长宽比,直接 resize 会把脸拉变形,框就不准了。我的实现是这样:
import cv2 import numpy as np def letterbox(img, size=640): h, w = img.shape[:2] scale = min(size / w, size / h) nw, nh = int(w * scale), int(h * scale) resized = cv2.resize(img, (nw, nh)) canvas = np.zeros((size, size, 3), dtype=np.uint8) # 灰边填充,注意记录 pad 偏移量 top = (size - nh) // 2 left = (size - nw) // 2 canvas[top:top + nh, left:left + nw] = resized return canvas, scale, left, top推理后拿到 [1, 8400, 5] 的输出,先按置信度阈值过滤,再做 NMS。阈值我习惯 conf 用 0.25、iou 用 0.45,和 PC 上验证时保持一致,板端效果才可对比。
boxes = outputs[0][..., :4] # 候选框坐标 scores = outputs[0][..., 4] # 单类时就是人脸得分 keep = cv2.dnn.NMSBoxes(boxes.tolist(), scores.tolist(), 0.25, 0.45)这里容易翻车的是坐标是归一化比例,画框前要乘回原图尺寸,还要减掉 letterbox 的偏移量。我第一次部署时忘了处理偏移,框整体往下偏了一块。后来把所有坐标换算封装成 postprocess 函数,输入输出都用原始图像坐标,整套流程就稳了。
5. 避坑与常见问题:YOLOv8 人脸检测实战踩坑记录
这一章是我自己做这个项目时实际踩过的坑,按阶段分成训练和数据部署两小节,每条都按现象、原因、解决的顺序写。这些坑看起来很基础,但每一个都够折腾一个晚上,对照着查能省下不少时间。
5.1 训练阶段的三个坑
坑一:训练曲线正常下降,预测时却一个框都没有。现象是 train loss 一直在掉,val 侧 mAP 却始终是 0,推理结果全空。原因多半不是模型问题,而是数据集划分出错:val 目录里的图片和 labels 里的 txt 对不上,或者 val 里根本没有对应标注,ultralytics 不会报错,只会默默把没标签的图片当负样本。解决方法是先跑一遍yolo val model=runs/detect/train/weights/best.pt data=data.yaml,看 val 侧图片总数和标注总数是否一致,再随机抽查几张图片和同名 txt 是否真的对应同一张脸。
坑二:GTX1660Ti 一启动训练就 OOM。现象是报CUDA out of memory,训练直接中断。原因是 6GB 显存扛不住默认的 batch=16 加 imgsz=640。解决方法是把 batch 降到 8 甚至 4,模型首选 yolov8n 而不是 yolov8m;如果还想要更高精度,imgsz 保持 640 不动,把提精度的事情放到数据侧去做,别拿显存硬扛。另外训练时关闭 AMP 之外的其它内存优化选项,减少显存碎片,也能降低 OOM 概率。
坑三:小脸几乎全部漏检。现象是近距离大脸能框出来,两三米外的人脸直接消失。原因有两层:一是 imgsz=640 时小脸在特征图上只占几个像素;二是训练集里小脸样本太少,模型根本没学会这个尺度。解决方法是把 imgsz 提到 960 重新训练,同时在小脸样本上做复制粘贴式增强,或者把输入图切块分别检测再合并结果。这类问题属于数据分布问题,靠调 conf 阈值是救不回来的。
5.2 数据与部署阶段的坑
坑四:训练时报标签读取错误或类别数对不上。现象是日志里出现FileNotFoundError,或者训练时显示有 2 个类别而 data.yaml 里只写了 1 个。原因是转换脚本在 VOC 转 YOLO 时把类别编号从 1 开始,而 YOLO 要求从 0 开始,标签里一旦出现 1,ultralytics 就认为存在第二类。解决方法是统一在转换脚本里把原始类别减 1,转换后写一个校验脚本,检查每个 txt 里的 class_id 是否都小于 nc,顺便把归一化后超出 0 到 1 范围的坐标过滤掉。
坑五:同一个权重在 PC 上检测正常,部署到 rk3588 后精度明显下降。现象是漏检变多、框位置偏移、置信度普遍偏低。原因是 int8 量化损失,加上校准集没选好,或者导出时 mean/std 和训练时不一致。解决方法是准备几百张覆盖各种光照角度的校准图重新量化,把归一化方式统一成 /255;如果量化后依然不可接受,就退回未量化模型或 fp16,用算力换精度。这个坑我在 rk3588 上反复折腾过,最后是训练时的预处理代码原样搬到量化脚本里才彻底解决。
6. 进阶:给 YOLOv8 人脸检测加实时界面与视频流加速
模型跑通之后,下一步就是把它变成一个能给别人演示和验收的东西。所谓 yolov8 界面设计,常见做法是 OpenCV 加 PyQt,或者用 ultralytics 自带的绘图接口加循环。我的习惯是先用 OpenCV 把实时检测跑起来,确认帧率可以接受,再考虑上 PyQt 界面。
import cv2 import time from ultralytics import YOLO model = YOLO("weights/best.pt") cap = cv2.VideoCapture(0) # 0 是本地摄像头,也可填 RTSP 地址 prev = time.time() while cap.isOpened(): ok, frame = cap.read() if not ok: break results = model.predict(frame, conf=0.25, imgsz=640, verbose=False) annotated = results[0].plot() # 用 time 差算 FPS,比看模型内部指标更直观 now = time.time() fps = 1.0 / (now - prev) prev = now cv2.putText(annotated, f"FPS: {fps:.1f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow("face detect", annotated) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()这段代码直接跑就能看到实时人脸框和 FPS。1660Ti 上单张 640 输入大约 30 毫秒,加上 OpenCV 显示,帧率能到 20 左右。想进一步压延迟有两个方向:一是把 imgsz 降到 480,人脸检测对尺寸变化不敏感,帧率提升明显;二是关掉 verbose 输出,减少 Python 层的字符串拼接开销。PyQt 界面则是另一个工程话题,推理要放到 QThread 里避免界面卡死,置信度滑条和显示区之间通过信号槽传帧,门槛比 OpenCV 循环高不少。
我自己做这类项目时有个习惯:先确认模型的 mAP 稳了再碰部署,不要一边调模型一边调量化,变量太多出了问题根本定位不了。rk3588 的 NPU 资源不是无限的,int8 量化能上就上,配合固定输入尺寸,板端帧率一般都能满足业务要求。人脸检测这类任务,数据比模型结构更能决定上限,把 WIDER FACE 当基准、自己的场景数据当主力,这条路比反复换网络模块靠谱。希望帮到你。
本文还有配套的精品资源,点击获取