常用数据集转换

  1. voc转yolo

import xml.etree.ElementTree as ET
import os
from os.path import join, splitext
from shutil import copyfile
from tqdm import tqdm

def get_file_list(root_dir, file_list, extend_name=['jpg']):
    if os.path.isfile(root_dir):
        if item.split('.')[-1] in extend_name:
            file_list.append(filePath)
        return file_list
    for item in os.listdir(root_dir):
        if os.path.isdir(os.path.join(root_dir,item)):
            subroot_dir = os.path.join(root_dir, item)
            get_file_list(subroot_dir, file_list)
        else:
            
            filePath = os.path.join(root_dir,item)
            if os.path.isfile(filePath):
                if item.split('.')[-1] in extend_name:
                    file_list.append(filePath)
    return file_list

def clear_dir(dir):
    dir_list = os.listdir(dir)
    for i in dir_list:
        abspath = os.path.join(os.path.abspath(dir), i)
        if os.path.isfile(abspath):
            os.remove(abspath)
        else:
            clear_dir(abspath)

def convert(size, box):
    dw = 1./size[0]
    dh = 1./size[1]
    x = (box[0] + box[1])/2.0
    y = (box[2] + box[3])/2.0
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return (x,y,w,h)

def convert_annotation(image_file_name, voc_annotation_dir, yolo_labels_dir):
    in_file = open(f'{voc_annotation_dir}{image_file_name}.xml', encoding='UTF-8')
    out_file = open(f'{yolo_labels_dir}/{image_file_name}.txt', 'w')

    tree = ET.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)

    if (w == 0) or (h == 0):
        print("{} width or height = 0".format(image_file_name))
        return False

    if (w <= h):
        print("Waring {} width <= height ...".format(image_file_name))

    for obj in root.iter('object'):
        cls = obj.find('name').text
        if cls not in CLASSES:
            continue
        cls_id = CLASSES.index(cls)
        xmlbox = obj.find('bndbox')
        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
        bb = convert((w,h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
    in_file.close()
    out_file.close()

    return True

def voc2yolo(voc_root_dir, voc_sub_dir, yolo_images_dir, yolo_labels_dir):
    voc_annotation_dir = join(voc_root_dir, voc_sub_dir, "Annotations/")
    voc_jpegimage_dir = join(voc_root_dir, voc_sub_dir, "JPEGImages/")

    images_list = os.listdir(voc_jpegimage_dir) # list image files
    for image_name in tqdm(images_list):
        image_file_name = splitext(image_name)[0]
        annotation_path = os.path.join(voc_annotation_dir, image_file_name + '.xml')
        jpegimage_path = join(voc_jpegimage_dir, image_name)
        if os.path.exists(annotation_path):
            yolo_label_sub_dir = os.path.join(yolo_labels_dir, voc_sub_dir)
            if not os.path.exists(yolo_label_sub_dir):
                os.makedirs(yolo_label_sub_dir)
            if(not convert_annotation(image_file_name, voc_annotation_dir, yolo_label_sub_dir)):
                print("label convert failed !!!")
                break
            yolo_image_sub_dir = os.path.join(yolo_images_dir, voc_sub_dir)
            if not os.path.exists(yolo_image_sub_dir):
                os.makedirs(yolo_image_sub_dir)
            copyfile(jpegimage_path, os.path.join(yolo_image_sub_dir, image_name))
        else:
            print("annotation file {} not exist !!!".format(annotation_path))
            break

CLASSES = ['dog', 'car', 'cat'] # 类别

if __name__ == '__main__':

    voc_root_dir = 'voc xml dir' # voc 标注文件目录
    yolo_root_dir = 'yolov txt dir' # 生成的yolo标注文件目录

    yolo_images_dir = os.path.join(yolo_root_dir, 'images')
    if not os.path.exists(yolo_images_dir):
        os.makedirs(yolo_images_dir)
    else:
        clear_dir(yolo_images_dir)

    yolo_labels_dir = os.path.join(yolo_root_dir, 'labels')
    if not os.path.exists(yolo_labels_dir):
        os.makedirs(yolo_labels_dir)
    else:
        clear_dir(yolo_labels_dir)

    voc_sub_dirs = [dir for dir in os.listdir(voc_root_dir) if os.path.isdir(os.path.join(voc_root_dir, dir))]
    print(voc_sub_dirs)
    
    for sub_dir in voc_sub_dirs:
        voc2yolo(voc_root_dir, sub_dir, yolo_images_dir, yolo_labels_dir)

  1. yolo转voc

import cv2
import os

xml_head = '''<annotation>
    <folder>VOC2007</folder>
    <filename>{}</filename>.
    <source>
        <database>The VOC2007 Database</database>
        <annotation>PASCAL VOC2007</annotation>
        <image>flickr</image>
        <flickrid>325991873</flickrid>
    </source>
    <owner>
        <flickrid>null</flickrid>
        <name>null</name>
    </owner>    
    <size>
        <width>{}</width>
        <height>{}</height>
        <depth>{}</depth>
    </size>
    <segmented>0</segmented>
    '''
xml_obj = '''
    <object>        
        <name>{}</name>
        <pose>Rear</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>{}</xmin>
            <ymin>{}</ymin>
            <xmax>{}</xmax>
            <ymax>{}</ymax>
        </bndbox>
    </object>
    '''
xml_end = '''
</annotation>'''


if __name__ == '__main__':
    
    yolo_label_dir = '../detect/yolo/labels' # yolo标注文件目录
    image_dir = '../detect/yolo/images' # 图像所在目录
    voc_label_dir = '../detect/voc/xml' # 生成的voc标注文件目录

    for file in os.scandir(yolo_label_dir):
        file_name = file.name
        yolo_label_path = os.path.join(yolo_label_dir, file_name)
        image_path = os.path.join(image_dir, file_name.split('.')[0] + '.jpg')
        voc_label_path = os.path.join(voc_label_dir, file_name.split('.')[0] + '.xml')
        if not os.path.exists(image_path):
            print('{} is not exist ...'.format(image_path))
            import sys 
            sys.exit(-1)
        
        obj = ''

        img = cv2.imread(image_path)
        img_h, img_w, img_ch = img.shape[0], img.shape[1], img.shape[2]
        head = xml_head.format(str(image_path), str(img_w), str(img_h), str(img_ch))
        with open(yolo_label_path, 'r') as f:
            for line in f.readlines():
                yolo_datas = line.strip().split(' ')
                label = yolo_datas[0].strip()
                center_x = round(float(str(yolo_datas[1]).strip()) * img_w)
                center_y = round(float(str(yolo_datas[2]).strip()) * img_h)
                bbox_width = round(float(str(yolo_datas[3]).strip()) * img_w)
                bbox_height = round(float(str(yolo_datas[4]).strip()) * img_h)
                
                xmin = str(int(center_x - bbox_width / 2 ))
                ymin = str(int(center_y - bbox_height / 2))
                xmax = str(int(center_x + bbox_width / 2))
                ymax = str(int(center_y + bbox_height / 2))
 
                obj += xml_obj.format(label, xmin, ymin, xmax, ymax)
                
        with open(voc_label_path, 'w') as f_xml:
            f_xml.write(head + obj + xml_end)

3. voc转coco

(1) 将voc图像文件、标注文件改成按照序号排列的方式

import os 
import shutil

if __name__ == '__main__':
    src_xml_dir = '../detect/voc/xml'
    src_image_dir = '../detect/voc/images'

    dst_xml_dir = '../detect/voc/Annotations'
    dst_image_dir = '../detect/voc/JPEGImages'

    
    for index, file in enumerate(os.scandir(src_image_dir)):
        file_name = file.name

        src_image_path = os.path.join(src_image_dir, file_name)
        dst_image_path = os.path.join(dst_image_dir, str(index) + '.jpg')
        src_xml_path = os.path.join(src_xml_dir, file_name.split('.')[0] + '.xml')
        dst_xml_path = os.path.join(dst_xml_dir, str(index) + '.xml')
        
        if os.path.exists(src_image_path) and os.path.exists(src_xml_path):
            shutil.copy(src_image_path, dst_image_path)
            shutil.copy(src_xml_path, dst_xml_path)

(2) 因为上述修改了图像与标注文件名称,需要在xml中也要做修改


import os
import xml.etree.ElementTree as ET

def revise_voc_xml(src_file_path, dst_file_path, name):
    tree = ET.parse(src_file_path)
    root = tree.getroot()
    file_name = root.find('filename')
    file_name.text = name 
    tree.write(dst_file_path, encoding='utf-8')


if __name__ == '__main__':

    src_xml_dir = '../detect/voc/Annotations'
    dst_xml_dir = '../detect/voc/Annotations_'

    for file in os.scandir(src_xml_dir):
        file_name = file.name
        name = file_name.split('.')[0] + '.jpg'
        src_file_path = os.path.join(src_xml_dir, file_name)
        dst_file_path = os.path.join(dst_xml_dir, file_name)
        revise_voc_xml(src_file_path, dst_file_path, name)
            

使用labelimg打开voc数据集查验一下是否生成正确

(3)voc转coco

#!/usr/bin/python

# pip install lxml

import sys
import os
import json
import xml.etree.ElementTree as ET
import glob

START_BOUNDING_BOX_ID = 1
PRE_DEFINE_CATEGORIES = None

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 ValueError("Can not find %s in %s." % (name, root.tag))
    if length > 0 and len(vars) != length:
        raise ValueError(
            "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 = filename.replace("\\", "/")
        filename = os.path.splitext(os.path.basename(filename))[0]
        return int(filename)
    except:
        raise ValueError("Filename %s is supposed to be an integer." % (filename))


def get_categories(xml_files):
    """Generate category name to id mapping from a list of xml files.
    
    Arguments:
        xml_files {list} -- A list of xml file paths.
    
    Returns:
        dict -- category name to id mapping.
    """
    classes_names = []
    for xml_file in xml_files:
        tree = ET.parse(xml_file)
        root = tree.getroot()
        for member in root.findall("object"):
            classes_names.append(member[0].text)
    classes_names = list(set(classes_names))
    classes_names.sort()
    return {name: i for i, name in enumerate(classes_names)}


def convert(xml_files, json_file):
    json_dict = {"images": [], "type": "instances", "annotations": [], "categories": []}
    if PRE_DEFINE_CATEGORIES is not None:
        categories = PRE_DEFINE_CATEGORIES
    else:
        categories = get_categories(xml_files)
    bnd_id = START_BOUNDING_BOX_ID
    for xml_file in xml_files:
        tree = ET.parse(xml_file)
        root = tree.getroot()
        path = get(root, "filename")
        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 ValueError("%d paths found in %s" % (len(path), xml_file))
        ## 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)
        ## Currently 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)

    os.makedirs(os.path.dirname(json_file), exist_ok=True)
    json_fp = open(json_file, "w")
    json_str = json.dumps(json_dict)
    json_fp.write(json_str)
    json_fp.close()


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(
        description="Convert Pascal VOC annotation to COCO format."
    )
    parser.add_argument("xml_dir", help="Directory path to xml files.", type=str)
    parser.add_argument("json_file", help="Output COCO format json file.", type=str)
    args = parser.parse_args()
    xml_files = glob.glob(os.path.join(args.xml_dir, "*.xml"))

    # If you want to do train/test split, you can pass a subset of xml files to convert function.
    print("Number of xml files: {}".format(len(xml_files)))
    convert(xml_files, args.json_file)
    print("Success: {}".format(args.json_file))

例如执行:

python voc2coco.py ../detect/voc/Annotations_/ ../detect/coco/output.json

其中../detect/voc/Annotations_/为最终修改后的voc数据标注文件目录,../detect/coco/output.json为生成的coco格式标注文件。

4. coco转voc

COCO数据集中目前存在的问题有:有些图片没有标注信息、有些图的标注框是一条线或点、有些图有标注框,但没有类名称、还有一些问题是图片是灰度图。

以下代码实现了COCO转VOC,将这些错误有问题数据分类收集用于查看,同时也不会保存到我们的目标数据集中,其中,灰度图转换成RGB格式也存到目标数据中。

from pycocotools.coco import COCO 
import  os, cv2, shutil
from lxml import etree, objectify
from tqdm import tqdm
from PIL import Image
import numpy as np
import time
import json

def cover_copy(src,dst):
    '''
    src和dst都必须是文件,该函数是执行覆盖操作
    '''
    if os.path.exists(dst):
        os.remove(dst)
        shutil.copy(src,dst)
    else:
        shutil.copy(src,dst)
def coco2voc(basedir='VOCdevkit/COCO_VOC',sourcedir='COCO'):
    """
    basedir:用来存放转换后数据和标注文件
    sourcedir:用来指定原始COCO数据集的存放位置
    """

    img_savepath= os.path.join(basedir,'JPEGImages')
    ann_savepath=os.path.join(basedir,'Annotations')
    main_path = os.path.join(basedir,"ImageSets/Main")
    for p in [basedir,img_savepath,ann_savepath,main_path]:
        if os.path.exists(p):
            shutil.rmtree(p)
            os.makedirs(p)
        else:
            os.makedirs(p)

    
    datasets = ['train2017','val2017']
    # datasets = ['val2017']

    for dataset in datasets:
        start = time.time()
        print(f"start {dataset}")
        no_ann=[] #用来存放没有标注数据的图片的id,并将这些图片复制到results文件夹中
        not_rgb=[] #是灰度图,同样将其保存

        annfile = 'instances_{}.json'.format(dataset)
        annpath=os.path.join(sourcedir,'annotations',annfile)
        
        print('loading annotations into memory...')
        tic = time.time()
        with open(annpath, 'r') as f:
            dataset_ann = json.load(f)
        assert type(
            dataset_ann
        ) == dict, 'annotation file format {} not supported'.format(
            type(dataset))
        print('Done (t={:0.2f}s)'.format(time.time() - tic))
        
        coco = COCO(annpath)
        classes = dict()
        for cat in coco.dataset['categories']:
            classes[cat['id']] = cat['name']
        imgIds = coco.getImgIds()
        # imgIds=imgIds[0:1000]#测试用,抽取10张图片,看下存储效果
        for imgId in tqdm(imgIds):
            img = coco.loadImgs(imgId)[0]
   
            filename = img['file_name']
            filepath=os.path.join(sourcedir,dataset,filename)

            annIds = coco.getAnnIds(imgIds=img['id'],  iscrowd=None)
            anns = coco.loadAnns(annIds)
            
            if not len(anns):
                # print(f"{dataset}:{imgId}该文件没有标注信息,将其复制到{dataset}_noann_result中,以使查看")
                no_ann.append(imgId)
                result_path = os.path.join(sourcedir,dataset+"_noann_result")
                dest_path = os.path.join(result_path,filename)
                if not os.path.exists(result_path):
                    os.makedirs(result_path)
                cover_copy(filepath,dest_path)
                continue #如果没有标注信息,则把没有标注信息的图片移动到相关结果文件 noann_result中,来进行查看 ,然后返回做下一张图
            #有标注信息,接着往下走,获取标注信息
            objs = []
            for ann in anns:
                name = classes[ann['category_id']]
                if 'bbox' in ann:
                    # print('bbox in ann',imgId)
                    bbox = ann['bbox']
                    xmin = (int)(bbox[0])
                    ymin = (int)(bbox[1])
                    xmax = (int)(bbox[2] + bbox[0])
                    ymax = (int)(bbox[3] + bbox[1])
                    obj = [name, 1.0, xmin, ymin, xmax, ymax]
                    #标错框在这里
                    if not(xmin-xmax==0 or ymin-ymax==0):
                        objs.append(obj)
 
                else:
                    print(f"{dataset}:{imgId}bbox在标注文件中不存在")# 单张图有多个标注框,某个类别没有框

                   
            annopath = os.path.join(ann_savepath,filename[:-3] + "xml") #生成的xml文件保存路径
            dst_path = os.path.join(img_savepath,filename)
           
            im = Image.open(filepath)
            image = np.array(im).astype(np.uint8)

            if im.mode != "RGB":
 
            # if img.shape[-1] != 3:
                
                

                # print(f"{dataset}:{imgId}该文件非rgb图,其复制到{dataset}_notrgb_result中,以使查看")
                # print(f"img.shape{image.shape} and img.mode{im.mode}")
                not_rgb.append(imgId)
                result_path = os.path.join(sourcedir,dataset+"_notrgb_result")
                dest_path = os.path.join(result_path,filename)
                if not os.path.exists(result_path):
                    os.makedirs(result_path)
                cover_copy(filepath,dest_path) #复制到notrgb_result来方便查看
                
                im=im.convert('RGB')
                image = np.array(im).astype(np.uint8)
                im.save(dst_path,quality=95)#图片经过转换后,放到我们需要的位置片
                im.close()

            else:
                
                cover_copy(filepath, dst_path)#把原始图像复制到目标文件夹
            E = objectify.ElementMaker(annotate=False)
            anno_tree = E.annotation(
                E.folder('VOC'),
                E.filename(filename),
                E.source(
                    E.database('COCO'),
                    E.annotation('VOC'),
                    E.image('COCO')
                ),
                E.size(
                    E.width(image.shape[1]),
                    E.height(image.shape[0]),
                    E.depth(image.shape[2])
                ),
                E.segmented(0)
            )

            for obj in objs:
                E2 = objectify.ElementMaker(annotate=False)
                anno_tree2 = E2.object(
                    E.name(obj[0]),
                    E.pose(),
                    E.truncated("0"),
                    E.difficult(0),
                    E.bndbox(
                        E.xmin(obj[2]),
                        E.ymin(obj[3]),
                        E.xmax(obj[4]),
                        E.ymax(obj[5])
                    )
                )
                anno_tree.append(anno_tree2)
            etree.ElementTree(anno_tree).write(annopath, pretty_print=True)
        print(f"{dataset}该数据集有{len(no_ann)}/{len(imgIds)}张图片没有instance标注信息,已经这些图片复制到{dataset}_noann_result中以使进行查看")
        print(f"{dataset}该数据集有{len(not_rgb)}/{len(imgIds)}张图片是非RGB图像,已经这些图片复制到{dataset}_notrgb_result中以使进行查看")
        duriation = time.time()-start
        print(f"数据集{dataset}处理完成用时{round(duriation/60,2)}分")

例如执行如下命令,对验证集进行转换:

python coco2voc.py --img_path src/coco/Images/val2017/ --json_path src/coco/Annotations/instances_val2017.json --save_path dst/val2017

5. objects365转voc

`Objects365`数据集共两个版本:2019 `Objects365`目标检测数据集,2020 `Objects365`物体检测数据集

- 2019版本官方不在提供下载,这里提供百度云盘下载地址:链接: [https://pan.baidu.com/s/1q1kpu1TWSobRhoXr-TE9PA](https://pan.baidu.com/s/1q1kpu1TWSobRhoXr-TE9PA) 提取码: qiva

- 2020版本官方下载地址:[https://open.baai.ac.cn/data-set-enter-detail/12647](https://open.baai.ac.cn/data-set-enter-detail/12647)

注:数据转换使用2019版本

数据目录结构:

/path/to/objects365
    Annotations
        train
            train.json
        val
            val.json
    Images
        train
            *.jpg
        val
            *.jpg

数据标签对应参考:object365_dict.txt

1:human
2:sneakers
3:chair
4:hat
5:lamp
6:bottle
7:cabinet_shelf
8:cup
9:car
10:glasses
11:picture_frame
12:desk
13:handbag
14:street lights
15:book
16:plate
17:helmet
18:leather shoes
19:pillow
20:glove
21:potted plant
22:bracelet
23:flower
24:monitor
25:storage box
26:plants pot_vase
27:bench
28:wine glass
29:boots
30:dining table
31:umbrella
32:boat
33:flag
34:speaker
35:trash bin_can
36:stool
37:backpack
38:sofa
39:belt
40:carpet
41:basket
42:towel_napkin
43:slippers
44:bowl
45:barrel_bucket
46:coffee table
47:suv
48:toy
49:tie
50:bed
51:traffic light
52:pen_pencil
53:microphone
54:sandals
55:canned
56:necklace
57:mirror
58:faucet
59:bicycle
60:bread
61:high heels
62:ring
63:van
64:watch
65:combine with bowl
66:sink
67:horse
68:fish
69:apple
70:traffic sign
71:camera
72:candle
73:stuffed animal
74:cake
75:motorbike_motorcycle
76:wild bird
77:laptop
78:knife
79:cell phone
80:paddle
81:truck
82:cow
83:power outlet
84:clock
85:drum
86:fork
87:bus
88:hanger
89:nightstand
90:pot_pan
91:sheep
92:guitar
93:traffic cone
94:tea pot
95:keyboard
96:tripod
97:hockey stick
98:fan
99:dog
100:spoon
101:blackboard_whiteboard
102:balloon
103:air conditioner
104:cymbal
105:mouse
106:telephone
107:pickup truck
108:orange
109:banana
110:airplane
111:luggage
112:skis
113:soccer
114:trolley
115:oven
116:remote
117:combine with glove
118:paper towel
119:refrigerator
120:train
121:tomato
122:machinery vehicle
123:tent
124:shampoo_shower gel
125:head phone
126:lantern
127:donut
128:cleaning products
129:sailboat
130:tangerine
131:pizza
132:kite
133:computer box
134:elephant
135:toiletries
136:gas stove
137:broccoli
138:toilet
139:stroller
140:shovel
141:baseball bat
142:microwave
143:skateboard
144:surfboard
145:surveillance camera
146:gun
147:Life saver
148:cat
149:lemon
150:liquid soap
151:zebra
152:duck
153:sports car
154:giraffe
155:pumpkin
156:Accordion_keyboard_piano
157:radiator
158:converter
159:tissue
160:carrot
161:washing machine
162:vent
163:cookies
164:cutting_chopping board
165:tennis racket
166:candy
167:skating and skiing shoes
168:scissors
169:folder
170:baseball
171:strawberry
172:bow tie
173:pigeon
174:pepper
175:coffee machine
176:bathtub
177:snowboard
178:suitcase
179:grapes
180:ladder
181:pear
182:american football
183:basketball
184:potato
185:paint brush
186:printer
187:billiards
188:fire hydrant
189:goose
190:projector
191:sausage
192:fire extinguisher
193:extension cord
194:facial mask
195:tennis ball
196:chopsticks
197:Electronic stove and gas stove
198:pie
199:frisbee
200:kettle
201:hamburger
202:golf club
203:cucumber
204:clutch
205:blender
206:tong
207:slide
208:hot dog
209:toothbrush
210:facial cleanser
211:mango
212:deer
213:egg
214:violin
215:marker
216:ship
217:chicken
218:onion
219:ice cream
220:tape
221:wheelchair
222:plum
223:bar soap
224:scale
225:watermelon
226:cabbage
227:router_modem
228:golf ball
229:pine apple
230:crane
231:fire truck
232:peach
233:cello
234:notepaper
235:tricycle
236:toaster
237:helicopter
238:green beans
239:brush
240:carriage
241:cigar
242:earphone
243:penguin
244:hurdle
245:swing
246:radio
247:CD
248:parking meter
249:swan
250:garlic
251:french fries
252:horn
253:avocado
254:saxophone
255:trumpet
256:sandwich
257:cue
258:kiwi fruit
259:bear
260:fishing rod
261:cherry
262:tablet
263:green vegetables
264:nuts
265:corn
266:key
267:screwdriver
268:globe
269:broom
270:pliers
271:hammer
272:volleyball
273:eggplant
274:trophy
275:board eraser
276:dates
277:rice
278:tape measure_ruler
279:dumbbell
280:hamimelon
281:stapler
282:camel
283:lettuce
284:goldfish
285:meat balls
286:medal
287:toothpaste
288:antelope
289:shrimp
290:rickshaw
291:trombone
292:pomegranate
293:coconut
294:jellyfish
295:mushroom
296:calculator
297:treadmill
298:butterfly
299:egg tart
300:cheese
301:pomelo
302:pig
303:race car
304:rice cooker
305:tuba
306:crosswalk sign
307:papaya
308:hair dryer
309:green onion
310:chips
311:dolphin
312:sushi
313:urinal
314:donkey
315:electric drill
316:spring rolls
317:tortoise_turtle
318:parrot
319:flute
320:measuring cup
321:shark
322:steak
323:poker card
324:binoculars
325:llama
326:radish
327:noodles
328:mop
329:yak
330:crab
331:microscope
332:barbell
333:Bread_bun
334:baozi
335:lion
336:red cabbage
337:polar bear
338:lighter
339:mangosteen
340:seal
341:comb
342:eraser
343:pitaya
344:scallop
345:pencil case
346:saw
347:table tennis  paddle
348:okra
349:starfish
350:monkey
351:eagle
352:durian
353:rabbit
354:game board
355:french horn
356:ambulance
357:asparagus
358:hoverboard
359:pasta
360:target
361:hotair balloon
362:chainsaw
363:lobster
364:iron
365:flashlight

object365_to_voc.py

import json
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
import cv2
import shutil
import os
import subprocess
import argparse
import glob
from tqdm import tqdm
import time


# cellphone:79 key:266 handbag:13 laptop:77
# classes_names = {79: "cellphone", 266: "key", 13: "handbag", 77: "laptop"}
# classes_names = {79: "cell phone", 13: "handbag", 77: "laptop"}

def save_annotations(classes_names, anno_file_path, imgs_file_path, output_anno_dir, output_dir, headstr, tailstr, objectstr, dataset):
    print(classes_names)
    # open json file(val.json or train.json)
    print("json load:", anno_file_path)
    with open(anno_file_path, 'r') as f:
        data = json.load(f)
        # 900w+
        print("anno count:", len(data["annotations"]))
        print("image count:", len(data["images"]))

        
        img_map = {}
        img_2_anno = {}
        for i in data["images"]:
            img_map[i["id"]] = i
            img_2_anno[i["id"]] = []

        for anno in data["annotations"]:
            if anno["category_id"] in classes_names.keys():
                img_2_anno[anno["image_id"]].append(anno)

        for _, id in enumerate(img_2_anno):
            annos = img_2_anno[id]
            if len(annos) > 0:
                # print("id:", id, "annos:", annos)
                time_start = time.time()
                img = img_map[id]
                img_name = img["file_name"]
                img_width = img["width"]
                img_height = img["height"]

                objs = []
                for anno in annos:
                    # print("anno:", anno)
                    print(img["file_name"])
                    # img_path use to find the image path
                    # bbox
                    bbox = anno["bbox"]
                    xmin = max(int(bbox[0]), 0)
                    ymin = max(int(bbox[1]), 0)
                    xmax = min(int(bbox[2] + bbox[0]), img_width)
                    ymax = min(int(bbox[3] + bbox[1]), img_height)
                    class_name = classes_names[anno["category_id"]]
                    obj = [class_name, xmin, ymin, xmax, ymax]
                    objs.append(obj)
                
                save_head(objs, imgs_file_path, img_name, output_anno_dir, output_dir, headstr, tailstr, objectstr, dataset, img_width, img_height)

                time_end = time.time()
                print("img id", id, "time(s)", time_end - time_start)

    print(" conver is done ")


def mkr(path):
    if os.path.exists(path):
        shutil.rmtree(path)

    os.makedirs(path)

def write_txt(output_dir, anno_path, img_path, dataset):
    list_name = output_dir + '/annotations_xml_object_{}.txt'.format(dataset)
    if not os.path.exists(list_name):
        with open(list_name, 'w', encoding="utf=8") as fs:
            print(fs)
    with open(list_name, 'r', encoding='utf-8') as list_fs:
        with open(list_name, 'a+', encoding='utf-8') as list_f:
            lines = os.path.basename(anno_path) + "\t" + img_path + "\n"
            list_f.write(lines)


def write_xml(anno_path, objs, img_path, output_dir, head, objectstr, tailstr, dataset):
    print(anno_path)
    with open(anno_path, 'w') as f:
        f.write(head)
        for obj in objs:
            f.write(objectstr % (obj[0], obj[1], obj[2], obj[3], obj[4]))
        f.write(tailstr)
        write_txt(output_dir, anno_path, img_path, dataset)


def save_head(objs, imgs_file_path, img_name, output_anno_dir, output_dir, headstr, tailstr, objectstr, dataset, img_width, img_height):
    # imgs = cv2.imread(os.path.join(imgs_file_path, img_name))
    anno_path = os.path.join(output_anno_dir, img_name[:-3] + "xml")
    print("anno_path:", anno_path)

    # if (imgs.shape[2] == 1):
    #     print(img_name + " not a RGB image")
    #     return

    head = headstr % (img_name, img_width, img_height, 3)
    write_xml(anno_path, objs, img_name, output_dir, head, objectstr, tailstr, dataset)


def find_anno_img(input_dir):
    # According input dir path find Annotations dir and Images dir
    anno_dir = os.path.join(input_dir, "Annotations")
    img_dir = os.path.join(input_dir, "Images")
    return anno_dir, img_dir


def main_object365(classes, input_dir, output_dir, headstr, tailstr, objectstr):
    # use ids match classes
    classes_names = {}
    with open("./object365_dict.txt", 'r') as f:
        print(classes)
        for line in f:
            (key, value) = line.strip().split(":")
            if not classes:
                classes_names[int(key)] = value
            else:
                if key in classes:
                    classes_names[int(key)] = value

    anno_dir, img_dir = find_anno_img(input_dir)
    for dataset in ["val", "train"]:
        # xml output dir path
        output_anno_dir = os.path.join(output_dir, dataset)
        if not os.path.exists(output_anno_dir):
            mkr(output_anno_dir)

        # read jsons file
        anno_file_path = os.path.join(anno_dir, dataset, dataset + ".json")
        # read imgs file
        imgs_file_path = os.path.join(img_dir, dataset)
        save_annotations(classes_names, anno_file_path, imgs_file_path, output_anno_dir, output_dir, headstr, tailstr, objectstr, dataset)

obj365_main.py

import os
import argparse
from object365_to_voc import main_object365

# 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>
# '''

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 make_parser():
    parser = argparse.ArgumentParser(description="conver annotations format to VOC")
    parser.add_argument("-i", "--input", dest="input_dir",required=True, help="the input dir of the picture", type=str)
    parser.add_argument("-c", "--class", dest="classes", nargs = "*",help="the class want to select, ['laptop', 'cat', 'dog']", type=str)
    parser.add_argument("--output-class", dest="output_class", action='store_true', default=False, help="output the class on the list.txt")
    parser.add_argument("-o", "--output", dest="output_dir",help="the output dir of the result", type=str)
    return parser


def main():
    parser = make_parser()

    args = vars(parser.parse_args())
    # dataset = args["dataset"]
    input_dir = args["input_dir"]
    classes = args["classes"]
    output_class = args["output_class"]
    output_dir = args["output_dir"]

    # anno_dir = os.path.join(output_dir, "annotations")
    main_object365(classes, input_dir, output_dir, headstr, tailstr, objstr)
        
if __name__ == '__main__':
    main()

如果将所有类别都进行转换,则执行命令:

python obj365_main.py -i "/obj365" -o "output/obj365"

如果只将部分类别进行转换,则执行

python obj365_main.py -i "/obj365" -o "output/obj365" -c 80 92 97 

其中 -i 为数据集输入路径,-o为转换输出路径,-c为类别序号,对应在object365_dict.txt中,可根据实际情况进行选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洪流之源

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值