使用YOLOV7训练BDD100K数据集(数据格式转化+训练全流程)

目录

1. 前言

1.1 BDD100K数据集详细介绍(此节可跳过)

1.2 BDD100K数据集简要介绍

1.3 下载必要文件

2. bdd100k转yolo数据集格式

2.1 为什么要转格式

2.2 具体步骤

2.2.1 解压文件

2.2.2 BDD100K转VOC格式

2.2.3 VOC转txt格式

3. 整理数据集结构

3.1 最终需要转成的格式

3.2 每个文件内部具体内容

4. 配置运行参数,开始训练


1. 前言

1.1 BDD100K数据集详细介绍(此节可跳过)

BDD100K 数据集,是加州大学伯克利分校 AI 实验室(BAIR)于 2018 年发布的,其包含的 10 万个高清视频序列,时长超过 1100 小时。其中,每个视频大约 40 秒长、720p、30 fps,还附有手机记录的 GPS/IMU 信息和时间戳,以显示大概的驾驶轨迹。BAIR 还对每个视频的第 10 秒对关键帧进行采样,得到 10 万张图片(图片尺寸:1280*720 ),并进行标注。这些图片还被标记了:图像标记、道路对象边界框、可驾驶区域、车道标记线和全帧实例分割。这些注释有助于理解不同场景中数据和对象统计的多样性。数据集中的视频是从美国各地收集的,涵盖不同时间、不同天气条件(包括晴天、阴天和雨天,以及白天和晚上的不同时间)和驾驶场景。收集数据集的地理位置分布在纽约、伯克利、旧金山等地。数据集中,道路目标检测是为公共汽车、交通灯、交通标志、人、自行车、卡车、摩托车、汽车、火车和乘车人等 100000 张图片上标注 2D 边界框;实例分割被用于探索具有像素级和丰富实例级注释,相关图像超过 10000 张;引擎区域是从 10 万张图片中学习复杂的可驾驶决策;车道标记是在 10 万张行车指南图片上的多种车道标注。车道标记类图片中,标注了实线、虚线、双线、单线等。该数据集由相关论文有《BDD100K: A Diverse Driving Video Database with Scalable Annotation Tooling》,该项目由伯克利 DeepDrive 产业联盟组织和赞助,该联盟研究计算机视觉和机器学习在汽车应用上的最新技术。

1.2 BDD100K数据集简要介绍

这是一个经典的驾驶场景数据集,由于使用YOLOV7训练目标检测,因此我们只用到BDD100K数据其中的一小部分,简而言之就是7万张训练集图片和3万张验证集图片,此外BDD100K还有测试集,但是测试集没有标注数据。如果下载的数据并没有达到以上数量,要么就是下载错了,要么就是没有完全解压出来。

1.3 下载必要文件

BDD100K数据集下载地址(百度网盘下载比较快):

BDD100K数据集高速下载地址(百度网盘) – 源码巴士https://code84.com/820142.html

yolov7项目地址:

GitHub - WongKinYiu/yolov7: Implementation of paper - YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object detectorshttps://github.com/WongKinYiu/yolov7

2. bdd100k转yolo数据集格式

2.1 为什么要转格式

bdd的标注格式是json格式,yolo格式是txt的,因此要转化。整体转化过程为bdd100k(json文件)---》voc格式(xml文件)---》yolo格式(txt文件)。

2.2 具体步骤

2.2.1 解压文件

下载完成BDD100K数据后,有如下这些压缩包:

解压如下圈出来的两个:

2.2.2 BDD100K转VOC格式

需要先新建val_xml文件夹,代码如下:

import os
import json
import sys
from xml.etree import ElementTree
from xml.etree.ElementTree import Element, SubElement
from lxml import etree
from xml.dom.minidom import parseString

#种类有'car', 'bus', 'person', 'bike', 'truck', 'motor', 'train', 'rider', 'traffic sign', 'traffic light',可以自己定义序号
categorys = ['car', 'bus', 'person', 'bike', 'truck', 'motor', 'train', 'rider', 'traffic sign', 'traffic light']

def parseJson(jsonFile):
    '''
      params:
        jsonFile -- BDD00K数据集的一个json标签文件
      return:
        返回一个列表的列表,存储了一个json文件里面的方框坐标及其所属的类,
        形如:[[325, 342, 376, 384, 'car'], [245, 333, 336, 389, 'car']]
    '''
    objs = []
    obj = []
    f = open(jsonFile)
    info = json.load(f)
    objects = info['frames'][0]['objects']
    for i in objects:
        if (i['category'] in categorys):
            obj.append(int(i['box2d']['x1']))
            obj.append(int(i['box2d']['y1']))
            obj.append(int(i['box2d']['x2']))
            obj.append(int(i['box2d']['y2']))
            obj.append(i['category'])
            objs.append(obj)
            obj = []
    
    return objs


class PascalVocWriter:

    def __init__(self, foldername, filename, imgSize, databaseSrc='Unknown', localImgPath=None):
        '''
        params:
          foldername -- 要存储的xml文件的父目录
          filename -- xml文件的文件名
          imgSize -- 图片的尺寸
          databaseSrc -- 数据库名,这里不需要,默认为Unknown
          localImaPath -- xml文件里面的<path></path>标签的内容
      '''
        self.foldername = foldername
        self.filename = filename
        self.databaseSrc = databaseSrc
        self.imgSize = imgSize
        self.boxlist = []
        self.localImgPath = localImgPath

    def prettify(self, elem):
        """
        params:
          elem -- xml的根标签,以<annotation>开始
        return:
          返回一个美观输出的xml(用到minidom),本质是一个str
        """
        xml = ElementTree.tostring(elem)
        dom = parseString(xml)
        
        prettifyResult = dom.toprettyxml('    ')
        return prettifyResult

    def genXML(self):
        """
        return:
          生成一个VOC格式的xml,返回一个xml的根标签,以<annotation>开始
        """
        
        if self.filename is None or \
                self.foldername is None or \
                self.imgSize is None or \
                len(self.boxlist) <= 0:
            return None

        top = Element('annotation')  
        folder = SubElement(top, 'folder')
        folder.text = self.foldername  

        filename = SubElement(top, 'filename')  
        filename.text = self.filename  

        localImgPath = SubElement(top, 'path')  
        localImgPath.text = self.localImgPath  

        source = SubElement(top, 'source')  
        database = SubElement(source, 'database')  
        database.text = self.databaseSrc  

        size_part = SubElement(top, 'size')  
        width = SubElement(size_part, 'width')  
        height = SubElement(size_part, 'height')  
        depth = SubElement(size_part, 'depth')  
        width.text = str(self.imgSize[1])  
        height.text = str(self.imgSize[0])  
        if len(self.imgSize) == 3:  
            depth.text = str(self.imgSize[2])
        else:
            depth.text = '1'

        segmented = SubElement(top, 'segmented')
        segmented.text = '0'
        return top

    def addBndBox(self, xmin, ymin, xmax, ymax, name):
        '''
        将检测对象框坐标及其对象类别作为一个字典加入到self.boxlist中
        params:
          xmin -- 检测框的左上角的x坐标
          ymin -- 检测框的左上角的y坐标
          xmax -- 检测框的右下角的x坐标
          ymax -- 检测框的右下角的y坐标
          name -- 检测框内的对象类别名
        '''
        bndbox = {'xmin': xmin, 'ymin': ymin, 'xmax': xmax, 'ymax': ymax}
        bndbox['name'] = name
        self.boxlist.append(bndbox)

    def appendObjects(self, top):
        '''
        在xml文件中加入检测框的坐标及其对象类别名
        params:
          top -- xml的根标签,以<annotation>开始
        '''
        for each_object in self.boxlist:
            object_item = SubElement(top, 'object')
            name = SubElement(object_item, 'name')
            name.text = str(each_object['name'])
            pose = SubElement(object_item, 'pose')
            pose.text = "Unspecified"
            truncated = SubElement(object_item, 'truncated')
            truncated.text = "0"
            difficult = SubElement(object_item, 'Difficult')
            difficult.text = "0"
            bndbox = SubElement(object_item, 'bndbox')
            xmin = SubElement(bndbox, 'xmin')
            xmin.text = str(each_object['xmin'])
            ymin = SubElement(bndbox, 'ymin')
            ymin.text = str(each_object['ymin'])
            xmax = SubElement(bndbox, 'xmax')
            xmax.text = str(each_object['xmax'])
            ymax = SubElement(bndbox, 'ymax')
            ymax.text = str(each_object['ymax'])

    def save(self, targetFile=None):
        '''
        以美观输出的xml格式来保存xml文件
        params:
          targetFile -- 存储的xml文件名,不包括.xml部分
        '''
        root = self.genXML()
        self.appendObjects(root)
        out_file = None
        subdir = self.foldername.split('/')[-1]
        if not os.path.isdir(subdir):
            os.mkdir(subdir)
        if targetFile is None:
            with open(self.foldername + '/' + self.filename + '.xml', 'w') as out_file:
                prettifyResult = self.prettify(root)
                out_file.write(prettifyResult)
                out_file.close()
        else:
            with open(targetFile, 'w') as out_file:
                prettifyResult = self.prettify(root)
                out_file.write(prettifyResult)
                out_file.close()

class PascalVocReader:
    def __init__(self, filepath):
        self.shapes = []
        self.filepath = filepath
        self.parseXML()
    def getShapes(self):
        return self.shapes
    def addShape(self, label, bndbox):
        xmin = int(bndbox.find('xmin').text)
        ymin = int(bndbox.find('ymin').text)
        xmax = int(bndbox.find('xmax').text)
        ymax = int(bndbox.find('ymax').text)
        points = [(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)]
        self.shapes.append((label, points, None, None))
    def parseXML(self):
        assert self.filepath.endswith('.xml'), "Unsupport file format"
        parser = etree.XMLParser(encoding='utf-8')
        xmltree = ElementTree.parse(self.filepath, parser=parser).getroot()
        filename = xmltree.find('filename').text
        for object_iter in xmltree.findall('object'):
            bndbox = object_iter.find("bndbox")
            label = object_iter.find('name').text
            self.addShape(label, bndbox)
        return True


def main(srcDir, dstDir):
    i = 1
    for dirpath, dirnames, filenames in os.walk(srcDir):
        for filepath in filenames:
            fileName = os.path.join(dirpath, filepath)
            print(fileName)
            print("processing: {}, {}".format(i, fileName))
            i = i + 1
            xmlFileName = filepath[:-5]  
            
            objs = parseJson(str(fileName))
            
            if len(objs):
                tmp = PascalVocWriter(dstDir, xmlFileName, (720, 1280, 3), fileName)
                for obj in objs:
                    tmp.addBndBox(obj[0], obj[1], obj[2], obj[3], obj[4])
                tmp.save()
            else:
                print(fileName)


if __name__ == '__main__':
    srcDir = './bdd100k/labels/100k/val'  
    dstDir = './bdd100k/labels/100k/val_xml'
    main(srcDir, dstDir)

2.2.3 VOC转txt格式

需要先新建train_txt文件夹,代码如下:

import glob
import xml.etree.ElementTree as ET
import os

#种类有'car', 'bus', 'person', 'bike', 'truck', 'motor', 'train', 'rider', 'traffic sign', 'traffic light',可以自己定义序号
class_names = ['car', 'bus', 'person', 'bike', 'truck', 'motor', 'train', 'rider', 'traffic sign', 'traffic light']



def single_xml_to_txt(xml_file, dstDir):
    tree = ET.parse(xml_file)
    root = tree.getroot()
    
    txt_file = dstDir + os.path.basename(xml_file).split('.')[0] + ".txt"
    with open(txt_file, 'w') as txt_file:
        for member in root.findall('object'):
            picture_width = int(root.find('size')[0].text)
            picture_height = int(root.find('size')[1].text)
            class_name = member[0].text
            class_num = class_names.index(class_name)
            box_x_min = int(member[4][0].text)  
            box_y_min = int(member[4][1].text)  
            box_x_max = int(member[4][2].text)  
            box_y_max = int(member[4][3].text)  
            x_center = (box_x_min + box_x_max) / (2 * picture_width)
            y_center = (box_y_min + box_y_max) / (2 * picture_height)
            width = (box_x_max - box_x_min) / (2 * picture_width)
            height = (box_y_max - box_y_min) / (2 * picture_height)
            print(class_num, x_center, y_center, width, height)
            txt_file.write(str(class_num) + ' ' + str(x_center) + ' ' + str(y_center) + ' ' + str(width) + ' ' + str(
                height) + '\n')


def dir_xml_to_txt(path, dstDir):
    i = 1
    for xml_file in glob.glob(path + '*.xml'):
        single_xml_to_txt(xml_file, dstDir)
        i += 1


def main(path, dstDir):
    dir_xml_to_txt(path, dstDir)

if __name__ == '__main__':
    srcDir = './bdd100k/labels/100k/train_xml/'
    dstDir = './bdd100k/labels/100k/train_txt/'
    main(srcDir, dstDir)

3. 整理数据集结构

3.1 最终需要转成的格式

最后把数据集整理成如下的格式

3.2 每个文件内部具体内容

images文件夹下面有:

val文件夹下面有:

4. 配置运行参数,开始训练

修改bdd100k.yaml文件后,终端执行:

python train.py --workers 8 --device 0 --batch-size 32 --data data/bdd100k.yaml --img 640 640 --cfg cfg/training/yolov7.yaml --weights '' --name yolov7 --hyp data/hyp.scratch.p5.yaml

成功开始训练

要将BDD100K数据集转换为COCO数据集格式,可以按照以下步骤进行操作: 1. 使用bdd2yolo.py文件将.xml文件转换为满足YOLO格式要求的.txt文件。这个文件将会把每张图片的标签信息保存在对应的.txt文件中,路径格式为.../bdd100k/labels/train2017/img001.txt。\[1\] 2. 创建一个新的COCO数据集文件夹,包括以下文件和文件夹: - annotations文件夹:用于存放标注信息的JSON文件。 - images文件夹:用于存放所有图片文件。 - train2017.txt、val2017.txt和test2017.txt文件:分别存放训练集、验证集和测试集的图片文件名。 3. 根据COCO数据集的格式要求,创建一个空的annotations文件夹,并在其中创建一个instances_train2017.json文件和一个instances_val2017.json文件。这些JSON文件将包含标注信息。 4. 遍历每个图片的.txt文件,将标注信息转换为COCO数据集的格式,并将其添加到对应的JSON文件中。 5. 将所有图片文件移动到images文件夹中。 完成以上步骤后,你就成功将BDD100K数据集转换为COCO数据集格式了。这样,你就可以在YOLOv5中使用COCO格式的数据集进行训练和测试了。\[1\] #### 引用[.reference_title] - *1* [TT100K/BDD100K数据集格式转换](https://blog.csdn.net/Jad_Goh/article/details/129033999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [BDD100K自动驾驶数据集格式转YOLO格式](https://blog.csdn.net/Boys_Wu/article/details/124253548)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值