yolo(darkent)格式转换为pascal VOC数据格式

yolo(darkent)格式转换为pascal VOC数据格式

Catalog

  • yolo(darkent)格式转换为pascal VOC数据格式
      • 一、认识VOC和YOLO数据格式
      • 二、构建xml的基本操作
      • 三、美化xml文件
      • 四、Yolo数据标注格式 to VOC数据标注格式
      • 五、Reference

一、认识VOC和YOLO数据格式

yolo(darkent)格式转换为pascal VOC数据格式_第1张图片
pascal VOC数据的标注格式为边框的(左上x,左上y,右下x,右下y)的坐标值,并且没有归一化,而yolo数据标注格式为(分类的id, 中心点坐标x,中心点坐标y,框的宽,框的高),yolo的数据标注格式是进行了归一化的。

pascal VOC数据标注的存储文件为XML,接下来简单地介绍一下XML文件。
XML文件的一个节点的基本结构为
yolo(darkent)格式转换为pascal VOC数据格式_第2张图片
VOC数据标注文件
以下就是一个标准的VOC标注文件。

<annotation>
	<folder>VOC2007</folder>
	<filename>000002.jpg</filename>
	<source>
		<database>The VOC2007 Database</database>
		<annotation>PASCAL VOC2007</annotation>
		<image>flickr</image>
		<flickrid>329145082</flickrid>
	</source>
	<owner>
		<flickrid>hiromori2</flickrid>
		<name>Hiroyuki Mori</name>
	</owner>
	<size>
		<width>335</width>
		<height>500</height>
		<depth>3</depth>
	</size>
	<segmented>0</segmented>
	<object>
		<name>train</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>139</xmin>
			<ymin>200</ymin>
			<xmax>207</xmax>
			<ymax>301</ymax>
		</bndbox>
	</object>
</annotation>

二、构建xml的基本操作

使用python的xml.etree.ElementTree模块可以很方便地构建和解析xml文件,以下代码全部使用该库进行构建。

基本操作如下
yolo(darkent)格式转换为pascal VOC数据格式_第3张图片
举个例子,我们来构建一个最基本的xml文件

#   指定根节点tag是 'data'
root = ET.Element('data') 
#   指定data的子节点是country,并且attrib是'name' = Liechtenstein'
country = ET.SubElement(root,'country', {'name':'Liechtenstein'})
#   指定country的子节点是rank, rank.text = 1
rank = ET.SubElement(country,'rank')
rank.text = '1'
year = ET.SubElement(country,'year')
year.text = '2008'
#   构建tree
tree=ET.ElementTree(root)
#   写入tree
tree.write("demo.xml")

我们写入了一个基本的文件,文件名为demo.xml,打开该文件我们可以查看我们写入的xml结构

<data><country name="Liechtenstein"><rank>1</rank><year>2008</year></country></data>

可以看出,这样生成的xml文件并不美观,和VOC数据标注格式相比,可读性非常差。因此我们需要美化xml文件。

三、美化xml文件

使用以下脚本进行xml文件美化

def prettyXml(tree,indent='\t',newline='\n',level=0):
    """
    Pretty Xml File before writing
    :param tree: 创建好的父节点,未初始化的tree
    :param indent: 缩进“\t”
    :param newline: 换行“\n”
    :param level: 用于递归操作的变量,实现不同级别节点增加不同数量的缩进“\t”
    :return: 无
    """
    treeList=list(tree)
    for subElement in treeList:
        if treeList.index(subElement) < (len(treeList) - 1):
            subElement.tail=newline+indent*(level+1)
        else:
            subElement.tail=newline+indent*level
        prettyXml(subElement,indent,newline,level+1)
#   指定根节点tag是 'data'
root = ET.Element('data') 
#   指定data的子节点是country,并且attrib是'name' = Liechtenstein'
country = ET.SubElement(root,'country', {'name':'Liechtenstein'})
#   指定country的子节点是rank, rank.text = 1
rank = ET.SubElement(country,'rank')
rank.text = '1'
year = ET.SubElement(country,'year')
year.text = '2008'
#   构建tree
prettyXml(root)
tree=ET.ElementTree(root)
#   写入tree
tree.write("demo2.xml")

美化后的demo2.xml文件如下

<data><country name="Liechtenstein"><rank>1</rank>
		<year>2008</year>
	</country>
</data>

四、Yolo数据标注格式 to VOC数据标注格式

'''
Description: 
coding: utf-8
language: python3.8, tensorflow2.6
Author: JiaHao Shum
Date: 2022-03-20 16:09:52
'''

import numpy as np
from PIL import Image, ImageDraw
import os
import xml.etree.ElementTree as ET
from tqdm import tqdm

#	图片中包含的分类,需要和yolo中的class_id对应起来
classes = ['face', 'face with mask']

#	yolo格式标注文件存放路径
yolo_labels_dir = './Labels'
#	图片存放路径
images_dir = './JPEGImages'
#	voc格式标注文件存放路径
pascal_labels_dir = './XMLs'


def prettyXml(tree,indent='\t',newline='\n',level=0):
    """
    Pretty Xml File before writing
    :param tree: 创建好的父节点,未初始化的tree
    :param indent: 缩进“\t”
    :param newline: 换行“\n”
    :param level: 用于递归操作的变量,实现不同级别节点增加不同数量的缩进“\t”
    :return: 无
    """
    treeList=list(tree)
    for subElement in treeList:
        if treeList.index(subElement) < (len(treeList) - 1):
            subElement.tail=newline+indent*(level+1)
        else:
            subElement.tail=newline+indent*level
        prettyXml(subElement,indent,newline,level+1)


def convert_one_label(yolo_filename, images_dir, xml_save_path):
    """
    Func:
        将一张图片的yolo格式的标签转换成xml格式
    Args:
        yolo_filename:  yolo格式标签的完整路径
        images_dir:     images的存放地址
        xml_save_path:  xml格式标签的存放地址
    """
    
    filename = os.path.split(yolo_filename)[-1]
    image_filename = os.path.join(images_dir, str(filename[:-4]) + '.jpg')

    image = Image.open(image_filename)
    iw, ih = image.size

    """获得一张图片的yolo box"""
    if not os.path.exists(xml_save_path):
        os.makedirs(xml_save_path)
        
    bndbox = open(yolo_filename).readlines()
    bndbox = [x.strip() for x in bndbox]
    boxes_org = np.array([x.split() for x in bndbox], dtype='float32')
    boxes = np.zeros_like(boxes_org, dtype='int')
    boxes[:, -1] = boxes_org[:, 0].astype('int')
    boxes[:, 0] = (boxes_org[:, 1] - boxes_org[:, 3] / 2) * iw
    boxes[:, 1] = (boxes_org[:, 2] - boxes_org[:, 4] / 2) * ih
    boxes[:, 2] = (boxes_org[:, 1] + boxes_org[:, 3] / 2) * iw
    boxes[:, 3] = (boxes_org[:, 2] + boxes_org[:, 4] / 2) * ih

    """写入一张图片的xml"""
    xml_filename = os.path.join(xml_save_path, str(filename[:-4])+'.xml')

    root = ET.Element('annotation')
    root.text = '\n\t'

    for box in boxes:
        obj = ET.SubElement(root, 'object')
        
        name = ET.SubElement(obj, 'name')
        name.text = classes[box[-1]]
        
        bndbox = ET.SubElement(obj, 'bndbox')
       
        xmin = ET.SubElement(bndbox, 'xmin')
        ymin = ET.SubElement(bndbox, 'ymin')
        xmax = ET.SubElement(bndbox, 'xmax')
        ymax = ET.SubElement(bndbox, 'ymax')
        xmin.text,  = str(box[0]), 
        ymin.text,  = str(box[1]), 
        xmax.text,  = str(box[2]), 
        ymax.text,  = str(box[3]), 
    
    prettyXml(root)
    tree = ET.ElementTree(root)
    tree.write(xml_filename)


if __name__ == '__main__':

    filenames = os.listdir(yolo_labels_dir)
    filenames = [str(x) for x in filenames if x.endswith('.txt')]

    for filename in tqdm(filenames):
        yolo_filename = os.path.join(yolo_labels_dir, filename)
        convert_one_label(yolo_filename, images_dir, pascal_labels_dir)
    
    print('-'*50)
    print('Convertion Finish!')
    print('-'*50)

运行结果如下:
在这里插入图片描述
我们进入相应文件查看一下:
yolo(darkent)格式转换为pascal VOC数据格式_第4张图片

yolo(darkent)格式转换为pascal VOC数据格式_第5张图片
可见我们已经成功转换

五、Reference

http://www.4k8k.xyz/article/weixin_42997255/100090114
https://docs.python.org/zh-cn/3.9/library/xml.etree.elementtree.html?highlight=subelement#xml.etree.ElementTree.SubElement
https://blog.csdn.net/weixin_42782150/article/details/106219001
https://blog.csdn.net/jq_98/article/details/123316087
https://blog.csdn.net/qq_39507748/article/details/110819929

你可能感兴趣的:(目标检测Yolo,神经网络,目标检测)