数据转换实在是个烦人的工作,被折磨了很久决定抽出时间整理一下,仅供参考。

在一个项目中,我需要将已有的VOC的xml标注文件转化成COCO的数据格式,为了方便理解,文章按如下顺序介绍:

  • XML文件内容长什么样
  • COCO的数据格式长什么样
  • XML如何转化成COCO格式

VOC XML长什么样?

下面我只把重要信息题练出来,如下所示:


<annotation>
    <folder>文件夹目录</folder>
    <filename>图片名.jpg</filename>
    <path>path_to\at002eg001.jpg</path>
    <source>
        <database>Unknown</database>
    </source>
    <size>
        <width>550</width>
        <height>518</height>
        <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
        <name>Apple</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>292</xmin>
            <ymin>218</ymin>
            <xmax>410</xmax>
            <ymax>331</ymax>
        </bndbox>
    </object>
    <object>
...
    </object>
</annotation>

可以看到一个xml文件包含如下信息:

  • folder: 文件夹
  • filename:文件名
  • path:路径
  • source:我项目里没有用到
  • size:图片大小
  • segmented:图像分割会用到,本文仅以目标检测(bounding box为例进行介绍)
  • object:一个xml文件可以有多个object,每个object表示一个box,每个box有如下信息组成:
    • name:改box框出来的object属于哪一类,例如Apple
    • bndbox:给出左上角和右下角的坐标
    • truncated:略
    • difficult:略

COCO长什么样?

COCO目录啥样?

不同于VOC,一张图片对应一个xml文件,coco是直接将所有图片以及对应的box信息写在了一个json文件里。通常整个coco目录长这样:

coco
|______annotations # 存放标注信息
| |__train.json
| |__val.json
| |__test.json
|______trainset # 存放训练集图像
|______valset # 存放验证集图像
|______testset # 存放测试集图像

COCO的json文件啥样?

一个标准的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": "http://images.cocodataset.org/val2017/000000397133.jpg",# 网路地址路径
"date_captured": "2013-11-14 17:02:52", # 数据获取日期
"flickr_url": "http://farm7.staticflickr.com/6116/6255196340_da26cf2c9e_z.jpg",# flickr网路地址
},
……,
……
]
  • categories:这个很好理解,就是你的类别信息。

    其中需要注意的是:

    • 有一个key是“supercategory”,之所以有这个是因为在COCO数据集中有的类别其实是可以归类为同一类的,例如猫和狗都属于Animal
    • id编号是从1开始的,0默认为背景

      示例如下:
"categories": [
{
"supercategory": "person", # 主类别
"id": 1, # 类对应的id (0 默认为背景)
"name": "person" # 子类别
},
{
"supercategory": "Animal",
"id": 2,
"name": "bicycle"
},
{
"supercategory": "vehicle",
"id": 3,
"name": "car"
},
……
……
],

如何将XML转化为COCO格式

下面直接搬运别人已经写好的代码,亲测有效。使用注意事项:须先安装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])

参考资料

微信公众号:AutoML机器学习

MARSGGBO♥原创

如有意合作或学术讨论欢迎私戳联系~
邮箱:marsggbo@foxmail.com


2019-7-8

如何将VOC XML文件转化成COCO数据格式的更多相关文章

  1. 将XML文件转化成NSData对象

    NSData *xmlData = [[NSData alloc]initWithContentsOfFile:[NSString stringWithFormat:@"%@/People. ...

  2. PO 审批及生成xml文件

    *********************************************************************** * Report : YTST_RAINY_MM2 * ...

  3. 根据xml文件生成javaBean

    原 根据xml文件生成javaBean 2017年08月15日 18:32:26 吃完喝完嚼益达 阅读数 1727 版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出 ...

  4. json串转化成xml文件、xml文件转换成json串

    1.json串转化成xml文件 p=[{"name":"tom","age":30,"sex":"男" ...

  5. 训练自己数据-xml文件转voc格式

    首先我们有一堆xml文件 笔者是将mask-rcnn得到的json标注文件转为xml的 批量json转xml方法:https://www.cnblogs.com/bob-jianfeng/p/1112 ...

  6. python解析VOC的xml文件并转成自己需要的txt格式

    在进行神经网络训练的时候,自己标注的数据集往往会有数据量不够大以及代表性不强等问题,因此我们会采用开源数据集作为训练,开源数据集往往具有特定的格式,如果我们想将开源数据集为我们所用的话,就需要对其格式 ...

  7. Android开发学习---使用XmlPullParser解析xml文件

    Android中解析XML的方式主要有三种:sax,dom和pull关于其内容可参考:http://blog.csdn.net/liuhe688/article/details/6415593 本文将 ...

  8. XML文件序列化和反序列化的相关内容

    问题缘由: XML反序列化出错,XML 文档(2, 2)中有错误,不应有 <configuration xmlns=''> 解决方法: 其实这个是很简单的,因为一般来说都是XML文档书写错 ...

  9. LinqToXML~读XML文件

    linq的出现,带给我们的是简结,快速,可读性,它由linq to sql,linq to object,linq to XML组成,我的博客之前有对linq to sql的讲解,而今天,我将讲一个l ...

随机推荐

  1. Maven 教程(11)— Maven远程仓库的各种配置

    原文地址:https://blog.csdn.net/liupeifeng3514/article/details/79545408 1.远程仓库的配置 在平时的开发中,我们往往不会使用默认的中央仓库 ...

  2. MPI linux Ubuntu cluster 集群

    在局域网内安装mpi,并进行并行计算.MPICH3. 下载源码: wget http://www.mpich.org/static/downloads/3.3.1/mpich-3.3.1.tar.gz ...

  3. Ansible14:Playbook条件语句

    目录 简介 when关键字 1. when基本使用 2. 比较运算符 3. 逻辑运算符 条件判断与tests 判断变量 判断执行结果 判断路径 判断字符串 判断整除 其他tests 条件判断与bloc ...

  4. Visual Studio pro key license 2019

    仅供学习交流使用,勿用作其他用途!!!!   Visual Studio 2019 Enterprise BF8Y8-GN2QH-T84XB-QVY3B-RC4DF Visual Studio 201 ...

  5. Python协程深入理解(转)

    原文:https://www.cnblogs.com/zhaof/p/7631851.html 从语法上来看,协程和生成器类似,都是定义体中包含yield关键字的函数.yield在协程中的用法: 在协 ...

  6. [Atcoder ARC103D]Robot Arms

    题目大意:平面上有$n$个点,要求你构造$m$条边(满足$m\leqslant40$),使得可以从原点到达给定的$n$个点(边必须平行于坐标轴).并要求输出每一条边的方向,每条边必须都使用,无解输出$ ...

  7. FusionInsight大数据开发---Kafka应用开发

    Kafka应用开发 了解Kafka应用开发适用场景 熟悉Kafka应用开发流程 熟悉并使用Kafka常用API 进行Kafka应用开发 Kafka的定义Kafka是一个高吞吐.分布式.基于发布订阅的消 ...

  8. Tomcat 对静态资源的处理

    Tomcat 中的请求都是由 Servlet 处理,静态资源也不例外.在默认的 web.xml 中,配置了一个 DefaultServlet 用于处理静态资源,它支持缓存和断点续传. DefaultS ...

  9. Prometheus监控学习笔记之Grafana用户权限分组

    Grafana有账户分权和管理功能,不过和一般的账户分权管理操作不一样.Grafana通过admin账户建立organization,在不通的org内配置数据源和用户,用户隶属于不通的org,只能看到 ...

  10. Python语言控制运算的优先级

    Python语言碰上计算式同时出现在一个指令内时,除了括号"(".")"最优外,其余计算优先次序如下: 次方(**). 乘法.除法.求余数(%).求整数(//) ...