【TensorFlow】Python解析xml文件


       最近在項目中使用TensorFlow訓練目標檢測模型,在制作自己的數據集時使用了labelimg軟件對圖片進行標注,產生了VOC格式的數據,但標注生成的xml文件標簽值難免會產生個別錯誤造成程序無法跑通,或后期有修改xml中標簽值的需求,所以得使用Python代碼對xml文件進行解析操作,當然也是參考了各種博客,故在此總結一下。

1. xml文件格式

由labelimg標注生成的xml文件格式如下所示,

<annotation>
    <folder>images1</folder>
    <filename>0.png</filename>
    <path>C:\Users\White\Desktop\images1\0.png</path>
    <source>
        <database>Unknown</database>
    </source>
    <size>
        <width>1080</width>
        <height>1920</height>
        <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
        <name>box</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>345</xmin>
            <ymin>673</ymin>
            <xmax>475</xmax>
            <ymax>825</ymax>
        </bndbox>
    </object>
    <object>
        <name>box</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>609</xmin>
            <ymin>1095</ymin>
            <xmax>759</xmax>
            <ymax>1253</ymax>
        </bndbox>
    </object>
</annotation>

xml是可擴展標記語言,主要用來標記數據和定義數據類型,從結構上看,有以下特點:

只有一個根結點,其中結點中可以有屬性;

一個結點由標簽對組成,如<folder></folder>;

結點的標簽對可以有屬性,如<object  id = 0></object>;

標簽對中可以存儲數據,如<object  id = 0>123</object>;

結點可以相互嵌套,形成子結點,如<object  id = 0>  <name>box</name></object>;

2.xml.dom解析xml

文件對象模型(Document Object Model,簡稱DOM),dom在解析xml文件時一次性將整個文檔加載至內存,在內存中使用樹結構來保存xml文件中的標簽元素和結構,同時你可以使用dom中的函數來解析獲取文件信息或修改保存信息,以下為使用dom統計某個文件夾下xml文件中每類標簽數量的Python代碼,

#coding:utf-8
import os.path
import xml.dom.minidom

class_nums = {"box": 0, "person": 0}
n = 0
xmldir = "E:\\Project\\object\\merged_xml"

for xmlfile in os.listdir(xmldir):
     print(xmlfile)
     # 打開xml文件
     dom = xml.dom.minidom.parse(os.path.join(xmldir, xmlfile))
     # 獲得元素對象
     root = dom.documentElement
     name_length = len(root.getElementsByTagName('name'))
     # 獲取標簽對name之間的值
     for i in range(name_length):
          key = str(root.getElementsByTagName('name')[i].firstChild.data)
          #print(key)
          if key in class_nums.keys():
              class_nums[key] += 1
     # print('node', root.getElementsByTagName('filename')[0].firstChild.data)
     # print('node', filename.firstChild.data)
     n += 1
print('processed file number is ', n)
for key in class_nums:
   print(key, ':', class_nums[key])

1. 導入Python中的xml.dom文件解析模塊

import xml.dom.minidom

2. 獲取dom對象和結點:xml.dom.minidom是Python用於處理xml文件的模塊,其具有函數xml.dom.minidom.parse()

dom = xml.dom.minidom.parse(os.path.join(xmldir, xmlfile))

該函數傳入xml文件的路徑和文件名的字符串,用於打開一個xml文件,並得到dom文檔樹,並通過documentElement得到根結點,

root = dom.documentElement

name_length = len(root.getElementsByTagName('name'))

如果我們知道結點的名稱,可以通過root.getElementsByTagName('  ')來獲得所有名稱等於傳入字符串的結點,並可以通過索引來獲得相同名稱的不同結點,如下所示,

name0 = root.getElementsByTagName('name')[0]

name1 = root.getElementsByTagName('name')[1]

4. 獲取標簽對之間的數據:firstChild 屬性返回被選節點的第一個子節點,.data表示獲取該節點的數據。

 key = str(root.getElementsByTagName('name')[i].firstChild.data)

5. 補充:獲取標簽的屬性值getAttribute(),以前面的<object  id = 0>123</object>為例,

objectlist = root.getElementsByTagName('object')
object0 = objectlist[0]
id0=object0.getAttribute("id")

3.xml.etree.ElementTree解析xml

ElementTree也是處理xml文件的模塊,其具有兩種類型,python實現型的xml.etree.ElementTree和c語言實現型的xml.etree.cElementTree,后者比前者的速度更快,內存消耗更少。以下為將xml文件轉換為CSV文件的代碼示例,

#coding:utf-8
import os
import glob  
import pandas as pd  
import xml.etree.ElementTree as ET  


def xml_to_csv(path):  
    xml_list = []  
    for xml_file in glob.glob(path + '/*.xml'):
        #返回解析樹
        tree = ET.parse(xml_file)
        #獲取根節點
        root = tree.getroot()  
        # print(root)
        # 根據標簽名查找root下的所有標簽,並獲取其值
        print(root.find('filename').text)
        #對所有目標進行解析
        for member in root.findall('object'):  
            value = (root.find('filename').text,  
                     int(root.find('size')[0].text),   #width  
                     int(root.find('size')[1].text),   #height  
                     member[0].text,  #object name
                     int(member[4][0].text),  #xmin
                     int(float(member[4][1].text)),  #ymin
                     int(member[4][2].text),   #xmax
                     int(member[4][3].text)    #ymax
                     )  
            xml_list.append(value)  
    column_name = ['filename', 'width', 'height', 'class', 'xmin', 'ymin', 'xmax', 'ymax']
    #pandas創建帶列名的二維數據表
    xml_df = pd.DataFrame(xml_list, columns=column_name)  
    return xml_df

1.首先導入解析模塊,

import xml.etree.ElementTree as ET

2. 返回解析樹和獲取根節點,

#返回解析樹
        tree = ET.parse(xml_file)
        #獲取根節點
        root = tree.getroot()

3. 可以通過根節點root可以遍歷下一層的節點,.tag和.attrib可以獲取標簽名和標簽屬性,.text獲取標簽中的值,

for rootChild in root:
    print( 'tagname:', rootChild.tag, 'attribute:', rootChild.attrib, 'value:', rootChild.text)

4. 可以使用下標訪問各層節點,

int(member[4][0].text),  #xmin
int(float(member[4][1].text)),  #ymin

5. 使用findall()查找所有相同標簽名的標簽,

#根據標簽名查找root下的所有標簽
    objectList = root.findall("object")

6.通過set和get來修改標簽屬性名,

object0 = root.findall('object')[0]
id = object0.get('id')
object0.set('id', 111)
id2 = object0.get('id')
print (id, id2)

4. 其他解析方式

  還可以通過xml.sax模塊來解析xml,但前兩者已經足夠應付大部分需求,在這里就不展開介紹了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM