不同数据集格式转换处理

Python os.path() 模块

os.path.abspath(path) 返回绝对路径
os.path.basename(path) 返回文件名
os.path.dirname(path) 返回文件路径
os.path.exists(path) 如果路径 path 存在,返回 True;如果路径 path 不存在,返回 False。
os.path.join(path1[, path2[, ...]]) 把目录和文件名合成一个路径
os.path.normcase(path) 转换path的大小写和斜杠
os.path.normpath(path) 规范path字符串形式
os.path.realpath(path) 返回path的真实路径
os.path.split(path) 把路径分割成 dirname 和 basename,返回一个元组
os.path.splitext(path) 分割路径,返回路径名和文件扩展名的元组

print( os.path.basename('/root/runoob.txt') ) # 返回文件名

print( os.path.dirname('/root/runoob.txt') ) # 返回目录路径

print( os.path.split('/root/runoob.txt') ) # 分割文件名与路径

print( os.path.join('root','test','runoob.txt') ) # 将目录和文件名合成一个路径

执行以上程序输出结果为:

runoob.txt
/root
('/root', 'runoob.txt')
root/test/runoob.txt

shutil.copy() 模块具体用法
    shutil.copy(source, destination)(这种复制形式使用的前提是必须要有 os.chdir(你要处理的路径))
        source/destination 都是字符串形式的路劲,其中destination是:
            1、可以是一个文件的名称,则将source文件复制为新名称的destination
            2、可以是一个文件夹,则将source文件复制到destination中
            3、若这个文件夹不存在,则将source目标文件内的内容复制到destination中

shutil.copytree() 模块具体用法
    shutil.copytree(source, destination)(这种复制形式无需 os.chdir() 便可操作)
        source 、 destination 可以是不同的文件夹、不同的硬盘空间,
        可以更为方便的实现文件的备份

目标检测数据集的格式一般有.json(coco)、.txt(yolo)、.xml(voc)

本博客主要总结不同数据集格式之间的转换。

数据集的格式

XML

voc数据集格式介绍参考见VOC数据集格式介绍_望天边星宿的博客-CSDN博客_voc数据集格式

XML解析常用于ET.parse

常用操作:
tree = ET.parse(xml_path)
root = tree.getroot() # 获取根节点
node.tag # 获取节点名字,string
node…attrib # 获取节点全部属性,dict
要想遍历某一层节点下的子节点,貌似只可以一层一层,从根节点开始,慢慢遍历:
xml文件用上一篇的xml即可,写了小段实际操作代码 如下:

def xml_read(content):
    tree = ET.parse('../../../../androidmanifest.xml')
    root = tree.getroot()
    for child in root:
        if child.tag == 'application':
            for child1 in child:
                if child1.tag == 'activity':
                    for key, value in child1.attrib.items():
                        if value == content:
                            str_tag = ''
                            str_attrib = ''
                            for inter_child in child1:
                                if inter_child.tag == 'intent-filter':
                                    for child2 in inter_child:
                                        str_tag += child2.tag + '|'
                                        for key, value in child2.attrib.items():
                                            str_attrib += value + '|'
                                if 'action' in str_tag and 'category' in str_tag and len(inter_child) == 2 and 'android.intent.action.MAIN' in str_attrib and 'android.intent.category.LAUNCHER' in str_attrib:
                                    return True
                                else:
                                    return False

代码的主要意思就是:
1.先通过根节点获取application节点,再在application中遍历tag名称为activity的标签
2.获取activity节点的属性,,若其中有和content相同的话,那么我们继续遍历activity节下的子节点(跳到步骤3),如果没有和content相同的话,就不用理会;
3. 先判断有没有intent-filter节点,没有则后面判读会失败,如果有,则继续遍历intent-filter下所有节点中是不是有action和category,如果有,则继续判断这两个标签的属性值,是不是和下面的if条件中符合,符合则返回结果为true,不符合或者没有action\category其中任意一个子节点的话,都是返回false。
 

COCO

coco数据集格式介绍参考见coco数据集格式介绍_ Clear butterfly的博客-CSDN博客_coco数据集格式

yolo

不同数据集格式转换处理_第1张图片

数据格式转换

xml(voc)转json(coco)

1、xmin,ymin,width,height 转为 xmin,ymin,xmax,ymax

前2个坐标不需要动,只需要动后2个,规则如下:

xmax= xmin+width-1

ymax= ymin+height-1

[xmin, ymin, xmin+width-1, ymin+height-1]

2、xmin,ymin,xmax,ymax 转为 x,y,width,height

前2个坐标不需要动,只需要动后2个,规则如下:

width = xmax-xmin+1

height = ymax-ymin+1

[xmin, ymin, xmax-xmin+1, ymax-ymin+1]

关键词:VOC数据集和COCO数据集之间对应的关系。

voc2coco.py完整代码:

#实现voc数据集格式(.xml)-->coco数据集格式(.json)

import os
import shutil
import numpy as np
import json
import xml.etree.ElementTree as ET
# 检测框的ID起始值
START_BOUNDING_BOX_ID = 1
# 类别列表无必要预先创建,程序中会根据所有图像中包含的ID来创建并更新
PRE_DEFINE_CATEGORIES = {}
# If necessary, pre-define category and its id
PRE_DEFINE_CATEGORIES = {"crazing": 1, "inclusion": 2, "patches": 3, "pitted_surface": 4,
                          "rolled-in_scale":5, "scratches": 6}

#遍历xml树根的节点信息
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 NotImplementedError('Can not find %s in %s.'%(name, root.tag))
    if length > 0 and len(vars) != length:
        raise NotImplementedError('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 convert(xml_list, xml_dir, json_file):
    '''
    :param xml_list: 需要转换的XML文件列表
    :param xml_dir: XML的存储文件夹
    :param json_file: 导出json文件的路径
    :return: None
    '''
    list_fp = xml_list
    image_id=0
    # 标注json的基本结构
    json_dict = {"images":[],
                 "type": "instances",
                 "annotations": [],
                 "categories": []}
    categories = PRE_DEFINE_CATEGORIES
    bnd_id = START_BOUNDING_BOX_ID
    for line in list_fp:
        line = line.strip()
        print(" Processing {}".format(line))
        # 解析XML        
        #使用root.find / root.findall()方法在xml中定位所需的信息;
        #然后使用(.text)返回child/grandchild元素的值。
        xml_f = os.path.join(xml_dir, line)
        tree = ET.parse(xml_f)
        root = tree.getroot()
        filename = root.find('filename').text  # 取出图片名字
        image_id+=1
        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)
        # [循环嵌套]处理每个标注的检测框
        for obj in get(root, 'object'):
            # 取出检测框类别名称
            category = get_and_check(obj, 'name', 1).text
            # 更新类别ID字典
            if category not in categories:
                new_id = len(categories)
                categories[category] = new_id+1
            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)
            annotation = dict()
            annotation['area'] = o_width*o_height
            annotation['iscrowd'] = 0
            annotation['image_id'] = image_id
            annotation['bbox'] = [xmin, ymin, o_width, o_height]
            annotation['category_id'] = category_id
            annotation['id'] = bnd_id
            annotation['ignore'] = 0
            # 设置分割数据,点的顺序为逆时针方向
            annotation['segmentation'] = [[xmin,ymin,xmin,ymax,xmax,ymax,xmax,ymin]]

            json_dict['annotations'].append(annotation)
            bnd_id = bnd_id + 1

    # 写入类别ID字典
    for cate, cid in categories.items():
        cat = {'supercategory': 'none', 'id': cid, 'name': cate}
        json_dict['categories'].append(cat)
    # 导出到json
    #mmcv.dump(json_dict, json_file)????
    #print(type(json_dict))

    json_data = json.dumps(json_dict)
    with  open(json_file, 'w') as w:
        w.write(json_data)


if __name__ == '__main__':
    root_path = './'
    #生成coco-2014格式
    if not os.path.exists(os.path.join(root_path,'coco/annotations')):
        os.makedirs(os.path.join(root_path,'coco/annotations'))
    if not os.path.exists(os.path.join(root_path, 'coco/train')):
        os.makedirs(os.path.join(root_path, 'coco/train'))
    if not os.path.exists(os.path.join(root_path, 'coco/val')):
        os.makedirs(os.path.join(root_path, 'coco/val'))
    
    #####{可修改}xml文件地址
    ##注意分开xml和图片
    xml_dir = os.path.join(root_path,'xml555') ##或:xml_dir = 'path'
    ##划分训练集和验证集
    xml_labels = os.listdir(xml_dir)
    np.random.shuffle(xml_labels)
    
    #9:1划分
    split_point = int(len(xml_labels)/10)

    # validation data
    xml_list = xml_labels[0:split_point]
    json_file = os.path.join(root_path,'coco/annotations/instances_val.json')
    convert(xml_list, xml_dir, json_file)
    for xml_file in xml_list:
        img_name = xml_file[:-4] + '.jpg'
        #####{可修改}voc图片路径
        shutil.copy(os.path.join(root_path, 'voc', img_name),
                    os.path.join(root_path, 'coco/val', img_name))    #复制文件(夹)
    # train data
    xml_list = xml_labels[split_point:]
    json_file = os.path.join(root_path,'coco/annotations/instances_train.json')
    convert(xml_list, xml_dir, json_file)
    for xml_file in xml_list:
        img_name = xml_file[:-4] + '.jpg'
        #####{可修改}voc图片路径
        shutil.copy(os.path.join(root_path, 'voc', img_name),
                    os.path.join(root_path, 'coco/train', img_name))
    

json(coco)转txt(yolo)

import json
import os
import xml.etree.ElementTree as ET
import cv2
 
 
# 标准化输出x, y, w, h格式
# 将x1, y1, x2, y2转换成yolov5所需要的x, y, w, h格式
def xyxy2xywh(size, box):
    dw = 1. / size[0]
    dh = 1. / size[1]
    x = (box[0] + box[2]) / 2 * dw
    y = (box[1] + box[3]) / 2 * dh
    w = (box[2] - box[0]) * dw
    h = (box[3] - box[1]) * dh
    return (x, y, w, h) 
 
 
def json2txt(path):
    #print(len(os.listdir(path)))
    pic_path = 'E:/Desktop/pv/pic'  # 原始图片路径
    txt_out_path = './backgr' # 转换后txt保存路径
    # 遍历每一个json文件
    for file in os.listdir(path):
        print(file)
        if "json" in str(file):
            with open(os.path.join(path, file), 'r') as f:
                data = json.load(f)
                #print(data)
                points = data['shapes'][0]['points']
                pic = file.split('.')[0]+'.png'
                pic_name = os.path.join(pic_path, pic)
                txt_name = file.split(".")[0] + ".txt"
                imread = cv2.imread(pic_name)
                h = imread.shape[0]
                w = imread.shape[1]
                print(imread.shape)
                xmin = points[0][0]
                ymin = points[0][1]
                xmax = points[2][0]
                ymax = points[2][1]
                box = [float(xmin), float(ymin), float(xmax),
                       float(ymax)]
                #print(box)
                # # 将x1, y1, x2, y2转换成yolov5所需要的x, y, w, h格式
                bbox = xyxy2xywh((w, h), box)
                print(bbox)
 
                # # 写入目标文件中,格式为 id x y w h
                with open(os.path.join(txt_out_path, txt_name), 'w') as out_file:
                    out_file.write(str(1) + " " + " ".join(str(x) for x in bbox) + '\n')
                out_file.close()
                #exit()
 
 
if __name__ == '__main__':
    # json格式数据路径
    jsonpath = './json'
    json2txt(jsonpath)

txt(yolo)转json(coco)

import os
import json
import cv2
import random
import time
from PIL import Image

coco_format_save_path='./'                      #要生成的标准coco格式标签所在文件夹
yolo_format_classes_path='./cls.txt'     #类别文件,一行一个类
yolo_format_annotation_path='./pv-txt'        #yolo格式标签所在文件夹
img_pathDir='./pv-agu'                        #图片所在文件夹

with open(yolo_format_classes_path,'r') as fr:                               #打开并读取类别文件
    lines1=fr.readlines()
# print(lines1)
categories=[]                                                                 #存储类别的列表
for j,label in enumerate(lines1):
    label=label.strip()
    categories.append({'id':j+1,'name':label,'supercategory':'None'})         #将类别信息添加到categories中
# print(categories)

write_json_context=dict()                                                      #写入.json文件的大字典
write_json_context['info']= {'description': '', 'url': '', 'version': '', 'year': 2022, 'contributor': '', 'date_created': '2022-10-24'}
write_json_context['licenses']=[{'id':1,'name':None,'url':None}]
write_json_context['categories']=categories
write_json_context['images']=[]
write_json_context['annotations']=[]

#接下来的代码主要添加'images'和'annotations'的key值
imageFileList=os.listdir(img_pathDir)                                           #遍历该文件夹下的所有文件,并将所有文件名添加到列表中
for i,imageFile in enumerate(imageFileList):
    imagePath = os.path.join(img_pathDir,imageFile)                             #获取图片的绝对路径
    image = Image.open(imagePath)                                               #读取图片,然后获取图片的宽和高
    W, H = image.size

    img_context={}                                                              #使用一个字典存储该图片信息
    #img_name=os.path.basename(imagePath)                                       #返回path最后的文件名。如果path以/或\结尾,那么就会返回空值
    img_context['file_name']=imageFile
    img_context['height']=H
    img_context['width']=W
    img_context['date_captured']='2022-10-24'
    img_context['id']=i                                                         #该图片的id
    img_context['license']=1
    img_context['color_url']=''
    img_context['flickr_url']=''
    write_json_context['images'].append(img_context)                            #将该图片信息添加到'image'列表中


    txtFile=imageFile[:18]+'.txt'                                               #获取该图片获取的txt文件
    with open(os.path.join(yolo_format_annotation_path,txtFile),'r') as fr:
        lines=fr.readlines()                                                   #读取txt文件的每一行数据,lines2是一个列表,包含了一个图片的所有标注信息
    for j,line in enumerate(lines):

        bbox_dict = {}                                                          #将每一个bounding box信息存储在该字典中
        # line = line.strip().split()
        # print(line.strip().split(' '))

        class_id,x,y,w,h=line.strip().split(' ')                                          #获取每一个标注框的详细信息
        class_id,x, y, w, h = int(class_id), float(x), float(y), float(w), float(h)       #将字符串类型转为可计算的int和float类型

        xmin=(x-w/2)*W                                                                    #坐标转换
        ymin=(y-h/2)*H
        xmax=(x+w/2)*W
        ymax=(y+h/2)*H
        w=w*W
        h=h*H

        bbox_dict['id']=i                                                    #bounding box的坐标信息
        bbox_dict['image_id']=i
        bbox_dict['category_id']=class_id                                               #注意目标类别要加一
        bbox_dict['iscrowd']=0
        height,width=abs(ymax-ymin),abs(xmax-xmin)
        bbox_dict['area']=height*width
        bbox_dict['bbox']=[xmin,ymin,w,h]
        bbox_dict['segmentation']=[[xmin,ymin,xmax,ymin,xmax,ymax,xmin,ymax]]
        write_json_context['annotations'].append(bbox_dict)                               #将每一个由字典存储的bounding box信息添加到'annotations'列表中

name = os.path.join(coco_format_save_path,"train"+ '.json')
with open(name,'w') as fw:                                                                #将字典信息写入.json文件中
    json.dump(write_json_context,fw,indent=2)

你可能感兴趣的:(目标检测,python,开发语言)