整理使用yolo来训练数据集时,常用到的数据处理脚本

数据处理功能脚本

我们在处理数据集时基本流程基本是:
1、确定标签
2、数据整理
3、数据集拆分
4、格式转换
以下将主要通过这四步记录模型训练之前数据处理的相关工作

1、确定标签

1.1、 查看xml文件夹的标签种类

做数据集做操作(已标注过)首先要知道数据集的种类有那些,故需要统计Annotations文件夹下xml标签数量种类

import os
from unicodedata import name
import xml.etree.ElementTree as ET
import glob


def count_num(indir):
    '''
    统计Annotations文件夹下xml标签数量种类
    :param indir: 文件夹路径
    :return:
    '''
    label_list = []
    # 提取xml文件列表
    os.chdir(indir)
    annotations = os.listdir('.')
    print(annotations)
    print(len(annotations))
    # annotations = glob.glob(str(annotations) + '*.xml')
    print(annotations)
    print(len(annotations))

    dict = {}  # 新建字典,用于存放各类标签名及其对应的数目
    for i, file in enumerate(annotations):  # 遍历xml文件

        # actual parsing
        in_file = open(file, encoding='utf-8')
        tree = ET.parse(in_file)
        root = tree.getroot()

        # 遍历文件的所有标签
        for obj in root.iter('object'):
            name = obj.find('name').text
            if (name in dict.keys()):
                dict[name] += 1  # 如果标签不是第一次出现,则+1
            else:
                dict[name] = 1  # 如果标签是第一次出现,则将该标签名对应的value初始化为1

    # 打印结果
    print("各类标签的数量分别为:")
    for key in dict.keys():
        print(key + ': ' + str(dict[key]))
        label_list.append(key)
    print("标签类别如下:")
    print(label_list)


if __name__ == '__main__':
    # xml文件所在的目录,修改此处
    indir = r'E:\训练结果\变电站17种缺陷检测\train\annots'

    count_num(indir)  # 调用函数统计各类标签数目

运行后可以得出该annotations下存在的标签种类及统计数量运行后可以得出该annotations下存在的标签种类及统计数量

1.2、 批量修改标签属性

如果遇到数据集中的标签不符合想要的类型,需要全部修改,则需要批量对xml做调整

#!/usr/bin/env python3.8
# -*- coding: utf-8 -*-

import glob
import xml.etree.ElementTree as ET


def replace_label(old, new, path):
    """
        function: 使用python xml解析树解析xml文件,批量修改xml文件里object节点下name节点的text
        date:2023/10/26
    """
    # path = r'D:/pythonProject/1028_fall/fire1026/Annotations'    # xml文件夹路径
    i = 0
    for xml_file in glob.glob(path + '/*.xml'):
        # print(xml_file)
        tree = ET.parse(xml_file)
        obj_list = tree.getroot().findall('object')
        for per_obj in obj_list:
            if per_obj[0].text == f'{old}':    # 人物标签“person”
                per_obj[0].text = f'{new}'    # 修改成“coverallno”
                i = i+1

        tree.write(xml_file)    # 将改好的文件重新写入,会覆盖原文件
    print('共完成了{}处替换'.format(i))


if __name__ == '__main__':
    replace_label('smoking', 'xy', r'D:\数据集\通用\吸烟\smokeing\smokeing\test\Annotations')

2、数据整理

2.1、 挑选指定的标签种类

如果遇到数据集中标签种类繁多,需要整理出指定标签种类的数据,或根据需求只获取部分数据集中的标签的时候,可以使用如下脚本处理

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import os
import xml.etree.ElementTree as ET
import shutil

#根据自己的情况修改相应的路径
ann_filepath = 'Annotations/'
img_filepath = 'JPEGImages/'
img_savepath = 'test/JPEGImages/'
ann_savepath = 'test/Annotations/'
if not os.path.exists(img_savepath):
    os.mkdir(img_savepath)

if not os.path.exists(ann_savepath):
    os.mkdir(ann_savepath)


classes = ['aeroplane', 'bicycle', 'tvmonitor']    #这里是需要提取的类别

def save_annotation(file):
    tree = ET.parse(ann_filepath + '/' + file)

    root = tree.getroot()
    result = root.findall("object")
    bool_num = 0
    for obj in result:
        if obj.find("name").text not in classes:
            root.remove(obj)
        else:
            bool_num = 1
    if bool_num:
        tree.write(ann_savepath + file)
        return True
    else:
        return False

def save_images(file):
    name_img = img_filepath + os.path.splitext(file)[0] + ".jpg"
    shutil.copy(name_img, img_savepath)
    #文本文件名自己定义,主要用于生成相应的训练或测试的txt文件
    with open('test/test.txt', 'a') as file_txt:
        file_txt.write(os.path.splitext(file)[0])
        file_txt.write("\n")
    return True

if __name__ == '__main__':
    for f in os.listdir(ann_filepath):
        if save_annotation(f):
            save_images(f)

2.2、 修改文件名后缀

在数据处理时,如果数据是由很多方面整合而来的,那么数据可能存在不统一的情况,甚至jpg和JPG也会出现,还有jpeg、png等需要统一为jpg格式。

#!/usr/bin/env python3.8
# -*- coding: utf-8 -*-

import os

# 想要更改图片所在的根目录
rootdir = r"F:\20240104-小动物闯入数据集\Annotations"
# 获取目录下文件名清单
files = os.listdir(rootdir)

# 对文件名清单里的每一个文件名进行处理
for filename in files:
    portion = os.path.splitext(filename)  # portion为名称和后缀分离后的列表   #os.path.splitext()将文件名和扩展名分开
    if portion[1] == ".jpg":  # 如果为tiff则更改名字
        newname = portion[0] + ".xml"  # 要改的新后缀  #改好的新名字
        print(filename)  # 打印出要更改的文件名
        os.chdir(rootdir)  # 修改工作路径
        os.rename(filename, newname)  # 在工作路径下对文件名重新命名

2.3、 删除宽高为0的xml

import os
import xml.etree.ElementTree as ET

"""
    查找指定文件夹下所有.xml文件的width元素及其值。
"""


def search_width_element(folder_path):
    for filename in os.listdir(folder_path):
        if filename.endswith('.xml'):
            xml_path = os.path.join(folder_path, filename)
            tree = ET.parse(xml_path)
            root = tree.getroot()

            for element in root.iter():
                if element.tag == 'width' and element.text == '0':
                    print(f"在文件 '{filename}' 中找到符合格式的元素:")
                    print(ET.tostring(element, encoding='utf-8').decode('utf-8'))
                    os.remove(xml_path)
                    print(f"已删除文件 '{filename}'。")
                    print('-' * 40)


folder_path = r'D:\数据集\通用\摔倒\fall\111\Annotations'

# search_width_element(folder_path)



"""
    删除文件夹中所有宽高为0的xml文件
"""


folder1_path = r"D:\数据集\通用\摔倒\fall\111\Annotations"
folder2_path = r"D:\数据集\通用\摔倒\fall\111\images"

# 获取folder1中的文件名(不包括扩展名)列表
folder1_files = [os.path.splitext(file)[0] for file in os.listdir(folder1_path)]

# 获取folder2中的文件名(不包括扩展名)列表
folder2_files = [os.path.splitext(file)[0] for file in os.listdir(folder2_path)]

# 遍历folder2中的文件
for file_name in folder2_files:
    if file_name not in folder1_files:
        xml_path = os.path.join(folder2_path, file_name + ".xml")
        jpg_path = os.path.join(folder2_path, file_name + ".jpg")

        if os.path.exists(xml_path):
            print(f"Removing xml file: {file_name}.xml")
            os.remove(xml_path)

        if os.path.exists(jpg_path):
            print(f"Removing jpg file: {file_name}.jpg")
            os.remove(jpg_path)

2.4、 删除文件名中存在的中文符号

import os
import re
import shutil

# 源文件夹路径
src_folder = r'D:\pythonProject\1scripts\images\test'
# 新建的目标文件夹路径
target_folder = r'D:\pythonProject\1scripts\images\test_chinese'

# 如果目标文件夹不存在,则创建它
if not os.path.exists(target_folder):
    os.makedirs(target_folder)

# 定义一个正则表达式,用于匹配包含中文和括号的文件名
filename_pattern = re.compile(r'[\u4e00-\u9fff()]+')  # 匹配Unicode范围内的中文字符和括号

# 遍历源文件夹中的所有文件和子文件夹
for dir_path, dir_names, file_names in os.walk(src_folder):
    for file_name in file_names:
        if filename_pattern.search(file_name):  # 如果文件名匹配正则表达式
            src_file_path = os.path.join(dir_path, file_name)
            target_file_path = os.path.join(target_folder, file_name)

            # 将包含中文字符或括号的文件移动到目标文件夹
            shutil.move(src_file_path, target_file_path)


# 注意:这段代码会将源目录下满足条件的所有文件移动到目标文件夹,而不是复制。
# 如果你需要复制文件,请使用shutil.copy2()函数替换shutil.move()函数。

2.5、 批量重命名

#!/usr/bin/env python3.8
# -*- coding: utf-8 -*-
"""
    function: 检查数据集中是否都是jpg格式
    date:2023/10/26
"""

import os
import glob

pathjoin = os.path.join
prefix = '20240129_'  # yjsk_sf6_6


def rename(ddir, extname, i=1):
    patern = f'{ddir}/*{extname}'  # 构建文件路径模式
    anotfiles = glob.glob(patern)  # 获取所有满足文件路径模式的文件路径
    '遍历所有文件'
    try:
        for anotfile in anotfiles:  # 遍历所有文件路径
            anotname = os.path.split(anotfile)[1]  # 获取文件名
            imname = anotname[:-len(extname)] + '.xml'  # 获取对应的xml文件名

            imfile = pathjoin(ddir, imname)  # 构建xml文件路径
            if os.path.exists(imfile):  # 判断xml文件是否存在
                print(anotfile)  # 输出标注文件路径
                # tanotname = f'{prefix}{i}{extname}'
                # tanotfile = pathjoin(ddir, tanotname)
                # timname = f'{prefix}{i}.jpg'    #  f'{prefix}{i}.jpg'
                timname = f'{prefix}{anotname[:-len(extname)]}.xml'  # 构建新的xml文件名
                timfile = pathjoin(ddir, timname)  # 构建新的xml文件路径
                # os.rename(anotfile, tanotfile)
                os.rename(imfile, timfile)  # 重命名xml文件
                i += 1  # 计数器加1
    except Exception as e:
        print(e)


ddir = r'G:\20240102缺陷\xy\Annotations'  # 标注文件夹路径
rename(ddir, extname='.xml')  # 调用rename函数重命名xml文件

2.6、 比较两个文件夹的不同

import os
import filecmp

folder1 = r'F:\20240104-小动物闯入数据集\给你2\Annotations'
folder2 = r'F:\20240104-小动物闯入数据集\给你2\images'

files1 = set(os.listdir(folder1))
files2 = set(os.listdir(folder2))

different_files = files1.symmetric_difference(files2)

for file in different_files:
    if file in files1:
        file_path = os.path.join(folder1, file)
        if os.path.isfile(file_path):
            print(f'{file} is different in {folder1}')
    if file in files2:
        file_path = os.path.join(folder2, file)
        if os.path.isfile(file_path):
            print(f'{file} is different in {folder2}')

2.7、 百度爬取指定类型图片

import requests
import os
import re
import io
import time  # 限制爬虫速度
from PIL import Image


def get_images_from_baidu(keyword, num_images, save_dir, image_format='jpg', max_size=None):
    """
    从百度图片下载指定关键词的图片,并保存到指定目录中。
    #:param urlip: str,代理IP的API链接。
    :param keyword: str,搜索关键词。
    :param num_images: int,要下载的图片数量。
    :param save_dir: str,图片保存的目录。
    :param image_format: str,图片格式,默认为 'jpg'。
    :param max_size: tuple,限制图片的最大尺寸,格式为 (width, height)。
    """

    # """获取代理IP"""
    # urlip = urlip
    # while 1:
    #     try:
    #         r = requests.get(urlip, timeout=10)
    #     except:
    #         continue
    #
    #     ip = r.text.strip()
    #     if '请求过于频繁' in ip:
    #         print('IP请求频繁')
    #         time.sleep(1)
    #         continue
    #     break
    # proxies = {
    #     'https': '%s' % ip
    # }

    # 设置请求头
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
    }
    # 设置请求的 URL
    url = 'https://image.baidu.com/search/acjson?'
    # url = '180.112.239.48:31085'
    # 初始化计数器
    n = 0
    # 每页显示的图片数量
    rn = 30
    # 计算需要请求的页数
    page_num = (num_images + rn - 1) // rn
    # 循环遍历每一页,获取图片链接并下载保存
    for pn in range(0, rn * page_num, rn):
        # 请求参数
        params = {
            'tn': 'resultjson_com',
            'logid': '7603311155072595725',
            'ipn': 'rj',
            'ct': 201326592,
            'is': '',
            'fp': 'result',
            'queryWord': keyword,
            'cl': 2,
            'lm': -1,
            'ie': 'utf-8',
            'oe': 'utf-8',
            'adpicid': '',
            'st': -1,
            'z': '',
            'ic': '',
            'hd': '',
            'latest': '',
            'copyright': '',
            'word': keyword,
            's': '',
            'se': '',
            'tab': '',
            'width': '',
            'height': '',
            'face': 0,
            'istype': 2,
            'qc': '',
            'nc': '1',
            'fr': '',
            'expermode': '',
            'force': '',
            'cg': '',
            'pn': pn,
            'rn': rn,
            'gsm': '1e',
            '1618827096642': ''
        }
        # 发送请求
        response = requests.get(url=url, headers=headers, params=params)
        # 检查响应是否成功
        if response.ok:
            print('Request success.')
        # 解析响应数据并提取图片链接
        response.encoding = 'utf-8'
        html = response.text
        image_url_list = re.findall('"thumbURL":"(.*?)",', html, re.S)
        # 如果指定的目录不存在,则创建目录
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        # 下载并保存图片
        for image_url in image_url_list:
            if n >= num_images:
                break
            try:
                # 获取图片数据
                image_data = requests.get(url=image_url, headers=headers).content
                # 打开图片并获取尺寸
                image = Image.open(io.BytesIO(image_data))
                width, height = image.size
                # 如果指定了最大尺寸并且图片尺寸超过了最大尺寸,则跳过该图片
                if max_size and (width > max_size[0] or height > max_size[1]):
                    continue
                # 保存图片
                with open(os.path.join(save_dir, f'{n:06d}.{image_format}'), 'wb') as fp:
                    fp.write(image_data)
                n += 1
            except (requests.RequestException, IOError) as e:
                print(f'Error occurred when downloading image: {e}')
    print(f'Successfully downloaded {n} images to {save_dir} directory.')


if __name__ == "__main__":
    # 从终端获取用户输入的关键词和要下载的图片数量和图片格式
    # urlip = '120.34.13.185:48510'
    keyword = input('请输入要搜索的图片关键词:')
    num_images = input('请输入要下载的图片数量:')
    image_format = input('请输入要下载的图片格式(默认为 jpg):') or 'jpg'
    max_width = input('请输入图片的最大宽度(默认为不限制):') or None
    max_height = input('请输入图片的最大高度(默认为不限制):') or None
    if max_width and max_height:
        max_size = (int(max_width), int(max_height))
    else:
        max_size = None
    num_images = int(num_images)
    # 构造保存图片的目录
    save_dir = os.path.join('.', 'images', keyword)
    # 调用函数进行图片下载
    get_images_from_baidu(keyword, num_images, save_dir, image_format, max_size)

2.8、 文件夹下图片和xml分离


import os
import shutil

def move_files(folder_path, images_folder, annotations_folder):
    for item in os.listdir(folder_path):
        item_path = os.path.join(folder_path, item)
        if os.path.isfile(item_path):
            if item.endswith('.jpg') or item.endswith('.JPG') or item.endswith('.xml'):
                if item.endswith('.jpg') or item.endswith('.JPG'):
                    shutil.move(item_path, os.path.join(images_folder, item))
                else:
                    shutil.move(item_path, os.path.join(annotations_folder, item))
        elif os.path.isdir(item_path):
            move_files(item_path, images_folder, annotations_folder)

# 文件夹路径
folder_path = r'D:\pythonproject\1222_test_txt'

# 图片文件夹路径
images_folder = r'D:\pythonproject\1222_test_txt\images'
if not os.path.exists(images_folder):
    os.makedirs(images_folder)

# 标签xmk文件夹路径
annotations_folder = r'D:\pythonproject\1222_test_txt\annotations'
if not os.path.exists(annotations_folder):
    os.makedirs(annotations_folder)

move_files(folder_path, images_folder, annotations_folder)


2.9(备用)、 修改xml元素宽高(提前把图片移出)

# coding=utf-8
import xml.dom.minidom
import os

path = r'D:\数据集\通用\摔倒\fall\111\Annotations'  # 需要修改mxl文件夹的地址
files = os.listdir(path)  # 得到文件夹下所有文件名称

for xmlFile in files:  # 遍历文件夹
    xml_path = os.path.join(path, xmlFile)
    if not os.path.isdir(xml_path):  # 判断是否是文件夹,不是文件夹才打开
        dom = xml.dom.minidom.parse(xml_path)  # 读入xml
        root = dom.documentElement
        width = root.getElementsByTagName('width')
        height = root.getElementsByTagName('height')
        width0 = width[0]  # 原来的width
        # print('width原来为',width0.firstChild.data)
        width0.firstChild.data = 0  # 修改width
        # print('width修改后为',width0.firstChild.data)
        height0 = height[0]  # 原来的height
        # print('height原来为',height0.firstChild.data)
        height0.firstChild.data = 0  # 修改height
        # print('height修改后为',height0.firstChild.data)

        with open(xml_path, 'w') as fh:
            dom.writexml(fh)
            print('{}写入width和height成功!'.format(xmlFile))

3、拆分数据集

3.1、 (A-txt-linux)splitDataset

import os
import random

trainval_percent = 0.8
train_percent = 0.8
xmlfilepath = r'/home/1212_tongyong/'
# xmlfilepath = r'/home/1212_tongyong/Annotations/'
txtsavepath = r'/home/1212_tongyong/ImageSets/'
total_xml = os.listdir(xmlfilepath)

num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)

ftrainval = open(r'/home/1212_tongyong/ImageSets/trainval.txt', 'w')
ftest = open(r'/home/1212_tongyong/ImageSets/test.txt', 'w')
ftrain = open(r'/home/1212_tongyong/ImageSets/train.txt', 'w')
fval = open(r'/home/1212_tongyong/ImageSets/val.txt', 'w')

for i in list:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        ftrainval.write(name)
        if i in train:
            ftrain.write(name)
        else:
            fval.write(name)
    else:
        ftest.write(name)

ftrainval.close()
ftrain.close()
fval.close()
ftest.close()

3.2、 (A-txt-linux)splitDataset

import os
import random

'''
只通过xml或labels文件夹生成ImageSets文件夹下的txt文件,不涉及图片的复制和移动
'''

trainval_percent = 0.8
train_percent = 0.8
xmlfilepath = r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\labels'
# xmlfilepath = r'D:\数据集\通用\小动物入侵\mouse\Annotations'
txtsavepath = r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\ImageSets'
total_xml = os.listdir(xmlfilepath)

num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)

ftrainval = open(r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\ImageSets\trainval.txt', 'w')
ftest = open(r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\ImageSets\test.txt', 'w')
ftrain = open(r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\ImageSets\train.txt', 'w')
fval = open(r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\ImageSets\val.txt', 'w')

for i in list:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        ftrainval.write(name)
        if i in train:
            ftrain.write(name)
        else:
            fval.write(name)
    else:
        ftest.write(name)

ftrainval.close()
ftrain.close()
fval.close()
ftest.close()

3.3、 (B-dir-linux)splitDataset


# -*- coding: utf-8 -*-

import os
import random
import shutil

def moveimg(fileDir, tarDir):
    """
        function: 数据集拆分训练集和验证集。使用时先将数据集分验证集,再分测试集
        params:
            fileDir: 原始数据集路径
            tarDir: 目标数据集路径
        return:
            无
        date:2023/10/26
    """
    pathDir = os.listdir(fileDir)  # 取图片的原始路径
    filenumber = len(pathDir)
    rate = 0.10
    picknumber = int(filenumber * rate)  # 按照rate比例从文件夹中取一定数量图片
    sample = random.sample(pathDir, picknumber)  # 随机选取picknumber数量的样本图片
    print('sample:', sample)
    print(len(sample))
    for name in sample:
        shutil.move(fileDir + name, tarDir + "/" + name)
    return


def movelabel(file_list, file_label_train, file_label_val):
    """
        function: 数据集拆分训练集和验证集

            先将数据集分验证集,再分测试集
        date:2023/10/26
    """
    for i in file_list:
        i = i.lower()
        if i.endswith('.jpg'):
            # filename = file_label_train + "\\" + i[:-4] + '.xml'
            filename = file_label_train + "/" + i[:-4] + '.txt'
            if os.path.exists(filename):
                shutil.move(filename, file_label_val)
                print(i + "处理成功!")


if __name__ == '__main__':
    fileDir = "/home/fire/train/images" + "/"  # 源图片文件夹路径
    tarDir = "/home/fire/train/test_images"  # 图片移动到新的文件夹路径
    moveimg(fileDir, tarDir)
    file_list = os.listdir(tarDir)
    file_label_train = "/home/fire/train/labels"  # 源图片标签路径
    file_label_val = "/home/fire/train/test_labels"  # 对应标签移动到新的文件夹路径
    # 移动到新的文件路径
    movelabel(file_list, file_label_train, file_label_val)


4、格式转换

4.1、 (A-txt-linux)XML2TXT.py


# -*- coding: utf-8 -*-
# xml解析包
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join


sets = ['train', 'test', 'val']
classes = ['bjdsyc', 'bj_wkps', 'yw_nc', 'xmbhyc', 'kgg_ybh', 'gbps', 'yw_gkxfw', 'hxq_gjbs', 'bj_bpmh', 'jyz_pl', 'bj_bpps', 'sly_dmyw', 'wcaqm', 'wcgz', 'ywzt_yfyc', 'hxq_gjtps', 'xy']


# 进行归一化操作
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[1])/2.0   # 物体在图中的中心点x坐标
    y = (box[2] + box[3])/2.0   # 物体在图中的中心点y坐标
    w = box[1] - box[0]         # 物体实际像素宽度
    h = box[3] - box[2]         # 物体实际像素高度
    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]



def convert_annotation(image_id: object):
    '''
    将对应文件名的xml文件转化为label文件,xml文件包含了对应的bunding框以及图片长款大小等信息,
    通过对其解析,然后进行归一化最终读到label文件中去,也就是说
    一张图片文件对应一个xml文件,然后通过解析和归一化,能够将对应的信息保存到唯一一个label文件中去
    labal文件中的格式:class x y w h  同时,一张图片对应的类别有多个,所以对应的bunding的信息也有多个
    '''
    # 对应的通过year 找到相应的文件夹,并且打开相应image_id的xml文件,其对应bund文件
    in_file = open('/home/1212_tongyong/Annotations/%s.xml' % (image_id), encoding='utf-8')
    print(image_id)
    # 准备在对应的image_id 中写入对应的label,分别为
    # <object-class> <x> <y> <width> <height>
    out_file = open('/home/1212_tongyong/labels/%s.txt' % (image_id), 'w', encoding='utf-8')
    # 解析xml文件
    tree = ET.parse(in_file)
    # 获得对应的键值对
    root = tree.getroot()
    # 获得图片的尺寸大小
    size = root.find('size')
    # 如果xml内的标记为空,增加判断条件
    if size != None:
        # 获得宽
        w = int(size.find('width').text)
        # 获得高
        h = int(size.find('height').text)
        # 遍历目标obj
        for obj in root.iter('object'):
            # 获得difficult ??
            difficult = obj.find('difficult').text
            # 获得类别 =string 类型
            cls = obj.find('name').text
            # 如果类别不是对应在我们预定好的class文件中,或difficult==1则跳过
            if cls not in classes or int(difficult) == 1:
                continue
            # 通过类别名称找到id
            cls_id = classes.index(cls)
            # 找到bndbox 对象
            xmlbox = obj.find('bndbox')
            # 获取对应的bndbox的数组 = ['xmin','xmax','ymin','ymax']
            b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
                 float(xmlbox.find('ymax').text))
            print(image_id, cls, b)
            # 带入进行归一化操作
            # w = 宽, h = 高, b= bndbox的数组 = ['xmin','xmax','ymin','ymax']
            bb = convert((w, h), b)
            # bb 对应的是归一化后的(x,y,w,h)
            # 生成 calss x y w h 在label文件中
            out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')


# 返回当前工作目录
wd = getcwd()
print(wd)


for image_set in sets:
    '''
    对所有的文件数据集进行遍历
    做了两个工作:
    1.将所有图片文件都遍历一遍,并且将其所有的全路径都写在对应的txt文件中去,方便定位
    2.同时对所有的图片文件进行解析和转化,将其对应的bundingbox 以及类别的信息全部解析写到label 文件中去
         最后再通过直接读取文件,就能找到对应的label 信息
    '''
    # 先找labels文件夹如果不存在则创建
    if not os.path.exists('/home/1212_tongyong/labels/'):
        os.makedirs('/home/1212_tongyong/labels/')
    # 读取在ImageSets/Main 中的train、test..等文件的内容
    # 包含对应的文件名称
    image_ids = open('/home/1212_tongyong/ImageSets/%s.txt' % (image_set)).read().strip().split()
    print(image_ids)
    # 打开对应的2012_train.txt 文件对其进行写入准备
    list_file = open('/home/1212_tongyong/%s.txt' % (image_set), 'w')
    # 将对应的文件_id以及全路径写进去并换行
    for image_id in image_ids:
        list_file.write('/home/1212_tongyong/images/%s.jpg\n' % (image_id))
        # 调用  year = 年份  image_id = 对应的文件名_id
        convert_annotation(image_id)
    # 关闭文件
    list_file.close()
# print(image_ids)
# print(image_id)
# print()

4.2、 (A-txt-win)XML2TXT.py


# -*- coding: utf-8 -*-
# xml解析包
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join


sets = ['train', 'test', 'val']
classes = ['mouse', 'bird']


# 进行归一化操作
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[1])/2.0   # 物体在图中的中心点x坐标
    y = (box[2] + box[3])/2.0   # 物体在图中的中心点y坐标
    w = box[1] - box[0]         # 物体实际像素宽度
    h = box[3] - box[2]         # 物体实际像素高度
    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]



def convert_annotation(image_id: object):
    '''
    将对应文件名的xml文件转化为label文件,xml文件包含了对应的bunding框以及图片长款大小等信息,
    通过对其解析,然后进行归一化最终读到label文件中去,也就是说
    一张图片文件对应一个xml文件,然后通过解析和归一化,能够将对应的信息保存到唯一一个label文件中去
    labal文件中的格式:class x y w h  同时,一张图片对应的类别有多个,所以对应的bunding的信息也有多个
    '''
    # 对应的通过year 找到相应的文件夹,并且打开相应image_id的xml文件,其对应bund文件
    in_file = open(r'D:\pythonProject\0105-animal\mouse\Annotations\%s.xml' % (image_id), encoding='utf-8')
    print(image_id)
    # 准备在对应的image_id 中写入对应的label,分别为
    # <object-class> <x> <y> <width> <height>
    out_file = open(r'D:\pythonProject\0105-animal\mouse\labels\%s.txt' % (image_id), 'w', encoding='utf-8')
    # 解析xml文件
    tree = ET.parse(in_file)
    # 获得对应的键值对
    root = tree.getroot()
    # 获得图片的尺寸大小
    size = root.find('size')
    # 如果xml内的标记为空,增加判断条件
    if size != None:
        # 获得宽
        w = int(size.find('width').text)
        # 获得高
        h = int(size.find('height').text)
        # 遍历目标obj
        for obj in root.iter('object'):
            # 获得difficult ??
            difficult = obj.find('difficult').text
            # 获得类别 =string 类型
            cls = obj.find('name').text
            # 如果类别不是对应在我们预定好的class文件中,或difficult==1则跳过
            if cls not in classes or int(difficult) == 1:
                continue
            # 通过类别名称找到id
            cls_id = classes.index(cls)
            # 找到bndbox 对象
            xmlbox = obj.find('bndbox')
            # 获取对应的bndbox的数组 = ['xmin','xmax','ymin','ymax']
            b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
                 float(xmlbox.find('ymax').text))
            print(image_id, cls, b)
            # 带入进行归一化操作
            # w = 宽, h = 高, b= bndbox的数组 = ['xmin','xmax','ymin','ymax']
            bb = convert((w, h), b)
            # bb 对应的是归一化后的(x,y,w,h)
            # 生成 calss x y w h 在label文件中
            out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')


# 返回当前工作目录
wd = getcwd()
print(wd)


for image_set in sets:
    '''
    对所有的文件数据集进行遍历
    做了两个工作:
    1.将所有图片文件都遍历一遍,并且将其所有的全路径都写在对应的txt文件中去,方便定位
    2.同时对所有的图片文件进行解析和转化,将其对应的bundingbox 以及类别的信息全部解析写到label 文件中去
         最后再通过直接读取文件,就能找到对应的label 信息
    '''
    # 先找labels文件夹如果不存在则创建
    if not os.path.exists(r'D:\pythonProject\0105-animal\mouse\labels'):
        os.makedirs(r'D:\pythonProject\0105-animal\mouse\labels')
    # 读取在ImageSets/Main 中的train、test..等文件的内容
    # 包含对应的文件名称
    image_ids = open(r'D:\pythonProject\0105-animal\mouse\ImageSets\%s.txt' % (image_set)).read().strip().split()
    print(image_ids)
    # 打开对应的2012_train.txt 文件对其进行写入准备
    list_file = open(r'D:\pythonProject\0105-animal\mouse\%s.txt' % (image_set), 'w')
    # 将对应的文件_id以及全路径写进去并换行
    for image_id in image_ids:
        list_file.write(r'D:\pythonProject\0105-animal\mouse\images\%s.jpg'%(image_id)+ '\n')
        # 调用  year = 年份  image_id = 对应的文件名_id
        convert_annotation(image_id)
    # 关闭文件
    list_file.close()
# print(image_ids)
# print(image_id)
# print()

4.3、 (B-dir)XML2TXT.py

# coding=utf-8
import cv2
import xml.etree.ElementTree as ET
import os

"""
分图片,不使用train.txt文件
train、val、test分别转

"""


def convert(size, box):
    """
    将box转换为yolo的格式
    :param size:
    :param box:
    :return:
    """
    dw = 1. / (size[0])
    dh = 1. / (size[1])
    x = (box[0] + box[1]) / 2.0 - 1
    y = (box[2] + box[3]) / 2.0 - 1
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x * dw
    w = w * dw
    y = y * dh
    h = h * dh
    return x, y, w, h


def read_cls_txt(filenam):
    pos = []

    clsfile = open(filenam)
    rows = len(clsfile.readlines())
    print("There are %d lines in %s" % (rows, filenam))
    if (rows == 0):
        print(filenam, ": there is no lines")
        return pos

    with open(filenam, 'r') as file_to_read:
        while True:
            lines = file_to_read.readline()
            if not lines:
                break
                pass
            print("line:", lines)
            pos.append(lines.rstrip("\n"))
            pass
        print(filenam, pos)
    return pos


def xml2yolotxt():
    """
    将xml文件转换为yolo的txt文件

    :return:
    """
    xml_path = r'D:\数据集\通用\小动物入侵\mouse\Annotations'  # XML文件路径,不涉及图片
    obj_img_path = r'D:\数据集\通用\小动物入侵\mouse\labels'   # 转换后的txt文件保存路径
    xml_path_list = []
    obj_img_path_list = []
    obj_img_path_loss_list = []
    size_list = []
    classes = read_cls_txt(r'D:\数据集\通用\小动物入侵\mouse\classes.txt')
    print("classes:", classes)
    for xml_name in os.listdir(xml_path):

        x_name = xml_name.split(".")[0]
        print("xml_name:", xml_name)
        xml_path_list.append(xml_name)
        obj_img_path_list.append(obj_img_path)
        root = ET.parse(xml_path + "/" + xml_name).getroot()

        size = root.find('size')
        size_list.append(size)
        w = int(size.find('width').text)

        print('w:', w)
        h = int(size.find('height').text)
        print('h:', h)
        with open(obj_img_path + "/" + x_name + "" + ".txt", "w") as out_file:

            for obj in root.iter('object'):
                difficult = obj.find('difficult').text
                cls = obj.find('name').text
                if cls not in classes or int(difficult) == 1:
                    print('转化失败的xml:', obj_img_path)
                    obj_img_path_loss_list.append(obj_img_path)
                    continue
                cls_id = classes.index(cls)
                xmlbox = obj.find('bndbox')
                b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
                     float(xmlbox.find('ymax').text))
                bb = convert((w, h), b)
                out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')

    print(len(xml_path_list))
    print(len(obj_img_path_list))

    print(len(size_list))
    print(obj_img_path_loss_list)
    print(len(obj_img_path_loss_list))


xml2yolotxt()

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值