首页 / 资讯中心 / 文章详情

人脸表情检测数据集VOC转YOLO实战:8279张标注与避坑指南

人脸表情检测数据集VOC转YOLO实战:8279张标注与避坑指南 ★ FEATURED ARTICLE
简介面向目标检测与人脸表情识别方向的研究者和开发者此数据集包含8279张真实人脸表情图片和8279个对应的XML标注文件标注了fear、sad、surprised、contempt、anger、neutral、disgust、happy共8类表情每类约1035个标注框类别分布均衡可直接用于YOLO、SSD、Faster R-CNN等主流检测模型的训练与评估。数据采用Pascal VOC格式由labelImg工具画矩形框完成标注规范附带说明txt还提供各类别框数统计便于划分训练集、验证集与测试集。压缩包内共16559个文件其中jpg图片与xml标注文件各8279个且一一对应另含1个说明文件整体容量约890.41MB文件名统一为emotion_编号适合脚本批量处理和后续数据增强。此数据集为A版与B、C、D版文件无交叉重复可合并使用以大幅扩充训练样本。目前已有1162人浏览学习能有效节省数据采集与人工标注时间适合算法实验、毕业设计及真实场景下表情识别模型的迭代优化。1. 人脸表情检测数据集8279张VOC标注能直接喂给YOLO训练吗做目标检测的同行都清楚人脸表情识别这种任务最耗时间的不是调模型而是攒数据。自己拿labelImg一张张画框画到八百张就想吐更别说凑齐八类、每类一千多个框的均衡分布。这份数据集给的是Pascal VOC格式8279张jpg配8279个xml类别是fear、sad、surprised、contempt、anger、neutral、disgust、happy这8类每个类别框数压在1035左右分布非常干净。它不带任何分割路径txt也不带yolo格式的txt拿到手是一堆图片加标注文件需要自己转格式、自己划分训练集。这对新手是好事——转换过程能把VOC结构彻底摸一遍对熟手也就是一条命令的事。适合正在做人脸表情识别、课堂专注度检测、驾驶疲劳预警这类场景的前期数据储备。2. Pascal VOC格式拆解xml里到底存了什么2.1 一张jpg配一个xml先看标注文件长什么样这份数据集每个jpg文件对应用一个同名xmlxml里存的是目标类别名和矩形框坐标。VOC的xml结构是固定套路根节点是annotation下面有folder、filename、size、object这些子节点。打开一个典型的标注文件看到的内容结构大致是这样annotation folderemotion_dataset/folder filenameemotion_1125.jpg/filename size width640/width height480/height depth3/depth /size object namehappy/name bndbox xmin120/xmin ymin80/ymin xmax300/xmax ymax260/ymax /bndbox /object /annotation文件名和标注文件是严格对应的emotion_1125.jpg对应emotion_1125.xml不存在一张图多个xml的情况。size里给的是原图宽高转换成YOLO格式时要用它做归一化分母bndbox里的四个整数是矩形框的像素坐标左上角xmin, ymin右下角xmax, ymax。这里有个容易忽略的点xml里filename字段不一定可靠很多工具导出时会带上相对路径或改过文件名我一般直接遍历xml文件名来配对而不是读xml里的filename字段。2.2 类别均衡性8类各1000多框这个分布意味着什么这份数据集的标注统计非常干净每个类别标注的框数几乎一样只有neutral是1034框其余7类都是1035框。这个差异只有1个框基本可以忽略但在训练时有个实际好处类别均衡意味着不用做复杂的重采样直接用默认的loss权重就能训练。类别框数fear1035sad1035surprised1035contempt1035anger1035neutral1034disgust1035happy1035不过要提醒一句框数均衡只代表标注数量均衡不代表每个框的目标大小均衡。人脸检测里最常见的坑就是小目标太多如果原图分辨率不高很多脸框占整图面积不到5%YOLO的默认anchor尺寸会不太友好后面训练时建议把imgsz调大或者开mosaic增强。另外A、B、C、D四个版本的文件名没有交叉重复这意味着你可以把四个版本全部下下来混合训练数据量直接翻四倍类别分布依然是均衡的。我自己会先验证一下各版本之间图像分辨率分布是否一致再决定混合策略。3. 把VOC转成YOLO格式转换脚本与四个边界坑3.1 转换脚本从xml读出坐标再归一化VOC格式本身不能直接喂给YOLO训练YOLO系列要求txt文件每行是类别id x_center y_center width height四个坐标值都是相对于图片宽高的比例必须是0到1之间的小数。转换脚本是最常规的写法用Python的xml.etree.ElementTree解析import xml.etree.ElementTree as ET import os voc_root dataset_8279 # 存放jpg和xml的目录 yolo_root yolo_labels # 输出的yolo标注目录 os.makedirs(yolo_root, exist_okTrue) class_names [fear, sad, surprised, contempt, anger, neutral, disgust, happy] for xml_file in sorted(os.listdir(voc_root)): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_root, xml_file)) root tree.getroot() # 读取原图尺寸做归一化分母 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) if img_w 0 or img_h 0: print(warning: zero size in, xml_file) continue lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: print(unknown class:, name, in, xml_file) continue cls_id class_names.index(name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 归一化到0~1 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) base xml_file.replace(.xml, ) with open(os.path.join(yolo_root, base .txt), w) as f: f.write(\n.join(lines)) print(done, total files:, len(os.listdir(yolo_root)))这段脚本的逻辑是遍历所有xml解析每张图的宽高再逐个读取object节点里的类别名和框坐标转成中心点加宽高的归一化格式。几个参数建议class_names的顺序就是YOLO训练时的类别顺序不能随意改一旦训练开始这个顺序要写进data.yaml里保持一致保留6位小数足够再长也提升不了精度反而让文件变大。跑完检查一下输出的txt数量和xml数量是否一致这一步能挡住90%的目录路径问题。3.2 路径映射与训练集划分别把txt和图片放错目录很多人转换完就直接训练结果YOLO报错找不到图片。核心原因是YOLO的txt里只写了标注图片路径要在data.yaml里配。推荐目录结构是这样emotion_dataset/ ├── images/ │ ├── train/ # 6623张jpg │ └── val/ # 1656张jpg ├── labels/ │ ├── train/ # 对应txt │ └── val/ # 对应txt └── data.yaml用8比2划分的话8279张图训练集6623张、验证集1656张。划分时注意两点第一jpg和对应的txt必须同时进入同一个子集不能图片在train而标注在val第二这份数据集没有提供现成的train.txt/val.txt需要自己生成文件列表。常见做法是随机打乱后按比例划分同时把图片文件复制到images/train和images/val同名的txt复制到对应labels目录。不要偷懒只改扩展名关联YOLO的Loader是默认在同路径下找同名txt的。3.3 四个边界坑坐标越界、空框、类别名大小写、文件名后缀转格式不是跑通脚本就完了容易翻车的细节都在数据里。第一条是坐标越界labelImg画框手一抖可能出现xmax大于图片宽度、ymax大于图片高度的情况归一化后数值超过1YOLO训练时直接报错或产生NaN loss。症状是loss突然变成nan或者训练过程正常但验证集mAP是0。解决办法是在转换脚本里加一句np.clip把归一化后的w和h限制在0到1之间也可以顺手检查xminxmax、yminymax不满足的直接跳过。第二条是空框某些xml文件里object节点存在但坐标读出来是0转换后txt是空文件。YOLO不会报error但会把这张图当背景图处理拉低召回率。我的习惯是转换完统计一下每个txt的行数有0行的单独列出来看看是不是漏标。第三条是类别名大小写和空格标注时写上Neutral或者Happy 这类带空格的名字会让class_names.index(name)直接抛异常。数据集的类别名是统一小写没空格的但混合B、C、D版本训练时最好跑一遍类别名统计保证四份数据的类别名完全一致再合并。第四条是文件名后缀有的xml里filename写的是.jpeg而实际文件是.jpg如果直接用filename字段去读图片会找不到文件。正确习惯是遍历xml文件名生成配对不信任xml内部的filename字段。4. 用YOLOv8跑起来数据配置与训练参数4.1 data.yaml怎么配路径、类别名、类别数量YOLOv8的数据配置文件是训练的入口写错一个路径全部白搭。本数据集是8类我这个场景下data.yaml是这么写的path: /home/user/emotion_dataset # 改成你自己的绝对路径 train: images/train val: images/val names: 0: fear 1: sad 2: surprised 3: contempt 4: anger 5: neutral 6: disgust 7: happy注意names字典的键值顺序必须和转换脚本里class_names的顺序完全一致。fear是id 0happy是id 7中间不能跳号。path建议写绝对路径相对路径在换工作目录时容易踩坑。如果你用的是YOLOv5或YOLOv6配置项基本一致只是有些版本要求nc: 8显式声明类别数量加上也无妨。4.2 训练命令与参数怎么设从yolov8n起步数据集不算大8279张图纯检测任务我一般先用yolov8n跑一个baseline确认数据没问题再上yolov8s或yolov8m。训练命令yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ patience15 \ device0几个参数的实践值imgsz640是检测任务的默认值如果原图里的人脸都偏小建议试imgsz800batch16要看显存8G显存跑n模型没问题跑s模型就降到8patience是早停轮数设15到20之间比较稳设太小模型还没收敛就停了。这里有一个容易被忽略的点YOLO训练时默认开mosaic增强这在对人脸小目标检测时是把双刃剑mosaic把小脸裁切得更小训练后期如果验证集mAP到50%就上不去考虑把mosaic关掉再训一轮对比。4.3 验证与baseline精度不保证怎么设预期原数据集的描述说得很实在它不对训练出的模型权重精度作任何保证只保证标注准确合理。这意味着第一次跑验证集mAP可能在某个区间波动不要慌先看三个指标第一training loss曲线有没有稳定下降没有下降说明学习率或数据路径有问题第二验证集的mAP0.5是否大于mAP0.5:0.95正常情况下前者比后者高20到30个百分点第三每类的AP分布是否均匀如果某个类别AP特别低说明该类别的标注框质量或样本难度有问题。验证命令yolo detect val \ modelruns/detect/train/weights/best.pt \ datadata.yaml跑完看results.csv里的per-class AP。对表情识别这类细粒度任务mAP0.5:0.95达到0.5以上已经算可用因为contempt和neutral这种类别本身视觉差异就很微妙模型混淆是常态不是数据集的锅。5. 人脸表情检测避坑翻车记录与排查清单5.1 现象训练loss正常下降但验证mAP一直为0原因图片和txt文件划分不一致图片在train目录但txt漏复制或者txt文件名与jpg不一致。YOLO在加载时找不到对应标注会把该图当背景处理。解决先跑一遍脚本核对images/train下的jpg数量和labels/train下的txt数量是否一致再抽查5个文件名确保emotion_1125.jpg对应emotion_1125.txt。这种错往往出在shuffle划分时用了一套文件列表复制时却按另一个列表操作。5.2 现象训练中期loss出现NaN原因标注框坐标越界归一化后宽度或高度大于1。这份数据集标注质量整体靠谱但不排除个别xml里xmax比图片宽度还大YOLO算IoU时出现无效值。解决按3.3的说法在转换脚本里加clip修正对每个归一化后的w和h做min(max(..., 0), 1)限制。改完后重新生成全部txt不要只修复报错的那几张。5.3 现象pr曲线里某类AP明显低于其他类原因neutral这类表情本身就是“没有表情”框内特征不显著容易被误判成其他类另外收尾时neutral只有1034框比其余类少1个框虽然不影响训练但在小模型上可能造成最后一类学得不够。解决在data.yaml里给neutral设置稍高的类别权重或者在训练后单独查看它的混淆矩阵确认它主要被误判成哪些类再做针对性后处理。5.4 现象直接拿A版训练效果不错混合B版后指标反而下降原因四份数据集的图片来源可能不同光照、人脸角度、相机距离有差异混合后数据分布变了模型在训练初期需要更多epoch来适应。解决混合四版数据时先做一次分辨率统计如果各版本原图尺寸差异大统一用imgsz缩放不要单独缩放图片再标注。训练epoch适当增加早停阈值从10调到20。5.5 现象可视化时发现xmin大于xmax原因labelImg画框时拖动方向偶尔反向或者标注完成后手动编辑坐标时把左右数值填反。解决转换脚本里加一个坐标交换逻辑if xmin xmax: xmin, xmax xmax, xmin。这个检查放在归一化之前成本极低能挡掉后续所有的边界计算错误。6. 再往前走一步把VOC转成COCO给检测框架用以及表情增强别乱翻6.1 转COCO给mmdetection和Detectron2如果你不想只用YOLO想把这份数据喂给Faster R-CNN或者DETR需要转成COCO的json格式。VOC转COCO并不是复杂的事但要处理好categories的id映射和annotations的bbox顺序。COCO的bbox是x_min, y_min, width, height而YOLO是中心点加宽高很多人转完就忘改顺序结果训练时模型学了个寂寞。转换的重点是维护一个images列表和annotations列表每张图给一个递增的image_id每个框给一个递增的annotation_id类别id保持0到7。DETR这类Transformer模型要求每个batch里所有图的目标数量一致所以转换时最好统计一下每张图的最大目标数做batch padding时心里有数。6.2 数据增强的度人脸表情不能瞎翻转人脸表情检测有个反直觉的坑水平翻转这个增强在通用目标检测里是默认开启的但在表情识别上要谨慎。像surprised、disgust这类表情受面部不对称影响比较大歪嘴、挑眉这种细节翻转后会变样模型学到的是“翻转后的表情特征”推理时反而更糊涂。我一般只在训练集上做轻微的仿射变换和亮度扰动翻转开关保持关闭如果实在想加翻转只加垂直方向5度以内的小角度旋转不要做90度翻转。从那以后我每次拿到新数据集第一件事都是写一个统计脚本数文件对账、统计各类框数、随机画几张图叠加框可视化这三步走完才谈训练。尤其是可视化这一步能一眼看出坐标有没有错位、框有没有把人脸框全比等到训练完看mAP再回头找原因省一晚上时间。这份8类人脸表情的VOC数据类别均衡、格式干净按上面的流程走一遍很快能产出第一版可用的检测权重。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站