使用Yolov8 训练交通标志数据集:TT100K数据集划分

news2025/7/21 16:51:25

使用Yolov8 训练交通标志数据集:TT100K数据集划分(一)

  • 一、数据集下载
  • 二、划分数据集
  • 三、目录放置


一、数据集下载

  1. 官方网址:TT100K
    在这里插入图片描述

  2. 数据集对比
    在这里插入图片描述
    源码如下:

    def classes(filedir):
        with open(filedir) as f:
            classes = json.load(f)
            classes_list = classes['types']
            return len(classes_list)
            
    c_2016 = classes("./annotations.json")
    c_2021 = classes("./annotations_all.json")
    print(f'TT100K-2016 交通标志的类别:{c_2016}')
    print(f'TT100K-2021 交通标志的类别:{c_2021}')
    print(f'差值:{c_2021-c_2016}')
    
  3. 查看类别数目

    # 读TT100K原始数据集标注文件
    with open(annotations) as p:
        # 读取 json 文件
        file = json.load(p)
        # 获取图字典
        img_dict = file['imgs']
        types = file['types']
        
        classes_list = dict()
        for i in types:
        # i 为所有的类别: 如:‘pl80’, 'w9', 'p6'
        # 将每一个类别作为字典中的键,值为: 空列表 []
            classes_list[i] = 0
        
        for img_id in img_dict:
            img = images_dict[img_id]
            obj_list = img['objects']
            # 统计实际有多少种类别
            for obj in obj_list:
                classes_list[obj['category']] += 1
    
        # 大于0的有201个
        d = []
        for keym, val in classes_list.items():
            if val > 0:
                d.append(val)
        
        print(len(d))
    

二、划分数据集

数据集划分有四个步骤,依次按照一下步骤执行即可:

  1. 获取每个类别有多少图片的 classes_statistics.json 文件。此过程会产生一个 json 文件,保存了分类类别,每个类别的详细信息,以及所有图片信息。
    在这里插入图片描述
    在这里插入图片描述
    源码如下:

    def classes_statistics():
        """
        类别统计函数,统计每一个类别下有多少张图片
        保存在 classes_statistics.json 文件中
        :return:
        """
        # 读TT100K原始数据集标注文件
        with open(annotations) as origin_json:
            # 读取 json 文件
            origin_dict = json.load(origin_json)
            # 获取类别列表
            classes_list = origin_dict['types']
            # 获取图字典
            images_dict = origin_dict['imgs']
    
        # 建立统计每个类别包含的图片的字典
        sta = dict()
        for i in classes_list:
            # i 为所有的类别: 如:‘pl80’, 'w9', 'p6'
            # 将每一个类别作为字典中的键,值为: 空列表 []
            sta[i] = []
    
        # 遍历TT100K的imgs
        for image_id in images_dict:
            image_element = images_dict[image_id]
            image_path = image_element['path']
    
            # 添加图像的信息到dataset中
            image_path = image_path.split('/')[-1]  # 如:‘62627.jpg’
            obj_list = image_element['objects']
    
            # 遍历每张图片的标注信息,将每张图片的路径添加到对应类别的字典中去
            for anno_dic in obj_list:
                label_key = anno_dic['category']
                # 防止一个图片多次加入一个标签类别
                if image_path not in sta[label_key]:
                    sta[label_key].append(image_path)
        # 数据清洗--只保留包含图片数超过100的类别
        # 清洗之后还剩下:45种类别
        result = {k: v for k, v in sta.items() if len(v) > 100}
    
        # 将所有标注写入文件 此时写入超过100张图片的
        with open('./all_classes.json', 'w', encoding="utf-8") as f:
            json.dump(result, f, ensure_ascii=False, indent=2)
    
        # 记录所有保留的图片
        saved_images = []
        for i in result:
            saved_images.extend(result[i])
        # 第二次数据清洗。同一张图片有多个类别标注,所以要去重
        old_saved_images = len(saved_images)
        saved_images = list(set(saved_images))
        new_saved_images = len(saved_images)
        # print(f"total types is: {len(result)}")
    
        type_list = list(result.keys())
        # 字典,三个键:类型(列表),详细信息(字典),所有类型的图片数据(列表)
        result2 = {"type": type_list, "details": result, "images": saved_images}
        # 保存结果
        json_name = classes_statistics_name
        with open(json_name, 'w', encoding="utf-8") as f:
            json.dump(result2, f, ensure_ascii=False, indent=2)
        print(f"类别统计文件已保存:{json_name}")
    
        return type_list, result, saved_images, result2, old_saved_images, new_saved_images
    

    结果如下:
    在这里插入图片描述

  2. 将 TT100K 原始数据集转换为 COCO 数据集格式,并根据图片中各类别的数量比例将数据划分为训练集、验证集和测试集,以便后续的模型训练和评估。此步骤生成,train.json, test.json, val.json 文件,划分比例为7:2:1。
    源码如下:

    def original_datasets2object_datasets_re():
        '''
        重新划分数据集
        将 TT100K 原始数据集转换为 COCO 数据集格式,并根据图片中各类别的数量比例将数据划分为训练集、验证集和测试集,以便后续的模型训练和评估。
        :return:
        '''
        # 读TT100K原始数据集标注文件
        with open(annotations) as origin_json:
            origin_dict = json.load(origin_json)
        with open(classes_statistics_name) as select_json:
            select_dict = json.load(select_json)
            classes = select_dict['type']
        train_dataset = {'info': {}, 'licenses': [], 'categories': [], 'images': [], 'annotations': []}
        val_dataset = {'info': {}, 'licenses': [], 'categories': [], 'images': [], 'annotations': []}
        test_dataset = {'info': {}, 'licenses': [], 'categories': [], 'images': [], 'annotations': []}
        label = {}  # 记录每个标志类别的id
        count = {}  # 记录每个类别的图片数
        owntype_sum = {}
        info = {
            "year": 2021,  # 年份
            "version": '1.0',  # 版本
            "description": "TT100k_to_coco",  # 数据集描述
            "contributor": "Tecent&Tsinghua",  # 提供者
            "url": 'https://cg.cs.tsinghua.edu.cn/traffic-sign/',  # 下载地址
            "date_created": 2021 - 1 - 15
        }
        licenses = {
            "id": 1,
            "name": "null",
            "url": "null",
        }
        train_dataset['info'] = info
        val_dataset['info'] = info
        test_dataset['info'] = info
        train_dataset['licenses'] = licenses
        val_dataset['licenses'] = licenses
        test_dataset['licenses'] = licenses
    
        # 建立类别和id的关系
        for i, cls in enumerate(classes):
            train_dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'traffic_sign'})
            val_dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'traffic_sign'})
            test_dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'traffic_sign'})
            label[cls] = i
            count[cls] = 0
            owntype_sum[cls] = 0
    
        images_dic = origin_dict['imgs']
    
        obj_id = 1
    
        # 计算出每个类别共‘包含’的图片数
        for image_id in images_dic:
            image_element = images_dic[image_id]
            image_path = image_element['path']
            image_name = image_path.split('/')[-1]
            # 在所选的类别图片中
            if image_name not in select_dict['images']:
                continue
            # 处理TT100K中的标注信息
            obj_list = image_element['objects']
            # 记录图片中包含最多的实例所属的type
            includes_type = {}
            for anno_dic in obj_list:
                if anno_dic["category"] not in select_dict["type"]:
                    continue
                # print(anno_dic["category"])
                if anno_dic["category"] in includes_type:
                    includes_type[anno_dic["category"]] += 1
                else:
                    includes_type[anno_dic["category"]] = 1
            # print(includes_type)
            own_type = max(includes_type, key=includes_type.get)
            owntype_sum[own_type] += 1
    
        # TT100K的annotation_all转换成coco的格式
        for image_id in images_dic:
            image_element = images_dic[image_id]
            image_path = image_element['path']
            image_name = image_path.split('/')[-1]
            # 在所选的类别图片中
            if image_name not in select_dict['images']:
                continue
            print(f"dealing with {image_path} ")
            # 处理TT100K中的标注信息
            obj_list = image_element['objects']
            # 记录图片中包含最多的实例所属的type
            includes_type = {}
            for anno_dic in obj_list:
                if anno_dic["category"] not in select_dict["type"]:
                    continue
                # print(anno_dic["category"])
                if anno_dic["category"] in includes_type:
                    includes_type[anno_dic["category"]] += 1
                else:
                    includes_type[anno_dic["category"]] = 1
            # print(includes_type)
            own_type = max(includes_type, key=includes_type.get)
            count[own_type] += 1
            num_rate = count[own_type] / owntype_sum[own_type]
    
            # 划分数据集 7:2:1,train_set,val_set,test_set。
            if num_rate < 0.7:
                dataset = train_dataset
            elif num_rate < 0.9:
                dataset = val_dataset
            else:
                print("dataset=test_dataset")
                dataset = test_dataset
    
            for anno_dic in obj_list:
                if anno_dic["category"] not in select_dict["type"]:
                    continue
                x = anno_dic['bbox']['xmin']
                y = anno_dic['bbox']['ymin']
                width = anno_dic['bbox']['xmax'] - anno_dic['bbox']['xmin']
                height = anno_dic['bbox']['ymax'] - anno_dic['bbox']['ymin']
                label_key = anno_dic['category']
    
                dataset['annotations'].append({
                    'area': width * height,
                    'bbox': [x, y, width, height],
                    'category_id': label[label_key],
                    'id': obj_id,
                    'image_id': image_id,
                    'iscrowd': 0,
                    'segmentation': [[x, y, x + width, y, x + width, y + height, x, y + height]]
                })
                # 每个标注的对象id唯一
                obj_id += 1
            im = cv2.imread(os.path.join(datasets_dir, image_path))
            H, W, _ = im.shape
            # 添加图像的信息到dataset中
            dataset['images'].append({'file_name': image_name,
                                      'id': image_id,
                                      'width': W,
                                      'height': H})
        # 保存结果
        for phase in ['train', 'val', 'test']:
            # 创建保存位置的文件夹
            os.makedirs(RETT100K_dir+'/labels', exist_ok=True)
            json_name = RETT100K_dir+f'/labels/{phase}.json'
            with open(json_name, 'w', encoding="utf-8") as f:
                if phase == 'train':
                    json.dump(train_dataset, f, ensure_ascii=False, indent=2)
                if phase == 'val':
                    json.dump(val_dataset, f, ensure_ascii=False, indent=2)
                if phase == 'test':
                    json.dump(test_dataset, f, ensure_ascii=False, indent=2)
    

    执行结果:
    在这里插入图片描述
    在这里插入图片描述

  3. 写入归一化的标注文件, 生成 yolo 格式的标注信息。获取标注信息,并划分 yolo 格式的标注信息 *.txt 文件夹
    源码如下:

    def coco_json2yolo_txt(class_json):
        # 将标注位置坐标做归一化处理
        def convert(size, box):
            dw = 1. / (size[0])
            dh = 1. / (size[1])
            x = box[0] + box[2] / 2.0
            y = box[1] + box[3] / 2.0
            w = box[2]
            h = box[3]
            x = round(x * dw, 6)
            w = round(w * dw, 6)
            y = round(y * dh, 6)
            h = round(h * dh, 6)
            return (x, y, w, h)
        json_file = os.path.join(RETT100K_dir+f'/labels/{class_json}.json')
        ana_txt_save_path = os.path.join(RETT100K_dir+'labels', class_json)  # 保存的路径
        data = json.load(open(json_file, 'r'))
        if not os.path.exists(ana_txt_save_path):
            os.makedirs(ana_txt_save_path)
        id_map = {}  # coco数据集的id不连续!重新映射一下再输出!
        with open(os.path.join(ana_txt_save_path, 'classes.txt'), 'w') as f:
            # 写入classes.txt
            for i, category in enumerate(data['categories']):
                f.write(f"{category['name']}\n")
                id_map[category['id']] = i
        # 写入图像相对路径的文件位置
        list_file = open(os.path.join(ana_txt_save_path, '%s.txt' % class_json.format()), 'w')
        for img in tqdm(data['images']):
            filename = img["file_name"]
            img_width = img["width"]
            img_height = img["height"]
            img_id = img["id"]
            head, tail = os.path.splitext(filename)
            ana_txt_name = head + ".txt"  # 对应的txt名字,与jpg一致
            f_txt = open(os.path.join(ana_txt_save_path, ana_txt_name), 'w')
            for ann in data['annotations']:
                if ann['image_id'] == img_id:
                    box = convert((img_width, img_height), ann["bbox"])
                    f_txt.write("%s %s %s %s %s\n" % (id_map[ann["category_id"]], box[0], box[1], box[2], box[3]))
            f_txt.close()
            # 将图片的相对路径写入文件
            list_file.write('/%s/%s.jpg\n' % (class_json.format(), head))
        list_file.close()
    
    # 3. 写入归一化的标注文件, 生成 yolo 格式的标注信息
    phase=['train', 'val', 'test']
    for phase in phase:
    	coco_json2yolo_txt(phase)
    

    执行结果:
    在这里插入图片描述
    在这里插入图片描述

  4. 拷贝图片,数据集划分完成。
    源码如下:

    def divide_TrainValTest(source, target, pre_datasets):
        '''
        创建文件路径并复制数据集
        :param source: 源文件位置
        :param target: 目标文件位置
        :param pre_datasets: 预处理数据集路径
        '''
        # 创建输出目录
        for i in ['train', 'val', 'test']:
            os.makedirs(f"{target}/{i}", exist_ok=True)
        
        # 获取当前处理的数据集类型
        dataset_type = None
        if 'train' in source:
            dataset_type = 'train'
        elif 'val' in source:
            dataset_type = 'val'
        elif 'test' in source:
            dataset_type = 'test'
        
        # 收集所有需要处理的文件
        file_list = []
        for root, dirs, files in os.walk(source):
            for file in files:
                file_name = os.path.splitext(file)[0]
                # 排除无关项
                if file_name in ['classes', 'test', 'train', 'val', '10106-checkpoint']:
                    continue
                file_list.append(file_name)
    
        for file_name in tqdm(file_list, desc=f"处理{dataset_type}数据集", unit="文件"):
            image_path = f"{file_name}.jpg"
            source_path = os.path.join(pre_datasets, image_path)
            target_path = os.path.join(target, dataset_type, image_path)
            
            # 复制文件并处理异常
            try:
                shutil.copyfile(source_path, target_path)
            except Exception as e:
                print(f"警告: 无法复制文件 {image_path}: {e}")
        
        print(f"{dataset_type}数据集处理完成!")
    

    执行结果:
    在这里插入图片描述

三、目录放置

  1. 原数据集的目录结构:保持下载时目录不变即可。

    E:\机器学习\TT00K\tt100k_2021>tree
    E:.
    ├─train
    ├─test
    └─other
    
  2. 处理后的数据集目录结:

    E:\机器学习\TT00K\RETT100K_2021>tree
    E:.
    ├─images
    │  ├─test
    │  ├─train
    │  └─val
    └─labels
        ├─test
        ├─train
        └─val
    
  3. 处理完之后就可以使用 yolo v8/v11 训练了。
    data.yaml 文件如下:

    path: ../datasets/RE_TT100K_2021/
    train: images/train
    val: images/val
    test: images/test
    
    # 分类总数
    nc: 45
    # 分类类别
    names: ['pl80', 'p6', 'p5', 'pm55', 'pl60', 'ip', 'p11', 'i2r', 'p23', 'pg', 'il80', 'ph4', 'i4', 'pl70', 'pne', 'ph4.5', 'p12', 'p3', 'pl5', 'w13', 'i4l', 'pl30', 'p10', 'pn', 'w55', 'p26', 'p13', 'pr40', 'pl20', 'pm30', 'pl40', 'i2', 'pl120', 'w32', 'ph5', 'il60', 'w57', 'pl100', 'w59', 'il100', 'p19', 'pm20', 'i5', 'p27', 'pl50']
    

    在这里插入图片描述


END


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2395158.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NLP学习路线图(十三):正则表达式

在自然语言处理&#xff08;NLP&#xff09;的浩瀚宇宙中&#xff0c;原始文本数据如同未经雕琢的璞玉。而文本预处理&#xff0c;尤其是其中至关重要的正则表达式技术&#xff0c;正是将这块璞玉转化为精美玉器的核心工具集。本文将深入探讨正则表达式在NLP文本预处理中的原理…

javaweb-maven以及http协议

1.maven坐标&#xff1a; 坐标是资源的唯一标识&#xff0c;通过该坐标可以唯一定位资源位置&#xff1b; 2.坐标的组成&#xff1a; groupId:定义当前项目隶书的组织名称&#xff1b; artifactId&#xff1a;定义当前maven项目名称 version&#xff1a;定义项目版本 3.依…

华为OD机试真题—— 最少数量线段覆盖/多线段数据压缩(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 2025华为OD真题目录+全流程解析/备考攻略/经验分享 华为OD机试真题《最少数量线段覆盖/多线段数…

C语言创意编程:用趣味实例玩转基础语法(2)

文章目录 0. 前言1. &#x1f4ca; 动态条形图1.1 程序效果展示1.2 完整代码解析1.3 关键技术详解1.3.1 Unicode字符应用1.3.2 函数封装思想1.3.3 输入处理1.3.4 跨平台考虑 2. &#x1f524; 字母金字塔2.1 程序效果展示2.2 完整代码解析2.3 关键技术详解2.3.1 嵌套循环结构2.…

OpenCV CUDA模块图像处理------颜色空间处理之GPU 上对两张带有 Alpha 通道的图像进行合成操作函数alphaComp()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该函数用于在 GPU 上对两张带有 Alpha 通道的图像进行合成操作。支持多种常见的 Alpha 合成模式&#xff08;Porter-Duff 合成规则&#xff09;&…

OpenWebUI(1)源码学习构建

1. 前言 通过docker镜像拉取安装就不介绍了&#xff0c;官方的命令很多。本节主要撸一撸源码&#xff0c;所以&#xff0c;本地构建 2. 技术框架和启动环境 后端python&#xff0c;前端svelte 环境要求&#xff1a;python > 3.11 &#xff0c;Node.js > 20.10 3. 源…

npm error Cannot find module ‘negotiator‘ 的处理

本想运行npm create vuelatest&#xff0c;但提示&#xff1a; npm error code MODULE_NOT_FOUND npm error Cannot find module negotiator npm error Require stack: npm error - C:\Users\Administrator\AppData\Roaming\nvm\v18.16.1\node_modules\npm\node_modules\tuf-j…

爬虫入门指南-某专利网站的专利数据查询并存储

免责声明 本教程仅用于教育目的&#xff0c;演示如何合法获取公开专利数据。在实际操作前&#xff0c;请务必&#xff1a; 1. 仔细阅读目标网站的robots.txt文件和服务条款 2. 控制请求频率&#xff0c;避免对服务器造成负担 3. 仅获取和使用公开数据 4. 不用于商业用途或…

SQL(Database Modifications)

目录 Insertion Specifying Attributes in INSERT Adding Default Values&#xff08;缺省值&#xff09; Inserting Many Tuples Creating a Table Using the SELECT INTO Statement Deletion Example: Deletion Semantics of Deletion Updates Example: Update Sev…

【android bluetooth 案例分析 04】【Carplay 详解 2】【Carplay 连接之手机主动连车机】

1. 背景 在【android bluetooth 案例分析 04】【Carplay 详解 1】【CarPlay 在车机侧的蓝牙通信原理与角色划分详解】中我们从整理上介绍了车机中 carplay 相关基础概念。 本节 将详细分析 iphone手机主动 连接 车机carplay 这一过程。 先回顾一下 上一节&#xff0c; carpla…

【仿muduo库实现并发服务器】实现时间轮定时器

实现时间轮定时器 1.时间轮定时器原理2.项目中实现目的3.实现功能3.1构造定时任务类3.2构造时间轮定时器每秒钟往后移动添加定时任务刷新定时任务取消定时任务 4.完整代码 1.时间轮定时器原理 时间轮定时器的原理类似于时钟&#xff0c;比如现在12点&#xff0c;定一个3点的闹…

day15 leetcode-hot100-28(链表7)

2. 两数相加 - 力扣&#xff08;LeetCode&#xff09; 1.模拟 思路 最核心的一点就是将两个链表模拟为等长&#xff0c;不足的假设为0&#xff1b; &#xff08;1&#xff09;设置一个新链表newl来代表相加结果。 &#xff08;2&#xff09;链表1与链表2相加&#xff0c;具…

​​知识图谱:重构认知的智能革命​

在数字经济的浪潮中&#xff0c;知识图谱正悄然掀起一场认知革命。它不仅是技术的迭代&#xff0c;更是人类从“数据依赖”迈向“知识驱动”的里程碑。当谷歌用知识图谱优化搜索引擎、银行用它穿透复杂的金融欺诈网络、医院用它辅助癌症诊疗时&#xff0c;这项技术已悄然渗透到…

【计算机网络】4网络层①

这篇笔记讲IPv4和IPv6。 为了解决“IP地址耗尽”问题,有三种措施: ①CIDR(延长IPv4使用寿命) ②NAT(延长IPv4使用寿命) ③IPv6(从根本上解决IP地址耗尽问题) IPv6 在考研中考查频率较低,但需掌握基础概念以防冷门考点,重点结合数据报格式和与 IPv4 的对比记忆。…

MATLAB中的table数据类型:高效数据管理的利器

MATLAB中的table数据类型&#xff1a;高效数据管理的利器 什么是table数据类型&#xff1f; MATLAB中的table是一种用于存储列向数据的数据类型&#xff0c;它将不同类型的数据组织在一个表格结构中&#xff0c;类似于电子表格或数据库表。自R2013b版本引入以来&#xff0c;t…

Dropout 在大语言模型中的应用:以 GPT 和 BERT 为例

引言 大型语言模型&#xff08;LLMs&#xff09;如 GPT&#xff08;生成式预训练 Transformer&#xff09;和 BERT&#xff08;双向编码器表示 Transformer&#xff09;通过其强大的语言理解和生成能力&#xff0c;彻底改变了自然语言处理&#xff08;NLP&#xff09;领域。然…

gitLab 切换中文模式

点击【头像】--选择settings 选择【language】,选择中文&#xff0c;点击【保存】即可。

133.在 Vue3 中使用 OpenLayers 实现画多边形、任意编辑、遮罩与剪切处理功能

&#x1f3ac; 效果演示截图&#xff08;先睹为快&#xff09; ✨ 功能概览&#xff1a; ✅ 鼠标画任意形状多边形&#xff1b; ✏️ 点击“修改边界”可拖动顶点&#xff1b; &#x1f7e5; 点击“遮罩”后地图除多边形区域外变红&#xff1b; ✂️ 点击“剪切”后仅显示选…

4.8.4 利用Spark SQL实现分组排行榜

在本次实战中&#xff0c;我们的目标是利用Spark SQL实现分组排行榜&#xff0c;特别是计算每个学生分数最高的前3个成绩。任务的原始数据由一组学生成绩组成&#xff0c;每个学生可能有多个成绩记录。我们首先将这些数据读入Spark DataFrame&#xff0c;然后按学生姓名分组&am…

【五子棋在线对战】一.前置知识的了解

前置知识的了解 前言1.Websocketpp1.1 使用Websocketpp的原因1.2 Websocket常用接口1.3 Websocket搭建服务器流程 2.JsonCpp2.1 Json 数据对象类的表示2.2序列化和反序列化的接口2.3 演示代码 3.Mysql![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/93305f423b544fc1…