coco数据集转voc数据集

image

1|0前言


在模型训练的学习中voc和coco是最常见的两种格式,并且经常需要互相转换,本篇提供coco数据集转voc数据集的方法。

2|0coco 格式分析


COCO的 全称是Common Objects in COntext,是微软团队提供的一个可以用来进行图像识别的数据集。MS COCO数 据集中的图像分为训练、验证和测试集。

假设有以下两个图像文件:

  • image1.jpg
  • image2.jpg

coco格式数据集:annotations.json

{ "images": [ { "id": 1, "file_name": "image1.jpg", "width": 640, "height": 480 }, { "id": 2, "file_name": "image2.jpg", "width": 800, "height": 600 } ], "annotations": [ { "id": 1, "image_id": 1, "category_id": 1, "bbox": [50, 50, 100, 100], "area": 10000, "segmentation": [ [ 50, 50, 50, 150, 150, 50 ] ], "iscrowd": 0 }, { "id": 2, "image_id": 2, "category_id": 2, "bbox": [150, 200, 200, 150], "area": 30000, "segmentation": [ [ 150, 200, 150, 350, 350, 200 ] ], "iscrowd": 0 } ], "categories": [ { "id": 1, "name": "cat", "supercategory": "animal" }, { "id": 2, "name": "dog", "supercategory": "animal" } ] }

coco 数据集字段解析

coco 数据集是一个json文件,一共包括5个部分。

{ "info": info, # 数据集的基本信息 "licenses": [license], # 许可证 "images": [image], # 图片信息,名字和宽高 "annotations": [annotation], # 标注信息 "categories": [category] # 标签信息 }
info{ # 数据集信息描述 "year": int, # 数据集年份 "version": str, # 数据集版本 "description": str, # 数据集描述 "contributor": str, # 数据集提供者 "url": str, # 数据集下载链接 "date_created": datetime, # 数据集创建日期 } license{ "id": int, "name": str, "url": str, } image{ # images是一个list,存放所有图片(dict)信息。image是一个dict,存放单张图片信息 "id": int, # 图片的ID编号(每张图片ID唯一) "width": int, # 图片宽 "height": int, # 图片高 "file_name": str, # 图片名字 "license": int, # 协议 "flickr_url": str, # flickr链接地址 "coco_url": str, # 网络连接地址 "date_captured": datetime, # 数据集获取日期 } annotation{ # annotations是一个list,存放所有标注(dict)信息。annotation是一个dict,存放单个目标标注信息。 "id": int, # 目标对象ID(每个对象ID唯一),每张图片可能有多个目标 "image_id": int, # 对应图片ID "category_id": int, # 对应类别ID,与categories中的ID对应 "segmentation": RLE or [polygon], # 实例分割,对象的边界点坐标[x1,y1,x2,y2,....,xn,yn] "area": float, # 对象区域面积 "bbox": [xmin,ymin,width,height], # 目标检测,对象定位边框[x,y,w,h] "iscrowd": 0 or 1, # 表示是否是人群 } categories{ # 类别描述 "id": int, # 类别对应的ID(0默认为背景) "name": str, # 子类别名字 "supercategory": str, # 主类别名字 }

需要注意的是coco数据集标注的坐标。xmin ymin width height和voc有很大差异,分别代表:

  • xmin 左上角x轴坐标
  • ymin 左上角y轴坐标
  • width 图片像素宽
  • heidht 图片像素高

3|0voc 格式分析


voc 全称 The PASCAL Visual Object Classes,它由Visual Object Classes(可视对象类)和挑战(Challenge)等竞赛项目开发, 开始于2005年,结束于2012年最后一届 。
VOC数据集包含许多不同类型的图像,每个图像都标注了一些可视对象,如人,汽车,狗等。这些标注包括每个对象的位置,大小和类别等信息。
常见的voc数据集是voc2007 和voc 2012,当然在模型训练过程肯定都会自己标注数据集,导出为voc格式。
image

voc 数据集的格式:

<annotation> <folder>17</folder> # 图片所处文件夹 <filename>77258.bmp</filename> # 图片名 <path>~/frcnn-image/61/ADAS/image/frcnn-image/17/77258.bmp</path> <source> #图片来源相关信息 <database>Unknown</database> </source> <size> #图片尺寸 <width>640</width> <height>480</height> <depth>3</depth> </size> <segmented>0</segmented> #是否有分割label <object> 包含的物体 <name>car</name> #物体类别 <pose>Unspecified</pose> #物体的姿态 <truncated>0</truncated> #物体是否被部分遮挡(>15%) <difficult>0</difficult> #是否为难以辨识的物体, 主要指要结体背景才能判断出类别的物体。虽有标注, 但一般忽略这类物体 <bndbox> #物体的bound box <xmin>2</xmin> #左 <ymin>156</ymin> #上 <xmax>111</xmax> #右 <ymax>259</ymax> #下 </bndbox> </object> </annotation>

重要的信息包括:filename, size, object 等。除此之外,还有一个主要注意的点就是标注的坐标,xmin,ymin,xmax,ymax是标注的四个角,分别代表:

  • xmin: 左上角x轴坐标
  • ymin:左上角y周坐标
  • xmax: 右下角x轴坐标
  • ymax:右下角y轴坐标

4|0转换脚本


coco和voc之间有一些差异,而这些差异就是在转换过程中需要适配的。总的来说包括:

  1. coco数据集是1:n,一个json文件保存所有标注信息,对应n张图片;voc数据集是n:n,每一个图片对应一个xml的标注文件
  2. coco数据集将标注框信息、图片元数据分开保存,用id关联。而voc数据集图片元数据和标注信息放在一起

知道了两者之间的差异,coco转voc的基本思路就呼之欲出。整体思路大概是:从coco数据集中解析出图片信息,以图片为基本单位找到对应的标注信息。遍历所有图片,每一个图片生成一个xml文件,保存图片的元数据和标注框。

import json import os import shutil from collections import defaultdict from xml.etree.ElementTree import Element, SubElement, tostring class CocoToVoc: def __init__(self, voc_gt_dir: str, coco_json_path: str) -> None: self.voc_gt_dir = voc_gt_dir self.coco_json_path = coco_json_path self.categories_dict = {} self.annotations_dict = defaultdict(list) def create_voc_annotations(self) -> None: if os.path.exists(self.voc_gt_dir): shutil.rmtree(self.voc_gt_dir) os.makedirs(self.voc_gt_dir) with open(self.coco_json_path, 'r') as f: coco_data = json.load(f) self.categories_dict = {label["id"]: label["name"] for label in coco_data['categories']} for ann in coco_data['annotations']: self.annotations_dict[ann["image_id"]].append(ann) for image_info in coco_data['images']: image_id = image_info['id'] annotations = self.annotations_dict[image_id] xml_content = self.create_xml_annotation(image_info, annotations) xml_filename = os.path.splitext(image_info['file_name'])[0] + '.xml' xml_filepath = os.path.join(self.voc_gt_dir, xml_filename) with open(xml_filepath, 'w') as f: f.write(xml_content) @staticmethod def indent(elem: Element, level: int = 0) -> None: """xml保存美化""" i = "\n" + level * "\t" if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + "\t" if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: CocoToVoc.indent(elem, level + 1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i def create_xml_annotation(self, image_info: Element, annotations: list) -> Element: root = Element('annotation') # 图片基础信息 folder = SubElement(root, 'folder') folder.text = 'VOC2007' filename = SubElement(root, 'filename') filename.text = image_info['file_name'] path = SubElement(root, 'path') path.text = os.path.join(image_info['file_name']) # 图片宽高信息 size = SubElement(root, 'size') width = SubElement(size, 'width') width.text = str(image_info['width']) height = SubElement(size, 'height') height.text = str(image_info['height']) depth = SubElement(size, 'depth') depth.text = '3' # Assuming RGB images segmented = SubElement(root, 'segmented') segmented.text = '0' # 标注信息 for ann in annotations: obj = SubElement(root, 'object') name = SubElement(obj, 'name') name.text = self.categories_dict.get(ann['category_id']) bndbox = SubElement(obj, 'bndbox') xmin = SubElement(bndbox, 'xmin') xmin.text = str(ann['bbox'][0]) ymin = SubElement(bndbox, 'ymin') ymin.text = str(ann['bbox'][1]) xmax = SubElement(bndbox, 'xmax') xmax.text = str(ann['bbox'][0] + ann['bbox'][2]) ymax = SubElement(bndbox, 'ymax') ymax.text = str(ann['bbox'][1] + ann['bbox'][3]) self.indent(root) return tostring(root, encoding='unicode', method='xml') if __name__ == '__main__': voc_gt_dir, coco_json_path = "./gt", "./coco-annotations.json" coco_to_voc = CocoToVoc(voc_gt_dir, coco_json_path) coco_to_voc.create_voc_annotations()

5|0示例coco数据集


可以使用该数据集作为测试

coco-annotations.json

{ "images": [ {"id": 1, "file_name": "2012_004328.jpg", "height": 500, "width": 328}, {"id": 2, "file_name": "2012_004315.jpg", "height": 375, "width": 500}, {"id": 3, "file_name": "2012_004329.jpg", "height": 500, "width": 333}, {"id": 4, "file_name": "2012_004317.jpg", "height": 500, "width": 375}, {"id": 5, "file_name": "2012_004312.jpg", "height": 329, "width": 500}, {"id": 6, "file_name": "2012_004310.jpg", "height": 500, "width": 345}, {"id": 7, "file_name": "2012_004309.jpg", "height": 375, "width": 500}, {"id": 8, "file_name": "2012_004326.jpg", "height": 375, "width": 500}, {"id": 9, "file_name": "2012_004330.jpg", "height": 500, "width": 375}, {"id": 10, "file_name": "2012_004319.jpg", "height": 312, "width": 500}, {"id": 11, "file_name": "2012_004331.jpg", "height": 375, "width": 500}], "categories": [ {"id": 1, "name": "person"}], "annotations": [ {"id": 1, "image_id": 1, "category_id": 1, "bbox": [59, 220, 107, 195], "iscrowd": 0}, {"id": 2, "image_id": 1, "category_id": 1, "bbox": [219, 226, 49, 106], "iscrowd": 0}, {"id": 3, "image_id": 2, "category_id": 1, "bbox": [392, 239, 81, 136], "iscrowd": 0}, {"id": 4, "image_id": 3, "category_id": 1, "bbox": [57, 88, 227, 309], "iscrowd": 0}, {"id": 5, "image_id": 4, "category_id": 1, "bbox": [64, 131, 106, 179], "iscrowd": 0}, {"id": 6, "image_id": 5, "category_id": 1, "bbox": [152, 22, 188, 258], "iscrowd": 0}, {"id": 7, "image_id": 6, "category_id": 1, "bbox": [71, 7, 274, 493], "iscrowd": 0}, {"id": 8, "image_id": 7, "category_id": 1, "bbox": [110, 212, 188, 163], "iscrowd": 0}, {"id": 9, "image_id": 8, "category_id": 1, "bbox": [245, 31, 250, 344], "iscrowd": 0}, {"id": 10, "image_id": 9, "category_id": 1, "bbox": [230, 133, 140, 308], "iscrowd": 0}, {"id": 11, "image_id": 10, "category_id": 1, "bbox": [274, 82, 27, 55], "iscrowd": 0}, {"id": 12, "image_id": 11, "category_id": 1, "bbox": [102, 25, 106, 205], "iscrowd": 0}]}

__EOF__

本文作者goldsunshine
本文链接https://www.cnblogs.com/goldsunshine/p/18205073.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   金色旭光  阅读(425)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
历史上的今天:
2020-05-22 python版本升级到3.8以及安装虚拟环境
点击右上角即可分享
微信分享提示