资讯动态

VisDrone2019 DET与VID标注转COCO格式实战指南

发布时间:2026/10/13 11:20:30 来源:尧图企业网站定制
简介这份资源面向计算机视觉初学者与需要处理无人机视角数据的研究者解决VisDrone2019数据集在目标检测与视频跟踪任务中格式不兼容的问题。它提供将DET目标检测与VID视频目标检测与跟踪两类标注转换为COCO格式的完整方案便于直接接入主流检测与跟踪框架进行训练和评测。压缩包共3个文件包含2个Python脚本与1份Markdown说明文档整体仅约3KB其中脚本承担核心转换逻辑说明文档则交代了需要修改的路径位置只需按提示调整路径即可运行对新手十分友好。目前已有2210人学习下载说明该方案在同类数据预处理需求中具备一定参考价值。读者可借此快速完成VisDrone到COCO的标注映射省去自行解析原始标注的时间同时理解DET与VID两种标注结构的差异为后续模型训练、视频目标跟踪实验及数据管线搭建提供可直接复用的工具脚本与排错思路。1. 无人机视角下的数据格式困局为什么 VisDrone 的 DET 和 VID 总让人卡在第一步做无人机视觉项目的人多半遇到过这个场景好不容易拿到 VisDrone2019 数据集兴冲冲想用主流检测框架跑一版 baseline结果发现标注文件是 txt 格式一行行逗号分隔跟 COCO 的 JSON 结构完全对不上。更麻烦的是VisDrone 同时提供了 DET静态图像检测和 VID视频序列跟踪两套标注两者的目录组织、标注字段含义、甚至坐标定义都有细微差别。很多人在这里就翻车了——要么直接放弃换数据集要么手写脚本转了一半发现类别映射错了训练时 loss 不降反升排查半天才发现是 category_id 从 0 开始还是从 1 开始的问题。这篇笔记要解决的就是这个卡点把 VisDrone2019 的 DET 和 VID 两种标注格式完整、正确地转换成 COCO 格式。COCO 之所以值得转是因为它几乎是目标检测和跟踪领域的事实标准主流框架的 dataloader 默认就吃这套结构转完之后你能直接复用大量现成的训练配置、评估脚本和可视化工具。适合正在做无人机检测、多目标跟踪或者想拿 VisDrone 做预训练再迁移到其他场景的从业者。下面从格式差异讲起一步步落到可复现的转换脚本和参数说明。2. 先搞清楚 VisDrone 的 DET 和 VID 到底差在哪动手写转换脚本之前必须先把两套标注的字段含义和目录结构吃透。很多人转换失败不是因为代码写得差而是因为对源格式的理解有偏差尤其是 VID 的帧间关联信息如果直接按 DET 的方式处理跟踪任务就没法做了。2.1 DET 标注的字段定义与目录组织VisDrone2019-DET 的目录结构通常是这样的VisDrone2019-DET-train/下面有images/和annotations/两个子目录图像和标注文件一一对应命名规则是0000001.jpg对应0000001.txt。标注文件每一行代表一个目标实例字段用逗号分隔依次是bbox_left, bbox_top, bbox_width, bbox_height, score, object_category, truncation, occlusion这里有几个容易踩坑的点。第一score字段在检测任务里通常表示标注置信度值为 1 表示有效标注0 表示忽略区域转换时一般只保留 score1 的样本。第二object_category的取值是 0 到 11其中 0 是忽略类1 到 10 是实际类别11 是其他。第三truncation和occlusion是 0/1 标志位COCO 格式里没有直接对应字段但可以塞进attributes或直接丢弃。坐标是左上角加宽高的绝对像素值这点和 COCO 的[x, y, width, height]一致不需要额外转换。2.2 VID 标注的帧间关联与序列结构VisDrone2019-VID 面向视频序列目录组织是sequences/下每个视频一个文件夹里面是连续帧图像标注文件在annotations/下同名 txt。字段比 DET 多两个frame_index, target_id, bbox_left, bbox_top, bbox_width, bbox_height, score, object_category, truncation, occlusionframe_index表示当前帧号target_id是跨帧跟踪的目标 ID。这两个字段是 VID 的核心价值——它们让同一目标在不同帧之间建立了关联。转换成 COCO 时如果只做检测可以忽略target_id按帧拆成独立图像如果要做跟踪就需要保留target_id并在 COCO 的annotations里额外记录或者转成 COCO 的 video 扩展格式。常见做法是检测任务按帧展开跟踪任务保留序列信息用video_id和track_id字段扩展。2.3 为什么 COCO 是下游任务的最优中转格式COCO 的 JSON 结构分三大块images记录图像元信息id、文件名、宽高annotations记录每个实例id、image_id、category_id、bbox、area、iscrowdcategories记录类别映射。这种结构的优势在于一是自描述所有信息在一个文件里不用再去解析目录二是生态成熟几乎每个框架都有现成的 COCO 解析器三是扩展性好加字段不影响原有解析。对于 VisDrone 这种多类别、多任务的数据集转成 COCO 后可以无缝接入检测、跟踪、甚至分割的 pipeline。代价是 JSON 文件会比 txt 大不少但这点存储开销换来的是工程上的便利值得。3. 手写转换脚本从 txt 到 COCO JSON 的完整实现理解了格式差异接下来就是落地。这一章给出一个可复用的转换脚本同时处理 DET 和 VID 两种输入输出标准 COCO JSON。脚本用 Python 写只依赖标准库和Pillow读图像尺寸不引入重型依赖。3.1 读取 VisDrone txt 并构建中间数据结构先定义一个解析函数把 txt 读成结构化列表。注意要处理空文件和异常行实际数据里偶尔会有格式不规范的行。import os import json from PIL import Image # VisDrone 类别映射0 是忽略类1-10 是实际类别11 是其他 VISDRONE_CATEGORIES { 0: ignored, 1: pedestrian, 2: people, 3: bicycle, 4: car, 5: van, 6: truck, 7: tricycle, 8: awning-tricycle, 9: bus, 10: motor, 11: others } def parse_visdrone_txt(txt_path, is_vidFalse): 解析 VisDrone 标注文件返回实例列表 instances [] if not os.path.exists(txt_path): return instances with open(txt_path, r) as f: for line in f: line line.strip() if not line: continue parts line.split(,) # VID 有 10 个字段DET 有 8 个 expected 10 if is_vid else 8 if len(parts) ! expected: continue # 跳过格式异常行 if is_vid: frame_idx, target_id, x, y, w, h, score, cat, trunc, occ map(int, parts) else: x, y, w, h, score, cat, trunc, occ map(int, parts) frame_idx, target_id 0, -1 # 只保留有效标注忽略 score0 和忽略类 if score 0 or cat 0: continue instances.append({ frame_index: frame_idx, target_id: target_id, bbox: [x, y, w, h], category_id: cat, truncation: trunc, occlusion: occ }) return instances这段代码的关键点expected字段数校验防止解析错位score 0和cat 0的过滤逻辑对应 VisDrone 的忽略区域定义VID 模式下保留frame_index和target_id为后续跟踪任务留数据。参数is_vid控制解析分支调用时根据数据集类型传入。3.2 构建 COCO 的 images、annotations、categories 三段结构COCO 的核心是三个列表的组装。images需要每张图的 id、文件名、宽高annotations需要全局唯一的 id、关联的 image_id、category_id、bbox、areacategories直接从类别映射生成。def build_coco(instances_by_image, image_dir, is_vidFalse): 将按图像分组的实例列表组装成 COCO 字典 coco { info: {description: VisDrone converted to COCO}, images: [], annotations: [], categories: [] } # 类别从 1 开始0 留给背景 for cid, name in VISDRONE_CATEGORIES.items(): if cid 0: continue coco[categories].append({id: cid, name: name, supercategory: object}) ann_id 1 img_id 1 for img_name, insts in instances_by_image.items(): img_path os.path.join(image_dir, img_name) with Image.open(img_path) as im: width, height im.size coco[images].append({ id: img_id, file_name: img_name, width: width, height: height }) for inst in insts: x, y, w, h inst[bbox] coco[annotations].append({ id: ann_id, image_id: img_id, category_id: inst[category_id], bbox: [x, y, w, h], area: w * h, iscrowd: 0, truncation: inst[truncation], occlusion: inst[occlusion] }) ann_id 1 img_id 1 return cocoarea字段是 COCO 评估时计算 mAP 用的必须填。iscrowd对 VisDrone 一般设 0因为它的忽略区域已经通过 score 过滤掉了。truncation和occlusion是额外加的字段标准 COCO 解析器会忽略它们但自定义分析时能用上。3.3 DET 与 VID 的分支处理与输出DET 和 VID 的主要差异在图像组织方式DET 是扁平目录VID 是序列目录。下面这个函数根据类型走不同分支。def convert_visdrone(root_dir, output_json, is_vidFalse): 主转换入口 instances_by_image {} if is_vid: seq_dir os.path.join(root_dir, sequences) ann_dir os.path.join(root_dir, annotations) for seq_name in sorted(os.listdir(seq_dir)): seq_path os.path.join(seq_dir, seq_name) if not os.path.isdir(seq_path): continue ann_path os.path.join(ann_dir, seq_name .txt) insts parse_visdrone_txt(ann_path, is_vidTrue) # 按帧号分组映射到具体图像文件名 frames {} for inst in insts: frames.setdefault(inst[frame_index], []).append(inst) for frame_idx, frame_insts in frames.items(): img_name f{seq_name}_{frame_idx:07d}.jpg instances_by_image[img_name] frame_insts image_dir seq_dir # 实际读取时需要拼序列路径 else: img_dir os.path.join(root_dir, images) ann_dir os.path.join(root_dir, annotations) for ann_file in sorted(os.listdir(ann_dir)): if not ann_file.endswith(.txt): continue img_name ann_file.replace(.txt, .jpg) insts parse_visdrone_txt(os.path.join(ann_dir, ann_file), is_vidFalse) instances_by_image[img_name] insts image_dir img_dir coco build_coco(instances_by_image, image_dir, is_vid) with open(output_json, w) as f: json.dump(coco, f) print(fSaved {len(coco[images])} images, {len(coco[annotations])} annotations)VID 分支里图像文件名需要按序列和帧号重新构造实际使用时要把image_dir改成能定位到具体帧的路径。DET 分支直接按文件名对应。输出 JSON 后可以用pycocotools加载验证。4. 转换过程中最容易翻车的五个坑转换脚本写起来不复杂但实际跑数据时总会有各种意外。这一章记录几个我踩过的坑按现象、原因、解决三段式写清楚。4.1 类别 ID 从 0 还是 1 开始导致训练 loss 异常现象转换完 JSON 直接拿去训练模型 loss 一直不降或者评估时 mAP 为 0。原因VisDrone 的类别从 0 开始但 0 是忽略类实际类别是 1 到 10。如果直接把原始 category_id 写进 COCO而框架默认类别从 0 开始索引就会错位。解决在build_coco里显式跳过 0类别从 1 开始并在训练配置里确认num_classes设为 10 而不是 11。4.2 VID 帧号与图像文件名对不上现象VID 转换后images列表里的文件名在磁盘上找不到。原因VisDrone 的 VID 图像命名不一定是序列名_帧号.jpg不同批次的命名规则可能有差异。解决先ls一个序列目录看实际命名再调整img_name的构造逻辑。稳妥做法是遍历图像目录建立帧号到文件名的映射而不是硬编码格式。4.3 空标注文件和全忽略帧的处理现象某些图像没有任何有效标注转换后这些图在images里但annotations为空。原因VisDrone 里确实存在无目标的帧或者所有目标都是忽略类。解决这本身不是错误COCO 允许图像没有标注。但如果框架的 dataloader 不支持空标注需要在转换时过滤掉这些图或者保留但在训练时跳过。4.4 坐标越界与负值现象pycocotools加载时报 bbox 越界警告。原因部分标注的 x、y 或宽高可能超出图像边界或者出现负值。解决在build_coco里加裁剪逻辑把 bbox 限制在[0, 0, width, height]范围内宽高为负的直接丢弃。4.5 JSON 文件过大导致加载慢现象VID 数据集转完后 JSON 几百 MB加载要几十秒。原因VID 帧数多标注量大JSON 文本冗余。解决如果只做检测按序列拆成多个 JSON如果做跟踪考虑用 COCO 的 video 扩展或改用更紧凑的格式。另外json.dump时加separators(,, :)能减小体积。5. 转换后的验证与进阶用法转完不是终点验证一遍才能放心用。这一章讲怎么确认转换正确以及几个能提升效率的技巧。5.1 用 pycocotools 做加载与统计验证最直接的验证方式是加载 JSON 并打印统计信息。下面这段代码检查图像数、标注数、类别分布还能可视化几张图确认 bbox 位置对不对。from pycocotools.coco import COCO import matplotlib.pyplot as plt import matplotlib.patches as patches coco COCO(visdrone_det_train.json) print(fImages: {len(coco.imgs)}, Annotations: {len(coco.anns)}) # 类别分布 cats coco.loadCats(coco.getCatIds()) for cat in cats: ids coco.getAnnIds(catIds[cat[id]]) print(f{cat[name]}: {len(ids)}) # 可视化第一张图 img_ids coco.getImgIds() img_info coco.loadImgs(img_ids[0])[0] ann_ids coco.getAnnIds(imgIds[img_info[id]]) anns coco.loadAnns(ann_ids) fig, ax plt.subplots(1) ax.imshow(plt.imread(img_info[file_name])) for ann in anns: x, y, w, h ann[bbox] ax.add_patch(patches.Rectangle((x, y), w, h, fillFalse, edgecolorred)) plt.show()统计信息能快速暴露类别映射错误可视化能确认坐标没有偏移。如果 bbox 整体偏移或缩放多半是坐标转换时除了什么问题。5.2 从 COCO 反推跟踪任务的数据组织如果原始数据是 VID 且要做跟踪转换时保留的target_id就派上用场了。可以在 COCO 的annotations里加track_id字段然后按video_id和frame_id组织成跟踪格式。常见做法是每个序列一个 JSONimages按帧排序annotations里带track_id评估时用 MOT 的指标脚本。这样既复用了 COCO 的解析生态又保留了跟踪所需的时序关联。5.3 批量转换多个 split 的脚本组织实际项目里通常要处理 train、val、test-dev、test-challenge 多个 split。建议写一个配置文件或命令行参数把 root 目录、输出路径、是否 VID 作为参数传入循环调用convert_visdrone。这样换数据集或换 split 时不用改代码。另外转换完记得校验每个 JSON 的images数量是否和磁盘文件数一致差太多说明文件名映射有问题。我自己的习惯是每次转换完先跑一遍统计脚本把类别分布和官方论文里的数字对一下偏差超过 5% 就回去查解析逻辑。这个习惯帮我省了很多事后排查的时间。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑