☰
基于YOLOv5的智能人脸标注工具:三种模式实现高效预标注与格式导出
2026/9/30 6:30:45 网站建设 项目流程

简介:面向计算机视觉开发者与数据标注工程师的智能标注工具,基于YOLOv5实现人脸检测的自动化标注流程,解决人工逐帧框选效率低、标签格式统一难的问题。支持webcam实时采集、批量图片标注与视频帧标注,可自定义检测模型,并输出PASCAL VOC XML、MS COCO JSON、YOLO TXT等通用标签格式,覆盖常见检测任务需求。资源包共59个文件,涵盖Python源码、预训练模型、Markdown教程、示例图片及演示视频,压缩包整体78.68MB,附带清晰的分类目录,便于按需检索与二次开发。已有338人学习下载,适合具备一定Python与YOLO基础的研究者、算法工程师用于构建人脸数据集或快速验证检测效果。

1. 基于YOLOv5的智能人脸标注工具:三个模式把我从手动圈框里解放出来

先说说结论:这套基于YOLOv5的智能人脸数据集标注工具,把一份人脸检测数据集从零标注到可训练,时间大约能压到原来的三分之一甚至更少。它不只是一个 LabelImg 的替代品,而是先用训练好的检测模型做预标注,再让用户通过 webcam、图片、视频三种模式去确认和修正。对要自建人脸检测数据集的从业者来说,尤其是手头有大量视频帧、需要批量圈框的场景,这个工具能把最枯燥的“画框”环节自动化。源码整体清晰,主程序加 util 工具包,模型可以替换成自己在 YOLOv5 上训练出来的权重,导出的标签支持 PASCAL VOC、MS COCO、YOLO TXT 三种格式,直接衔接训练流程没有壁垒。不管你是做目标检测训练集,还是给监控视频跑批量预标注,它都有对应的入口。

2. 源码结构与模型选型:搞懂这三层,你才知道改哪里

2.1 主程序与 util 工具箱:face_labeling.py 到底做了什么

拿到这套工具,我习惯先不看 readme,而是先把目录结构过一遍。解压后的骨架大概是下面这个样子:

# 项目根目录 face-labeling/ ├── face_labeling.py # 主程序入口(webcam/img/video 三种模式) ├── requirements.txt # Python 依赖列表 ├── models/ # 预训练检测模型 │ ├── widerface-m.pt # WIDERFace 数据集训练的权重 │ └── darkface-m.pt # DarkFace 数据集训练的权重 ├── data/ │ ├── imgs/ # 测试图片目录 │ └── videos/ # 测试视频目录 └── util/ # 工具脚本包 ├── args_yaml.py # 参数与默认配置管理 ├── model_opt.py # 模型加载相关 ├── obj_opt.py # 目标框坐标与类别处理 ├── path_opt.py # 路径处理 ├── voc_xml.py # 导出 PASCAL VOC XML ├── coco_json.py # 导出 MS COCO JSON ├── yolo_txt.py # 导出 YOLO TXT ├── time_format.py # 时间格式工具 └── log.py # 日志输出

顶层三个东西最重要:face_labeling.py是所有模式的入口;models/里两个.pt文件是预训练检测权重;util/里的脚本负责导出各种标注格式、管理路径和日志。data/只是测试用的图像和视频目录,正式标注时完全可以指向自己的目录。

face_labeling.py的主流程并不复杂,就是典型的“载入模型→读入数据→推理→显示结果→等待人工确认→保存标注”循环。webcam 模式按下a键捕获当前帧,模型在这一帧上跑一次推理,画出预测框,人工按键盘确认后写入标签;q键退出。图片和视频模式则分别遍历目录下的文件,把推理结果保存成选定的标签格式。这个流程里最值得注意的一点是:人工不是被完全取代,而是只做“确认/修正框”的工作,这和纯手动标注的体力消耗完全不在一个量级。

util目录里每个脚本承担一件事:voc_xml.py、coco_json.py、yolo_txt.py分别完成三种数据集的导出;model_opt.py负责加载模型权重相关操作;args_yaml.py集中管理命令行参数和默认配置;obj_opt.py处理目标框的坐标与类别;path_opt.py处理路径检测。后面想要换自己的模型,主要改动集中在model_opt.py和args_yaml.py,这是我几次改权重之后比较明确的经验。另外包里还有CodeCheck.md和v03.md这两份文档,前者像代码规范或检查清单,后者像版本迭代说明,开始改代码前先扫一眼,能避免拿旧命令去跑新逻辑。

2.2 两个内置权重:widerface-m.pt 和 darkface-m.pt 在什么场景用

模型选型直接决定预标注的准头。这套工具内置了两个权重文件,文件名已经说明了它们的出身:widerface-m.pt是在 WIDERFace 数据集上训练的模型,darkface-m.pt是在 DarkFace 数据集上训练的模型。两者都是人脸检测方向常用的公开数据集,但场景差异很明显。

WIDERFace 包含大量日常光照、复杂背景和尺度多变的人脸样本,适合白天、室内、摄像头发来的常规视频帧;DarkFace 的样本集中在低光照、夜间监控这类场景,对暗光人脸的召回明显更好。如果你的素材是监控视频里晚上拍的那种,默认用 widerface 权重会漏掉相当一部分暗部的人脸,换 darkface 权重会好很多。

如果素材比较特殊,比如全是俯视角度的无人机画面,公开权重大概率表现一般。这套工具本身就允许自定义模型,我的习惯是先用自带权重跑一批数据,看漏检率再决定是否需要换权重训练。切换模型参数的入口在args_yaml.py里,默认权重路径写成模型文件名,测试时可以直接在models/目录里改文件名来切换。不过不建议这样做,因为 readme 里记录了模型来源,直接改名会丢失模型和训练数据之间的对应关系,后面排查问题更容易糊涂。

2.3 环境准备:requirements.txt 和 PyTorch/CUDA 版本对应关系

项目携带了requirements.txt,安装方式并不特殊。在手头没有可用环境时,我一般会用 conda 新建一个干净环境,再安装依赖:

# 创建干净环境并激活 conda create -n yololabel python=3.9 -y conda activate yololabel # 安装项目依赖 pip install -r requirements.txt

安装结束后,单独验证 PyTorch 和 CUDA 是否打通是个更稳的步骤:

# 打印PyTorch版本并检查CUDA是否可用 python -c "import torch; print(torch.__version__, torch.cuda.is_available())"

我建议单独验证是因为:YOLOv5 系代码对 PyTorch 版本比较敏感,requirements.txt里的版本号只是一个下限参考。如果本机 CUDA 驱动和 PyTorch 的编译版本不对应,经常出现torch.cuda.is_available()返回False,模型退到 CPU 推理,速度瞬间掉一个数量级。此时不需要改业务代码,把 PyTorch 换成与本地 CUDA 驱动匹配的版本即可。

显存方面也要提前评估。如果你的显卡只有 4GB 左右,跑默认输入尺寸可能中途崩掉。常见做法是把推理尺寸从 640 调低到 320,标注场景里漏检的几个框肉眼能看出来,人工顺手补上就行,整体仍然比纯手动快。这个参数同样在args_yaml.py或启动参数里控制,改完不用动模型权重。

压缩包里还有yolov5_widerface.md、yolov5_darkface.md、yolov5_pytorch_gpu.md这类配套文档,主要是环境配置记录和两个权重的训练说明。第一次使用前我会打开看一眼,确认权重对应的输入尺寸和训练数据来源,后续如果出现推理结果不理想,能少用一些“玄学排查法”。

3. 三种标注模式跑通:webcam 抓帧、图片批量、视频抽帧的参数与输出

3.1 webcam 实时标注:a 键抓帧、q 键退出的交互逻辑

webcam 模式最简单的启动方式就是不带任何参数:

# 启动摄像头实时标注 python face_labeling.py

脚本默认打开索引为 0 的摄像头。实时画面的意思是模型在每一帧上持续推理,画面里会实时绘制出人脸框。按a键把当前帧保存下来,同时把这一帧的检测结果写入标注文件;按q键退出程序。这里的“标注”不是手动画框,而是在实时画面里捕获帧,让模型预标注,再对结果做确认。

实际使用中,我觉得这个模式最适合快速收集个人照片或工位摄像头素材,比如为一个小型人脸识别项目攒几十张带标注的图片。需要注意的坑是,笔记本内置摄像头和 USB 摄像头并存时,默认的0不一定是物理摄像头。如果程序打开的是黑屏或者错误画面,先检查系统相机应用里设备编号,再改脚本中访问摄像头的设备号参数。

另外,webcam 模式下按a抓帧是人工触发,所以画面里没人脸的时候不要急着按,等目标进入画面合适位置再操作。这样产出的帧质量更高,后续人工确认的工作量也小。

3.2 图片模式:单张预标注到批量目录

图片模式适用于已经整理好的图片文件夹,启动命令是:

# 使用默认图片目录 data/imgs python face_labeling.py -m img

默认扫描data/imgs目录;想标注自己的目录,用-imd指定:

# 指定自己的图片目录 python face_labeling.py -m img -imd ./img_dir

-m是模式开关,取img表示图片模式;-imd接收输入目录。支持的图片格式是jpg | jpeg | png | bmp | tif | webp,目录下不符合这些后缀的文件会被跳过。这种批量遍历行为很适合处理“从视频里抽出来的一堆帧”或者“从手机相册导出的照片集”。

在这个模式里,我会先把输出目录建好,再把待标图片分类放进去:正样本,也就是有人脸的图,和负样本,没人脸但容易误检的图,分开处理。这样在确认框时不用反复切换思路,导出后的训练数据也更干净。批量处理前,先拿三五张图跑一遍,确认模型推理和人脸框都正常再全量跑,是一个能省很多返工时间的习惯。

3.3 视频模式:批量 mp4 抽帧与标注

视频模式对监控录像、采访视频这类长素材比较友好:

# 使用默认视频目录 data/videos python face_labeling.py -m video # 指定自己的视频目录 python face_labeling.py -m video -vd ./video_dir

-vd指定视频目录,默认读data/videos,支持的视频格式明确写了mp4。程序会在拆帧、推理、标注之间循环,把视频中的有效人脸帧提取出来,同时生成这一帧的标注。用它处理批量视频时注意两点:第一,-vd指向的目录里最好只放视频文件,避免程序读到非视频文件后打印一堆报错信息干扰判断;第二,长视频会产出大量高度相似的帧,如果后续要拿去训练模型,最好在标注后做一次帧去重,否则数据集中重复样本太多,训练出来的模型会偏向重复出现的场景。

视频模式还有一个实际的坑:如果视频分辨率是 4K,直接把原帧送进检测器会明显变慢,显存占用也高。常见做法是先抽帧到 1080P 再标注,也就是在-vd前用 ffmpeg 做一次统一的尺寸缩放,速度能提上来,标注框精度不会受影响。另外,10 分钟 1080P 视频按 30fps 算就是 18000 帧,即便没人脸的有效帧也会占满磁盘。视频标注前,先确认工具的抽帧间隔参数,别让无效帧把磁盘撑爆。

3.4 参数入口与自定义模型:args_yaml.py 里的默认配置

util/args_yaml.py负责集中管理命令行参数,包括-m、-imd、-vd,以及模型权重路径、推理尺寸、置信度阈值这些和检测质量直接相关的配置。实际工作中,我更倾向于直接修改这个文件里的默认值,而不是每次启动都敲一长串参数。

如果你是自己训练好的 YOLOv5 权重,替换流程一般是:把权重文件放到models/目录,然后在args_yaml.py里把检测权重路径指向新文件,最后用小批量图片验证一下导出结果。这里的难点不是替换本身,而是确认新模型的类别编号和你要导出的标签格式一致。尤其是类别数量变化后,COCO JSON 里的category_id、YOLO TXT 里的第一列都会受影响,必须在导出前对齐。标签文件默认输出在图片或视频同级目录,或者由args_yaml.py指定的输出目录控制,开始标注前先确认这个路径,避免标完一批找不到文件。

4. 三种标签格式导出:VOC XML、COCO JSON、YOLO TXT 的转换与对应

4.1 三种坐标体系差异:先看懂再动手

标注工具最终产出的是文本标签,但三种格式的坐标体系完全不同。下面这个表我每次切换训练框架时都会再核对一遍:

格式坐标基准典型内容量纲
YOLO TXT归一化类别id + x_center + y_center + width + height0~1
PASCAL VOC XML绝对像素xmin + ymin + xmax + ymax像素
MS COCO JSON绝对像素x + y + width + height像素

YOLO TXT 是训练 YOLOv5 最直接的标签格式,坐标除以图片宽高后落在一个 0 到 1 之间的相对坐标系里;VOC 和 COCO 都是像素坐标。如果把 YOLO TXT 直接丢给 COCO 训练的框架,检测框会全部变成图片左上角那一条细线。这类问题最隐蔽,因为程序不报错,但训练出来的模型完全废掉。

4.2 YOLO TXT:一行为一个目标的归一化坐标

util/yolo_txt.py负责把检测结果写成 YOLO 格式。每一行对应一个目标:

0 0.501953 0.481481 0.156250 0.324074

第一个数字是类别编号,人脸检测通常就是 0;后面依次是中心点 x、中心点 y、框宽、框高,全部是归一化数值。生成这种文件后,我习惯用下面的命令抽查一下内容:

# 查看一个YOLO格式标注文件的具体内容 cat data/imgs/sample.txt

顺带讲一下坐标换算,方便排查问题。YOLO 归一化坐标转像素坐标的逻辑可以简写成这样:

# 将YOLO归一化坐标转成绝对像素坐标 img_w, img_h = 1920, 1080 # 原图宽高 x_center, y_center, w, h = 0.501953, 0.481481, 0.156250, 0.324074 x_min = int((x_center - w / 2) * img_w) y_min = int((y_center - h / 2) * img_h) x_max = int((x_center + w / 2) * img_w) y_max = int((y_center + h / 2) * img_h) print(x_min, y_min, x_max, y_max)

这里的关键是:宽度和高度在 YOLO 格式里同样是归一化后的相对值,计算绝对坐标时必须把中心点加减半宽半高再乘图像尺寸,而不是直接用像素宽高去乘。很多格式转换脚本的 bug 都出在这一步,需要留意。

4.3 PASCAL VOC XML:树形结构与 bndbox

VOC 格式是 XML,util/voc_xml.py会生成类似下面的结构:

<annotation> <folder>JPEGImages</folder> <filename>000001.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>face</name> <bndbox> <xmin>386</xmin> <ymin>194</ymin> <xmax>686</xmax> <ymax>544</ymax> </bndbox> </object> </annotation>

注意bndbox里的四个坐标是绝对像素值,且遵循左上右下原则。VOC 格式本身不限制同一张图片里目标对象的数量,可以有多个<object>节点。和 YOLO TXT 相比,VOC XML 不需要做归一化,所以目视检查时更直接,缺点是文件体积大、解析速度慢。

在训练环节,VOC 格式一般要配合class_names列表使用,检测程序通过<name>字段把类别名称映射成数字编号。如果训练代码里类别顺序和标注文件里的顺序不一致,模型学到的类别就会错位,这属于配置错误而不是标注错误,排查时要先看类别列表。

4.4 MS COCO JSON:annotations 数组与 category_id

COCO 格式是一个大 JSON 文件,适合目标检测、实例分割共用的训练管线。util/coco_json.py输出的内容大致有三个顶层字段:images、annotations、categories。images记录文件名和尺寸,annotations里每条记录对应一个检测框,包含image_id、bbox、area、category_id等字段。

验证生成好的 JSON 有没有语法问题,用 Python 自带命令就行:

# 校验COCO JSON的合法性,并查看前50行内容 python -m json.tool coco_annotations.json | head -50

如果程序报缺少逗号、括号不匹配之类的错误,说明 JSON 序列化环节出了问题,多数情况是图片元数据缺失导致某个字段变成了空值。COCO 格式里最容易漏的一个字段是area,不少训练框架加载时不会主动检查,但跑评估阶段会报错,所以生成后最好抽查几条记录,确认area和bbox是同一组坐标计算出来的。

4.5 三类格式和训练工程的衔接

我的通用建议是:一个人脸检测项目里,原始标注只保留一份,作为“主格式”。如果训练框架是 YOLOv5,就把主格式定为 YOLO TXT;如果用 Detectron2 或 MMDetection,就定为 COCO JSON。其他格式在需要时用 util 里的脚本转换,而不是手工维护几个副本。格式转换本身不难,难的是转换过程中坐标精度的丢失,以及类别编号重新映射时的错位。

这里还想强调一点:工具虽然能导出三种格式,但每张图片对应的标签文件名必须和图片名一致。YOLO 训练时,images/000001.jpg会自动去找labels/000001.txt,文件名对不上,训练会报大量找不到标签的警告,这种问题在数据量大了以后非常难排查。所以标注完第一件事,就是检查文件名是否一一对应。可以用一个简单的 shell 循环快速核对:

# 列出images目录下存在但labels目录中缺失对应txt的图片 for img in images/*.jpg; do base=$(basename "$img" .jpg) [ -f "labels/${base}.txt" ] || echo "缺少标签: $img" done

5. 避坑指南:五类典型问题的现象、原因与解决

5.1 依赖装完还是报找不到模块

现象:执行python face_labeling.py提示ModuleNotFoundError,明明已经pip install -r requirements.txt装过依赖。

原因:conda 环境没激活、多个 Python 环境并存,pip 装到了另一个解释器下;或者依赖版本太新,函数接口对不上 YOLOv5 代码的调用方式。

解决:先用which python和pip list确认当前解释器路径,再重装一次依赖。如果pip list里能看到包,但程序还是找不到,多半是当前 shell 激活的环境和 pip 安装的环境不是一个。YOLOv5 这类项目对依赖版本比较敏感,尤其是 torch、numpy、opencv-python 这三个,最好锁到和代码配套的版本。这种现象和环境耦合很紧,每次都值得先查解释器再查版本。

5.2 显存不够导致推理中断

现象:视频模式下跑几分钟,程序报CUDA out of memory,然后进程直接退出,已经标注的帧也丢了。

原因:推理尺寸太大,或者视频帧没有缩放直接送进模型,长视频持续运行还会累积显存碎片。

解决:把args_yaml.py里的推理尺寸从 640 降到 320,显存占用大概是原来的四分之一;视频先统一缩放到 1080P 再标注。如果显卡只有 2GB,直接切 CPU 推理反而比反复崩掉更省时间,因为 320 分辨率下 CPU 推理的速度还可以接受。确认显存状况可以看nvidia-smi的占用,如果其他进程占了显存,优先关掉不必要的程序。

5.3 模型加载时提示权重不兼容

现象:加载自己训练的best.pt时,提示Missing keys或Unexpected keys,有时直接加载失败。

原因:权重文件是用不同版本的 YOLOv5 保存的,模型结构定义和权重键名不匹配;或者换了别人改过的网络结构,骨干网络层数对不上。

解决:先看报错里的键名差异,缺的多半是新加的网络层。确认自己替换的权重和代码库版本一致,最简单的办法是先用同一套 YOLOv5 代码重新导出权重再替换。项目自带的两个-m.pt文件是在当前代码结构下验证过的,如果只是训练数据不同,重新用配套代码导出一次权重就好。

5.4 导出标签后坐标偏移

现象:标注文件打开后,框的位置和图片内容对不上,整体偏左或偏上,或者框比人脸大一圈。

原因:图片被预处理(resize、letterbox)后,坐标没有映射回原图尺寸;或者 YOLO 格式和 VOC/COCO 格式转换时,中心点加减宽高算错。

解决:先看原始图片实际宽高和标签里保存的<size>是否一致,如果不一致,说明预处理映射出了问题。再用第 4.2 节的换算脚本抽查几个框,把标签坐标转成像素坐标,叠加到原图上看看偏移方向。常见做法是把“原图尺寸”作为全局配置固定住,所有格式转换都基于同一组宽高,而不是每次从文件名里猜。

5.5 视频模式反复输出同一个人的重复帧

现象:视频里同一张脸被连续标注了几十次,数据集变得特别冗余,训练时模型对某个固定场景过拟合。

原因:视频帧率高,检测器每帧都推理,同一段画面被重复取样。人脸在画面里停 3 秒,按 30fps 就会产生 90 个几乎一样的标注样本。

解决:标注完成后用帧去重清洗一遍。最简单的方式是按文件内容做 MD5 比较,内容完全相同的帧只保留一份:

# 对帧文件做MD5排序,找出内容完全相同的重复帧 md5sum frames/*.jpg | sort | uniq -w32 -d

更省事的做法是在视频处理时按间隔抽帧,比如每 10 帧取 1 帧再送进标注工具。间隔数值取决于视频里人脸移动的速度,移动快就取密一点,移动慢就取疏一点。还有一个底层逻辑:如果同一个人的正脸和侧脸都被反复标注,模型学到的是“这个人长什么样”,而不是“人脸长什么样”,清洗数据时要刻意保留不同角度、不同姿态的帧,而不是连续时间戳的帧。

6. 进阶:用这套标注数据微调自己的YOLOv5人脸模型

标注工具只是第一步,真正让人脸检测器适配自己业务场景的,是用这批带标注数据去做一次小规模微调。我自己走通的路径是这样的:先把工具导出的图片和标签,整理成 YOLOv5 约定的目录结构:

# 建立YOLOv5训练数据目录,图片和标签严格同名 dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/

图片和同名标签按 8:2 左右划分训练集和验证集,注意同名:images/train/000001.jpg必须有labels/train/000001.txt对应。随后用公开权重做起点,先跑大约 50 轮,验证数据读取和 Loss 下降是否正常,确认正常后再继续训练。

再有一个验证技巧:把训练好的模型拿出来,跑一段工具没见过的视频,不仅看 mAP 指标,直接看框有没有贴住人脸。如果发现某个场景漏检严重,就把这类困难样本挑出来,用这个工具补标后加到训练集里。这个“标注→训练→挑错→再标注”的循环,才是这套工具真正的价值所在。

我对微调的几个判断供参考:学习率不要直接抄 YOLOv5 默认值,人脸检测微调通常从 0.001 级别开始,配合余弦退火或固定步长都行;训练轮次以 Loss 不再下降为准,小数据量往往 50 轮就到头了,再多容易过拟合。另一个细节是置信度阈值:默认 0.25 可能让低置信度误检框混进标注里,导出前把阈值调到 0.5 左右,数据质量会好很多。这个参数同样可以在args_yaml.py里调,不需要改代码。

以前我拿到人脸数据,第一反应就是打开 LabelImg 手动画框,画到后面眼睛都是花的。现在我会让工具先跑预标注,人工只做确认和补框。从那以后我每次拿到新的标注工具,都会先做三件事:跑通三张图的预标注、抽查一张标签文件、确认 YOLO 和 VOC/COCO 的坐标映射,再决定是否全量标注。这个流程帮我避开了不少格式错位的暗坑,希望帮到你。

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

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

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

立即咨询