简介基于YOLOv5的智能人脸标注工具源码与模型包面向需要批量构建人脸数据集的算法工程师和计算机视觉学习者借助YOLOv5检测能力可将图片、视频乃至webcam实时画面中的人脸自动标注出来支持自定义检测模型、一键导出PASCAL VOC XML、MS COCO JSON、YOLO TXT标签支持a键捕获视频帧、q键退出显著提升标注效率。整个压缩包共59个文件约78.68MB以Python脚本、Markdown教程、预训练模型为主辅以示例图片和演示视频源码涵盖图片批量标注、视频标注、模型配置等模块文档对YOLOv5 GPU环境配置及widerface/darkface数据集训练均有说明目录内还包含依赖、配置与版本信息便于自定义适配到实际项目。目前已有338人学习下载拿到后可直接运行源码主程序进行实时标注也可根据自身数据情况调整检测模型与标注格式适合快速搭建自己的人脸数据集标注流水线。1. 一个 YOLOv5 人脸标注工具能省下你几天时间先说清楚它解决什么问题做人脸检测、人脸识别或者安防项目的人大概率都经历过这种场景从网上或客户那边拿到几千张带人脸的图片第一反应是打开 LabelImg 一张一张框人脸。白天框到晚上鼠标点得手酸回头一看才标了两百张。这个标题里说的“基于YOLOv5的智能人脸数据集标注工具”本质就是把“人工从零画框”变成“人工修正粗框”的半自动标注方案——用预训练的 YOLOv5 模型先跑一遍推理把图片里的人脸位置、大小、置信度都算出来再把这批粗标结果导入标注工具或直接转成训练格式你只需要人工确认和微调。它的核心价值在于模型干粗活人干细活。一张图片从“打开、框选、选类别、保存”的四步操作被压缩成“看一眼、有偏差就拖一下框、保存”。对几百上千张的数据集来说时间成本能省一半以上。适合的人群很明确在做数据积累的算法工程师、准备自己训 YOLOv5 人脸模型的初学者、以及需要快速验证某个思路但不想在标注上耗太久的独立开发者。这篇文章不聊论文也不做平台推广就按我自己平时搭这种工具的流程把方案、代码、参数和坑一条条讲清楚。2. 为什么用 YOLOv5 做半自动标注方案选型与模型拆解2.1 半自动标注不是新概念但 YOLOv5 让它变得顺手数据标注工具这个领域其实已经有不少成熟产品。CVAT、LabelStudio、LabelImg 都是常用选择最新的 label-studio 还支持模型辅助标注接口也开放。但问题在于这些平台要么部署成本高要么要把数据传到自己不想传的地方要么在“离线单机 纯 Python 脚本”这个场景下太重。你自己写个百来行的推理脚本配合 LabelImg 的“打开标注目录 自动加载 XML”功能反而是最顺手的一条路。选择 YOLOv5 而不是 Faster R-CNN 或 EfficientDet理由很实际第一YOLOv5 的推理代码轻量、依赖少、单张显卡能跑很高帧数哪怕用 CPU 也能在几秒内处理一张 640x640 的图第二它的预训练权重里有 COCO 的 80 类其中 person 类对脸部也有一定的检出能力但更常见的是拿专门的人脸权重或者直接用 YOLOv5 官方提供的 yolov5s.pt 做迁移微调第三YOLOv5 的标签格式txt 格式一行一个对象内容是 class x_center y_center width height和 LabelImg 的 Pascal VOC 格式XML之间有非常成熟的转换脚本几乎没有转换成本。2.2 源码里通常会带什么一份工具包的基本组成拿到“基于YOLOv5的智能人脸数据集标注工具源码模型”你先别急着跑先按目录结构过一遍确认里面有哪些部分。一般这类工具包会包含以下几块完整的 YOLOv5 推理工程包含 models、utils、data 这些标准目录、一个人脸检测权重文件可能是 .pt 格式也可能同时附带 ONNX 导出脚本、负责“读图 → 跑推理 → 生成 LabelImg 可读格式”的标注导出脚本、以及一个将标注结果从 XML/VOC 或 JSON 转成 YOLOv5 训练格式的转换脚本。有的工具包还会把 train.py 或 val.py 放进来说明它能直接继续训练。目录/文件作用落地时你要关心的点models/YOLOv5 模型定义yolov5s.yaml 等确认是否带预训练权重加载逻辑weights/ 或 *.pt人脸检测模型权重问清楚最低要求是 CPU 还是显卡显存多少auto_label.py 或 detect_export.py推理 粗标输出看它输出的是 XML 还是 txt是否和你的标注工具匹配voc2yolo.py / yolo2voc.py格式转换转换时类别 id 对齐是最容易翻车的点data/图片样本 标签示例确认类别名是 face 还是 person一致拿到包以后第一件事不是跑而是先看权重文件的后缀和它要求的输入尺寸。.pt文件对应 PyTorch 版本.onnx文件对应推理框架或 CPU 部署。YOLOv5 默认输入是 640x640如果你手里的图片大多是 1920x1080 这种大图那么跑推理之前按下等比缩放或者直接 resize 到 640然后再跑。这个尺寸和数据标注没有直接关系但影响粗框的准确度——原图太大直接缩放小脸可能丢失。所以我在项目里一般会先看一眼数据里人脸的最小尺寸占比如果很多小人脸就把推理尺寸提到 960 或 1280代价是速度变慢但粗标质量会好很多。2.3 模型推理的置信度阈值第一个要调的参数推理得到的人脸框必须经过置信度筛选。很多人第一次跑这种工具发现生成的框几十个图片上刷了一层框——因为默认 conf_thres 太低。标真人脸数据集时我把置信度阈值定在 0.25 到 0.5 之间追求召回宁可多框别漏框就设 0.25追求精确框出来基本都是人脸就设 0.5。NMS 的 IoU 阈值保持默认 0.45 就行。这里有一个细节粗标工具要的是“宁可框错不可漏框”因为错框你可以顺手删掉漏框你得手动补补框和拖框相比补框的时间成本更大。另外一个容易被忽略的点是类别过滤。如果权重里同时包含 person 和 face 两个类别而你只想标人脸那要在代码里加一个类别映射。YOLOv5 的检测结果是一个 tensor每一行是x1 y1 x2 y2 conf cls按 cls 过滤就行。你不要直接拿所有类别去生成标注否则后期你还要按类别删一轮。3. 从源码到落地跑通智能标注工具的最小工程3.1 环境准备与目录结构约定先把 YOLOv5 的推理环境装好。Python 3.8/3.9 是兼容性最好的版本PyTorch 用 1.8 到 2.x 都行。安装依赖时有个先后顺序先把 PyTorch 装好再装 requirements.txt否则 pip 会自动给你装一个 CPU 版或者版本不对的 PyTorch后面推理慢到你怀疑人生。# 1. 创建虚拟环境 conda create -n yolov5_label python3.9 -y conda activate yolov5_label # 2. 安装 PyTorch根据自己的 CUDA 版本调整 # 如果不知道自己的 CUDA 版本先运行 nvidia-smi 查看 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 3. 安装 YOLOv5 依赖 pip install -r requirements.txt # 4. 验证 GPU 是否可用 python -c import torch; print(torch.cuda.is_available())这段命令的逻辑是先建干净环境避免本机其他 Python 包干扰。requirements.txt 里包括 opencv-python、numpy、matplotlib、pillow、pyyaml 这些常见依赖。执行第 4 步时如果输出 False说明 GPU 没识别到要么是 CUDA 版本不对要么是 PyTorch 装了 CPU 版不要硬着头皮用 CPU 跑大批量图片不然标注效率直线下降。目录结构建议这样组织方便后续把原始图片、粗标结果和人工修正后的标签分开放dataset/ ├── raw_images/ # 原始图片 ├── auto_labels/ # 模型生成的粗标 XML 或 txt ├── yolo_labels/ # 人工修正后转换成的 YOLO 训练格式 ├── train.txt # 训练集路径列表每行一个图片绝对路径 └── val.txt # 验证集路径列表这个结构的好处在于raw_images 是只读的模型不会去动它auto_labels 是生产出来的半成品可以随时清空重新生成yolo_labels 才是最终喂给 YOLOv5 训练的产物。我见过不少人把所有文件混在一起结果跑完训练发现标签和图片对不上最后只能重新检查。3.2 推理脚本加载模型、批量预测、输出 XML下面这一段是从 YOLOv5 官方 detect.py 改造出来的核心逻辑作用是把一个文件夹里的图片全部跑一遍预测然后按 Pascal VOC 格式把结果保存成 XML。这样 LabelImg 在打开图片时能自动加载同名的 XML 文件显示已标注框。import torch import os import cv2 import xml.etree.ElementTree as ET from pathlib import Path # 关键参数权重路径、数据路径、置信度阈值 weights_path weights/yolov5s_face.pt source_dir dataset/raw_images output_dir dataset/auto_labels conf_thres 0.25 # 低阈值优先保证召回宁可多框不可漏框 imgsz 640 # 推理尺寸小脸多就改成 960 或 1280 os.makedirs(output_dir, exist_okTrue) # 加载模型注意这里自动判断 CPU 还是 GPU device torch.device(cuda:0 if torch.cuda.is_available() else cpu) model torch.hub.load(ultralytics/yolov5, custom, pathweights_path, force_reloadFalse) model.conf conf_thres model.iou 0.45 # NMS 的 IoU 阈值 image_paths list(Path(source_dir).glob(*.jpg)) list(Path(source_dir).glob(*.png)) for img_path in image_paths: # 读取图片并推理 img cv2.imread(str(img_path)) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) results model(img_rgb, sizeimgsz) # results.pandas().xyxy[0] 是每个检测框的信息包含 xmin ymin xmax ymax confidence class name detections results.pandas().xyxy[0] # 构建 Pascal VOC XML与 LabelImg 兼容 annotation ET.Element(annotation) ET.SubElement(annotation, folder).text os.path.basename(source_dir) ET.SubElement(annotation, filename).text img_path.name ET.SubElement(annotation, path).text str(img_path) size ET.SubElement(annotation, size) ET.SubElement(size, width).text str(img.shape[1]) ET.SubElement(size, height).text str(img.shape[0]) ET.SubElement(size, depth).text 3 for _, row in detections.iterrows(): xmin, ymin, xmax, ymax int(row[xmin]), int(row[ymin]), int(row[xmax]), int(row[ymax]) cls_name str(row[name]) # 注意类别名人脸一般是 face 或 person conf float(row[confidence]) # 这里可以加一个最小尺寸过滤太小的框大概率是误检 if xmax - xmin 10 or ymax - ymin 10: continue obj ET.SubElement(annotation, object) ET.SubElement(obj, name).text cls_name ET.SubElement(obj, pose).text Unspecified ET.SubElement(obj, truncated).text 0 ET.SubElement(obj, difficult).text 0 bndbox ET.SubElement(obj, bndbox) ET.SubElement(bndbox, xmin).text str(xmin) ET.SubElement(bndbox, ymin).text str(ymin) ET.SubElement(bndbox, xmax).text str(xmax) ET.SubElement(bndbox, ymax).text str(ymax) ET.SubElement(obj, confidence).text f{conf:.3f} # 保存 XML tree ET.ElementTree(annotation) xml_path os.path.join(output_dir, img_path.stem .xml) tree.write(xml_path, encodingutf-8, xml_declarationTrue) print(fProcessed {img_path.name}, saved {xml_path})这段代码的核心逻辑有两层。外层是遍历图片文件夹内层是每个图片跑一次 YOLOv5 推理把结果结构化后写成 XML。参数说明conf_thres控制的是置信度阈值0.25 是比较保守的人脸框召回值如果你发现误检太多调到 0.4 左右最平衡imgsz是推理输入尺寸默认 640图片里人脸区域小于 32x32 像素时需要调大模型加载用了 torch.hub首次运行会下载依赖后面离线也能跑。运行结束后你到 dataset/auto_labels 里看 XML 文件数量是否和图片数量一致。不一致的话优先排查图片后缀名是否被 glob 匹配到以及图片是否损坏。打开一个 XML 看内容确认 bndbox 坐标值是否在图片尺寸范围内——这一步是在为后面的格式转换和人工修正做准备。3.3 把 XML 粗标转成 YOLO 训练格式边界框转换脚本LabelImg 保存的 XML 坐标是绝对像素坐标xmin、ymin、xmax、ymaxYOLOv5 训练要求的是归一化中心坐标x_center、y_center、width、height全部除以图片宽高。这一步必须单独写一个转换脚本因为很多人手工改标签时在 LabelImg 里直接另存成了 XML后面训练 YOLOv5 时找不到标签就开始怀疑模型有问题。这里先转格式再人工修正顺序不要反。python voc2yolo.py --xml_dir dataset/auto_labels --img_dir dataset/raw_images --out_dir dataset/yolo_labels --classes face person转换脚本的核心逻辑是把绝对坐标转成相对坐标顺便生成每个类的 id 映射。这里有一个常见错误是类别 id 从 1 开始计数YOLO 要求从 0 开始。YOLOv5 的 data.yaml 里 class 顺序要和这里映射完全一致否则标签写着 class_id0实际训练却把 0 当成了 person模型直接跑偏。# voc2yolo.py 核心转换函数 import os, cv2 def voc_to_yolo(xml_path, img_width, img_height, class_map): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): cls_name obj.find(name).text.strip() if cls_name not in class_map: continue # 跳过不在类别列表里的检测框 class_id class_map[cls_name] bbox obj.find(bndbox) xmin int(float(bbox.find(xmin).text)) ymin int(float(bbox.find(ymin).text)) xmax int(float(bbox.find(xmax).text)) ymax int(float(bbox.find(ymax).text)) # 防止坐标越界同时避免零面积框 xmin max(0, min(xmin, img_width - 1)) ymin max(0, min(ymin, img_height - 1)) xmax max(xmin 1, min(xmax, img_width - 1)) ymax max(ymin 1, min(ymax, img_height - 1)) # 转归一化中心坐标 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return lines坐标转换这部分有几个封装的逻辑值得专门说。第一坐标越界保护是必须的模型检测出的框偶尔会超出图像边界直接写进训练集会让损失计算出现 NaN。第二面积过滤在推理阶段已经做了一道这里再做一道是为了防止个别人工修正时把框拖没了。第三class_id 和你后续 data.yaml 的类别顺序要人工核对一次这一步没有自动化检查只能靠肉眼对比。3.4 在 LabelImg 中修正粗标让半自动闭环真正生效LabelImg 打开图片并自动加载同名 XML 后左侧会显示已存在的框。你只需要对明显偏大或偏小的框做拖拽修正然后对误检框按 Delete 删除对漏检的人脸按 W 键新建框类别选 face。速度比从零开始标注快得多。操作顺序是先用 LabelImg 打开 auto_labels 目录再打开 raw_images保存路径选同一个 auto_labels 目录这样改完后 XML 会覆盖原文件。LabelImg 默认读取的标签类别从classes.txt里定义路径是data/predefined_classes.txt。如果你生成的 XML 里出现了 classes.txt 没有的类别名LabelImg 会报错或自动清掉。所以你在跑推理脚本之前先把predefined_classes.txt改成face和person两行。这就是所谓“先把格式定义好再动手”的原则不然改到一半发现类别名对不上之前的粗标全废。4. 避坑与常见问题半自动标注工具最容易翻车的五个细节4.1 现象模型框出来全是 person没有 face 类别这是最典型的坑。原因在于你下载的预训练权重是 COCO 模型COCO 里没有人脸类别只有 person。所以模型把整张脸连带脖子、肩膀框在一起而不是紧贴脸部轮廓。解决方式是换一个人脸专用的预训练权重或者拿几百张人脸数据微调一下 model。在代码层面也可以做后处理检测到 person 类时取 bbox 的上半部分比如 ymin 到 ymax 的 40% 高度当做人脸框但这个方案只对正脸有效侧脸或低头场景会框歪所以最稳的办法还是换成专用权重。4.2 现象转换后的 txt 里有些行全为 0 或出现负数打开生成的 txt 标签文件发现0 0.000000 0.000000 0.000000 0.000000这种行说明 XML 里的 bndbox 节点有异常。原因通常有两个一是在 LabelImg 里手工标注时误操作把框拖没了或宽高为 0二是 yolo2voc/voc2yolo 脚本里没有加坐标保护某个坐标为负值恰好被系统当作 0 处理。解决方式是把 voc2yolo 脚本里那一行越界保护代码加上然后在转换后统计一下每行标签的长度是否为 5长度不对的直接删除同时回到原图确认这个目标是否值得保留。4.3 现象LabelImg 打开图片不显示粗标框XML 文件存在文件名和图片名一致目录也一致但 LabelImg 就是光秃秃一张图。原因大概率是 XML 里的 filename 节点和实际文件名不一致。LabelImg 判断对应关系的方式是“同目录下同名 .xml”但如果 XML 里的 filename 写的是绝对路径或者大小写不同LabelImg 就找不到。解决方式是把 XML 里的 filename 节点统一改成纯文件名不要带路径另外检查 XML 的 encoding 是否为 UTF-8LabelImg 对中文路径支持不好建议项目路径不要带中文。4.4 现象推理时 GPU 利用率很低速度还是慢迷你 batch 推理是 YOLOv5 的强项但很多人写工具时把循环写成单张图片逐个预测batch_size1 导致 GPU 利用率只有百分之十几。如果你有几千张图片要跑建议把推理部分改成 YOLOv5 官方 detect.py 的批量推理模式或者用 DataLoader 分批次送入模型。一个简单参数是model(img, sizeimgsz)里的 img 直接传一个 batch 的 tensor比如 8 张图堆成的 8x3x640x640而不是单张图片。修改后速度提升是 3-5 倍。还有个玄学torch.hub 加载的模型默认不做 batch 推理优化你要确认权重加载后没有自动进入 eval 之外的其他延迟模式。4.5 现象导入训练后 loss 直接爆掉或者模型完全学不到东西数据集标注工具产出的一批标签直接拿去训练结果 train loss 前几个 epoch 乱跳val mAP 一直是 0。这个问题不在标注工具本身而在于粗标的框大小分布太极端。YOLOv5 训练时默认的 anchor 是根据 COCO 数据集聚类出来的而你的人脸框宽高比通常集中在 0.8-1.2和 COCO 的 anchor 分布差异很大。解决方式是在 YOLOv5 工程的 yolov5s.yaml 里使用kmeans重新聚类 anchorYOLOv5 官方提供utils/autoanchor.py运行python utils/autoanchor.py --cfg yolov5s.yaml --data dataset.yaml会在训练前自动更新 anchor 配置。这一步是很多数据标注工具使用者都踩过的坑看起来是训练问题根子出在标签的框分布上。5. 标完这几百张图以后把粗标数据变成能打的自有模型5.1 数据清洗粗标数据不能直接训先做两轮检查不管模型预标注质量多高人工检查不可跳过。我的经验规则是第一轮检查只看“有没有漏框”花 15 分钟把 XML 密集出现的区域扫一遍第二轮检查专门看“框有没有覆盖关键特征”比如人脸的下巴边缘、头发边缘。粗标模型经常把头发边缘框进去导致训练出来的模型对戴帽子的人脸检测偏保守。清洗时直接改 XML 里 bndbox 坐标然后重新转 YOLO 格式不要手动改 txt因为 txt 一但改错格式训练阶段报错你还要回头找 XML。清洗完成后把数据按 8:1:1 切分成 train/val/test同时统计每个类别在不同图片中的分布比例。人脸数据集最常见的坑是大多数人脸都在图片中间或近距离样本单一导致模型在真实场景泛化能力差。你可以在清洗阶段把难样本模糊、侧脸、戴墨镜、小尺寸人脸单独挑出来放 val 里这样 val 指标才有参考意义。5.2 用清洗后的标签训练 YOLOv5几条高频参数建议数据准备好了以后直接用 YOLOv5 官方仓库跑训练。下面这份参数清单是我反复调出来的适用于人脸检测场景你可以在自己项目里直接参考。# train.py 关键参数 # 特别注意以下参数的设置逻辑不是越大越好而是根据数据量来 python train.py \ --data face_dataset.yaml \ # 必须包含 train/val 路径和 nc1, names[face] --weights yolov5s.pt \ # 建议用 COCO 预训练权重做迁移学习 --img 640 \ --batch 16 \ # 显存不够就调低到 8但别低于 4 --epochs 100 \ # 数据量 500 张内跑 100 轮足够 --cache ram \ # 小数据集直接缓存到内存加快训练 --hyp hyp.scratch-low.yaml # 人脸场景建议用 lower 的增强参数避免过度裁剪参数的解释逻辑是这样的--weights yolov5s.pt做迁移学习比从零训练收敛快得多尤其在你数据量不足一千张时预训练权重里的基础特征能帮你大幅压低 loss--hyp hyp.scratch-low.yaml里的增强参数比较克制不会对你的人脸做大幅旋转和裁剪因为人脸一旦被裁剪掉关键部位标签框就错位了这个错位在标注工具阶段不明显在训练阶段会被放大--cache ram适合图片总量在 3-5G 以内的小数据集能省去每轮重复读磁盘的 IO 时间。epochs 设置 100 但配合早停回调使用YOLOv5 默认会在 val mAP 连续 30 轮没有提升时自动停止。所以你不需要为了追求更高质量硬拉 epochs相反跑完之后检查runs/train/exp/weights/best.pt和last.pt的 val 指标如果 mAP0.5 有 0.98 以上说明数据标注质量不错如果在 0.85 以下优先去检查数据清洗和 label 坐标是否准确而不是换更大的模型。5.3 用训练好的模型反向再做一轮自动标注闭环迭代这是半自动标注工具最值钱的一种用法。第一轮人工修正完训练出你自己的 YOLOv5 模型以后再拿这个模型跑到那些还没标注的新图片上做预标注。因为你的模型是在你自己的数据分布上训练的粗标准确率会明显好于预训练权重的结果人工修正量从 50% 下降到 10% 左右都是可能的。而且每次迭代后模型会越来越强标注成本越来越低。我自己的习惯是把每次训练出的 best.pt 存一个带时间戳的副本防止后续迭代把权重改坏后找不到后悔药。6. 一个能再省一倍时间的进阶技巧批量检测的动态阈值策略最后一章分享一个我实际用下来觉得最值得照做的技巧。大多数标注工具的推理脚本用固定置信度阈值但真实数据集往往是“大部分图片人脸清晰大张少部分图片人脸很小或模糊”。固定阈值 0.25 时小脸容易被过滤掉漏标完了人工还得补调低到 0.1所有图片都会多出一堆误检框人工删框也浪费时间。我现在的做法是把阈值改成按批次动态调整——每张图片跑出检测结果后首先判断检测框的数量和置信度分布如果这张图检测出的框数超过 10 个就把置信度阈值提高到 0.4 过滤低质量框如果检测出的框数少于 2 个就把阈值降低到 0.1 重新跑一次确保难样本也能出来。这种策略比固定阈值多一次推理开销但只会在极端图片上触发整体标注效率提升反而明显。代码实现不复杂def dynamic_detect(model, img_rgb, base_conf0.25, img_size640): results model(img_rgb, sizeimg_size) det results.pandas().xyxy[0] if len(det) 10: model.conf 0.4 # 高阈值过滤误检 results model(img_rgb, sizeimg_size) elif len(det) 2: model.conf 0.1 # 低阈值召回小脸 results model(img_rgb, sizeimg_size) model.conf base_conf # 复位避免影响下一张图 return results.pandas().xyxy[0]这个函数在每一张图上调用时内部逻辑是“先按基准阈值跑一次再根据结果做第二次自适应重跑”。参数说明base_conf 是基准置信度一般取 0.25数量阈值 10 和 2 不是拍脑袋定的而是根据你数据集平均每张人脸数设置的如果你项目里平均每张图就 2 张人脸那阈值要下调到 5 和 1。用这个思路后我再也没遇到过“大图漏标了一堆小脸”的情况。我把这套流程跑成习惯以后最大的感受是标注工具的价值不只是省点鼠标更重要的是它逼着你把“模型 → 数据 → 人工”之间的协作关系想清楚。不要一上来就追求一次把模型训到多强先把自动标注的闭环转起来让数据自己滚起来效果比死磕某一个模型好得多。希望这篇能帮到你。本文还有配套的精品资源点击获取