
简介VOC电视检测数据集源自PASCAL VOC 2007挑战赛——计算机视觉领域广泛使用的物体检测基准专为电视/显示器tvmonitor这一单一类别构建面向计算机视觉方向学生、算法工程师及智能监控、家电识别应用开发者用于快速验证和迭代目标检测算法。压缩包共838个文件大小约21.27MB包含279张jpg原始图片、279个xml详细标注记录边界框、类别等对象级信息和280个txt坐标标签txt与xml两种格式可平滑适配YOLO、Faster R-CNN、SSD等主流检测框架省去格式转换烦恼。虽然样本量不大但足以支撑初步模型训练、参数调优与精度验证适合作为入门或算法对比数据集。目前已有282人浏览学习。基于该数据集构建的模型可落地于智能监控的电视屏幕识别、家庭自动化设备控制、增强现实中的虚拟屏幕叠加等真实场景兼具教学与工程参考价值。1. 拿到tvmonitor_VOCtrainval2007.zip这份电视监控检测数据集到底能做什么做电视监控屏检测的人都会遇到同一个尴尬公开数据集里大多是“电视”而不是“监控画面里的电视屏幕”一到真实机房场景就漏检。tvmonitor_VOCtrainval2007.zip是把 PASCAL VOC 2007 的 trainval 子集中属于 tvmonitor 类别的样本抽出来单独打包的数据集目的是让使用者不用再去翻几千个 XML 找电视监控屏的正样本。它适合用来跑通 YOLO 检测流程、做小样本微调以及验证数据增强和部署前后处理但不适合拿过来直接当生产数据用。原因是 VOC 2007 本身拍摄环境和监控机位差异很大必须先做转换、划分和难例过滤才能谈训练效果。2. 认识 VOC 2007 的 tvmonitor 子集目录结构、标注字段与 trainval 的取舍2.1 VOC 2007 的 tvmonitor 样本在文件系统里长什么样VOC 2007 官方数据组织方式是VOCdevkit/VOC2007下面三个目录和检测任务直接相关VOCdevkit/ └── VOC2007/ ├── JPEGImages/ # 原始图像jpg 格式 ├── Annotations/ # 每张图对应一个 XML 标注文件 └── ImageSets/ └── Main/ # 各个类别的 train / val / trainval 划分文件我拿到这类 zip 后第一件事不是解压而是先看它保留了哪些目录。有的打包者只挑出包含 tvmonitor 的图片和标注ImageSets 里可能只剩 tvmonitor_train.txt 和 tvmonitor_val.txt有的会保留完整的 VOC2007 结构。这两种都能训练区别在于要不要自己重新划分数据集。如果是完整结构直接信任官方划分如果只有图片和 XML就需要按比例拆分并且尽量保证正样本和负样本的比例合理。JPEGImages 里一张图可能同时有 person、chair、tvmonitor 等多个目标Annotations 里对应的 XML 会把所有目标都描述出来。即使这个 zip 是 tvmonitor 子集也不要假设每张图只有电视监控屏先拿 XML 统计一遍再决定转换脚本怎么写。2.2 一个 tvmonitor 的 XML 标注到底写了哪些信息VOC 2007 的标注格式比 COCO 的 JSON 透明得多一个典型示例长这样annotation folderVOC2007/folder filename008888.jpg/filename source databaseThe VOC2007 Database/database /source size width500/width height333/height depth3/depth /size object nametvmonitor/name poseFrontal/pose truncated0/truncated difficult0/difficult bndbox xmin185/xmin ymin96/ymin xmax360/xmax ymax246/ymax /bndbox /object /annotationxmin/ymin/xmax/ymax是目标左上角和右下角的像素坐标truncated1表示物体被图像边界截断difficult1表示该目标本身难以辨别官方评测时会忽略它。这里最难处理的就是 difficult 字段。很多人写转换脚本时把所有 object 全部当作正样本输出结果验证集 mAP 一直上不去问题就出在把难例当成标准正例去拟合。tvmonitor 类别的标注还有一个视觉特点屏幕本身通常是一个深色矩形但边框、底座、背后墙面都会进入包围框。如果直接按 bndbox 裁剪做分类会发现样本之间差异非常大有的电视只露出一角有的是完整的带底座电视柜。这是数据集本身的特点不是标注错误。2.3 为什么 trainval2007 比 test2007 更适合做落地训练VOC 2007 官方把数据分为 train、val、test 三部分这个 zip 名字里的 trainval 就是 train 加 val。trainval 的优势在于它是完整的监督信号来源训练时用 train验证时用 val两边都有标注不需要额外找一套标注数据来调参。test 部分虽然也有公开标注但在很多实际项目里大家习惯把 test 留到最后模拟线上评测或者直接用 VOC 官方服务器算指标。另外一个现实原因是tvmonitor 类别的正样本数量在 20 个类别里偏少如果只拿官方 train 部分做训练数据量会更紧张。把 trainval 整体拿进来然后在内部做一个分层划分比只用 train 更踏实。常见做法是把 ImageSets/Main/tvmonitor_train.txt 里的图像作为训练集tvmonitor_val.txt 作为验证集训练脚本只吃前者验证脚本只吃后者避免自己随机切分造成同一张图的相似帧同时出现在训练和验证中。3. 解压和盘点 zip 内的电视监控样本校验、过滤与类别分布一次说清3.1 zip 完整性校验与解压先别急着双击从网上下载的数据集 zip最常见的问题是下载不完整或压缩包伪加密。所谓伪加密是 zip 头部的加密标志位被置位但文件实际没有密码保护Windows 自带解压工具会弹密码框导致很多人误以为压缩包加密了。遇到这种情况先做完整性校验而不是直接去找密码。# 查看压缩包里的文件清单确认目录层级 unzip -l tvmonitor_VOCtrainval2007.zip | head -30 # 测试压缩包完整性末尾几行会显示 OK 或错误信息 unzip -t tvmonitor_VOCtrainval2007.zip | tail -5 # 完整解压到工作目录 mkdir -p ~/work/tvmonitor unzip -q tvmonitor_VOCtrainval2007.zip -d ~/work/tvmonitor第一条命令用于确认是不是预期的VOCdevkit/VOC2007结构第二条命令会逐个测试每个文件的 CRC 校验如果输出里有bad CRC或者Incorrect file size说明文件已经损坏。第三条命令解压后建议再用du -sh ~/work/tvmonitor看一眼总大小和压缩包尺寸做对比。我一般不会用unzip -o强制覆盖因为损坏的 zip 可能解出一半文件覆盖行为会让后续检查变得不可信。如果unzip -t报错但又能看到大部分文件名这个 zip 八成是伪加密或者文件头损坏。Linux 下可以试zip -FF damaged.zip --out fixed.zip修复Windows 下用 7-Zip 的“打开压缩包”功能工具识别到伪加密时通常能直接进去不需要输入密码。修复后重新跑一遍unzip -t直到没有报错再继续。3.2 用 ImageSets 过滤 tvmonitor 正样本一条命令生成图像清单VOC 的 ImageSets/Main 目录里每个类别对应三个文本文件tvmonitor_train.txt、tvmonitor_val.txt、tvmonitor_trainval.txt。每行是“图像文件名 标志位”标志位 1 表示该图包含 tvmonitor-1 表示不包含。这个文件是后续划分训练集和验证集的依据。cd ~/work/tvmonitor/VOCdevkit/VOC2007 # 查看 tvmonitor 训练清单格式 cat ImageSets/Main/tvmonitor_train.txt | head -10 # 提取所有包含 tvmonitor 的训练图像 id awk $2 1 {print $1} ImageSets/Main/tvmonitor_train.txt tv_train_positive.txt # 同样提取验证集正样本 awk $2 1 {print $1} ImageSets/Main/tvmonitor_val.txt tv_val_positive.txt # 统计各自数量 wc -l tv_train_positive.txt tv_val_positive.txt注意这里过滤的是“包含 tvmonitor 的图片”不是“只有 tvmonitor 的图片”。一张图里可能同时有 tvmonitor 和 person它仍然会出现在 tvmonitor_train.txt 中标志位为 1。如果你的项目目标是只检测电视监控屏其他目标在训练时会被当作背景这会让模型把 person、沙发等物体也学成背景特征后面在第 5 章会展开讲。awk 命令里$2 1是严格匹配不要写成$2因为有些格式里标志位是-1直接取$2会把负样本也带进来。3.3 实例级统计打开 zip 才发现类别不平衡就晚了很多人在这一步直接跳过统计解压完就开始转换格式结果训练到一半发现正样本只有几十张。tvmonitor 在 VOC 2007 里属于样本数量较少的类别我不建议只看图片数量而是要看 XML 里 tvmonitor 框的总数因为一张图可能有两个电视监控屏。# 统计所有 XML 中 tvmonitor 标注的总数 grep -h nametvmonitor/name Annotations/*.xml | wc -l # 统计包含 tvmonitor 的图片数量 grep -l nametvmonitor/name Annotations/*.xml | wc -l # 统计同时存在多个监控屏的图片 grep -l nametvmonitor/name Annotations/*.xml | while read f; do count$(grep -c nametvmonitor/name $f) if [ $count -gt 1 ]; then echo $f: $count; fi done第一个数字除以第二个数字就是平均每张图的实例数。如果平均实例数接近 1说明大多数场景是单屏如果明显大于 1说明数据里有不少多屏场景训练时要注意 batch 内正样本的分布。类别不平衡在这里有三个层级图片级很多图没有 tvmonitor、实例级有 tvmonitor 的图里屏数量不同、特征级白天室内场景占绝大多数夜间监控场景几乎没有。前两个统计命令能看明白第三个层级只能靠数据增强和外部数据补。这个统计结果也决定了后续训练参数。如果正样本图片不到 200 张就不要用太小的 batch 配大模型先用yolov8n或yolov8s做基线把增强参数拉开再去考虑模型容量。4. 把 VOC 数据转成 YOLOv8 训练集归一化坐标脚本、官方划分与关键参数4.1 VOC XML 转 YOLO txtPython 脚本与坐标归一化逻辑YOLOv8 训练时需要的标注格式是每个目标一行class_id x_center y_center width height四个坐标值都是相对于图像宽高的归一化值。VOC 的 XML 给出的是像素绝对坐标转换时必须做归一化否则训练直接崩。# voc2yolo.py # 用法: python voc2yolo.py --xml_dir Annotations --out_dir labels import argparse import xml.etree.ElementTree as ET from pathlib import Path # VOC 2007 官方 20 类顺序tvmonitor 在最后 VOC_CLASSES [ aeroplane, bicycle, bird, boat, bottle, bus, car, cat, chair, cow, diningtable, dog, horse, motorbike, person, pottedplant, sheep, sofa, train, tvmonitor ] def convert_annotation(xml_path, label_dir): tree ET.parse(xml_path) root tree.getroot() # 读取图像实际尺寸归一化以它为基准 size root.find(size) img_w int(size.findtext(width)) img_h int(size.findtext(height)) img_name root.findtext(filename) lines [] for obj in root.iter(object): name obj.findtext(name) difficult int(obj.findtext(difficult, 0)) # difficult1 样本不参与训练这是 VOC 官方的评测口径 if difficult 1 or name not in VOC_CLASSES: continue box obj.find(bndbox) xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) # 转 YOLO 归一化中心点 宽高全部除以图像尺寸 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 class_id VOC_CLASSES.index(name) lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) if not lines: return label_path label_dir / (Path(img_name).stem .txt) label_path.write_text(\n.join(lines)) def main(): parser argparse.ArgumentParser() parser.add_argument(--xml_dir, typestr, requiredTrue) parser.add_argument(--out_dir, typestr, requiredTrue) args parser.parse_args() xml_dir Path(args.xml_dir) label_dir Path(args.out_dir) label_dir.mkdir(parentsTrue, exist_okTrue) for xml_path in sorted(xml_dir.glob(*.xml)): convert_annotation(xml_path, label_dir) if __name__ __main__: main()这个脚本有两个关键参数值得说。第一个是difficult过滤逻辑obj.findtext(difficult, 0)表示 XML 里没写 difficult 字段时按 0 处理只有显式等于 1 才跳过。第二个是坐标归一化分母必须用 XML 里size标签的宽高而不是脚本自己去读图片宽高。原因是一个数据集如果图片被压缩过存储宽高和原图宽高可能不一致以 XML 为准才是标注时的原始坐标系。如果你的 zip 是纯 tvmonitor 子集想只保留电视监控屏这一个类别可以把VOC_CLASSES改成[tvmonitor]这样其他目标不会生成 label。但这种做法的副作用是模型会把 person、chair 等对象全部当作背景后面调参时如果发现假阳性很高就需要考虑要不要把这些对象标成负样本类别。4.2 官方 train/val 划分与 data.yaml 配置YOLOv8 期望的数据目录结构是images/train、images/val、labels/train、labels/val四个目录。把第 3 章生成的 tvmonitor_train_positive.txt 对应图片复制到 images/train再手动把同名的 txt 从 labels 目录同步过去。cd ~/work/tvmonitor/VOCdevkit/VOC2007 # 先转换所有 XML生成全部 label python voc2yolo.py --xml_dir Annotations --out_dir labels_all # 创建 YOLO 目录结构 mkdir -p yolo/images/train yolo/images/val yolo/labels/train yolo/labels/val # 按官方划分复制训练集图片和标签 while read img_id; do cp JPEGImages/${img_id}.jpg yolo/images/train/ cp labels_all/${img_id}.txt yolo/labels/train/ done tv_train_positive.txt # 验证集同样处理 while read img_id; do cp JPEGImages/${img_id}.jpg yolo/images/val/ cp labels_all/${img_id}.txt yolo/labels/val/ done tv_val_positive.txt复制 label 时有个容易漏的点如果脚本因为 difficult 过滤没生成某个图的 txt但该图在 tvmonitor_train_positive.txt 里cp会报错。这不是脚本问题而是那个图的 tvmonitor 样本全是 difficultYOLOv8 就会少一条正样本。此时建议把没有 txt 的图片也从训练清单里移除避免模型只看到背景。data.yaml是 YOLOv8 的数据集描述文件注意path写绝对路径最稳相对路径在换机器训练时容易找不到文件。path: /root/work/tvmonitor/VOCdevkit/VOC2007/yolo train: images/train val: images/val names: 0: tvmonitor这里names的索引必须和转换脚本里的class_id对上。如果脚本用的是完整 20 类列表tvmonitor 的 class_id 是 19yaml 里就要写成names: 19: tvmonitor只保留一个类别时也可以把脚本输出改成 0yaml 从 0 开始二者二选一保持统一即可。4.3 yolov8 训练命令与增强参数小样本数据集的调参取舍tvmonitor 子集的数据量不大训练命令不建议直接抄默认参数至少要改几个和增强、早停相关的配置。cd ~/work/tvmonitor yolo detect train \ datatvmonitor.yaml \ modelyolov8n.pt \ epochs120 \ imgsz640 \ batch16 \ patience15 \ close_mosaic10close_mosaic10的意思是训练最后 10 个 epoch 关闭 mosaic 增强。mosaic 增强对小数据集有明显收益它会把四张图拼成一张让模型见过更多背景组合但在训练后期mosaic 生成的样本和真实场景差异较大容易让回归头不稳定所以官方推荐在收尾阶段关闭。patience15表示验证集指标连续 15 个 epoch 不提升就停止小数据集用过拟合很快不设早停容易浪费时间。yolov8n.pt是从 COCO 预训练权重开始微调这个对 tvmonitor 训练非常重要。从零训练小数据集几乎必然过拟合用预训练权重相当于让模型已经知道“矩形物体”概念只是重新学电视监控屏的细节。如果显存充足yolov8s也可以试但 n 和 s 在几十张到一两百张的样本量下差距不明显先跑 n 做基线更合理。我还建议在训练前把图像元数据检查一遍有的 JPEG 是灰度图有的带 alpha 通道YOLO 读取时不会报错但训练统计量会被污染。5. 训练 tvmonitor 模型的 5 个高频坑现象、原因与排查解法5.1 把 difficult1 的样本当成正样本验证集 mAP 全报废现象训练损失正常下降验证集 mAP50 却一直徘徊在 0.3 附近而且置信度阈值越低误检框越多。原因VOC 2007 里一部分 tvmonitor 目标被标注为difficult1官方认为这些目标本来就难以辨认。如果转换脚本不看这个字段把它们全部当作正样本模型就会去拟合“模糊屏幕”这种特征推理时把墙上的装饰画、黑色窗户一并检测成电视监控屏。解决严格参照 4.1 脚本中的过滤逻辑在转换时跳过difficult1。这个字段不只是训练问题如果你后面要跑 VOC 官方评估评估代码同样会忽略 difficult 样本两边口径必须一致。另外注意异常值有些 XML 的 difficult 字段写的是字符串而不是数字用int()强转前建议先打印原始值确认。5.2 训练集全是室内日常场景上线后被夜间监控画面打穿现象白天样本验证 mAP 达到 0.8换到摄像头夜间补光画面几乎全部漏检同一块屏幕在红外下变成灰白色模型完全不认。原因VOC 2007 的数据采集环境主要是自然光下的室内外日常照片不是安防监控视角。监控场景里的电视屏幕有反光、信号干扰条纹、频闪屏幕区域还经常被 IR 补光照得发白这些特征在 VOC 里属于分布外数据。解决训练时把增强参数往拍摄环境方向拉常见做法是启用随机亮度和 Gamma 变换yolo detect train \ datatvmonitor.yaml \ modelyolov8n.pt \ epochs120 \ imgsz640 \ batch16 \ hsv_h0.1 \ hsv_s0.3 \ hsv_v0.4 \ degrees5其中hsv_v0.4是对亮度做最大幅度扰动模拟夜晚低照度degrees5是因为监控画面不会大幅旋转给太多角度反而让模型学到错误特征。如果项目允许收集少量现场数据我一般会专门标 50 张夜间监控画面做微调这比任何增强都管用。5.3 copy-paste 增强后标签没同步移动模型学到错位框现象训练损失震荡验证集定位误差大打印图片发现电视屏幕的位置和标注框明显错位。原因为了凑 tvmonitor 样本量很多人会自己写 copy-paste 增强把一张图的电视屏幕剪贴到另一张图但粘贴时只改图像像素没改目标框坐标。这种情况在用 albumentations 或 OpenCV 手写增强时特别常见。解决用 albumentations 做增强时必须把 bbox 参数一起传进去并且指定formatyolo否则工具不知道输入坐标格式import albumentations as A transform A.Compose( [ A.RandomBrightnessContrast(p0.5), A.HueSaturationValue(p0.3), ], bbox_paramsA.BboxParams( formatyolo, label_fields[class_labels], min_visibility0.3, ), )min_visibility0.3表示增强后框与目标的重叠率低于 30% 时丢弃该框这个参数能有效防止裁剪增强导致的标签失效。一个根上的经验是不要自己手写 copy-paste直接用 YOLOv8 内置的 mosaic 和 mixup至少标签不会错。5.4 Annotations 里是 .JPG 但 JPEGImages 里是 .jpg训练时负样本被放大现象训练日志里图片数量正常但 val 阶段大量图片没有标注mAP50 低得离谱排查后发现很多空 txt 文件。原因Windows 解压 zip 时不会改变文件名大小写但 Linux 文件系统大小写敏感。如果 XML 里filename008888.JPG/filename而磁盘图片叫 008888.jpg转换脚本生成的是 008888.txtYOLO 找图 008888.jpg 时实际是能找到的问题出在 OCR 对不上或者训练集复制阶段用的$img_id和实际文件名不一致导致 label 复制失败。没有 label 的图片被当成纯背景正样本比例瞬间被稀释。解决先统一文件名再跑脚本cd ~/work/tvmonitor/VOCdevkit/VOC2007 # 把所有 JPG 后缀改成小写 find JPEGImages -name *.JPG -exec bash -c mv $1 ${1%.JPG}.jpg _ {} \; # 检查 XML 里的 filename 是否都能在 JPEGImages 中找到 python3 - PY import xml.etree.ElementTree as ET from pathlib import Path for xml in sorted(Path(Annotations).glob(*.xml)): root ET.parse(xml).getroot() img root.findtext(filename) if not Path(JPEGImages, img).exists(): print(xml.name, img) PY这一步的输出如果是空的说明文件名全部对齐可以进入训练。我个人的习惯是无论解压环境是什么都在 Linux 下先统一后缀再做后续操作能省很多排查时间。5.5 zip 伪加密或分卷遗漏解压出来的 VOC 目录缺一半现象解压过程没有报错但 Annotations 和 JPEGImages 的数量差很多或者 ImageSets/Main 目录里只有 train 没有 val。原因下载工具中断导致的 zip 结构损坏遇到伪加密时某些解压器会直接跳过部分文件只解压出它能识别的条目。这类问题在 Windows 自带解压器上表现最明显它遇到伪加密条目弹密码框取消后后续文件全部中止。解决Linux 先尝试zip -FF修复Windows 用 7-Zip 打开压缩包后执行“修复”操作工具会重建压缩包索引。修复后一定重新跑unzip -t不要只看文件能列出来就认为完整。如果修复失败直接重新下载是最稳妥的别在损坏 zip 上花太多时间。6. 用 VOC 2007 的 mAP 协议给电视监控检测模型做终检一条命令与两道自查6.1 验证命令与指标含义训练完成后验证要用官方划分的 val 子集而不是训练时见过的数据。YOLOv8 的 val 命令会按你自己的 data.yaml 解析验证集并输出 mAP50 和 mAP50-95。cd ~/work/tvmonitor yolo detect val \ datatvmonitor.yaml \ modelruns/detect/train/weights/best.pt \ splitval输出结果里重点看三个指标指标含义参考表现mAP50IoU 阈值为 0.5 时的平均精度类内样本差异不大时应在 0.7 以上mAP50-95IoU 从 0.5 到 0.95 的平均精度对 tvmonitor 这种矩形目标通常比 mAP50 低 15 到 25 个点AR100单图最多取 100 个预测框时的平均召回目标数量少时不应低于 0.8注意这里的“参考表现”依赖训练集规模和难例比例不是固定标准。如果你的 tvmonitor 正样本都是困难标注mAP50 偏低是数据集决定的这时应该回到第 3 章重新审视 difficult 过滤逻辑而不是盲目加大模型。6.2 两道自查看曲线和看错例我对这类小类别模型有个固定习惯先看 PR 曲线再看置信度阈值。PR 曲线如果在中高召回区间出现明显断崖说明模型把一部分背景当成目标如果曲线整体靠右说明置信度分布合理可以直接用 0.25 做推理阈值。第二道自查是在验证集上找 10 张含多屏场景的图逐个看预测框和标注框的 IoU。mAP 是统计指标多屏场景的平均框位置偏移会被单屏场景稀释肉眼抽查能发现模型是不是只学会了一个固定宽高比。这类矩形检测目标很容易退化成“找深色矩形”如果发现验证集精度高但框全部卡在某个尺寸回到第 4 章把训练集里不同宽高比样本的分布打出来再决定是否补数据。如果验证集结果正常但真实监控画面仍然漏检不要再调训练参数先采集现场画面做一轮 hard example 挖掘。我现在的做法是训练结束后永远保留 best.pt 和 last.pt 两份权重last.pt 虽然 mAP 低一些但在某些光照条件下反而更稳线上部署前用现场样本对比一下再选这个习惯帮我躲过好几次光照泛化翻车。希望这篇笔记能让你拿到tvmonitor_VOCtrainval2007.zip后少走弯路从解压到 mAP 验证一遍跑通。本文还有配套的精品资源点击获取