前情提要

coco数据集的annotations是一个json文件,其格式对应了Python中的dict类型,所以我们可以用python中的json.loads()方法来将一个json文件读取为一个dict。

import json
coco = dict()
with open('./instances_val2017.json', 'r', encoding='UTF-8') as f:
    coco = json.loads(f.read())
print(coco.keys())

# Output:
# dict_keys(['info', 'licenses', 'images', 'annotations', 'categories'])

以上代码运行结果表明其有五个键,分别是'info', 'licenses', 'images', 'annotations', 'categories'。我们在合并数据集时,对于第一个键'info'和第二个键'licenses'我们在实际训练中完全不需要用到,可以不用操作,接下来我们对其他几个字段进行一一合并。

合并前的操作

首先读取自己和coco的官方数据集,并且找到coco官方数据集中图片的id和标签的id最大是多少,我们下一步进行合并的时候在比它们大的id开始,以防id重复。

import json

# 填写自己的数据集、coco数据集和合并后输出的路径
oursPath = 'our_annotations_val.json'
cocoPath = 'instances_val2017.json'
output_filename = './output.json'

# 读取自己的数据集
print('Loading ours...', end='')
ours = {}
with open(oursPath, 'r', encoding='UTF-8') as f:
    ours = json.loads(f.read())
print('Done')

# 读取coco官方数据集
print('Loading COCO...', end='')
output = {}
with open(cocoPath, 'r', encoding='UTF-8') as f:
    output = json.loads(f.read())
print('Done')

# 找到coco官方数据集中图片id和标签id最大是多少
print('Finding max ids of COCO...', end='')
coco_max_id = -1
max_anno_id = -1
for i in range(0, len(output['annotations'])):
    max_anno_id = output['annotations'][i]['id'] if output['annotations'][i]['id'] > max_anno_id else max_anno_id
for i in range(0, len(output['images'])):
    coco_max_id = output['images'][i]['id'] if output['images'][i]['id'] > coco_max_id else coco_max_id
print('Max(image_id) = {}, Max(anno_id) = {}'.format(coco_max_id, max_anno_id))

合并categories字段

我自己的数据集只有两个类别,所以这里我使用手动的方式来合并。

# 将新的两个类别插入,XXX91,YYY92
print('Processing categories...', end='')
output['categories'].append({'supercategory': 'indoor', 'id': 91, 'name': 'XXX'})
output['categories'].append({'supercategory': 'indoor', 'id': 92, 'name': 'YYY'})
print('Done')

合并images字段

在合并images和annotations字段的时候需要注意,这两个字段的值分别都是一个list,list中的每一个元素又是一个dict,其中的id字段都是整数类型的。

# 先处理ours的图片id,将其全部置于coco_max_id后,防止与coco自带的重复,并插入coco中
print("Processing images...", end='')
for i in range(0, len(ours['images'])):
    ours['images'][i]['id'] = ours['images'][i]['id'] + coco_max_id + 1
    ours['images'][i]['license'] = 1  # license 随便填即可
    output['images'].append(ours['images'][i])
print('Done')

合并annotations字段

这里我直接使用了1对应91,2对应92,如果类别多的话可以开一个list来映射

# 把每一个annotation的image_id加上coco_max_id来对应之前的id
print("Processing ours' image_id and category_id of annotations...", end='')
for i in range(0, len(ours['annotations'])):
    ours['annotations'][i]['image_id'] = ours['annotations'][i]['image_id'] + coco_max_id + 1
    ours['annotations'][i]['category_id'] = 91 if ours['annotations'][i]['category_id'] == 1 else 92
    ours['annotations'][i]['id'] = max_anno_id + i + 1
    output['annotations'].append(ours['annotations'][i])
print('Done')

写入文件

最后将dict写入json文件即可

# 将output字典写入json文件
print('Writing to {}...'.format(output_filename), end='')
with open(output_filename, 'w', encoding='utf-8') as f:
    json.dump(output, f)
print('Done')

print('Everything Done.')

总结

只需一个一个对应加进去即可,主要要注意id重复的问题,我的categories合并的部分没有处理好,欢迎发PR。

Last modification:July 15, 2022
If you think my article is useful to you, please feel free to appreciate