这篇文章将为大家详细讲解有关怎么将VOC XML文件转化成COCO数据格式,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
数据转换实在是个烦人的工作,被折磨了很久决定抽出时间整理一下,仅供参考。
在一个项目中,我需要将已有的VOC的xml标注文件转化成COCO的数据格式,为了方便理解,文章按如下顺序介绍:
XML文件内容长什么样
COCO的数据格式长什么样
XML如何转化成COCO格式
下面我只把重要信息题练出来,如下所示:
文件夹目录 图片名.jpg path_to\at002eg001.jpg Unknown 550 518 3 0 Apple Unspecified 0 0 292 218 410 331 ...
可以看到一个xml文件包含如下信息:
folder: 文件夹
filename:文件名
path:路径
source:我项目里没有用到
size:图片大小
segmented:图像分割会用到,本文仅以目标检测(bounding box为例进行介绍)
object:一个xml文件可以有多个object,每个object表示一个box,每个box有如下信息组成:
name:改box框出来的object属于哪一类,例如Apple
bndbox:给出左上角和右下角的坐标
truncated:略
difficult:略
不同于VOC,一张图片对应一个xml文件,coco是直接将所有图片以及对应的box信息写在了一个json文件里。通常整个coco目录长这样:
coco |______annotations # 存放标注信息 | |__train.json | |__val.json | |__test.json |______trainset # 存放训练集图像 |______valset # 存放验证集图像 |______testset # 存放测试集图像
一个标准的json文件包含如下信息:
{ "info": info, "images": [image], "annotations": [annotation], "licenses": [license], } info{ "year": int, "version": str, "description": str, "contributor": str, "url": str, "date_created": datetime, } image{ "id": int, "width": int, "height": int, "file_name": str, "license": int, "flickr_url": str, "coco_url": str, "date_captured": datetime, } license{ "id": int, "name": str, "url": str, }
是不是有点抽象?官网就是这样的,酸爽不酸爽,反正我看官网看的一脸懵。。。可能是还欠点修行
那么json里具体每一个是干嘛用的呢?且let me一一道来。(散装英语说的好爽)
info: 这个记录的是你的数据集信息,例如
"info": { # 数据集信息描述 "description": "COCO 2017 Dataset", # 数据集描述 "url": "http://cocodataset.org", # 下载地址 "version": "1.0", # 版本 "year": 2017, # 年份 "contributor": "COCO Consortium", # 提供者 "date_created": "2017/09/01" # 数据创建日期 } `
licenses: 记录的就是license。。。,license可以有多个,因为可能你是从多个渠道获得的数据,例如
"licenses": [ { "url": "http://creativecommons.org/licenses/by-nc-sa/2.0/", "id": 1, "name": "Attribution-NonCommercial-ShareAlike License" }, …… …… ],
images:这个其实就是记录每一张图片的信息,主要的有 文件名、宽、高、id,其他的可选,如:
"images": [ { "file_name": "000000397133.jpg", # 图片名 "id": 397133 # 图片的ID编号(每张图片ID是唯一的) "height": 427, # 高 "width": 640, # 宽 "license": 4, "coco_url": "https://cache.yisu.com/upload/information/20210524/357/20029.jpg",# 网路地址路径 "date_captured": "2013-11-14 17:02:52", # 数据获取日期 "flickr_url": "https://cache.yisu.com/upload/information/20210524/357/20030.jpg",# flickr网路地址 }, ……, …… ]
categories:这个很好理解,就是你的类别信息。
其中需要注意的是:
有一个key是“supercategory”,之所以有这个是因为在COCO数据集中有的类别其实是可以归类为同一类的,例如猫和狗都属于Animal
id编号是从1开始的,0默认为背景三门峡妇科医院http://www.0398hfyy.com/
示例如下:
"categories": [ { "supercategory": "person", # 主类别 "id": 1, # 类对应的id (0 默认为背景) "name": "person" # 子类别 }, { "supercategory": "Animal", "id": 2, "name": "bicycle" }, { "supercategory": "vehicle", "id": 3, "name": "car" }, …… …… ],
下面直接搬运别人已经写好的代码,亲测有效。使用注意事项:须先安装lxml库,另外你要确保你的xml文件里类别不要出错,例如我自己的数据集因为有的类别名称多了个下划线或者其他手贱误敲的字母,导致这些类别就被当成新的类别了。祝好运。
#!/usr/bin/python # pip install lxml import sys import os import json import xml.etree.ElementTree as ET START_BOUNDING_BOX_ID = 1 PRE_DEFINE_CATEGORIES = {} # If necessary, pre-define category and its id # PRE_DEFINE_CATEGORIES = {"aeroplane": 1, "bicycle": 2, "bird": 3, "boat": 4, # "bottle":5, "bus": 6, "car": 7, "cat": 8, "chair": 9, # "cow": 10, "diningtable": 11, "dog": 12, "horse": 13, # "motorbike": 14, "person": 15, "pottedplant": 16, # "sheep": 17, "sofa": 18, "train": 19, "tvmonitor": 20} def get(root, name): vars = root.findall(name) return vars def get_and_check(root, name, length): vars = root.findall(name) if len(vars) == 0: raise NotImplementedError('Can not find %s in %s.'%(name, root.tag)) if length > 0 and len(vars) != length: raise NotImplementedError('The size of %s is supposed to be %d, but is %d.'%(name, length, len(vars))) if length == 1: vars = vars[0] return vars def get_filename_as_int(filename): try: filename = os.path.splitext(filename)[0] return int(filename) except: raise NotImplementedError('Filename %s is supposed to be an integer.'%(filename)) def convert(xml_list, xml_dir, json_file): list_fp = open(xml_list, 'r') json_dict = {"images":[], "type": "instances", "annotations": [], "categories": []} categories = PRE_DEFINE_CATEGORIES bnd_id = START_BOUNDING_BOX_ID for line in list_fp: line = line.strip() print("Processing %s"%(line)) xml_f = os.path.join(xml_dir, line) tree = ET.parse(xml_f) root = tree.getroot() path = get(root, 'path') if len(path) == 1: filename = os.path.basename(path[0].text) elif len(path) == 0: filename = get_and_check(root, 'filename', 1).text else: raise NotImplementedError('%d paths found in %s'%(len(path), line)) ## The filename must be a number image_id = get_filename_as_int(filename) size = get_and_check(root, 'size', 1) width = int(get_and_check(size, 'width', 1).text) height = int(get_and_check(size, 'height', 1).text) image = {'file_name': filename, 'height': height, 'width': width, 'id':image_id} json_dict['images'].append(image) ## Cruuently we do not support segmentation # segmented = get_and_check(root, 'segmented', 1).text # assert segmented == '0' for obj in get(root, 'object'): category = get_and_check(obj, 'name', 1).text if category not in categories: new_id = len(categories) categories[category] = new_id category_id = categories[category] bndbox = get_and_check(obj, 'bndbox', 1) xmin = int(get_and_check(bndbox, 'xmin', 1).text) - 1 ymin = int(get_and_check(bndbox, 'ymin', 1).text) - 1 xmax = int(get_and_check(bndbox, 'xmax', 1).text) ymax = int(get_and_check(bndbox, 'ymax', 1).text) assert(xmax > xmin) assert(ymax > ymin) o_width = abs(xmax - xmin) o_height = abs(ymax - ymin) ann = {'area': o_width*o_height, 'iscrowd': 0, 'image_id': image_id, 'bbox':[xmin, ymin, o_width, o_height], 'category_id': category_id, 'id': bnd_id, 'ignore': 0, 'segmentation': []} json_dict['annotations'].append(ann) bnd_id = bnd_id + 1 for cate, cid in categories.items(): cat = {'supercategory': 'none', 'id': cid, 'name': cate} json_dict['categories'].append(cat) json_fp = open(json_file, 'w') json_str = json.dumps(json_dict) json_fp.write(json_str) json_fp.close() list_fp.close() if __name__ == '__main__': if len(sys.argv) <= 1: print('3 auguments are need.') print('Usage: %s XML_LIST.txt XML_DIR OUTPU_JSON.json'%(sys.argv[0])) exit(1) convert(sys.argv[1], sys.argv[2], sys.argv[3])
关于“怎么将VOC XML文件转化成COCO数据格式”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。