当前位置:首页 » 《随便一记》 » 正文

数据集格式相互转换——CoCo、VOC、YOLO、TT100K

15 人参与  2023年04月09日 12:25  分类 : 《随便一记》  评论

点击全文阅读


已实现数据集转换

一、CoCo1.1 CoCo2VOC1.2 CoCo2YOLO 二、VOC2.1 VOC2CoCo2.2 VOC2YOLO 三、YOLO3.1 YOLO2CoCo3.2 YOLO2VOC 四、TT100K4.1 TT100K2YOLO

一、CoCo

1.1 CoCo2VOC

from pycocotools.coco import COCOimport osfrom lxml import etree, objectifyimport shutilfrom tqdm import tqdmimport sysimport argparse# 将类别名字和id建立索引def catid2name(coco):    classes = dict()    for cat in coco.dataset['categories']:        classes[cat['id']] = cat['name']    return classes# 将标签信息写入xmldef save_anno_to_xml(filename, size, objs, save_path):    E = objectify.ElementMaker(annotate=False)    anno_tree = E.annotation(        E.folder("DATA"),        E.filename(filename),        E.source(            E.database("The VOC Database"),            E.annotation("PASCAL VOC"),            E.image("flickr")        ),        E.size(            E.width(size['width']),            E.height(size['height']),            E.depth(size['depth'])        ),        E.segmented(0)    )    for obj in objs:        E2 = objectify.ElementMaker(annotate=False)        anno_tree2 = E2.object(            E.name(obj[0]),            E.pose("Unspecified"),            E.truncated(0),            E.difficult(0),            E.bndbox(                E.xmin(obj[1]),                E.ymin(obj[2]),                E.xmax(obj[3]),                E.ymax(obj[4])            )        )        anno_tree.append(anno_tree2)    anno_path = os.path.join(save_path, filename[:-3] + "xml")    etree.ElementTree(anno_tree).write(anno_path, pretty_print=True)# 利用cocoAPI从json中加载信息def load_coco(anno_file, xml_save_path):    if os.path.exists(xml_save_path):        shutil.rmtree(xml_save_path)    os.makedirs(xml_save_path)    coco = COCO(anno_file)    classes = catid2name(coco)    imgIds = coco.getImgIds()    classesIds = coco.getCatIds()    for imgId in tqdm(imgIds):        size = {}        img = coco.loadImgs(imgId)[0]        filename = img['file_name']        width = img['width']        height = img['height']        size['width'] = width        size['height'] = height        size['depth'] = 3        annIds = coco.getAnnIds(imgIds=img['id'], iscrowd=None)        anns = coco.loadAnns(annIds)        objs = []        for ann in anns:            object_name = classes[ann['category_id']]            # bbox:[x,y,w,h]            bbox = list(map(int, ann['bbox']))            xmin = bbox[0]            ymin = bbox[1]            xmax = bbox[0] + bbox[2]            ymax = bbox[1] + bbox[3]            obj = [object_name, xmin, ymin, xmax, ymax]            objs.append(obj)        save_anno_to_xml(filename, size, objs, xml_save_path)def parseJsonFile(data_dir, xmls_save_path):    assert os.path.exists(data_dir), "data dir:{} does not exits".format(data_dir)    if os.path.isdir(data_dir):        data_types = ['train2017', 'val2017']        for data_type in data_types:            ann_file = 'instances_{}.json'.format(data_type)            xmls_save_path = os.path.join(xmls_save_path, data_type)            load_coco(ann_file, xmls_save_path)    elif os.path.isfile(data_dir):        anno_file = data_dir        load_coco(anno_file, xmls_save_path)if __name__ == '__main__':    """    脚本说明:        该脚本用于将coco格式的json文件转换为voc格式的xml文件    参数说明:        data_dir:json文件的路径        xml_save_path:xml输出路径    """    parser = argparse.ArgumentParser()    parser.add_argument('-d', '--data-dir', type=str, default='./data/labels/coco/train.json', help='json path')    parser.add_argument('-s', '--save-path', type=str, default='./data/convert/voc', help='xml save path')    opt = parser.parse_args()    print(opt)    if len(sys.argv) > 1:        parseJsonFile(opt.data_dir, opt.save_path)    else:        data_dir = './data/labels/coco/train.json'        xml_save_path = './data/convert/voc'        parseJsonFile(data_dir=data_dir, xmls_save_path=xml_save_path)

1.2 CoCo2YOLO

from pycocotools.coco import COCOimport osimport shutilfrom tqdm import tqdmimport sysimport argparseimages_nums = 0category_nums = 0bbox_nums = 0# 将类别名字和id建立索引def catid2name(coco):    classes = dict()    for cat in coco.dataset['categories']:        classes[cat['id']] = cat['name']    return classes# 将[xmin,ymin,xmax,ymax]转换为yolo格式[x_center, y_center, w, h](做归一化)def xyxy2xywhn(object, width, height):    cat_id = object[0]    xn = object[1] / width    yn = object[2] / height    wn = object[3] / width    hn = object[4] / height    out = "{} {:.5f} {:.5f} {:.5f} {:.5f}".format(cat_id, xn, yn, wn, hn)    return outdef save_anno_to_txt(images_info, save_path):    filename = images_info['filename']    txt_name = filename[:-3] + "txt"    with open(os.path.join(save_path, txt_name), "w") as f:        for obj in images_info['objects']:            line = xyxy2xywhn(obj, images_info['width'], images_info['height'])            f.write("{}\n".format(line))# 利用cocoAPI从json中加载信息def load_coco(anno_file, xml_save_path):    if os.path.exists(xml_save_path):        shutil.rmtree(xml_save_path)    os.makedirs(xml_save_path)    coco = COCO(anno_file)    classes = catid2name(coco)    imgIds = coco.getImgIds()    classesIds = coco.getCatIds()    with open(os.path.join(xml_save_path, "classes.txt"), 'w') as f:        for id in classesIds:            f.write("{}\n".format(classes[id]))    for imgId in tqdm(imgIds):        info = {}        img = coco.loadImgs(imgId)[0]        filename = img['file_name']        width = img['width']        height = img['height']        info['filename'] = filename        info['width'] = width        info['height'] = height        annIds = coco.getAnnIds(imgIds=img['id'], iscrowd=None)        anns = coco.loadAnns(annIds)        objs = []        for ann in anns:            object_name = classes[ann['category_id']]            # bbox:[x,y,w,h]            bbox = list(map(float, ann['bbox']))            xc = bbox[0] + bbox[2] / 2.            yc = bbox[1] + bbox[3] / 2.            w = bbox[2]            h = bbox[3]            obj = [ann['category_id'], xc, yc, w, h]            objs.append(obj)        info['objects'] = objs        save_anno_to_txt(info, xml_save_path)def parseJsonFile(json_path, txt_save_path):    assert os.path.exists(json_path), "json path:{} does not exists".format(json_path)    if os.path.exists(txt_save_path):        shutil.rmtree(txt_save_path)    os.makedirs(txt_save_path)    assert json_path.endswith('json'), "json file:{} It is not json file!".format(json_path)    load_coco(json_path, txt_save_path)if __name__ == '__main__':    """    脚本说明:        该脚本用于将coco格式的json文件转换为yolo格式的txt文件    参数说明:        json_path:json文件的路径        txt_save_path:txt保存的路径    """    parser = argparse.ArgumentParser()    parser.add_argument('-jp', '--json-path', type=str, default='./data/labels/coco/train.json', help='json path')    parser.add_argument('-s', '--save-path', type=str, default='./data/convert/yolo', help='txt save path')    opt = parser.parse_args()    if len(sys.argv) > 1:        print(opt)        parseJsonFile(opt.json_path, opt.save_path)        # print("image nums: {}".format(images_nums))        # print("category nums: {}".format(category_nums))        # print("bbox nums: {}".format(bbox_nums))    else:        json_path = './data/labels/coco/train.json'  # r'D:\practice\compete\goodsDec\data\train\train.json'        txt_save_path = './data/convert/yolo'        parseJsonFile(json_path, txt_save_path)        # print("image nums: {}".format(images_nums))        # print("category nums: {}".format(category_nums))        # print("bbox nums: {}".format(bbox_nums))

二、VOC

2.1 VOC2CoCo

import xml.etree.ElementTree as ETimport osimport jsonfrom datetime import datetimeimport sysimport argparsecoco = dict()coco['images'] = []coco['type'] = 'instances'coco['annotations'] = []coco['categories'] = []category_set = dict()image_set = set()category_item_id = -1image_id = 000000annotation_id = 0def addCatItem(name):    global category_item_id    category_item = dict()    category_item['supercategory'] = 'none'    category_item_id += 1    category_item['id'] = category_item_id    category_item['name'] = name    coco['categories'].append(category_item)    category_set[name] = category_item_id    return category_item_iddef addImgItem(file_name, size):    global image_id    if file_name is None:        raise Exception('Could not find filename tag in xml file.')    if size['width'] is None:        raise Exception('Could not find width tag in xml file.')    if size['height'] is None:        raise Exception('Could not find height tag in xml file.')    image_id += 1    image_item = dict()    image_item['id'] = image_id    image_item['file_name'] = file_name    image_item['width'] = size['width']    image_item['height'] = size['height']    image_item['license'] = None    image_item['flickr_url'] = None    image_item['coco_url'] = None    image_item['date_captured'] = str(datetime.today())    coco['images'].append(image_item)    image_set.add(file_name)    return image_iddef addAnnoItem(object_name, image_id, category_id, bbox):    global annotation_id    annotation_item = dict()    annotation_item['segmentation'] = []    seg = []    # bbox[] is x,y,w,h    # left_top    seg.append(bbox[0])    seg.append(bbox[1])    # left_bottom    seg.append(bbox[0])    seg.append(bbox[1] + bbox[3])    # right_bottom    seg.append(bbox[0] + bbox[2])    seg.append(bbox[1] + bbox[3])    # right_top    seg.append(bbox[0] + bbox[2])    seg.append(bbox[1])    annotation_item['segmentation'].append(seg)    annotation_item['area'] = bbox[2] * bbox[3]    annotation_item['iscrowd'] = 0    annotation_item['ignore'] = 0    annotation_item['image_id'] = image_id    annotation_item['bbox'] = bbox    annotation_item['category_id'] = category_id    annotation_id += 1    annotation_item['id'] = annotation_id    coco['annotations'].append(annotation_item)def read_image_ids(image_sets_file):    ids = []    with open(image_sets_file, 'r') as f:        for line in f.readlines():            ids.append(line.strip())    return idsdef parseXmlFilse(data_dir, json_save_path, split='train'):    assert os.path.exists(data_dir), "data path:{} does not exist".format(data_dir)    labelfile = split + ".txt"    image_sets_file = os.path.join(data_dir, "ImageSets", "Main", labelfile)    xml_files_list = []    if os.path.isfile(image_sets_file):        ids = read_image_ids(image_sets_file)        xml_files_list = [os.path.join(data_dir, "Annotations", f"{i}.xml") for i in ids]    elif os.path.isdir(data_dir):        # 修改此处xml的路径即可        # xml_dir = os.path.join(data_dir,"labels/voc")        xml_dir = data_dir        xml_list = os.listdir(xml_dir)        xml_files_list = [os.path.join(xml_dir, i) for i in xml_list]    for xml_file in xml_files_list:        if not xml_file.endswith('.xml'):            continue        tree = ET.parse(xml_file)        root = tree.getroot()        # 初始化        size = dict()        size['width'] = None        size['height'] = None        if root.tag != 'annotation':            raise Exception('pascal voc xml root element should be annotation, rather than {}'.format(root.tag))        # 提取图片名字        file_name = root.findtext('filename')        assert file_name is not None, "filename is not in the file"        # 提取图片 size {width,height,depth}        size_info = root.findall('size')        assert size_info is not None, "size is not in the file"        for subelem in size_info[0]:            size[subelem.tag] = int(subelem.text)        if file_name is not None and size['width'] is not None and file_name not in image_set:            # 添加coco['image'],返回当前图片ID            current_image_id = addImgItem(file_name, size)            print('add image with name: {}\tand\tsize: {}'.format(file_name, size))        elif file_name in image_set:            raise Exception('file_name duplicated')        else:            raise Exception("file name:{}\t size:{}".format(file_name, size))        # 提取一张图片内所有目标object标注信息        object_info = root.findall('object')        if len(object_info) == 0:            continue        # 遍历每个目标的标注信息        for object in object_info:            # 提取目标名字            object_name = object.findtext('name')            if object_name not in category_set:                # 创建类别索引                current_category_id = addCatItem(object_name)            else:                current_category_id = category_set[object_name]            # 初始化标签列表            bndbox = dict()            bndbox['xmin'] = None            bndbox['xmax'] = None            bndbox['ymin'] = None            bndbox['ymax'] = None            # 提取box:[xmin,ymin,xmax,ymax]            bndbox_info = object.findall('bndbox')            for box in bndbox_info[0]:                bndbox[box.tag] = int(box.text)            if bndbox['xmin'] is not None:                if object_name is None:                    raise Exception('xml structure broken at bndbox tag')                if current_image_id is None:                    raise Exception('xml structure broken at bndbox tag')                if current_category_id is None:                    raise Exception('xml structure broken at bndbox tag')                bbox = []                # x                bbox.append(bndbox['xmin'])                # y                bbox.append(bndbox['ymin'])                # w                bbox.append(bndbox['xmax'] - bndbox['xmin'])                # h                bbox.append(bndbox['ymax'] - bndbox['ymin'])                print('add annotation with object_name:{}\timage_id:{}\tcat_id:{}\tbbox:{}'.format(object_name,                                                                                                   current_image_id,                                                                                                   current_category_id,                                                                                                   bbox))                addAnnoItem(object_name, current_image_id, current_category_id, bbox)    json_parent_dir = os.path.dirname(json_save_path)    if not os.path.exists(json_parent_dir):        os.makedirs(json_parent_dir)    json.dump(coco, open(json_save_path, 'w'))    print("class nums:{}".format(len(coco['categories'])))    print("image nums:{}".format(len(coco['images'])))    print("bbox nums:{}".format(len(coco['annotations'])))if __name__ == '__main__':    """    脚本说明:        本脚本用于将VOC格式的标注文件.xml转换为coco格式的标注文件.json    参数说明:        voc_data_dir:两种格式            1.voc2012文件夹的路径,会自动找到voc2012/imageSets/Main/xx.txt            2.xml标签文件存放的文件夹        json_save_path:json文件输出的文件夹        split:主要用于voc2012查找xx.txt,如train.txt.如果用格式2,则不会用到该参数    """    voc_data_dir = 'D:/jinxData/voctest/Annotations'    json_save_path = 'D:/jinxData/voc/voc2coco/train.json'    split = 'train'    parseXmlFilse(data_dir=voc_data_dir, json_save_path=json_save_path, split=split)

在这里插入图片描述
在这里插入图片描述
将annotations目录下的所有xml标注文件按coco格式写入了json文件中。

2.2 VOC2YOLO

import osimport jsonimport shutilfrom lxml import etreefrom tqdm import tqdmcategory_set = set()image_set = set()bbox_nums = 0class VOC2YOLO:    def __init__(self):        self.original_datasets = 'voc'        self.to_datasets = 'yolo'    def parse_xml_to_dict(self, xml):        """        将xml文件解析成字典形式,参考tensorflow的recursive_parse_xml_to_dict        Args:            xml: xml tree obtained by parsing XML file contents using lxml.etree        Returns:            Python dictionary holding XML contents.        """        if len(xml) == 0:  # 遍历到底层,直接返回tag对应的信息            return {xml.tag: xml.text}        result = {}        for child in xml:            child_result = self.parse_xml_to_dict(child)  # 递归遍历标签信息            if child.tag != 'object':                result[child.tag] = child_result[child.tag]            else:                if child.tag not in result:  # 因为object可能有多个,所以需要放入列表里                    result[child.tag] = []                result[child.tag].append(child_result[child.tag])        return {xml.tag: result}    def write_classIndices(self, category_set):        class_indices = dict((k, v) for v, k in enumerate(category_set))        json_str = json.dumps(dict((val, key) for key, val in class_indices.items()), indent=4)        with open('class_indices.json', 'w') as json_file:            json_file.write(json_str)    def xyxy2xywhn(self, bbox, size):        bbox = list(map(float, bbox))        size = list(map(float, size))        xc = (bbox[0] + (bbox[2] - bbox[0]) / 2.) / size[0]        yc = (bbox[1] + (bbox[3] - bbox[1]) / 2.) / size[1]        wn = (bbox[2] - bbox[0]) / size[0]        hn = (bbox[3] - bbox[1]) / size[1]        return (xc, yc, wn, hn)    def parser_info(self, info: dict, only_cat=True, class_indices=None):        filename = info['annotation']['filename']        image_set.add(filename)        objects = []        width = int(info['annotation']['size']['width'])        height = int(info['annotation']['size']['height'])        for obj in info['annotation']['object']:            obj_name = obj['name']            category_set.add(obj_name)            if only_cat:                continue            xmin = round(float(obj['bndbox']['xmin']))            ymin = round(float(obj['bndbox']['ymin']))            xmax = round(float(obj['bndbox']['xmax']))            ymax = round(float(obj['bndbox']['ymax']))            bbox = self.xyxy2xywhn((xmin, ymin, xmax, ymax), (width, height))            if class_indices is not None:                obj_category = class_indices[obj_name]                object = [obj_category, bbox]                objects.append(object)        return filename, objects    def parseXmlFilse(self, voc_dir, save_dir):        assert os.path.exists(voc_dir), "ERROR {} does not exists".format(voc_dir)        if os.path.exists(save_dir):            shutil.rmtree(save_dir)        os.makedirs(save_dir)        xml_files = [os.path.join(voc_dir, i) for i in os.listdir(voc_dir) if os.path.splitext(i)[-1] == '.xml']        for xml_file in xml_files:            with open(xml_file) as fid:                xml_str = fid.read()            xml = etree.fromstring(xml_str)            info_dict = self.parse_xml_to_dict(xml)            self.parser_info(info_dict, only_cat=True)        with open(save_dir + "/classes.txt", 'w') as classes_file:            for cat in sorted(category_set):                classes_file.write("{}\n".format(cat))        class_indices = dict((v, k) for k, v in enumerate(sorted(category_set)))        xml_files = tqdm(xml_files)        for xml_file in xml_files:            with open(xml_file) as fid:                xml_str = fid.read()            xml = etree.fromstring(xml_str)            info_dict = self.parse_xml_to_dict(xml)            filename, objects = self.parser_info(info_dict, only_cat=False, class_indices=class_indices)            if len(objects) != 0:                global bbox_nums                bbox_nums += len(objects)                with open(save_dir + "/" + filename.split(".")[0] + ".txt", 'w') as f:                    for obj in objects:                        f.write(                            "{} {:.5f} {:.5f} {:.5f} {:.5f}\n".format(obj[0], obj[1][0], obj[1][1], obj[1][2],                                                                      obj[1][3]))if __name__ == '__main__':    voc2yolo = VOC2YOLO()    voc_dir = 'D:/jinxData/voctest/Annotations'    save_dir = 'D:/jinxData/voctest/convert'    voc2yolo.parseXmlFilse(voc_dir, save_dir)    print("image nums: {}".format(len(image_set)))    print("category nums: {}".format(len(category_set)))    print("bbox nums: {}".format(bbox_nums))

在这里插入图片描述
在这里插入图片描述
此处得到的是全部的标签信息,可根据如下代码进行train、val和test的比例划分:

import osimport randomdef voc_proportion_divide(xmlfilepath, txtsavepath, trainval_percent, train_percent):    '''    vod数据集比例自定义划分    Args:        xmlfilepath: xml文件的地址, xml一般存放在Annotations下,如'D:\jinx\Annatations'        txtsavepath:地址选择自己数据下的ImageSets/Main,如'D:\jinx\ImageSets\Main'        trainval_percent: 训练和验证集比例        train_percent: 训练集比例(如trainval_percent=0.8,train_percent=0.7表示0.7train、 0.1val、0.2test)    '''    total_xml = os.listdir(xmlfilepath)    if not os.path.exists(txtsavepath):        os.makedirs(txtsavepath)    num = len(total_xml)    list_index = range(num)    tv = int(num * trainval_percent)    tr = int(tv * train_percent)    trainval = random.sample(list_index, tv)    train = random.sample(trainval, tr)    file_trainval = open(txtsavepath + '/trainval.txt', 'w')    file_test = open(txtsavepath + '/test.txt', 'w')    file_train = open(txtsavepath + '/train.txt', 'w')    file_val = open(txtsavepath + '/val.txt', 'w')    for i in list_index:        name = total_xml[i][:-4] + '\n'        if i in trainval:            file_trainval.write(name)            if i in train:                file_train.write(name)            else:                file_val.write(name)        else:            file_test.write(name)    file_trainval.close()    file_train.close()    file_val.close()    file_test.close()

三、YOLO

3.1 YOLO2CoCo

import argparseimport jsonimport osimport sysimport shutilfrom datetime import datetimeimport cv2coco = dict()coco['images'] = []coco['type'] = 'instances'coco['annotations'] = []coco['categories'] = []category_set = dict()image_set = set()image_id = 000000annotation_id = 0def addCatItem(category_dict):    for k, v in category_dict.items():        category_item = dict()        category_item['supercategory'] = 'none'        category_item['id'] = int(k)        category_item['name'] = v        coco['categories'].append(category_item)def addImgItem(file_name, size):    global image_id    image_id += 1    image_item = dict()    image_item['id'] = image_id    image_item['file_name'] = file_name    image_item['width'] = size[1]    image_item['height'] = size[0]    image_item['license'] = None    image_item['flickr_url'] = None    image_item['coco_url'] = None    image_item['date_captured'] = str(datetime.today())    coco['images'].append(image_item)    image_set.add(file_name)    return image_iddef addAnnoItem(object_name, image_id, category_id, bbox):    global annotation_id    annotation_item = dict()    annotation_item['segmentation'] = []    seg = []    # bbox[] is x,y,w,h    # left_top    seg.append(bbox[0])    seg.append(bbox[1])    # left_bottom    seg.append(bbox[0])    seg.append(bbox[1] + bbox[3])    # right_bottom    seg.append(bbox[0] + bbox[2])    seg.append(bbox[1] + bbox[3])    # right_top    seg.append(bbox[0] + bbox[2])    seg.append(bbox[1])    annotation_item['segmentation'].append(seg)    annotation_item['area'] = bbox[2] * bbox[3]    annotation_item['iscrowd'] = 0    annotation_item['ignore'] = 0    annotation_item['image_id'] = image_id    annotation_item['bbox'] = bbox    annotation_item['category_id'] = category_id    annotation_id += 1    annotation_item['id'] = annotation_id    coco['annotations'].append(annotation_item)def xywhn2xywh(bbox, size):    bbox = list(map(float, bbox))    size = list(map(float, size))    xmin = (bbox[0] - bbox[2] / 2.) * size[1]    ymin = (bbox[1] - bbox[3] / 2.) * size[0]    w = bbox[2] * size[1]    h = bbox[3] * size[0]    box = (xmin, ymin, w, h)    return list(map(int, box))def parseXmlFilse(image_path, anno_path, save_path, json_name='train.json'):    assert os.path.exists(image_path), "ERROR {} dose not exists".format(image_path)    assert os.path.exists(anno_path), "ERROR {} dose not exists".format(anno_path)    if os.path.exists(save_path):        shutil.rmtree(save_path)    os.makedirs(save_path)    json_path = os.path.join(save_path, json_name)    category_set = []    with open(anno_path + '/classes.txt', 'r') as f:        for i in f.readlines():            category_set.append(i.strip())    category_id = dict((k, v) for k, v in enumerate(category_set))    addCatItem(category_id)    images = [os.path.join(image_path, i) for i in os.listdir(image_path)]    files = [os.path.join(anno_path, i) for i in os.listdir(anno_path)]    images_index = dict((v.split(os.sep)[-1][:-4], k) for k, v in enumerate(images))    for file in files:        if os.path.splitext(file)[-1] != '.txt' or 'classes' in file.split(os.sep)[-1]:            continue        if file.split(os.sep)[-1][:-4] in images_index:            index = images_index[file.split(os.sep)[-1][:-4]]            img = cv2.imread(images[index])            shape = img.shape            filename = images[index].split(os.sep)[-1]            current_image_id = addImgItem(filename, shape)        else:            continue        with open(file, 'r') as fid:            for i in fid.readlines():                i = i.strip().split()                category = int(i[0])                category_name = category_id[category]                bbox = xywhn2xywh((i[1], i[2], i[3], i[4]), shape)                addAnnoItem(category_name, current_image_id, category, bbox)    json.dump(coco, open(json_path, 'w'))    print("class nums:{}".format(len(coco['categories'])))    print("image nums:{}".format(len(coco['images'])))    print("bbox nums:{}".format(len(coco['annotations'])))if __name__ == '__main__':    """    脚本说明:        本脚本用于将yolo格式的标注文件.txt转换为coco格式的标注文件.json    参数说明:        anno_path:标注文件txt存储路径        save_path:json文件输出的文件夹        image_path:图片路径        json_name:json文件名字    """    anno_path = 'D:/jinxData/TT100K45/labels/test'    save_path = 'D:/jinxData/YOLO/yolo2coco/test'    image_path = 'D:/jinxData/TT100K45/images/test'    json_name = 'train.json'    parseXmlFilse(image_path, anno_path, save_path, json_name)

在这里插入图片描述
在这里插入图片描述
train和val同理。

3.2 YOLO2VOC

import argparseimport osimport sysimport shutilimport cv2from lxml import etree, objectify# 将标签信息写入xmlfrom tqdm import tqdmimages_nums = 0category_nums = 0bbox_nums = 0def save_anno_to_xml(filename, size, objs, save_path):    E = objectify.ElementMaker(annotate=False)    anno_tree = E.annotation(        E.folder("DATA"),        E.filename(filename),        E.source(            E.database("The VOC Database"),            E.annotation("PASCAL VOC"),            E.image("flickr")        ),        E.size(            E.width(size[1]),            E.height(size[0]),            E.depth(size[2])        ),        E.segmented(0)    )    for obj in objs:        E2 = objectify.ElementMaker(annotate=False)        anno_tree2 = E2.object(            E.name(obj[0]),            E.pose("Unspecified"),            E.truncated(0),            E.difficult(0),            E.bndbox(                E.xmin(obj[1][0]),                E.ymin(obj[1][1]),                E.xmax(obj[1][2]),                E.ymax(obj[1][3])            )        )        anno_tree.append(anno_tree2)    anno_path = os.path.join(save_path, filename[:-3] + "xml")    etree.ElementTree(anno_tree).write(anno_path, pretty_print=True)def xywhn2xyxy(bbox, size):    bbox = list(map(float, bbox))    size = list(map(float, size))    xmin = (bbox[0] - bbox[2] / 2.) * size[1]    ymin = (bbox[1] - bbox[3] / 2.) * size[0]    xmax = (bbox[0] + bbox[2] / 2.) * size[1]    ymax = (bbox[1] + bbox[3] / 2.) * size[0]    box = [xmin, ymin, xmax, ymax]    return list(map(int, box))def parseXmlFilse(image_path, anno_path, save_path):    global images_nums, category_nums, bbox_nums    assert os.path.exists(image_path), "ERROR {} dose not exists".format(image_path)    assert os.path.exists(anno_path), "ERROR {} dose not exists".format(anno_path)    if os.path.exists(save_path):        shutil.rmtree(save_path)    os.makedirs(save_path)    category_set = []    with open(anno_path + '/classes.txt', 'r') as f:        for i in f.readlines():            category_set.append(i.strip())    category_nums = len(category_set)    category_id = dict((k, v) for k, v in enumerate(category_set))    images = [os.path.join(image_path, i) for i in os.listdir(image_path)]    files = [os.path.join(anno_path, i) for i in os.listdir(anno_path)]    images_index = dict((v.split(os.sep)[-1][:-4], k) for k, v in enumerate(images))    images_nums = len(images)    for file in tqdm(files):        if os.path.splitext(file)[-1] != '.txt' or 'classes' in file.split(os.sep)[-1]:            continue        if file.split(os.sep)[-1][:-4] in images_index:            index = images_index[file.split(os.sep)[-1][:-4]]            img = cv2.imread(images[index])            shape = img.shape            filename = images[index].split(os.sep)[-1]        else:            continue        objects = []        with open(file, 'r') as fid:            for i in fid.readlines():                i = i.strip().split()                category = int(i[0])                category_name = category_id[category]                bbox = xywhn2xyxy((i[1], i[2], i[3], i[4]), shape)                obj = [category_name, bbox]                objects.append(obj)        bbox_nums += len(objects)        save_anno_to_xml(filename, shape, objects, save_path)if __name__ == '__main__':    """    脚本说明:        本脚本用于将yolo格式的标注文件.txt转换为voc格式的标注文件.xml    参数说明:        anno_path:标注文件txt存储路径        save_path:json文件输出的文件夹        image_path:图片路径    """    anno_path = 'D:/jinxData/TT100K45/labels/test'    save_path = 'D:/jinxData/YOLO/yolo2voc/test'    image_path = 'D:/jinxData/TT100K45/images/test'    parseXmlFilse(image_path, anno_path, save_path)    print("image nums: {}".format(images_nums))    print("category nums: {}".format(category_nums))    print("bbox nums: {}".format(bbox_nums))

在这里插入图片描述

在这里插入图片描述

train、val和test分别执行一次即可。

以上代码参考自博文数据转换。

四、TT100K

4.1 TT100K2YOLO

import osimport jsonfrom random import randomimport cv2import shutilimport jsonimport xml.dom.minidomfrom tqdm import tqdmimport argparseclass TT100K2COCO:    def __init__(self):        self.original_datasets = 'tt100k'        self.to_datasets = 'coco'    def class_statistics(self):        # os.makedirs('annotations', exist_ok=True)        # 存放数据的父路径        parent_path = 'D:/jinxData/TT100K/data'        # 读TT100K原始数据集标注文件        with open(os.path.join(parent_path, 'annotations.json')) as origin_json:            origin_dict = json.load(origin_json)            classes = origin_dict['types']        # 建立统计每个类别包含的图片的字典        sta = {}        for i in classes:            sta[i] = []        images_dic = origin_dict['imgs']        # 记录所有保留的图片        saved_images = []        # 遍历TT100K的imgs        for image_id in images_dic:            image_element = images_dic[image_id]            image_path = image_element['path']            # 添加图像的信息到dataset中            image_path = image_path.split('/')[-1]            obj_list = image_element['objects']            # 遍历每张图片的标注信息            for anno_dic in obj_list:                label_key = anno_dic['category']                # 防止一个图片多次加入一个标签类别                if image_path not in sta[label_key]:                    sta[label_key].append(image_path)        # 只保留包含图片数超过100的类别        result = {k: v for k, v in sta.items() if len(v) >= 100}        for i in result:            print("the type of {} includes {} images".format(i, len(result[i])))            saved_images.extend(result[i])        saved_images = list(set(saved_images))        print("total types is {}".format(len(result)))        type_list = list(result.keys())        result = {"type": type_list, "details": result, "images": saved_images}        print(type_list)        # 保存结果        json_name = os.path.join(parent_path, 'statistics.json')        with open(json_name, 'w', encoding="utf-8") as f:            json.dump(result, f, ensure_ascii=False, indent=1)    def original_datasets2object_datasets_re(self):        '''        重新划分数据集        :return:        '''        # os.makedirs('annotations2', exist_ok=True)        # 存放数据的父路径        parent_path = 'D:/jinxData/TT100K/data'        # 读TT100K原始数据集标注文件        with open(os.path.join(parent_path, 'annotations.json')) as origin_json:            origin_dict = json.load(origin_json)        with open(os.path.join(parent_path, 'statistics.json')) as select_json:            select_dict = json.load(select_json)            classes = select_dict['type']        train_dataset = {'info': {}, 'licenses': [], 'categories': [], 'images': [], 'annotations': []}        val_dataset = {'info': {}, 'licenses': [], 'categories': [], 'images': [], 'annotations': []}        test_dataset = {'info': {}, 'licenses': [], 'categories': [], 'images': [], 'annotations': []}        label = {}  # 记录每个标志类别的id        count = {}  # 记录每个类别的图片数        owntype_sum = {}        info = {            "year": 2021,  # 年份            "version": '1.0',  # 版本            "description": "TT100k_to_coco",  # 数据集描述            "contributor": "Tecent&Tsinghua",  # 提供者            "url": 'https://cg.cs.tsinghua.edu.cn/traffic-sign/',  # 下载地址            "date_created": 2021 - 1 - 15        }        licenses = {            "id": 1,            "name": "null",            "url": "null",        }        train_dataset['info'] = info        val_dataset['info'] = info        test_dataset['info'] = info        train_dataset['licenses'] = licenses        val_dataset['licenses'] = licenses        test_dataset['licenses'] = licenses        # 建立类别和id的关系        for i, cls in enumerate(classes):            train_dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'traffic_sign'})            val_dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'traffic_sign'})            test_dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'traffic_sign'})            label[cls] = i            count[cls] = 0            owntype_sum[cls] = 0        images_dic = origin_dict['imgs']        obj_id = 1        # 计算出每个类别共‘包含’的图片数        for image_id in images_dic:            image_element = images_dic[image_id]            image_path = image_element['path']            image_name = image_path.split('/')[-1]            # 在所选的类别图片中            if image_name not in select_dict['images']:                continue            # 处理TT100K中的标注信息            obj_list = image_element['objects']            # 记录图片中包含最多的实例所属的type            includes_type = {}            for anno_dic in obj_list:                if anno_dic["category"] not in select_dict["type"]:                    continue                # print(anno_dic["category"])                if anno_dic["category"] in includes_type:                    includes_type[anno_dic["category"]] += 1                else:                    includes_type[anno_dic["category"]] = 1            # print(includes_type)            own_type = max(includes_type, key=includes_type.get)            owntype_sum[own_type] += 1        # TT100K的annotation转换成coco的        for image_id in images_dic:            image_element = images_dic[image_id]            image_path = image_element['path']            image_name = image_path.split('/')[-1]            # 在所选的类别图片中            if image_name not in select_dict['images']:                continue            print("dealing with {} image".format(image_path))            # shutil.copy(os.path.join(parent_path,image_path),os.path.join(parent_path,"dataset/JPEGImages"))            # 处理TT100K中的标注信息            obj_list = image_element['objects']            # 记录图片中包含最多的实例所属的type            includes_type = {}            for anno_dic in obj_list:                if anno_dic["category"] not in select_dict["type"]:                    continue                # print(anno_dic["category"])                if anno_dic["category"] in includes_type:                    includes_type[anno_dic["category"]] += 1                else:                    includes_type[anno_dic["category"]] = 1            # print(includes_type)            own_type = max(includes_type, key=includes_type.get)            count[own_type] += 1            num_rate = count[own_type] / owntype_sum[own_type]            # 切换dataset的引用对象,从而划分数据集根据每个类别类别的总数量按7:2:1分为了train_set,val_set,test_set。            # 其中每个图片所属类别根据该图片包含的类别的数量决定(归属为含有类别最多的类别)            if num_rate < 0.7:                dataset = train_dataset            elif num_rate < 0.9:                dataset = val_dataset            else:                print("dataset=test_dataset")                dataset = test_dataset            for anno_dic in obj_list:                if anno_dic["category"] not in select_dict["type"]:                    continue                x = anno_dic['bbox']['xmin']                y = anno_dic['bbox']['ymin']                width = anno_dic['bbox']['xmax'] - anno_dic['bbox']['xmin']                height = anno_dic['bbox']['ymax'] - anno_dic['bbox']['ymin']                label_key = anno_dic['category']                dataset['annotations'].append({                    'area': width * height,                    'bbox': [x, y, width, height],                    'category_id': label[label_key],                    'id': obj_id,                    'image_id': image_id,                    'iscrowd': 0,                    # mask, 矩形是从左上角点按顺时针的四个顶点                    'segmentation': [[x, y, x + width, y, x + width, y + height, x, y + height]]                })                # 每个标注的对象id唯一                obj_id += 1            # 用opencv读取图片,得到图像的宽和高            im = cv2.imread(os.path.join(parent_path, image_path))            # print(image_path)            H, W, _ = im.shape            # 添加图像的信息到dataset中            dataset['images'].append({'file_name': image_name,                                      'id': image_id,                                      'width': W,                                      'height': H})        # 保存结果        for phase in ['train', 'val', 'test']:            json_name = os.path.join(parent_path, 'dataset/annotations/{}.json'.format(phase))            json_name = json_name.replace('\\', '/')            with open(json_name, 'w', encoding="utf-8") as f:                if phase == 'train':                    json.dump(train_dataset, f, ensure_ascii=False, indent=1)                if phase == 'val':                    json.dump(val_dataset, f, ensure_ascii=False, indent=1)                if phase == 'test':                    json.dump(test_dataset, f, ensure_ascii=False, indent=1)     def divide_TrainValTest(self, source, target):        '''        创建文件路径        :param source: 源文件位置        :param target: 目标文件位置        '''        # for i in ['train', 'val', 'test']:        #     path = target + '/' + i        #     if not os.path.exists(path):        #         os.makedirs(path)        # 遍历目录下的文件名,复制对应的图片到指定目录        for root, dirs, files in os.walk(source):            for file in files:                file_name = os.path.splitext(file)[0]                if file_name == 'train' or file_name == 'val' or file_name =='test' or file_name =='classes':                    continue                image_path = os.path.join(file_name + '.jpg')                # print(image_path)                if 'train' in source:                    shutil.copyfile('D:/jinxData/TT100K/data/image_reparation/'                                    + image_path, target + '/train/' + image_path)                elif 'val' in source:                    shutil.copyfile('D:/jinxData/TT100K/data/image_reparation/'                                    + image_path, target + '/val/' + image_path)                elif 'test' in source:                    shutil.copyfile('D:/jinxData/TT100K/data/image_reparation/'                                    + image_path, target + '/test/' + image_path)if __name__ == '__main__':    tt100k = TT100K2COCO()    # tt100k.class_statistics()    # tt100k.original_datasets2object_datasets_re()    # tt100k.coco_json2yolo_txt('train')    # tt100k.coco_json2yolo_txt('val')    # tt100k.coco_json2yolo_txt('test')    tt100k.divide_TrainValTest('D:/jinxData/TT100K/data/dataset/annotations/train', 'D:/jinxData/TT100K/data')    tt100k.divide_TrainValTest('D:/jinxData/TT100K/data/dataset/annotations/val', 'D:/jinxData/TT100K/data')    tt100k.divide_TrainValTest('D:/jinxData/TT100K/data/dataset/annotations/test', 'D:/jinxData/TT100K/data')

点击全文阅读


本文链接:http://zhangshiyu.com/post/58975.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1