目标检测各类数据集格式互转以及处理方法(VOC, COCO, txt)

在做目标检测时,我个人比较喜欢VOC格式的数据集,所以遇到COCO格式的数据集习惯转为VOC的,再进行处理。

代码地址

coco转voc格式

需要修改的只有路径以及jsonFileName 这个列表,都已经标明了比较清晰的注释。

'''
Author: TuZhou
Version: 1.0
Date: 2021-08-29 16:32:52
LastEditTime: 2021-08-29 17:44:57
LastEditors: TuZhou
Description: 
FilePath: \My_Yolo\datasets\coco_to_voc.py
'''

from pycocotools.coco import \
    COCO  # 这个包可以从git上下载https://github.com/cocodataset/cocoapi/tree/master/PythonAPI,也可以直接用修改后的coco.py
import os, cv2, shutil
from lxml import etree, objectify
from tqdm import tqdm
from PIL import Image

#要生成的voc格式图片路径
image_dir = './datasets/RoadSignsPascalVOC_Voc/images'
#要生成的voc格式xml标注路径
anno_dir = './datasets/RoadSignsPascalVOC_Voc/annotations'
#---------------------------------------------#
#你的coco格式的json类型和文件名,前者表示是train类型的json,后者表示该json文件的名字,类型名最好是与你的对应类型图片保存文件夹名一致
#我的json目录只有一个train类型,如果你有测试集的json文件,则可写成[['train', 'instance_train'], ['test', 'instance_test']]
jsonFileName = [['train', 'instance_train']]
#----------------------------------------------#


'''
Author: TuZhou
Description: 若模型保存文件夹不存在,创建模型保存文件夹,若存在,删除重建
param {*} path 文件夹路径
return {*}
'''
def mkr(path):
    if os.path.exists(path):
        shutil.rmtree(path)
        os.mkdir(path)
    else:
        os.mkdir(path) 


'''
Author: TuZhou
Description: 保存xml文件
param {*} filename
param {*} objs
param {*} filepath
return {*}
'''
def save_annotations(filename, objs, filepath):
    annopath = anno_dir + "/" + filename[:-3] + "xml"  # 生成的xml文件保存路径
    #print("filename", filename)
    dst_path = image_dir + "/" + filename
    img_path = filepath
    img = cv2.imread(img_path)
    #此处时对非RGB图像筛选,可注释
    # im = Image.open(img_path)
    # if im.mode != "RGB":
    #     print(filename + " not a RGB image")
    #     im.close()
    #     return
    # im.close()
    shutil.copy(img_path, dst_path)  # 把原始图像复制到目标文件夹
    E = objectify.ElementMaker(annotate=False)
    anno_tree = E.annotation(
        E.folder('1'),
        E.filename(filename),
        E.source(
            E.database('CKdemo'),
            E.annotation('VOC'),
            E.image('CK')
        ),
        E.size(
            E.width(img.shape[1]),
            E.height(img.shape[0]),
            E.depth(img.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)
 
 
def showbycv(coco, dataType, img, classes, origin_image_dir, verbose=False):
    filename = img['file_name']
    #NOTE:dataType表示coco格式中训练集或者测试集的图片文件夹名,但是我所有图片都放在JPEGImages文件夹中,所以此处为空,有需要的可以修改
    #dataType就是jsonFileName中的json类型,如果你的类型名和你的图片文件夹名一致,则可注释下行
    dataType = ''
    filepath = os.path.join(origin_image_dir, dataType, filename)
    I = cv2.imread(filepath)
    annIds = coco.getAnnIds(imgIds=img['id'], iscrowd=None)
    anns = coco.loadAnns(annIds)
    objs = []
    for ann in anns:
        name = classes[ann['category_id']]
        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 = [name, 1.0, xmin, ymin, xmax, ymax]
            objs.append(obj)
            if verbose:
                cv2.rectangle(I, (xmin, ymin), (xmax, ymax), (255, 0, 0))
                cv2.putText(I, name, (xmin, ymin), 3, 1, (0, 0, 255))
    save_annotations(filename, objs, filepath)
    if verbose:
        cv2.imshow("img", I)
        cv2.waitKey(0)
 
 
def catid2name(coco):  # 将名字和id号建立一个字典
    classes = dict()
    for cat in coco.dataset['categories']:
        classes[cat['id']] = cat['name']
        # print(str(cat['id'])+":"+cat['name'])
    return classes
 
 
'''
Author: TuZhou
Description: 
param {*} origin_anno_dir 原始coco的json文件目录
param {*} origin_image_dir 原始coco的图片保寸目录
param {*} verbose
return {*}
'''
def get_CK5(origin_anno_dir, origin_image_dir, verbose=False):
    for dataType, annoName in jsonFileName:
        #annFile = 'instances_{}.json'.format(dataType)
        annFile = annoName + '.json'
        annpath = os.path.join(origin_anno_dir, annFile)
        coco = COCO(annpath)
        classes = catid2name(coco)
        imgIds = coco.getImgIds()
        # imgIds=imgIds[0:1000]#测试用,抽取10张图片,看下存储效果
        for imgId in tqdm(imgIds):
            img = coco.loadImgs(imgId)[0]
            showbycv(coco, dataType, img, classes, origin_image_dir, verbose=False)
 
 
def main():
    base_dir = './datasets/RoadSignsPascalVOC_Voc'  # step1 这里是一个新的文件夹,存放转换后的图片和xml标注
    image_dir = os.path.join(base_dir, 'images')  # 在上述文件夹中生成images,annotations两个子文件夹
    anno_dir = os.path.join(base_dir, 'annotations')
    mkr(image_dir)
    mkr(anno_dir)
    origin_image_dir = './datasets/RoadSignsPascalVOC/JPEGImages'  # step 2原始的coco的图像存放位置
    origin_anno_dir = './datasets/RoadSignsPascalVOC_Coco/Annotations'  # step 3 原始的coco的标注存放位置
    verbose = True  # 是否需要看下标记是否正确的开关标记,若是true,就会把标记展示到图片上
    get_CK5(origin_anno_dir, origin_image_dir, verbose)
 
 
if __name__ == "__main__":
    main()

voc转coco格式

import xml.etree.ElementTree as ET
import os
import json
 
coco = dict()
coco['images'] = []
coco['type'] = 'instances'
coco['annotations'] = []
coco['categories'] = []
 
category_set = dict()
image_set = set()
 
category_item_id = -1
image_id = 20210000000
annotation_id = 0
 
 
def 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_id
 
 
def 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']
    coco['images'].append(image_item)
    image_set.add(file_name)
    return image_id
 
 
def 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 parseXmlFiles(xml_path):
    for f in os.listdir(xml_path):
        if not f.endswith('.xml'):
            continue
 
        bndbox = dict()
        size = dict()
        current_image_id = None
        current_category_id = None
        file_name = None
        size['width'] = None
        size['height'] = None
        size['depth'] = None
 
        xml_file = os.path.join(xml_path, f)
        print(xml_file)
 
        tree = ET.parse(xml_file)
        root = tree.getroot()
        if root.tag != 'annotation':
            raise Exception('pascal voc xml root element should be annotation, rather than {}'.format(root.tag))
 
        # elem is <folder>, <filename>, <size>, <object>
        for elem in root:
            current_parent = elem.tag
            current_sub = None
            object_name = None
 
            if elem.tag == 'folder':
                continue
 
            if elem.tag == 'filename':
                file_name = elem.text
                if file_name in category_set:
                    raise Exception('file_name duplicated')
 
            # add img item only after parse <size> tag
            elif current_image_id is None and file_name is not None and size['width'] is not None:
                if file_name not in image_set:
                    current_image_id = addImgItem(file_name, size)
                    print('add image with {} and {}'.format(file_name, size))
                else:
                    raise Exception('duplicated image: {}'.format(file_name))
                    # subelem is <width>, <height>, <depth>, <name>, <bndbox>
            for subelem in elem:
                bndbox['xmin'] = None
                bndbox['xmax'] = None
                bndbox['ymin'] = None
                bndbox['ymax'] = None
 
                current_sub = subelem.tag
                if current_parent == 'object' and subelem.tag == 'name':
                    object_name = subelem.text
                    if object_name not in category_set:
                        current_category_id = addCatItem(object_name)
                    else:
                        current_category_id = category_set[object_name]
 
                elif current_parent == 'size':
                    if size[subelem.tag] is not None:
                        raise Exception('xml structure broken at size tag.')
                    size[subelem.tag] = int(subelem.text)
 
                # option is <xmin>, <ymin>, <xmax>, <ymax>, when subelem is <bndbox>
                for option in subelem:
                    if current_sub == 'bndbox':
                        if bndbox[option.tag] is not None:
                            raise Exception('xml structure corrupted at bndbox tag.')
                        bndbox[option.tag] = int(option.text)
 
                # only after parse the <object> tag
                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 {},{},{},{}'.format(object_name, current_image_id, current_category_id,
                                                                    bbox))
                    addAnnoItem(object_name, current_image_id, current_category_id, bbox)
 
 
if __name__ == '__main__':
    xml_path = './datasets/RoadSignsPascalVOC/Annotations'    # 这是xml文件所在的地址
    json_file = './datasets/RoadSignsPascalVOC_Coco/Annotations/train.json'       # 这是你用xml_path下的xml文件生成的json文件                        
    parseXmlFiles(xml_path)                                       # 只需要改动这两个参数就行了
    json.dump(coco, open(json_file, 'w'))

转换好的coco格式的json文件我还未在训练中验证,但是我将转换后的json文件又通过第一份转voc的代码又转回voc格式,部署到训练中没有问题。

voc数据格式的处理

首先你的voc格式的数据集目录应该是这样的:
——annotations
——ImageSets
————Main
——images

其中annotations为你的xml标注文件,images为你的图片目录。
Main中保存划分数据集后的txt文件
来看看process_dataset.py中的代码:

'''
Author: TuZhou
Version: 1.0
Date: 2021-08-21 19:53:22
LastEditTime: 2021-08-29 17:05:35
LastEditors: TuZhou
Description: 划分数据集,将xml文件中的标注信息提取出与图片路径保存到新的txt文件中
FilePath: \My_Yolo\datasets\process_dataset.py
'''

from pathlib import Path
import yaml
import xml.etree.ElementTree as ET
from os import getcwd

import os
import random 
random.seed()

#自定义数据集配置文件的路径
yaml_path = './cfg/dataset.yaml'
#----------------------------------------------------------------------#
#   想要增加测试集修改trainval_percent
#   train_percent不需要修改
#----------------------------------------------------------------------#
trainval_percent = 0.9                  #所有数据集中训练集+验证集的比例
train_percent = 1                       #训练集+验证集中训练集的比例(可不管)默认1即可


'''
Author: TuZhou
Description: 读取数据集配置文件
param {*} yaml_path
return {*}返回配置文件中信息字典
'''
def read_dataset_yaml(yaml_path = './cfg/dataset.yaml'):
    yaml_file = Path(yaml_path)
    with open(yaml_file, encoding='utf-8') as f:
        yaml_dict = yaml.safe_load(f)
        
    return yaml_dict


'''
Author: TuZhou
Description: 从数据集中划分训练集,验证集,测试集,txt文件保存在ImageSets的Main文件夹中
param {*} xmlfilepath 数据集xml标注文件保存路径
param {*} saveSplitePath 数据集划分txt文件保存路径
return {*}
'''
def splite_dataset(xmlfilepath, saveSplitePath):

    #读取所有xml文件,存入列表中
    temp_xml = os.listdir(xmlfilepath)
    total_xml = []
    for xml in temp_xml:
        if xml.endswith(".xml"):
            total_xml.append(xml)

    #图片总数
    num = len(total_xml)  
    list = range(num)  
    numbers_tv = int(num * trainval_percent)            #获取训练和验证集数量
    numbers_train = int(numbers_tv * train_percent)  
    random.seed()
    trainval = random.sample(list, numbers_tv)          #从list中选取生成训练验证集的随机列表  
    random.seed()
    train = random.sample(trainval, numbers_train)                 #从训练验证集中选取生成训练集的随机列表


    print("train and val size: ", numbers_tv)
    print("train size: ", numbers_train)
    print("test size: ", num - numbers_tv)
    ftrainval = open(os.path.join(saveSplitePath,'trainval.txt'), 'w')  
    ftest = open(os.path.join(saveSplitePath,'test.txt'), 'w')  
    ftrain = open(os.path.join(saveSplitePath,'train.txt'), 'w')  
    fval = open(os.path.join(saveSplitePath,'val.txt'), 'w')  
    
    for i in list:  
        name=total_xml[i][:-4]+'\n'  
        if i in trainval:  
            ftrainval.write(name)  
            if i in train:  
                ftrain.write(name)  
            else:  
                fval.write(name)  
        else:  
            ftest.write(name)  
    
    ftrainval.close()  
    ftrain.close()  
    fval.close()  
    ftest .close()
    

def convert_annotation(xmlfilepath, image_id, list_file, classes):
    #in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id), encoding='utf-8')
    in_file = open(xmlfilepath + '%s.xml'%(image_id), encoding='utf-8')
    tree=ET.parse(in_file)
    root = tree.getroot()

    for obj in root.iter('object'):
        difficult = 0 
        if obj.find('difficult')!=None:
            difficult = obj.find('difficult').text
        cls = obj.find('name').text
        if cls not in classes or int(difficult)==1:
            continue
        cls_id = classes.index(cls)
        xmlbox = obj.find('bndbox')
        b = (int(float(xmlbox.find('xmin').text)), int(float(xmlbox.find('ymin').text)), int(float(xmlbox.find('xmax').text)), int(float(xmlbox.find('ymax').text)))
        list_file.write(" " + ",".join([str(a) for a in b]) + ',' + str(cls_id))
        #print(" " + ",".join([str(a) for a in b]) + ',' + str(cls_id))


def save_dataset_info(yaml_dict):
    wd = getcwd()
    sets = yaml_dict['sets']
    saveSplitePath = yaml_dict['saveSplitePath']
    xmlfilepath = yaml_dict['xmlfilepath']
    processedPath = yaml_dict['DatasetPath']
    ImagesDir = yaml_dict['ImagesDir']
    classes = yaml_dict['classes']
    image_format = yaml_dict['image_format']
    for image_set in sets:
        # image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set), encoding='utf-8').read().strip().split()
        # list_file = open('%s_%s.txt'%(year, image_set), 'w', encoding='utf-8')
        #读取划分后的图片名文件
        image_ids = open(saveSplitePath + '%s.txt'%(image_set), encoding='utf-8').read().strip().split()
        #创建要保存路径和标注的txt文件
        list_file = open(processedPath + '%s.txt'%(image_set), 'w', encoding='utf-8')

        for image_id in image_ids:
            #list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg'%(wd, year, image_id))
            list_file.write(wd + ImagesDir +  image_id + image_format)
            convert_annotation(xmlfilepath, image_id, list_file, classes)
            list_file.write('\n')
        list_file.close()
        

if __name__ == '__main__':
    yaml_dict = read_dataset_yaml(yaml_path)
    #划分数据集
    xmlfilepath, saveSplitePath = yaml_dict['xmlfilepath'], yaml_dict['saveSplitePath']
    splite_dataset(xmlfilepath, saveSplitePath)
    #保存数据集信息,即图片路径以及标注信息
    save_dataset_info(yaml_dict)
    

注意我对路径的一些配置保存在dataset.yaml文件中,你可先创建一个yaml文件保存一些配置。比如我的:

#数据集配置文件信息


#数据集xml标注文件保存路径
xmlfilepath: './datasets/RoadSignsPascalVOC_Voc/annotations/'
#数据集划分保存路径
saveSplitePath: './datasets/RoadSignsPascalVOC_Voc/ImageSets/Main/'
#数据集目录
DatasetPath: './datasets/RoadSignsPascalVOC_Voc/'
#图片路径,注意该路径开头前面要拼接根目录路径的,所以前面不要加'.'了
ImagesDir: '/datasets/RoadSignsPascalVOC_Voc/images/'       

#图片的格式
image_format: '.png'

# 分类种类数量
numbers_classes: 4

# 分类名
classes: ["trafficlight", "stop", "speedlimit", "crosswalk"]
#classes: ["hero", "grass", "wild_monster", "dragon", "soldier", "tower", "buff", "crystal"]

#处理后的文件名,存放图片保存路径以及其对应的标注信息,可默认不修改
#sets: [('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
sets: ['train', 'val', 'test']

经过处理后你的数据集会被划分,并且xml文件中的内容会被提取出来。
Main文件夹中会生成这么几个文件:
在这里插入图片描述
里面保存的是划分后的图片文件名。
在这里插入图片描述
另外在你的数据集目录下会生成存储提取的xml信息的txt文件。
在这里插入图片描述
其中保存了每张图片的路径以及该图片的标注信息。在这里插入图片描述
前面为图片路径,后面每五个数字为一组,表示图片中目标的左上角右下角x,y坐标以及对应的类别。

如果要处理的voc数据集中全部为测试集,可以更改process_dataset.py中的trainval_percent变量,将其赋值为0即可。

txt注释格式转coco

有的人拿到的数据集既不是voc,也不是coco格式,就直接就是处理好的txt格式,但是代码却是coco数据集或者voc数据集处理的形式,对于编码困难的可以试试以下转换方法,可以将txt转为coco,如果是voc格式,可以先转coco再通过上面的coco转voc再转换为voc格式。

注意你的txt注释文件形式是这样的:
annotations.txt:
在这里插入图片描述
图片文件名 x1 y1 x2 y2 label…
从左到右为图片文件名,目标左上角x坐标,目标左上角y坐标,目标右下角x坐标,目标右下角y坐标,目标所属类别
注意我的label是直接就是类别名,而不是int编号,为编号的直接category_id等于label就可
代码中几处需要修改的部分已经使用TODO中的NOTE标注出来了。

'''
Author: TuZhou
Version: 1.0
Date: 2021-09-11 16:50:47
LastEditTime: 2021-09-12 09:42:32
LastEditors: TuZhou
Description: 
FilePath: \python_test\txt_to_coco.py
'''
import json
import cv2
import path
import os


#第一个coco json类型
bnd_id_start = 1

#NOTE:你的分类
class_map = {"tamper":0}
#你的图片id,可自定义
my_image_id = 20210000000

times = 0

json_dict = {
    "images"     : [],
    "type"       : "instances",
    "annotations": [],
    "categories" : []
}

'''
Author: TuZhou
Description: 
param {*} L1:你的标注列表信息,注意只包含x1,y1,x2,y2,label,如[234,43,345,213,'person',34,54,23,34,'dog']
param {*} numbers:你要切分的长度,x1,y1,x2,y2,label长度为5
return {*}返回包含所有目标信息的二维列表
'''
def get_list_value(L1, numbers):
    obj_list = []
    for i in range(0, len(L1), numbers):
        l2 = L1[i: i + numbers]
        obj_list.append(l2)
    return obj_list


# NOTE:这里是你的txt文件的读取
with open(r'E:\user\baidu_pan\CASIA_object_detection\annotations.txt','r') as f:
    data = f.readlines()

bnd_id = bnd_id_start

#NOTE:这是你的图片格式,如果支持多种图片文件
images_format = ['.jpg', '.tif']


for d in data:
    content = d.strip().split(" ")
    print(content)
    filename = content[0]     #这里可能修改,txt文件每一行第一个属性是图片路径,通过split()函数把图像名分离出来就行
    for image_format in images_format:
        #NOTE:你的图片文件路径要改
        image_path = 'E:\\user\\baidu_pan\\CASIA_object_detection\\CAISA_Train_Images\\' + content[0] + image_format
        if os.path.exists(image_path):
            break
    img = cv2.imread(image_path)
    print(image_path)
    try:
        height,width = img.shape[0],img.shape[1]
        image_id = my_image_id
        my_image_id += 1
    except:
        times +=1
        print('file is error')

# type 已经填充

#定义image 填充到images里面
    image = {
        'file_name' : filename,  #文件名
        'height'    : height,    #图片的高
        'width'     : width,     #图片的宽
        'id'        : image_id   #图片的id,和图片名对应的
    }
    json_dict['images'].append(image)

    #print(content[5])
    # start_index = 1
    # if start_index != len(content) - 1:
    obj_lists = get_list_value(content[1:],5)
    for obj_list in obj_lists:
        #print(c)
        #xmin,ymin,xmax,ymax,label = c.strip().split(" ")
        xmin = int(obj_list[0])
        ymin = int(obj_list[1])
        xmax = int(obj_list[2])
        ymax = int(obj_list[3])
        o_width = abs(int(xmax) - int(xmin))
        o_height = abs(int(ymax) - int(ymin))

        area = o_width * o_height
        category_id = class_map[obj_list[4]]

        # #定义annotationhb
        annotation = {
            'area'          : area,  #
            'iscrowd'       : 0,
            'image_id'      : image_id,  #图片的id
            'bbox'          :[xmin, ymin, o_width,o_height],
            'category_id'   : int(category_id), #类别的id 通过这个id去查找category里面的name
            'id'            : bnd_id,  #唯一id ,可以理解为一个框一个Id
            'ignore'        : 0,
            'segmentation'  : []
        }
        print(category_id)

        json_dict['annotations'].append(annotation)

        bnd_id += 1
    #
#定义categories

#你得类的名字(cid,cate)对应
classes = ['tamper']

for i in range(len(classes)):

    cate = classes[i]
    cid = i
    category = {
        'supercategory' : 'none',
        'id'            : cid,  #类别的id ,一个索引,主键作用,和别的字段之间的桥梁
        'name'          : cate  #类别的名字比如房子,船,汽车
    }

    json_dict['categories'].append(category)



json_fp = open("train.json",'w')
json_str = json.dumps(json_dict)
json_fp.write(json_str)
json_fp.close()

我知道还有的小伙伴的txt文件注释格式可以和我的不一样,是不是这样的?
image_path xmin,ymin,xmax,ymax,label …
首个是图片路径而不是图片名,坐标等信息时通过逗号分隔而不是空格。
问题不大,代码很好改。

'''
Author: TuZhou
Version: 1.0
Date: 2022-01-14 10:10:06
LastEditTime: 2022-01-14 10:58:49
LastEditors: TuZhou
Description: 
FilePath: \My_Mobile_csp_Yolo2\datasets\txt_to_coco.py
'''
import json
import cv2


#第一个coco json类型

bnd_id_start = 1

times = 0
#分类部分在下面的代码
#你的图片id,可自定义
my_image_id = 20210000000


json_dict = {
    "images"     : [],
    "type"       : "instances",
    "annotations": [],
    "categories" : []
}




# 这里是你的txt文件的读取
with open(r'C:\Users\TuZhou\Desktop\desktop_code\My_Mobile_csp_Yolo2\datasets\RoadSignsPascalVOC\train.txt','r') as f:
    data = f.readlines()

bnd_id = bnd_id_start


for d in data:
    content = d.split(" ")
    filename = content[0].split("/")[-1]     #这里可能修改,txt文件每一行第一个属性是图片路径,通过split()函数把图像名分离出来就行
    img = cv2.imread(content[0])
    try:
        height,width = img.shape[0],img.shape[1]
        image_id = my_image_id
        my_image_id += 1
    except:
        times +=1
        print('file is error')

# type 已经填充

#定义image 填充到images里面
    image = {
        'file_name' : filename,  #文件名
        'height'    : height,    #图片的高
        'width'     : width,     #图片的宽
        'id'        : image_id   #图片的id,和图片名对应的
    }
    json_dict['images'].append(image)

#
    for c in content[1:]:
    	#NOTE:注意这里你的坐标信息是通过逗号隔开,所以split(","),但是你也许是通过空格隔开,直接split(" ")就可。
        xmin,ymin,xmax,ymax,label = c.strip().split(",")
        xmin = int(xmin)
        ymin = int(ymin)
        xmax = int(xmax)
        ymax = int(ymax)
        o_width = abs(int(xmax) - int(xmin))
        o_height = abs(int(ymax) - int(ymin))

        area = o_width * o_height
        category_id = label.strip()

        # #定义annotationhb
        annotation = {
            'area'          : area,  #
            'iscrowd'       : 0,
            'image_id'      : image_id,  #图片的id
            'bbox'          :[xmin, ymin, o_width,o_height],
            'category_id'   : int(category_id), #类别的id 通过这个id去查找category里面的name
            'id'            : bnd_id,  #唯一id ,可以理解为一个框一个Id
            'ignore'        : 0,
            'segmentation'  : []
        }
        print(category_id)

        json_dict['annotations'].append(annotation)

        bnd_id += 1
    #
#定义categories

#NOTE:你得类的名字(cid,cate)对应
# classes = ['0','1','2','3']
classes = ["trafficlight", "stop", "speedlimit", "crosswalk"]

for i in range(len(classes)):

    cate = classes[i]
    cid = i
    category = {
        'supercategory' : 'none',
        'id'            : cid,  #类别的id ,一个索引,主键作用,和别的字段之间的桥梁
        'name'          : cate  #类别的名字比如房子,船,汽车
    }

    json_dict['categories'].append(category)



json_fp = open(r"C:\Users\TuZhou\Desktop\desktop_code\My_Mobile_csp_Yolo2\datasets\RoadSignsPascalVOC\train.json",'w')
json_str = json.dumps(json_dict)
json_fp.write(json_str)
json_fp.close()

参考1
参考2
参考3

版权声明:本文为CSDN博主「良风抚旧」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Arcofcosmos/article/details/119983270

良风抚旧

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

yolov5训练数据集划分

yolov5训练数据集划分 按照默认8:1:1划分训练集,测试集,验证集。 txt文件出现在imageset文件夹。 import os import randomtrainval_pe

Pytorch—万字入门SSD物体检测

前言 由于初入物体检测领域,我在学习SSD模型的时候遇到了很多的困难。一部分困难在于相关概念不清楚,专业词汇不知其意,相关文章不知所云;另一部分困难在于网上大部分文章要么只是简要介绍了SS