您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)怎么將VOC XML文件轉(zhuǎn)化成COCO數(shù)據(jù)格式,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
數(shù)據(jù)轉(zhuǎn)換實(shí)在是個(gè)煩人的工作,被折磨了很久決定抽出時(shí)間整理一下,僅供參考。
在一個(gè)項(xiàng)目中,我需要將已有的VOC的xml標(biāo)注文件轉(zhuǎn)化成COCO的數(shù)據(jù)格式,為了方便理解,文章按如下順序介紹:
XML文件內(nèi)容長(zhǎng)什么樣
COCO的數(shù)據(jù)格式長(zhǎng)什么樣
XML如何轉(zhuǎn)化成COCO格式
下面我只把重要信息題練出來(lái),如下所示:
文件夾目錄 圖片名.jpg path_to\at002eg001.jpg Unknown 550 518 3 0 Apple Unspecified 0 0 292 218 410 331 ...
可以看到一個(gè)xml文件包含如下信息:
folder: 文件夾
filename:文件名
path:路徑
source:我項(xiàng)目里沒(méi)有用到
size:圖片大小
segmented:圖像分割會(huì)用到,本文僅以目標(biāo)檢測(cè)(bounding box為例進(jìn)行介紹)
object:一個(gè)xml文件可以有多個(gè)object,每個(gè)object表示一個(gè)box,每個(gè)box有如下信息組成:
name:改box框出來(lái)的object屬于哪一類(lèi),例如Apple
bndbox:給出左上角和右下角的坐標(biāo)
truncated:略
difficult:略
不同于VOC,一張圖片對(duì)應(yīng)一個(gè)xml文件,coco是直接將所有圖片以及對(duì)應(yīng)的box信息寫(xiě)在了一個(gè)json文件里。通常整個(gè)coco目錄長(zhǎng)這樣:
coco |______annotations # 存放標(biāo)注信息 | |__train.json | |__val.json | |__test.json |______trainset # 存放訓(xùn)練集圖像 |______valset # 存放驗(yàn)證集圖像 |______testset # 存放測(cè)試集圖像
一個(gè)標(biāo)準(zhǔn)的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, }
是不是有點(diǎn)抽象?官網(wǎng)就是這樣的,酸爽不酸爽,反正我看官網(wǎng)看的一臉懵。。??赡苁沁€欠點(diǎn)修行
那么json里具體每一個(gè)是干嘛用的呢?且let me一一道來(lái)。(散裝英語(yǔ)說(shuō)的好爽)
info: 這個(gè)記錄的是你的數(shù)據(jù)集信息,例如
"info": { # 數(shù)據(jù)集信息描述 "description": "COCO 2017 Dataset", # 數(shù)據(jù)集描述 "url": "http://cocodataset.org", # 下載地址 "version": "1.0", # 版本 "year": 2017, # 年份 "contributor": "COCO Consortium", # 提供者 "date_created": "2017/09/01" # 數(shù)據(jù)創(chuàng)建日期 } `
licenses: 記錄的就是license。。。,license可以有多個(gè),因?yàn)榭赡苣闶菑亩鄠€(gè)渠道獲得的數(shù)據(jù),例如
"licenses": [ { "url": "http://creativecommons.org/licenses/by-nc-sa/2.0/", "id": 1, "name": "Attribution-NonCommercial-ShareAlike License" }, …… …… ],
images:這個(gè)其實(shí)就是記錄每一張圖片的信息,主要的有 文件名、寬、高、id,其他的可選,如:
"images": [ { "file_name": "000000397133.jpg", # 圖片名 "id": 397133 # 圖片的ID編號(hào)(每張圖片ID是唯一的) "height": 427, # 高 "width": 640, # 寬 "license": 4, "coco_url": "https://cache.yisu.com/upload/information/20210524/357/20029.jpg",# 網(wǎng)路地址路徑 "date_captured": "2013-11-14 17:02:52", # 數(shù)據(jù)獲取日期 "flickr_url": "https://cache.yisu.com/upload/information/20210524/357/20030.jpg",# flickr網(wǎng)路地址 }, ……, …… ]
categories:這個(gè)很好理解,就是你的類(lèi)別信息。
其中需要注意的是:
有一個(gè)key是“supercategory”,之所以有這個(gè)是因?yàn)樵贑OCO數(shù)據(jù)集中有的類(lèi)別其實(shí)是可以歸類(lèi)為同一類(lèi)的,例如貓和狗都屬于Animal
id編號(hào)是從1開(kāi)始的,0默認(rèn)為背景三門(mén)峽婦科醫(yī)院http://www.0398hfyy.com/
示例如下:
"categories": [ { "supercategory": "person", # 主類(lèi)別 "id": 1, # 類(lèi)對(duì)應(yīng)的id (0 默認(rèn)為背景) "name": "person" # 子類(lèi)別 }, { "supercategory": "Animal", "id": 2, "name": "bicycle" }, { "supercategory": "vehicle", "id": 3, "name": "car" }, …… …… ],
下面直接搬運(yùn)別人已經(jīng)寫(xiě)好的代碼,親測(cè)有效。使用注意事項(xiàng):須先安裝lxml庫(kù),另外你要確保你的xml文件里類(lèi)別不要出錯(cuò),例如我自己的數(shù)據(jù)集因?yàn)橛械念?lèi)別名稱(chēng)多了個(gè)下劃線(xiàn)或者其他手賤誤敲的字母,導(dǎo)致這些類(lèi)別就被當(dāng)成新的類(lèi)別了。祝好運(yùn)。
#!/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])
關(guān)于“怎么將VOC XML文件轉(zhuǎn)化成COCO數(shù)據(jù)格式”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。