小技巧(5):将TT100K数据集转成VOC格式,并且用Python脚本选出45类超过100张的图片和XML

本文详细介绍了如何将TT100K数据集转换为符合VOC标准的格式,包括创建VOC文件夹结构、生成XML文件、删除不符合条件的图片及XML、以及生成train和val txt文件,以便于后续的图像分类任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇:小技巧(4):将txt中的某两列数据写入csv文件中,制作图像分类标签

一、相关准备

1.1 下载数据集

http://cg.cs.tsinghua.edu.cn/traffic-sign/data_model_code/data.zip
TT100K数据集下载下来大概19.2G,解压后的文件夹如图
在这里插入图片描述

1.2 下载代码文件

https://github.com/cqfdch/TT100K_to_VOC

1.3 将相关文件移入代码文件

在这里插入图片描述

讲TT100K中的train文件、test文件夹、annotations.json文件移入代码文件夹。

二、创建标准的VOC文件夹

import os

# 建立相关文件夹
# build voc2007 folder structure
def make_voc_dir():
    root_dir = os.getcwd()
    os.makedirs(root_dir+'/VOC2007')
    os.makedirs("VOC2007"+'/Annotations')
    os.makedirs("VOC2007" + '/JPEGImages/ ')
    os.makedirs("VOC2007"+'/ImageSets')
    os.makedirs("VOC2007"+'/ImageSets/Main')

if __name__ == '__main__':
    make_voc_dir()

在这里插入图片描述

三、生成整个数据集的XML文件

import os
import json
from lxml import etree as ET
from xml.dom import minidom
#找出训练集和测试集中的不在45类的标注图片的id
def edit_xml(objects, id, dir):
    save_xml_path = os.path.join(dir, "%s.xml" % id)  # xml

    root = ET.Element("annotation")
    # root.set("version", "1.0")  
    folder = ET.SubElement(root, "folder")
    folder.text = "none"
    filename = ET.SubElement(root, "filename")
    filename.text = id + ".jpg"
    source = ET.SubElement(root, "source")
    source.text = "none"
    owner = ET.SubElement(root, "owner")
    owner.text = "halftop"
    size = ET.SubElement(root, "size")
    width = ET.SubElement(size, "width")
    width.text = str(2048)
    height = ET.SubElement(size, "height")
    height.text = str(2048)
    depth = ET.SubElement(size, "depth")
    depth.text = "3"
    segmented = ET.SubElement(root, "segmented")
    segmented.text = "0"
    for obj in objects:  #  
        object = ET.SubElement(root, "object")
        name = ET.SubElement(object, "name")  # number
        name.text = obj["category"]

        # meaning = ET.SubElement(object, "meaning")  # name
        # meaning.text = inf_value[0]
        pose = ET.SubElement(object, "pose")
        pose.text = "Unspecified"
        truncated = ET.SubElement(object, "truncated")
        truncated.text = "0"
        difficult = ET.SubElement(object, "difficult")
        difficult.text = "0"
        bndbox = ET.SubElement(object, "bndbox")
        xmin = ET.SubElement(bndbox, "xmin")
        xmin.text = str(int(obj["bbox"]["xmin"]))
        ymin = ET.SubElement(bndbox, "ymin")
        ymin.text = str(int(obj["bbox"]["ymin"]))
        xmax = ET.SubElement(bndbox, "xmax")
        xmax.text = str(int(obj["bbox"]["xmax"]))
        ymax = ET.SubElement(bndbox, "ymax")
        ymax.text = str(int(obj["bbox"]["ymax"]))
    tree = ET.ElementTree(root)
    tree.write(save_xml_path, encoding="UTF-8", xml_declaration=True)
    root = ET.parse(save_xml_path) 
    file_lines = minidom.parseString(ET.tostring(root, encoding="Utf-8")).toprettyxml(
        indent="\t") 
    file_line = open(save_xml_path, "w", encoding="utf-8")  
    file_line.write(file_lines)
    file_line.close()
    
def  getDirId(dir):  # get the  id list  of id.png
    names = os.listdir(dir)
    ids = []
    for name in names:
        # path = os.path.join(dir, name)
        # img  = cv2.imread(path)
        # w, h, c = img.shape
        # if name.endswith(".jpg") or name.endswith(".png"):
            # ids["%s" % name.split(".")[0]] = [w, h, c]
        ids.append(name.split(".")[0])
    return ids

def is_tt45(objects):
    flag = True
    json_file = open('./TT100K_VOC_classes.json', 'r')
    results = json.load(json_file)
    for obj in objects:
        text = obj["category"]
        for key in results.keys():
            flag1 = False
            if key == text:
                flag1 =True
                break
        if flag1 == False:
            flag = False
            break
    return flag
filedir = "annotations.json"
annos = json.loads(open(filedir).read())

trainIds = getDirId("train/")
testIds = getDirId("test/")

ids = annos["imgs"].keys() #  all img ids in .json 

dir_train = "xmlLabel1/train"
dir_test = "xmlLabel1/test"
if not os.path.exists(dir_train):
    os.makedirs(dir_train)
if not os.path.exists(dir_test):
    os.makedirs(dir_test)

Not_TT45_list_train = []
Not_TT45_list_val = []
for id in ids:
#  json 中的ID图片有待检测目标,且该id图片在 train文件夹中
    if len(annos["imgs"][id]["objects"]) > 0 and (id in trainIds):
        objects = annos["imgs"][id]["objects"]
        flag = is_tt45(objects)
        if flag is False:
            Not_TT45_list_train.append(id+'\n')
        edit_xml(objects, id, dir=dir_train)
        
    elif len(annos["imgs"][id]["objects"]) > 0 and (id in testIds):
        objects = annos["imgs"][id]["objects"]
        flag = is_tt45(objects)
        if flag is False:
            Not_TT45_list_val.append(id+'\n')
        edit_xml(objects, id, dir=dir_test)
with open("Not_TT45_list_train.txt" ,"a") as f:
    f.writelines(Not_TT45_list_train)
with open("Not_TT45_list_val.txt" ,"a") as f:
    f.writelines(Not_TT45_list_val)
    

在这里插入图片描述

四、删除txt中id对应XML和图片

import os
import glob

# 删除txt中id对应XML和图片
def delete_train_jpg(train_txt):
    root_dir = os.getcwd()
    for line in open(train_txt ,"r"):
        file_id = line.strip()
        # print(file_id)
        file_path = os.path.join(root_dir,"train",file_id+'.jpg')
        # print(file_path)
        os.remove(file_path)


def delete_test_jpg(test_txt):
    root_dir = os.getcwd()
    for line in open(test_txt ,"r"):
        file_id = line.strip()
        # print(file_id)
        file_path = os.path.join(root_dir,"test",file_id+'.jpg')
        # print(file_path)
        os.remove(file_path)

def delete_train_xml(train_txt):
    root_dir = os.getcwd()
    root_path = os.path.join(root_dir,"xmlLabel1")
    for line in open(train_txt,"r"):
        file_id = line.strip()
        # print(file_id)
        file_path = os.path.join(root_path,"train",file_id+'.xml')
        # print(file_path)
        os.remove(file_path)

def delete_test_xml(test_txt):
    root_dir = os.getcwd()
    root_path = os.path.join(root_dir,"xmlLabel1")
    for line in open(test_txt,"r"):
        file_id = line.strip()
        # print(file_id)
        file_path = os.path.join(root_path,"test",file_id+'.xml')
        # print(file_path)
        os.remove(file_path)
if __name__ == '__main__':
    train_txt = "Not_TT45_list_train.txt"
    test_txt = "Not_TT45_list_val.txt"
    delete_train_jpg(train_txt)
    delete_train_xml(train_txt)
    delete_test_jpg(test_txt)
    delete_test_xml(test_txt)

五 、生成tain.txt和val.txt文件

第一步,生成train.txt
需要将4_spilt_data.py中的下面两行改成

files_path = "/home/ch/Object_Detection/dataset/TT100K_to_VOC/xmlLabel1/train"
val_rate = 0

然后运行4_spilt_data.py文件,可得到train.txt和val.txt文件,其中val.txt文件是空文件。将train.txt文件移入VOC2017/ImageSets/Main中,删除val.txt文件。

第二步、生成val.txt
需要将4_spilt_data.py中的下面两行改成

	files_path = "/home/ch/Object_Detection/dataset/TT100K_to_VOC/xmlLabel1/test"
	val_rate =1

然后运行4_spilt_data.py文件,可得到train.txt和val.txt文件,其中train.txt文件是空文件。将val.txt文件移入VOC2017/ImageSets/Main中,删除train.txt文件。

import os
import random


files_path = "/home/ch/Object_Detection/dataset/TT100K_YOLO_Label-master/xmlLabel1/train"
if not os.path.exists(files_path):
    print("文件夹不存在")
    exit(1)
val_rate = 0

files_name = sorted([file.split(".")[0] for file in os.listdir(files_path)])
files_num = len(files_name)
val_index = random.sample(range(0, files_num), k=int(files_num*val_rate))
train_files = []
val_files = []
for index, file_name in enumerate(files_name):
    if index in val_index:
        val_files.append(file_name)
    else:
        train_files.append(file_name)

try:
    train_f = open("train.txt", "x")
    eval_f = open("val.txt", "x")
    train_f.write("\n".join(train_files))
    eval_f.write("\n".join(val_files))
except FileExistsError as e:
    print(e)
    exit(1)

最后将生成的train.txt和val.txt放进VOC2017/ImageSets/Main文件夹中

下一篇:小技巧(6):进行BelgiumTSC交通标志数据集识别(定义自己的数据集)

### TT100K 数据集格式换方法 TT100K 数据集是一种广泛应用于交通标志检测识别任务的公开数据集[^1]。该数据集通常以特定的标注格式提供,例如 JSON 或 XML 格式。为了将其换为其他格式(如 COCO、Pascal VOC 或 YOLO 格式),需要对数据集的结构标注内容进行解析重构。 以下是一个通用的换流程及代码示例: #### 1. 理解 TT100K 数据集的原始格式 TT100K 数据集的标注文件通常以 JSON 格式存储,包含图像路径、别标签以及边界框信息。例如,一个典型的 JSON 文件可能具有以下结构: ```json { "images": [ { "filename": "image_0001.jpg", "width": 1920, "height": 1080, "regions": [ { "category": "speedlimit_50", "bbox": [100, 200, 300, 400] }, { "category": "stop", "bbox": [400, 500, 600, 700] } ] } ] } ``` 上述结构中,`regions` 列表包含了每个目标的边界框坐标。 #### 2. 换为目标格式 ##### (1) 换为 COCO 格式 COCO 格式要求将标注信息组织为 `annotations` `categories` 等字段。以下是一个 Python 示例代码,展示如何将 TT100K 数据集换为 COCO 格式: ```python import json def convert_to_coco(tt100k_json_path, output_path): with open(tt100k_json_path, 'r') as f: tt100k_data = json.load(f) images = [] annotations = [] categories = {} annotation_id = 1 for image_info in tt100k_data['images']: image_id = len(images) + 1 images.append({ "id": image_id, "file_name": image_info["filename"], "width": image_info["width"], "height": image_info["height"] }) for region in image_info["regions"]: category = region["category"] if category not in categories: categories[category] = len(categories) + 1 bbox = region["bbox"] x, y, w, h = bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1] annotations.append({ "id": annotation_id, "image_id": image_id, "category_id": categories[category], "bbox": [x, y, w, h], "area": w * h, "iscrowd": 0 }) annotation_id += 1 coco_data = { "images": images, "annotations": annotations, "categories": [{"id": v, "name": k} for k, v in categories.items()] } with open(output_path, 'w') as f: json.dump(coco_data, f, indent=4) convert_to_coco("tt100k.json", "coco_output.json") ``` ##### (2) 换为 Pascal VOC 格式 Pascal VOC 格式使用 XML 文件存储标注信息。以下是一个 Python 示例代码,展示如何将 TT100K 数据集换为 Pascal VOC 格式: ```python import xml.etree.ElementTree as ET def create_voc_xml(image_info, output_dir): root = ET.Element("annotation") folder = ET.SubElement(root, "folder") folder.text = "TT100K" filename = ET.SubElement(root, "filename") filename.text = image_info["filename"] size = ET.SubElement(root, "size") width = ET.SubElement(size, "width") width.text = str(image_info["width"]) height = ET.SubElement(size, "height") height.text = str(image_info["height"]) depth = ET.SubElement(size, "depth") depth.text = "3" for region in image_info["regions"]: obj = ET.SubElement(root, "object") name = ET.SubElement(obj, "name") name.text = region["category"] bndbox = ET.SubElement(obj, "bndbox") xmin = ET.SubElement(bndbox, "xmin") xmin.text = str(region["bbox"][0]) ymin = ET.SubElement(bndbox, "ymin") ymin.text = str(region["bbox"][1]) xmax = ET.SubElement(bndbox, "xmax") xmax.text = str(region["bbox"][2]) ymax = ET.SubElement(bndbox, "ymax") ymax.text = str(region["bbox"][3]) tree = ET.ElementTree(root) output_file = f"{output_dir}/{image_info['filename'].split('.')[0]}.xml" tree.write(output_file) # 假设 tt100k_data 是加载的 TT100K 数据 for image_info in tt100k_data['images']: create_voc_xml(image_info, "voc_output") ``` ##### (3) 换为 YOLO 格式 YOLO 格式要求将标注信息存储为 `.txt` 文件,每行表示一个目标的归一化后的边界框坐标。以下是一个 Python 示例代码,展示如何将 TT100K 数据集换为 YOLO 格式: ```python def convert_to_yolo(tt100k_json_path, output_dir, class_mapping): with open(tt100k_json_path, 'r') as f: tt100k_data = json.load(f) for image_info in tt100k_data['images']: output_file = f"{output_dir}/{image_info['filename'].split('.')[0]}.txt" with open(output_file, 'w') as f: for region in image_info["regions"]: category = region["category"] if category not in class_mapping: continue class_id = class_mapping[category] bbox = region["bbox"] x_center = (bbox[0] + bbox[2]) / (2 * image_info["width"]) y_center = (bbox[1] + bbox[3]) / (2 * image_info["height"]) width = (bbox[2] - bbox[0]) / image_info["width"] height = (bbox[3] - bbox[1]) / image_info["height"] f.write(f"{class_id} {x_center} {y_center} {width} {height}\n") class_mapping = {"speedlimit_50": 0, "stop": 1} convert_to_yolo("tt100k.json", "yolo_output", class_mapping) ``` ### 注意事项 在执行格式换时,请确保以下几点: - 检查原始数据集中是否有缺失或错误的标注信息。 - 确保别映射正确无误,尤其是在涉及多别的场景下。 - 在换过程中,注意边界框坐标的单位一致性(如像素值与归一化值之间的换)。
评论 63
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值