从零开始使用YOLO和Paddle——目标检测模型数据预处理:从json到txt、voc和coco数据格式

目录

一、简介

二、YOLO模型

        1.初始数据集格式

        2.转为YOLO的 txt 格式

 三、Paddle模型

        1.VOC2007数据格式

         2.COCO数据格式


一、简介

        本文主要记录在使用YOLO和Paddle Detection做目标检测任务时,初始数据集与模型输入数据集不匹配时,对初始数据集预处理中的代码。

二、YOLO模型

        1.初始数据集格式

        初始的训练数据格式分为图片数据的 jpg 格式和边缘框的 json 格式。以一个边缘框的 json文件为例。json文件中是一个列表,列表每个元素代表当前图片中一个框的信息。

"id" : 边缘框索引数
"name" : 图片名
"box" : 边缘框坐标表示参数(四个数中前两数代表矩形框左上点xy坐标,后两数右下点xy坐标)

        2.转为YOLO的 txt 格式

        YOLO支持 txt 格式的标注文件,具体如下图,文本文件.txt 的每一行代表当前图片中的一个边缘框,每一行的五个数中第一个数代表区域的类别(如边缘框选择区域的内容是猫?狗?大象?)。该项目中暂不涉及检测区域类别任务,因此所有框类别都是0类。后四个数是将初始 json 文件中的 “box” 经过处理归一化后的边缘框信息(具体的数据变化逻辑在代码处注解中有解释)。

"yolo格式"
'类别号',  '左上x坐标',  '左上y坐标',  '右下x坐标',  '右下y坐标'

        YOLO可以训练的标注框文件格式:

        将初始 json 文件转为 yolo 可读的训练数据格式,并对完整数据集进行拆分为训练train和测试val数据集,其Python代码如下。

import os
import json
from PIL import Image, ImageDraw, ImageChops
import random
from shutil import copyfile

# 图片和锚框数据保存路径
jpg_path = '填入实际图片所在地址'
json_path = '填入实际json标注文件所在地址'
txt_path = '创建你需要保存yolo txt的文件夹,将创建地址填入'


# yolo模型数据保存路径
yolo_jpg = '创建训练测试数据集图片文件夹,将创建地址填入'
yolo_lab = '创建训练测试数据集标注框文件夹,将创建地址填入'

# 训练测试验证数据比重设置
weight = [0.9, 0.1]    


#####################(分界线)上述是需根据情况修改的参数,以下函数不需修改


# 锚框归一化
def convert(size, box):  # size:(原图w,原图h) , box:(xmin,xmax,ymin,ymax)
    dw = 1. / size[0]  # 1/w
    dh = 1. / size[1]  # 1/h
    x = (box[0] + box[2]) / 2.0  # 物体在图中的中心点x坐标
    y = (box[1] + box[3]) / 2.0  # 物体在图中的中心点y坐标
    w = box[2] - box[0]  # 物体实际像素宽度
    h = box[3] - box[1]  # 物体实际像素高度
    x = x * dw  # 物体中心点x的坐标比(相当于 x/原图w)
    w = w * dw  # 物体宽度的宽度比(相当于 w/原图w)
    y = y * dh  # 物体中心点y的坐标比(相当于 y/原图h)
    h = h * dh  # 物体宽度的宽度比(相当于 h/原图h)
    return [x, y, w, h]  # 返回 相对于原图的物体中心点的x坐标比,y坐标比,宽度比,高度比,取值范围[0-1]

# 将锚框json文件归一化后保存为yolo可读的txt文件
def normal_box(json_path, jpg_path,txt_path):
    for file in os.listdir(json_path):
        basename = file.split('.')[0]
        box_file = os.path.join(json_path, basename+'.json')
        jpg_file = os.path.join(jpg_path, basename+'.jpg')
        img = Image.open(jpg_file)
        img_size = (img.width, img.height)       # 获取图片尺寸
        with open(box_file, 'r') as f:
            box_list = json.load(f)
        for box_dict in box_list:
            box = box_dict['box'][:-1]         # 删除实际标注的锚框类别
            conv_box = convert(img_size, box)
            conv_box.insert(0, 0)              # 不对锚框类别进行训练
            save = os.path.join(txt_path, basename+'.txt')
            for box_txt in conv_box:
                with open(save, 'a') as f:
                    f.writelines(str(box_txt)+' ')
            with open(save, 'a') as f:
                f.write('\n')
        print(file + '  success')

# 复制文件——拆分后的数据移动到对应文件夹
def copy_file(file_path_list, file_path, target_path, last_name):
    for file in file_path_list:
        real_path = os.path.join(file_path, file+last_name)
        copyfile(real_path, target_path+file+last_name)     #copyfile(文件所在地,复制目标路径)
        print(file + last_name + '  success')

# 拆分数据集
def set_train_test_val(jpg_path, txt_path, yolo_jpg, yolo_lab, weight):
    filelist = os.listdir(jpg_path)
    length = len(filelist)
    random.shuffle(filelist)
    tr_namelist = [tr[:-4] for tr in filelist[:int(weight[0]*length)]]
    va_namelist = [va[:-4] for va in filelist[int(weight[0]*length):]]       # 文件basename不含格式后缀
    os.makedirs(yolo_jpg+'train')               # 创建图片训练 train文件夹
    os.makedirs(yolo_jpg+'val')                 # 创建图片测试 val文件夹
    os.makedirs(yolo_lab+'train')               # 创建标签训练 train文件夹
    os.makedirs(yolo_lab+'val')                 # 创建标签测试 val文件夹
    copy_file(tr_namelist, jpg_path, yolo_jpg+'train/','.jpg')    # 图片jpg训练集train
    copy_file(va_namelist, jpg_path, yolo_jpg + 'val/', '.jpg')   # 图片jpg验证集val
    copy_file(tr_namelist, txt_path, yolo_lab + 'train/', '.txt')  # 标签label训练集train
    copy_file(va_namelist, txt_path, yolo_lab + 'val/', '.txt')    # 图片label训练集val

if __name__ =='__main__':
    # 总数据集数据归一化与txt格式标准化处理
    normal_box(json_path, jpg_path, txt_path)

    # 训练集train、测试集text、验证集val划分
    set_train_test_val(jpg_path, txt_path, yolo_jpg, yolo_lab, weight)

        经过上述程序运行后,可以在设置的保存文件夹内得到如下的结果。根据设置的训练和测试样本的权重比例,将不同的训练集放入 images 和 labels 文件夹下。

 三、Paddle模型

        1.VOC2007数据格式

        对于paddle中的VOC,本人是将已有的yolo中 txt 文件转成 xml 格式来构建的VOC数据集。首先,需了解VOC格式在文件夹上的架构。VOC按照数据的类型(图片or框)来作为文件夹主逻辑的。其中完整数据的图片和标注分别放在JPEGImages和Annotations下,对于训练集和测试集的划分是在 ImageSets 下创建两个 txt 文件标明。txt文件中是不带后缀的图片文件名。

        将YOLO 中 txt 文件转为 VOC 的 xml 格式的Python代码如下。下述代码只创建了数据集的标签数据和划分训练测试的txt文件,图片数据jpg没有进行移动,如果需要构建一个完整的数据集,只需将初始数据集中的图片复制到VOC下的JPEGImages下即可。

import os
from xml.dom.minidom import Document
import cv2
import random
from shutil import copyfile
import json

# txt, jpg文件和xml文件保存地址
jpgfiles = '填入实际图片所在地址'
txtfiles = '填入实际yolo的txt标注文件所在地址'
xmlfiles = '创建所需保存xml文件的路径'

# voc数据中训练测试样本划分方式txt文件保存地址
voc_txtfiles = './VOC2007/ImageSets/Main/'

# 训练测试验证数据比重设置
weight = [0.95, 0.05] 

#####################(分界线)上述是需根据情况修改的参数,以下函数不需修改

## voc数据集构建(.xml)
# txt转xml函数
def translate(xmlfiles, jpgfiles, txtfiles):
    dic = {'0' : "ZW"}
    files = os.listdir(jpgfiles)
    for i,jpg in enumerate(files):
        # 获取图片信息
        jpgfile = os.path.join(jpgfiles, jpg)
        image = cv2.imread(jpgfile)  # 路径不能有中文
        Pheight, Pwidth, Pdepth = image.shape  # 图片大小

        # 获取标签信息
        basename = jpg.split('.')[0]
        txtfile = os.path.join(txtfiles, basename+'.txt')
        txtFile = open(txtfile)
        txtList = txtFile.readlines()

        # xml文件编写
        # 头信息
        xmlBuilder = Document()
        annotation = xmlBuilder.createElement("annotation")  # 创建annotation标签
        xmlBuilder.appendChild(annotation)

        # folder项目名信息
        folder = xmlBuilder.createElement("folder")  # folder标签
        foldercontent = xmlBuilder.createTextNode("page detection")
        folder.appendChild(foldercontent)
        annotation.appendChild(folder)  # folder标签结束

        # filename文件名信息
        filename = xmlBuilder.createElement("filename")  # filename标签
        filenamecontent = xmlBuilder.createTextNode(jpg)
        filename.appendChild(filenamecontent)
        annotation.appendChild(filename)  # filename标签结束

        # path地址信息
        path = xmlBuilder.createElement("path")  # path标签
        pathcontent = xmlBuilder.createTextNode(jpgfiles+jpg)
        path.appendChild(pathcontent)
        annotation.appendChild(path)  # path标签结束

        # source信息
        source = xmlBuilder.createElement("source")  # source标签
        database = xmlBuilder.createElement("database")  # source子标签database
        databasecontent = xmlBuilder.createTextNode("Unknown")
        database.appendChild(databasecontent)
        source.appendChild(database)  # source子标签database结束
        annotation.appendChild(source)  # source标签结束

        # size图片信息
        size = xmlBuilder.createElement("size")  # size标签
        width = xmlBuilder.createElement("width")  # size子标签width
        widthcontent = xmlBuilder.createTextNode(str(Pwidth))
        width.appendChild(widthcontent)
        size.appendChild(width)  # size子标签width结束
        height = xmlBuilder.createElement("height")  # size子标签height
        heightcontent = xmlBuilder.createTextNode(str(Pheight))
        height.appendChild(heightcontent)
        size.appendChild(height)  # size子标签height结束
        depth = xmlBuilder.createElement("depth")  # size子标签depth
        depthcontent = xmlBuilder.createTextNode(str(Pdepth))
        depth.appendChild(depthcontent)
        size.appendChild(depth)  # size子标签depth结束
        annotation.appendChild(size)  # size标签结束

        # 标签信息
        for j in txtList:
            oneline = j.strip().split(" ")  # 拆分yolo中关键信息
            object = xmlBuilder.createElement("object")  # object 标签
            # name边缘框类型
            picname = xmlBuilder.createElement("name")  # name标签
            namecontent = xmlBuilder.createTextNode(dic[oneline[0]])  # 边缘框类型
            picname.appendChild(namecontent)
            object.appendChild(picname)  # name标签结束
            # 默认标签(无需修改)
            pose = xmlBuilder.createElement("pose")  # pose标签
            posecontent = xmlBuilder.createTextNode("Unspecified")
            pose.appendChild(posecontent)
            object.appendChild(pose)  # pose标签结束
            truncated = xmlBuilder.createElement("truncated")  # truncated标签
            truncatedContent = xmlBuilder.createTextNode("0")
            truncated.appendChild(truncatedContent)
            object.appendChild(truncated)  # truncated标签结束
            difficult = xmlBuilder.createElement("difficult")  # difficult标签
            difficultcontent = xmlBuilder.createTextNode("0")
            difficult.appendChild(difficultcontent)
            object.appendChild(difficult)  # difficult标签结束

            # box边缘框信息
            # yolo中心相对坐标转化为左上右下绝对位置坐标
            mathData_x1 = int(((float(oneline[1])) * Pwidth) - (float(oneline[3])) * 0.5 * Pwidth)
            mathData_y1 = int(((float(oneline[2])) * Pheight) - (float(oneline[4])) * 0.5 * Pheight)
            mathData_x2 = int(((float(oneline[1])) * Pwidth) + (float(oneline[3])) * 0.5 * Pwidth)
            mathData_y2 = int(((float(oneline[2])) * Pheight + 1) + (float(oneline[4])) * 0.5 * Pheight)

            bndbox = xmlBuilder.createElement("bndbox")  # bndbox标签
            xmin = xmlBuilder.createElement("xmin")  # xmin标签
            xminContent = xmlBuilder.createTextNode(str(mathData_x1))
            xmin.appendChild(xminContent)
            bndbox.appendChild(xmin)  # xmin标签结束

            ymin = xmlBuilder.createElement("ymin")  # ymin标
            yminContent = xmlBuilder.createTextNode(str(mathData_y1))
            ymin.appendChild(yminContent)
            bndbox.appendChild(ymin)  # ymin标签结束

            xmax = xmlBuilder.createElement("xmax")  # xmax标签
            xmaxContent = xmlBuilder.createTextNode(str(mathData_x2))
            xmax.appendChild(xmaxContent)
            bndbox.appendChild(xmax)  # xmax标签结束

            ymax = xmlBuilder.createElement("ymax")  # ymax标签
            ymaxContent = xmlBuilder.createTextNode(str(mathData_y2))
            ymax.appendChild(ymaxContent)
            bndbox.appendChild(ymax)  # ymax标签结束
            object.appendChild(bndbox)  # bndbox标签结束
            annotation.appendChild(object)  # object标签结束

        f = open(xmlfiles + basename + ".xml", 'w')
        xmlBuilder.writexml(f, indent='', newl='\n', addindent='\t', encoding='utf-8')
        f.close()
        print(basename + ' success')

# voc2007数据划分txt文件
def set_voc(xmlfiles, voc_txtfiles, weight):
    filelist = os.listdir(xmlfiles)
    length = len(filelist)
    random.shuffle(filelist)
    trival_namelist = [tr[:-4] for tr in filelist[:int(weight[0] * length)]]
    val_namelist = [te[:-4] for te in filelist[int(weight[0] * length):]]    # 文件basename不含格式后缀
    trival = open(voc_txtfiles+'trainval.txt', 'w')
    val = open(voc_txtfiles+'val.txt', 'w')
    for i in trival_namelist:
        trival.write(i+'\n')
    for j in val_namelist:
        val.write(j+'\n')
    trival.close()
    val.close()

if __name__ == '__main__':
    # 将txt文件转为xml格式
    translate(xmlfiles, jpgfiles, txtfiles)
    # 构建voc文件格式
    set_voc(xmlfiles, voc_txtfiles, weight)

        xml 文件具体格式如下图所示,其中的框信息中四个数和初始 json 文件中框信息的规则一致,显而易见,它们都是未经过归一化处理的绝对坐标信息。

         2.COCO数据格式

        COCO数据文件架构格式如下。其主要是按照训练样本集的类型来划分的,每个训练集下的架构一样。图片数据放在JPEGImages下,当前训练集所有的框数据汇总为一个json文件。

        上述 json 文件的具体格式如下,整体是一个字典,字典里有三个键对,分别代表图片信息“images”、框信息“annotations”、类别信息“categories”。

        实现上述转化的 Python 代码如下。

import os
from xml.dom.minidom import Document
import cv2
import random
from shutil import copyfile
import json

# json、jpg文件保存地址
jpg_path = '填入实际图片所在地址'
json_path = '填入实际json标注文件所在地址'
# coco数据格式保存地址:训练集、测试集、验证集
cocofiles = './coco/'
# 训练测试验证数据比重设置
weight = [0.9, 0.05, 0.05] 

#####################(分界线)上述是需根据情况修改的参数,以下函数不需修改

## coco数据集构建(.json)
# 复制文件——拆分后的数据移动到对应文件夹
def copy_file(file_path_list, file_path, target_path, last_name):
    for file in file_path_list:
        real_path = os.path.join(file_path, file+last_name)
        copyfile(real_path, target_path+file+last_name)     #copyfile(文件所在地,复制目标路径)
        print(file + last_name + '  success')

# 划分coco训练测试验证集
def set_train_test_val(jpgfiles, cocofiles, weight):
    filelist = os.listdir(jpgfiles)
    length = len(filelist)
    random.shuffle(filelist)
    tr_namelist = [tr[:-4] for tr in filelist[:int(weight[0] * length)]]
    te_namelist = [te[:-4] for te in filelist[int(weight[0] * length):int((weight[0] + weight[1]) * length)]]
    va_namelist = [va[:-4] for va in filelist[int((weight[0] + weight[1]) * length):]]  # 文件basename不含格式后缀
    copy_file(tr_namelist, jpgfiles, cocofiles + 'train/JPEGImages/', '.jpg')  # 图片jpg训练集train
    copy_file(te_namelist, jpgfiles, cocofiles + 'test/JPEGImages/', '.jpg')  # 图片jpg测试集test
    copy_file(va_namelist, jpgfiles, cocofiles + 'val/JPEGImages/', '.jpg')  # 图片jpg验证集val

# 根据数据集生产相应annotation文件.json格式
def create_anno(cocofiles, t, jsonfiles):
    jpgfile = os.path.join(cocofiles,t+'/JPEGImages/')
    images = []
    annotation = []
    j = 0          # 框id计数
    for i, jpg in enumerate(os.listdir(jpgfile)):
        basename = jpg.split('.')[0]
        print(basename)
        # 获取图片信息:图片名;高宽;id
        jpgdir = os.path.join(jpgfile, jpg)
        image = cv2.imread(jpgdir)
        h, w, _ = image.shape  # 图片大小
        jpg_dict = {"file_name": str(jpg),
                    "height": h,
                    "width": w,
                    "id": i}
        images.append(jpg_dict)

        # 获取边缘框信息:area; bbox;类别;图片id;框id
        jsondir = os.path.join(jsonfiles, basename+'.json')
        with open(jsondir, 'r') as f:
            box_list = json.load(f)
        for box in box_list:
            # 框定义为左上坐标(x,y)和框的宽高
            x,y,w,h = box["box"][0], box["box"][1], box["box"][2]-box["box"][0], box["box"][3]-box["box"][1]
            area = w*h
            box_dict ={"area": area,  "iscrowd": 0,
                       "bbox": list((x,y,w,h)),
                       "category_id": 1,
                       "ignore": 0,
                       "image_id": i,
                       "id": j  }
            j += 1    # 框id计数
            annotation.append(box_dict)
    # 类别信息字典
    categories = [{"supercategory": "none", "id": 1, "name": "ZW"}]
    result = {"images":images, "annotations":annotation, "categories":categories}
    savefile = os.path.join(cocofiles, t+'/annotations.json')
    with open(savefile, 'w') as f:
        json.dump(result, f)
    print(t+'  success')

if __name__ == '__main__':
    # 划分数据集
    set_train_test_val(jpgfiles, cocofiles, weight)
    # 保存coco格式的annotations
    create_anno(cocofiles, 'train', jsonfiles)
    create_anno(cocofiles, 'test', jsonfiles)
    create_anno(cocofiles, 'val', jsonfiles)

  • 25
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Keras深度学习实战(15)——从零开始实现YOLO目标检测是一篇非常实用的教程。YOLO(You Only Look Once)是一种流行的实时目标检测算法,其核心思想是将目标检测任务视为回归问题,并通过卷积神经网络实现端到端的检测。这篇教程提供了一步一步的实现代码,让读者能够快速了解并实践YOLO目标检测的方法。 首先,教程介绍了YOLO的工作原理和网络结构。YOLO将输入图像划分为多个网格,每个网格负责预测包含在该网格中的目标。每个网格预测包含目标的方框的位置和类别,以及目标的置信度。 接下来,教程详细介绍了如何实现YOLO的网络结构。使用Keras库,创建了一个具有卷积和池化层的卷积神经网络。还使用了Anchor Boxes,用来预测不同比例和宽高比的目标。 教程还介绍了如何预处理输入图像,包括将图像调整为适当的大小,并将目标边界框转换YOLO需要的格式。然后,选择了合适的损失函数,训练了模型,以及进行了模型评估和预测。 最后,教程提供了一些改进和扩展的思路,包括使用更大的数据集进行训练、调整网络结构和超参数等等。 通过这篇教程,读者可以了解到YOLO目标检测的基本原理和实现步骤。并且,使用Keras库可以很方便地实现和训练自己的YOLO模型。无论是对于已经有一定深度学习基础的读者,还是对于刚刚开始学习的读者,这篇教程都是非常有价值的参考资料。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值