目标检测voc转yolo格式

yolo数据格式

每一行为一个物体,每个物体以(类别 x y w h),类别从0开始,坐标所有值都为相对值,取值范围为0~1。

类别 物体中心x坐标,物体中心y坐标,宽度w,高度h
0 0.546242 0.231124 0.526212 0.734124

把voc 目标框的坐标信息(xmin,xmax,ymin,ymax)提取出来转换成yolo格式(x,y,width,height),并且重新保存。

# todo
#  把voc数据集标注(xml)信息转换成yolo标注格式(txt),并将对应图像文件复制到对应文件夹
#  根据voc的json文件,生成对应的names标签
import os
from lxml import etree
import json
import shutil
from tqdm import tqdm

voc_root = r'D:\pycharm_project\practice\VOCdevkit'
voc_version = 'VOC2012'

# voc  imagesets 下面main中保存的训练集验证集信息
train_txt = 'train.txt'
val_txt = "val.txt"

# jason文件位置
json_path = r'D:\pycharm_project\practice\VOCdevkit\VOC2012\pascal_voc_classes.json'

# 转换后文件保存目录
save_file_root = r'D:\pycharm_project\practice\yolov3-spp\my_yolo_dataset'

# 整理路径
voc_images_path = os.path.join(voc_root,voc_version,"JPEGImages") #图片路径
voc_xml_path = os.path.join(voc_root,voc_version,"Annotations") #标注信息路径
train_txt_path = os.path.join(voc_root,voc_version,'ImageSets','Main',train_txt) #训练集图片名称路径
val_txt_path = os.path.join(voc_root,voc_version,'ImageSets','Main',val_txt) #测试集图片路径

if os.path.exists(save_file_root) is False:
    os.makedirs(save_file_root)

# 验证文件是否存在
assert os.path.exists(voc_images_path),"voc_images_path not exist"
assert os.path.exists(voc_xml_path),"voc_xml_path not exist"
assert os.path.exists(train_txt_path),"train_txt_path not exist"
assert os.path.exists(val_txt_path),"val_txt_path not exist"



def xml_to_dict(xml):
    """
    把xml递归成嵌套字典 object按列表存放
    :param xml: xml文件
    :return: dict:包含xml各种信息
    """
    if len(xml) == 0:
        # 遍历到底层 返回信息
        return xml.text

    result={}
    for child in xml:
        child_child = xml_to_dict(child)# 递归获取信息
        if child.tag != 'object': # 本层非object 直接按字典存
            result[child.tag] = child_child #本层tag的内容是下层
        else: # 本层是object 用列表存 不然多个object 会只保存最后一个object
            if child.tag not in result:
                result[child.tag] = []
            result[child.tag].append(child_child)
    return result


def trans_info(file_names, classes_dict,save_path,train_or_val="train"):
    """
    把voc格式 转成 yolo 注意yolo类别从0开始
    :param file_names: list: 文件名(不包括后缀)
    :param classes_dict: 类别信息
    :param save_path: 要保存的路径
    :param train_or_val: train or val
    :return: None
    """
    for file_name in tqdm(file_names,desc="转换{}文件中".format(train_or_val)):
        if file_name.strip() == '':
            continue
        xml_path = os.path.join(voc_xml_path,file_name+'.xml')
        #读取对应file_name
        with open(xml_path) as f:
            xml_str = f.read()
        # read读出来str 再转回xml文件
        xml = etree.fromstring(xml_str)
        # 传入xml文件,返回转换后的字典
        annotations_info = xml_to_dict(xml)

        # 读取信息
        img_height = float(annotations_info['size']['height'])
        img_width = float(annotations_info['size']['width'])
        if os.path.exists(os.path.join(save_path,train_or_val)) is False:
            os.makedirs(os.path.join(save_path,train_or_val))
        with open(os.path.join(save_path,train_or_val,file_name+'.txt'),'w') as f:
            for i,obj in enumerate(annotations_info['object']):
                # 获取坐标信息
                xmin = float(obj['bndbox']['xmin'])
                xmax = float(obj['bndbox']['xmax'])
                ymin = float(obj['bndbox']['ymin'])
                ymax = float(obj['bndbox']['ymax'])
                # 等会要相对化所以先转成浮点
                name = obj['name']
                index = classes_dict[name] -1 # yolo的编号是从0开始 voc是从1开始

                #转成yolo (label xcenter ycenter width height)
                x_center = xmin+(xmax-xmin)/2
                y_center = ymin+(ymax-ymin)/2
                w = xmax-xmin
                h = ymax-ymin
                # 保留6位小数
                x_center = round(x_center/img_width,6)
                y_center = round(y_center/img_height,6)
                w = round(w/img_width,6)
                h = round(h/img_height,6)

                #转换成字符串
                info = [str(j) for j in [index,x_center,y_center,w,h]]

                # 保存
                if i == 0:
                    # 用空格隔开
                    f.write(' '.join(info))
                else:
                    f.write('\n'+' '.join(info))

        # 把图片复制到对应文件夹
        if os.path.exists(os.path.join(save_path,'Image')) is False:
            os.makedirs(os.path.join(save_path,'Image'))
        shutil.copyfile(os.path.join(voc_images_path,file_name+'.jpg'),os.path.join(save_path,'Image',file_name+'.jpg'))


def create_class_names(class_dict: dict):
    keys = class_dict.keys()
    with open(os.path.join(save_file_root,'my_data_label.names'), "w") as w:
        for index, k in enumerate(keys):
            if index + 1 == len(keys):
                w.write(k)
            else:
                w.write(k + "\n")

def main():
    # todo 读取train.txt ,val.txt 把里面的文件转移,并且信息修改成yolo的格式
    # 打开json文件并且加载成字典
    # 把train和val文件复制过来
    shutil.copyfile(train_txt_path, os.path.join(save_file_root,'train.txt'))
    shutil.copyfile(val_txt_path, os.path.join(save_file_root,'val.txt'))

    f1 = open(json_path, 'r')
    class_dict = json.load(f1)
    #读取 train.txt

    with open(train_txt_path) as f:
        train_file_names = [file_name for file_name in f.read().split('\n')]
        print(train_file_names)


    trans_info(train_file_names,class_dict,save_file_root,"train")
    with open(val_txt_path) as f:
        train_file_names = [file_name for file_name in f.read().split('\n')]
        print(train_file_names)
    trans_info(train_file_names, class_dict, save_file_root, "val")

    # todo 读取json文件 生成data.names
    create_class_names(class_dict)


if __name__ == '__main__':
    main()

你可能感兴趣的:(学习记录,pytorch,python,深度学习)