PASCAL Annotation格式数据标签文件转换为VOC格式数据标签文件

需要一个VOC格式的摩托车数据集,于是只好将从网上搜集到的一些PASCAL Annotation格式标注好的摩托车数据集转换成VOC格式的
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

代码如下:

# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
import re
from shutil import copyfile

root_path="C:\\Users\\dell\\Downloads\\摩托车_my\\"
srcTxt_path="C:\\Users\\dell\\Downloads\\摩托车_my\\VOC2006\\test\\Annotations\\"
srcPic_path="C:\\Users\\dell\\Downloads\\摩托车_my\\VOC2006\\test\\PNGImages\\"
output_root_path="C:\\Users\\dell\\Downloads\\摩托车_my\\PASCAL2VOC_Output\\"

def prettyXml(element, indent, newline, level = 0): # elemnt为传进来的Elment类,参数indent用于缩进,newline用于换行
    if element:  # 判断element是否有子元素
        if element.text == None or element.text.isspace(): # 如果element的text没有内容
            element.text = newline + indent * (level + 1)
        else:
            element.text = newline + indent * (level + 1) + element.text.strip() + newline + indent * (level + 1)
    #else:  # 此处两行如果把注释去掉,Element的text也会另起一行
        #element.text = newline + indent * (level + 1) + element.text.strip() + newline + indent * level
    temp = list(element) # 将elemnt转成list
    for subelement in temp:
        if temp.index(subelement) < (len(temp) - 1): # 如果不是list的最后一个元素,说明下一个行是同级别元素的起始,缩进应一致
            subelement.tail = newline + indent * (level + 1)
        else:  # 如果是list的最后一个元素, 说明下一行是母元素的结束,缩进应该少一个
            subelement.tail = newline + indent * level
        prettyXml(subelement, indent, newline, level = level + 1) # 对子元素进行递归操作

def PASCAL2VOC():
    if not os.path.exists(output_root_path + srcTxt_path.replace(root_path, '')):
        print("Create a xml dir: " + output_root_path + srcTxt_path.replace(root_path, ''))
        os.makedirs(output_root_path + srcTxt_path.replace(root_path, ''))
    if not os.path.exists(output_root_path + srcPic_path.replace(root_path, '')):
        print("Create a pic dir: " + output_root_path + srcPic_path.replace(root_path, ''))
        os.makedirs(output_root_path + srcPic_path.replace(root_path, ''))
    for txt_filename in os.listdir(srcTxt_path):
        if txt_filename.split(".")[-1]=="txt":
            # 创建一个root element<annotation>
            annotation = ET.Element("annotation")
            # 直接通过SubElement类为<annotation>添加多个子元素<folder><filename><path><source><size><segmented><object>
            folder = ET.SubElement(annotation, "folder")
            folder.text = 'Tarin_picture'
            filename = ET.SubElement(annotation, "filename")
            path = ET.SubElement(annotation, "path")
            source = ET.SubElement(annotation, "source")
            database = ET.SubElement(source, "database")
            size = ET.SubElement(annotation, "size")
            width = ET.SubElement(size, "width")
            height = ET.SubElement(size, "height")
            depth = ET.SubElement(size, "depth")
            segmented = ET.SubElement(annotation, "segmented")
            segmented.text = "0"

            with open(srcTxt_path + txt_filename, "r") as file:
                for line in file.readlines():
                    picNameRegex = re.compile(r'Image filename : "(.*)"')
                    mo1 = picNameRegex.search(line)
                    if (mo1 != None):
                        database.text = mo1.group(1).split('/')[0]
                        filename.text = mo1.group(1).split('/')[-1]
                        path.text = srcPic_path + filename.text
                        filename.text = database.text + '_' + filename.text
                    picSizeRegex = re.compile(r'Image size (.*) : (.*) x (.*) x (.*)')
                    mo2 = picSizeRegex.search(line)
                    if (mo2 != None):
                        width.text = mo2.group(2)
                        height.text = mo2.group(3)
                        depth.text = mo2.group(4)
                    if database.text == 'Caltech' or 'VOC2005_1':
                        motorbikeRegex = re.compile(
                            r'Bounding box for object (\d)+ "(PASmotorbikeSide)"(.*) : (.*) - (.*)')
                    if database.text == 'VOC2005_2':
                        motorbikeRegex = re.compile(
                            r'Bounding box for object (\d)+ "(PASmotorbike|PASmotorbikeSide)"(.*) : (.*) - (.*)')
                    if database.text == 'VOC2006':
                        motorbikeRegex = re.compile(
                            r'Bounding box for object (\d)+ "(PASmotorbike|PASmotorbikeLeft|PASmotorbikeRight|PASmotorbikeDifficult|PASmotorbikeTrunc|PASmotorbikeTruncDifficult|PASmotorbikeRear|PASmotorbikeFrontal)"(.*) : (.*) - (.*)')
                    mo3 = motorbikeRegex.findall(line)
                    if (mo3 != None):
                        for obj_in_pic in mo3:
                            object = ET.SubElement(annotation, "object")
                            name = ET.SubElement(object, "name")
                            name.text = obj_in_pic[1]
                            pose = ET.SubElement(object, "pose")
                            pose.text = "Unspecified"
                            struncated = ET.SubElement(object, "struncated")
                            struncated.text = "0"
                            difficult = ET.SubElement(object, "difficult")

                            if ('Difficult' in obj_in_pic[1]):
                                difficult.text = "1"
                            else:
                                difficult.text = "0"
                            bndbox = ET.SubElement(object, "bndbox")
                            xmin = ET.SubElement(bndbox, "xmin")
                            xmin.text = obj_in_pic[3].split('(')[-1].split(',')[0]
                            ymin = ET.SubElement(bndbox, "ymin")
                            ymin.text = obj_in_pic[3].split(' ')[-1].split(')')[0]
                            xmax = ET.SubElement(bndbox, "xmax")
                            xmax.text = obj_in_pic[4].split('(')[-1].split(',')[0]
                            ymax = ET.SubElement(bndbox, "ymax")
                            ymax.text = obj_in_pic[4].split(' ')[-1].split(')')[0]
            prettyXml(annotation, '\t', '\n')
            annotationTree = ET.ElementTree(element=annotation)
            annotationTree.write(output_root_path+ srcTxt_path.replace(root_path, '') + filename.text.split('.')[0]+ ".xml",encoding="utf-8")
            copyfile(path.text, output_root_path + srcPic_path.replace(root_path, '') + filename.text)
if __name__ == "__main__":
    PASCAL2VOC()

转换完成后发现标注框的坐标值有为负的情况,需要再写个脚本对新生成的数据集做一下筛选,挑出那些出现了负值的标注文件和对应的图像。
在这里插入图片描述代码如下:

# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
xml_path='E:\\***\\摩托车_my\\PASCAL2VOC_Output\\my_motorbike_data_set\\Annotations\\'
pic_path='E:\\***\\摩托车_my\\PASCAL2VOC_Output\\my_motorbike_data_set\\PNGImages\\'
dst_path=r'E:/***/摩托车_my/output_after_check_PASCAL2VOC_Output'
def shear_dile(src, dst):
    if os.path.isdir(src):
        if not os.listdir(src):
            os.rmdir(src)
            print('移除空目录: ' + src)
        else:
            for d in os.listdir(src):
                shear_dile(os.path.join(src, d), dst)
    if os.path.isfile(src):
        print("文件剪切:", src)
        fn = os.path.basename(src)
        if not os.path.exists(dst + './' + fn):
            #os.rename(src, dst + './' + fn)
            os.rename(src, dst + fn)


def process_xml(src_path):
    # 读取源xml文件
    num=0
    for xml_filename in os.listdir(src_path):
        et = ET.parse(xml_path+xml_filename)
        # 获取第一个标签为"filename"的“直接”subelement
        filename = et.find("filename")
        picname = filename.text
        size = et.find("size")
        width = size.find("width")
        height = size.find("height")
        depth = size.find("depth")
        with open('数据集图像尺寸统计.txt', 'a') as file:  # 设置文件对象
            str = picname +" "+ width.text + " " +height.text +" "+ depth.text + "\n"
            file.write(str)
        for e in et.findall("object"):
            name = e.find("name")
            bndbox = e.find("bndbox")
            xmin = bndbox.find("xmin")
            ymin = bndbox.find("ymin")
            xmax = bndbox.find("xmax")
            ymax = bndbox.find("ymax")
            if any([(int(xmin.text)<0),(int(ymin.text)<0), (int(xmax.text)<0),(int(ymax.text)<0)]):
                num=num+1

                with open('数据标注框坐标为负图像统计.txt', 'a') as f:  # 设置文件对象
                    str1 = picname + " " + "宽:" + width.text + " 高:" + height.text + " 通道数:" + depth.text + "\n"
                    str2 = "name:" + name.text + " bndbox:" + xmin.text + " " + ymin.text + " " + xmax.text + " " + ymax.text + "\n"
                    f.write(str1+str2)
        if num==0:
            if not os.path.exists(dst_path +'/Annotations/'):
                print("Create a xml dir: " + dst_path +'/Annotations/')
                os.makedirs(dst_path +'/Annotations/')
            if not os.path.exists(dst_path+'/JPEGImages/'):
                print("Create a pic dir: " + dst_path+'/JPEGImages/')
                os.makedirs(dst_path+'/JPEGImages/')
            shear_dile(pic_path+picname ,dst_path+'/JPEGImages/')
            shear_dile(xml_path+picname.split('.')[0]+'.xml',dst_path +'/Annotations/')
        num=0
process_xml(xml_path)

运行得到

数据集图像尺寸统计.txt内容如下:

在这里插入图片描述
对图像尺寸分布进行统计,绘制散点图

代码如下:

# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import warnings
import numpy as np
warnings.filterwarnings("ignore")
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False   #matplotlib画图中中文显示会有问题,需要这两行设置默认字体


file_path="数据集图像尺寸统计.txt"

def scatter_diagram(file_path):
    with open('数据集图像尺寸统计.txt', 'r') as file:
        x=[]
        y=[]
        i=0
        for line in file.readlines():
            width=line.split(" ")[1]
            height=line.split(" ")[2]
            x.append(int(width))
            y.append(int(height))
            i=i+1

        plt.title('数据集图像尺寸统计')
        plt.xlabel('width')
        plt.ylabel('height')
        colors = '#00FF00'
        area = np.pi * 2 ** 2  # 点面积
        plt.scatter(x, y, s=area, c=colors, alpha=0.4, label='width和height')
        plt.legend(('real data', 'fitted line'))
        plt.show()
        #plt.savefig(r'E:\***\摩托车_my\数据集图像长宽分布图.png', dpi=300)
scatter_diagram(file_path)

在这里插入图片描述
【参考网址】:
【1】python xml格式美化 https://blog.csdn.net/udbipyga/article/details/77140494

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Pascal VOC 2012数据集是一个常用的计算机视觉数据集,其中包含了20个不同类别的物体。YOLO是一种目标检测算法,它使用不同的标签格式。下面是将Pascal VOC 2012数据转换为YOLO格式的步骤: 1. 首先,需要将Pascal VOC 2012数据集中的每个图像的标注信息转换为YOLO格式标签。YOLO标签格式为:`class_id x_center y_center width height`,其中`class_id`是物体类别的编号,`x_center`和`y_center`是物体边界框中心点的相对坐标(范围为0到1),`width`和`height`是物体边界框的相对宽度和高度(也是相对于图像的宽度和高度)。 2. 对于每个图像,需要根据其标注信息生成一个对应的YOLO格式标签文件标签文件的命名应与图像文件的命名相对应,并使用`.txt`作为文件扩展名。 3. 在标签文件中,每一行表示一个物体的标注信息。每行的格式应为`class_id x_center y_center width height`,并使用空格分隔。 4. `class_id`是物体类别的编号,从0开始计数,对应于数据集中的不同类别。 5. `x_center`和`y_center`是物体边界框中心点的相对坐标,计算方法为:`x_center = (bbox_x + bbox_width/2) / image_width`,`y_center = (bbox_y + bbox_height/2) / image_height`,其中`bbox_x`和`bbox_y`是物体边界框左上角点的坐标,`bbox_width`和`bbox_height`是物体边界框的宽度和高度。 6. `width`和`height`是物体边界框的相对宽度和高度,计算方法为:`width = bbox_width / image_width`,`height = bbox_height / image_height`。 7. 将所有图像的标签文件保存在一个文件夹中,与图像文件夹对应。 8. 最后,将转换后的数据集用于YOLO的训练。 下面是一个示例,假设有一张图像的标注信息如下: ``` Object: cat Bounding box: (x=100, y=200, width=50, height=80) Image size: (width=800, height=600) ``` 将其转换为YOLO格式标签信息: ``` 0 0.1625 0.3667 0.0625 0.1333 ``` 这表示该图像中的物体是类别0(cat),边界框中心点的相对坐标为(0.1625, 0.3667),边界框的相对宽度和高度分别为(0.0625, 0.1333)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值