본문 바로가기

개발/Python

[Python] XML 파싱하기(with Pascal VOC)

Pascal VOC

Pascal VOC 방식은 Object detection을 위한 데이터 셋인 Pascal VOC dataset의 구조를 의미한다. 이 구조는 하나의 입력 이미지에 대한 정보를 xml파일로 표시한다.

 

[그림 1] 예시 이미지

 

[그림 1]과 같이 2개의 손 끝을 탐지해야 하는 입력 이미지가 존재한다고 했을 때, 이에 대한 데이터의 정보는 다음과 같은 xml 파일로 표시된다.

<annotation>
    <folder></folder>
    <filename>case1-5_jpg.rf.654eac65f62fddc076e25a286ae8e46e.jpg</filename>
    <path>case1-5_jpg.rf.654eac65f62fddc076e25a286ae8e46e.jpg</path>
    <size>
        <width>270</width>
        <height>187</height>
        <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
        <name>object</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <occluded>0</occluded>
        <bndbox>
            <xmin>57</xmin>
            <xmax>94</xmax>
            <ymin>40</ymin>
            <ymax>76</ymax>
        </bndbox>
    </object>
    <object>
        <name>object</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <occluded>0</occluded>
        <bndbox>
            <xmin>45</xmin>
            <xmax>86</xmax>
            <ymin>104</ymin>
            <ymax>134</ymax>
        </bndbox>
    </object>
</annotation>

 

Pascal VOC는 xml 파일에 이처럼 파일명과 경로, 이미지의 크기와 같은 정보 뿐만 아니라, object detection에 필요한 이미지 내 존재하는 객체의 이름과 영역 등과 같은 객체의 정보가 주어진다. 이러한 정보들을 활용하여 object detection에 활용해야 한다.

XML 파싱

Object detection 모델에서 IoU 값을 측정하기 위해서는 평가 이미지의 ground truth 박스 크기를 필요로 한다. 그럴 때는 xml 파일에서탐지해야 하는 객체들의 bndbox 태그를 확인하면 된다. 그렇다면 python에서 어떻게 이를 파싱할 수 있을까?

from xml.etree.ElementTree import parse

xml_file = 'test.xml'

tree = parse(xml_file)
root = tree.getroot()

for object in root.iter('object'):
    xmin = int(object.find('bndbox').findtext('xmin'))
    ymin = int(object.find('bndbox').findtext('ymin'))
    xmax = int(object.find('bndbox').findtext('xmax'))
    ymax = int(object.find('bndbox').findtext('ymax'))

    print(xmin, ymin, xmax, ymax)

parse

xml 파일 파싱에는 xml.etree.ElementTree 모듈의 parse 함수가 사용된다. parse 함수를 통해 xml 파일의 구조를 트리 형태로 만들고, 각 노드들을 방문하며 필요한 정보들을 얻어낸다.

getroot() 함수는 트리의 root를 찾는 함수이다. 트리에 대한 탐색은 루트로부터 시작되기 때문에 트리의 시작점을 지정해준다.

iter

주어진 예시와 같이 object라는 이름의 태그는 여러 개가 존재하기 때문에 이를 반복적으로 호출해야 할 필요가 있다. 그 기능을 해주는 함수가 바로 iter 함수이다. iter 함수는 주어진 매개변수에 해당하는 태그가 존재하지 않을 때까지 반복해서 해당 태그를 조회한다.

이 기능을 수행하고 나서는 xml 파일 내에 있는 object 태그가 object라는 변수에 할당된다. 예시 xml 파일에서 첫 번째로 반복문이 수행되면 (57, 94, 40, 76) 값의 bndbox를 갖고 있는 object 태그가 할당되고, 그 다음 호출에는 (45, 86, 104, 134) 값의 bndbox를 갖고 있는 object 태그가 할당된다. 그러고 나면 더 조회할 object 태그가 없기 때문에 반복문이 종료된다.

find, findtext

태그가 할당되고 나면 이제 태그로부터 필요한 정보들을 찾아서 파싱을 진행해야 한다. 이 때, 필요한 정보들을 찾을 수 있도록 해주는 함수가 find 함수이다. find 함수의 매개변수로는 찾고자 하는 태그의 이름이 들어간다. 이 코드에서 내가 찾고 싶은 정보는 bndbox이므로 bndbox를 매개변수로 넣어주면 이에 해당하는 태그를 찾아서 할당해준다.

그렇게 되면 object.find('bndbox')에는 bndbox 태그의 내용이 할당된다. 이제 내가 찾고 싶은 정보의 마지막에 도착했다. 내가 알고싶은 정보는 bndbox 태그 내에 있는 xmin, ymin, xmax, ymax에 대한 정보이다. 각 태그 내에 text의 형태로 저장이 되어 있는 데이터를 조회할 때는 findtext 함수를 사용하면 된다. find 함수와 마찬가지로 매개변수로 주어지는 태그에 해당하는 정보를 할당하는데, 이 때, text의 형태로 저장하게 된다.

 

최종적으로 xml 파일에 대해서 파싱을 진행한 결과는 xml 파일 내에 존재하는 2개 객체의 박스 정보로 '57, 94, 40, 76'과 '45, 86, 104, 134'가 출력되게 된다.

정리

json 파일의 경우는 python에서 dictionary처럼 접근해서 파싱을 진행하면 되기 때문에 상대적으로 간단하지만 xml 파일의 경우는 json 파일과 매우 다르다. xml 파일 자체를 트리 구조로 표현한 뒤, 각 태그를 트리를 구성하고 있는 노드로 접근하는 방식으로 파싱을 진행하면 된다. 이에 대한 더 자세한 내용은 공식문서를 참고하면 된다.

 

xml.etree.ElementTree — ElementTree XML API — Python 3.10.0 문서

소스 코드: Lib/xml/etree/ElementTree.py xml.etree.ElementTree 모듈은 XML 데이터를 구문 분석하고 만들기 위한 단순하고 효율적인 API를 구현합니다. 버전 3.3에서 변경: 이 모듈은 가능할 때마다 빠른 구현을

docs.python.org

 

반응형