有时候我们会抽取一些公开数据集的某些类别数据作为自己补充训练数据。抽取VOC2012数据集指定类别之前讲到过,参考:Yolov3 行人检测 – 使用Yolov3训练从VOC2012抽取出来的行人数据
本文介绍抽取COCO数据集的指定类别并将标签保存为XML
格式。代码是参考网上的,不过对代码进行了整理和注释,直接看代码!
from pycocotools.coco import COCO
import os
import shutil
from tqdm import tqdm
import skimage.io as io
import matplotlib.pyplot as plt
import cv2
from PIL import Image, ImageDraw
headstr = """\
<annotation>
<folder>VOC</folder>
<filename>%s</filename>
<source>
<database>My Database</database>
<annotation>COCO</annotation>
<image>flickr</image>
<flickrid>NULL</flickrid>
</source>
<owner>
<flickrid>NULL</flickrid>
<name>company</name>
</owner>
<size>
<width>%d</width>
<height>%d</height>
<depth>%d</depth>
</size>
<segmented>0</segmented>
"""
objstr = """\
<object>
<name>%s</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>%d</xmin>
<ymin>%d</ymin>
<xmax>%d</xmax>
<ymax>%d</ymax>
</bndbox>
</object>
"""
tailstr = '''\
</annotation>
'''
# 创建文件夹,如果存在,先删除在建立,否则直接建立文件夹
def mkr(path):
if os.path.exists(path):
shutil.rmtree(path)
os.mkdir(path)
else:
os.mkdir(path)
def id2name(coco):
classes = dict()
for cls in coco.dataset['categories']:
classes[cls['id']] = cls['name']
return classes
# 在目标文件夹下面写入xml文件
def write_xml(anno_path, head, objs, tail):
f = open(anno_path, "w")
# 写头
f.write(head)
# 写object
for obj in objs:
f.write(objstr % (obj[0], obj[1], obj[2], obj[3], obj[4]))
# 写尾
f.write(tail)
# 保存转后好的xml和对应的jpg到指定文件夹
def save_annotations_and_imgs(coco, dataset, filename, objs):
anno_path = anno_dir + filename[:-3] + 'xml'
img_path = dataDir + dataset + '/' + filename
print(img_path)
dst_imgpath = img_dir + filename
img = cv2.imread(img_path)
# 拷贝xml对应的jpg到指定文件夹
shutil.copy(img_path, dst_imgpath)
head = headstr % (filename, img.shape[1], img.shape[0], img.shape[2])
tail = tailstr
write_xml(anno_path, head, objs, tail)
def showimg(coco, dataset, img, classes, cls_id, show=True):
global dataDir
I = Image.open('%s/%s/%s' % (dataDir, dataset, img['file_name']))
# 通过id,得到注释的信息
annIds = coco.getAnnIds(imgIds=img['id'], catIds=cls_id, iscrowd=None)
# print(annIds)
anns = coco.loadAnns(annIds)
# print(anns)
# coco.showAnns(anns)
objs = []
for ann in anns:
class_name = classes[ann['category_id']]
if class_name in classes_names:
print(class_name)
if 'bbox' in ann:
bbox = ann['bbox']
xmin = int(bbox[0])
ymin = int(bbox[1])
xmax = int(bbox[2] + bbox[0])
ymax = int(bbox[3] + bbox[1])
obj = [class_name, xmin, ymin, xmax, ymax]
objs.append(obj)
draw = ImageDraw.Draw(I)
draw.rectangle([xmin, ymin, xmax, ymax])
if show:
plt.figure()
plt.axis('off')
plt.imshow(I)
plt.show()
return objs
if __name__ == '__main__':
# 提取类的保存路径,路径下面是image和Annotation文件夹
savepath = "./coco_person/"
img_dir = savepath + 'images/'
anno_dir = savepath + 'XMLAnnotations/'
# 被提取的数据集列表,依情况而定,可为['train2014', 'train2017']
datasets_list = ['train2017']
print(datasets_list)
# 提取的类别,coco有80类,这里写要提取类的名字,以person为例
classes_names = ['person']
# 原始数据集路径,路径下是原始数据集的images和Annotations文件夹
dataDir = './COCO/'
# 建立提取类的保存文件夹
mkr(img_dir)
mkr(anno_dir)
for dataset in datasets_list:
# 原来数据集的json文件路径
annFile = '{}/annotations/instances_{}.json'.format(dataDir, dataset)
# COCO API for initializing annotated data
coco = COCO(annFile)
# show all classes in coco
classes = id2name(coco)
print(classes)
# [1, 2, 3, 4, 6, 8]
classes_ids = coco.getCatIds(catNms=classes_names)
print(classes_ids)
for cls in classes_names:
# 得到提取类的ID
cls_id = coco.getCatIds(catNms=[cls])
img_ids = coco.getImgIds(catIds=cls_id)
print(cls, len(img_ids))
for imgId in tqdm(img_ids):
img = coco.loadImgs(imgId)[0]
filename = img['file_name']
print(filename)
objs = showimg(coco, dataset, img, classes, classes_ids, show=False)
print(objs)
save_annotations_and_imgs(coco, dataset, filename, objs)