質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
XML

XMLは仕様の1つで、マークアップ言語群を構築するために使われています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

XMLパーサ

XML文書のテキストデータだけを抜き出して、アプリケーションソフトが利用しやすい形式に変換させるソフトウェアをXMLパーサと呼びます。

Q&A

解決済

2回答

2101閲覧

xmlファイルへの要素書き込みができない

Saba3

総合スコア15

XML

XMLは仕様の1つで、マークアップ言語群を構築するために使われています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

XMLパーサ

XML文書のテキストデータだけを抜き出して、アプリケーションソフトが利用しやすい形式に変換させるソフトウェアをXMLパーサと呼びます。

0グッド

0クリップ

投稿2021/07/21 02:13

前提・実現したいこと

yolo形式の座標情報をSSDなどのxmlファイル形式へ変更したい。

発生している問題・エラーメッセージ

1つの物体に対しては座標変換して情報を書き込むことはできました。
しかし、以下の問題が発生しました。

①2つ以上の物体になると座標情報が上書きされてしまいます。
なので、追加の<object>要素を追加しようと試みました。
しかし、追加ができませんでした。

②要素を追加した後のxmlファイルに座標情報を入力したいです。しかし、同じobject要素に書きこまれてしまいました。

以下に作成したいxmlの一例を示します。

<annotation> <folder>JPEGImages</folder> <filename>000-(2).jpg</filename> <path>C:\Users\rh731\pytorch_advanced-master\2_objectdetection\original_data\JPEGImages\000-(2).jpg</path> <source> <database>Unknown</database> </source> <size> <width>4000</width> <height>1824</height> <depth>3</depth> </size> <segmented>0</segmented> <object> <name>plastic</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>481</xmin> <ymin>319</ymin> <xmax>810</xmax> <ymax>617</ymax> </bndbox> </object> <object> <name>plastic</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>652</xmin> <ymin>259</ymin> <xmax>978</xmax> <ymax>538</ymax> </bndbox> </object> </annotation>

該当のソースコード

元々一つの物体のobject要素はある前提としています。
そのxmlファイルに新たに要素を追加する関数をadd_objectとしています。
以下にxmlファイルに新たなを追加する関数を要素objectを追加する関数を示します。

python

1def add_object(name): 2 3 os.chdir('./converted_to_xml') 4 xml_name = name.replace('txt', 'xml') 5 6 # xmlに新たな物体情報を追加する 7 tree = ET.parse(xml_name) 8 root = tree.getroot() 9 10 for annotation in root.findall('object'): 11 object = ET.SubElement(annotation, 'object') 12 name = ET.SubElement(object, 'name') 13 pose = ET.SubElement(object, 'pose') 14 truncated = ET.SubElement(object, 'truncated') 15 difficult = ET.SubElement(object, 'difficult') 16 bndbox = ET.SubElement(object, 'bndbox') 17 xmin = ET.SubElement(bndbox, 'xmin') 18 ymin = ET.SubElement(bndbox, 'ymin') 19 xmax = ET.SubElement(bndbox, 'xmax') 20 ymax = ET.SubElement(bndbox, 'ymax') 21 22 tree = ET.ElementTree(root) 23 fl = xml_name 24 tree.write(fl) 25 26 os.chdir('../') 27 28 return fl 29

試したこと

・add_object関数内で座標情報を入力しようとしましたが、初めのobject要素に値が上書きされてしまいました。
・関数をコピーして別のpythonファイルで実行しました。しかし、同様の問題が発生しました。

補足情報(FW/ツールのバージョンなど)

・yolo形式からSSD形式へ座標情報を変換する関数はできました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自己解決

以下のように全体のコードを変えると問題が解決しました。

python:yolo_to_xml.py

1import xml.etree.ElementTree as ET 2import xml.dom.minidom as md 3import os 4import numpy as np 5 6from PIL import Image 7 8def create_xml(file_name, object_number): 9 10 #保存フォルダへ移動 11 os.chdir('./converted_to_xml') 12 13 #xmlファイルを生成する 14 annotation = ET.Element('annotation') 15 folder = ET.SubElement(annotation, 'folder') 16 filename = ET.SubElement(annotation, 'filename') 17 path = ET.SubElement(annotation, 'path') 18 source = ET.SubElement(annotation, 'source') 19 database = ET.SubElement(source, 'database') 20 size = ET.SubElement(annotation, 'size') 21 width = ET.SubElement(size, 'width') 22 height = ET.SubElement(size, 'height') 23 depth = ET.SubElement(size, 'depth') 24 segment = ET.SubElement(annotation, 'segment') 25 26 for i in range(object_number): 27 object = ET.SubElement(annotation, 'object') 28 name = ET.SubElement(object, 'name') 29 pose = ET.SubElement(object, 'pose') 30 truncated = ET.SubElement(object, 'truncated') 31 difficult = ET.SubElement(object, 'difficult') 32 bndbox = ET.SubElement(object, 'bndbox') 33 xmin = ET.SubElement(bndbox, 'xmin') 34 ymin = ET.SubElement(bndbox, 'ymin') 35 xmax = ET.SubElement(bndbox, 'xmax') 36 ymax = ET.SubElement(bndbox, 'ymax') 37 38 tree = ET.ElementTree(annotation) 39 fl = file_name 40 tree.write(fl) 41 42 os.chdir('../') 43 44 return fl 45 46def add_object(name): 47 48 os.chdir('./converted_to_xml') 49 xml_name = name.replace('txt', 'xml') 50 51 # xmlに新たな物体情報を追加する 52 tree = ET.parse(xml_name) 53 root = tree.getroot() 54 55 for annotation in root.findall('object'): 56 object = ET.SubElement(annotation, 'object') 57 name = ET.SubElement(object, 'name') 58 pose = ET.SubElement(object, 'pose') 59 truncated = ET.SubElement(object, 'truncated') 60 difficult = ET.SubElement(object, 'difficult') 61 bndbox = ET.SubElement(object, 'bndbox') 62 xmin = ET.SubElement(bndbox, 'xmin') 63 ymin = ET.SubElement(bndbox, 'ymin') 64 xmax = ET.SubElement(bndbox, 'xmax') 65 ymax = ET.SubElement(bndbox, 'ymax') 66 67 tree = ET.ElementTree(root) 68 fl = xml_name 69 tree.write(fl) 70 71 os.chdir('../../') 72 73 return fl 74 75def read_txt(name): 76 txt_path = os.path.join('test/', name) 77 78 with open(txt_path) as f: 79 l_strip = [s.strip() for s in f.readlines()] 80 81 #画像中の物体の個数を出力 82 #print(len(l_strip)) 83 #物体が2つ以上ならxmlファイルを書き換える 84 if len(l_strip) != '1': 85 xml_name = name.replace('.txt', '.xml') 86 create_xml(xml_name, len(l_strip)) 87 88 #txtデータ読み込み 89 for i, j in enumerate(l_strip): 90 91 #データを分割する 92 # splitは番号、x座標、y座標、Width、Heightの順に格納されているリスト型 93 split = j.split() 94 95 #辞書型に代入する 96 data = {'number':0,'x_coordinate':0, 'y_coordinate':0, 'width':0, 'height':0} 97 data['number'] = split[0] 98 data['x_coordinate'] = split[1] 99 data['y_coordinate'] = split[2] 100 data['width'] = split[3] 101 data['height'] = split[4] 102 103 #作成したxmlへ代入 104 xml_name = name.replace('.txt', '.xml') 105 yolo_to_xml(data, xml_name, name, i) 106 107 return name 108 109def yolo_to_xml(data, xml_name, name, object_counter): 110 111 #保存用の辞書型生成 112 coordinated_data = {'x_min': 0, 'x_max': 0, 'y_min': 0, 'y_max': 0} 113 114 #画像データの取得 115 os.chdir('../') 116 os.chdir('./images/test') 117 im_name = name.replace('.txt', '.jpg') 118 im = np.array(Image.open(im_name)) 119 120 #object_nameの変更(任意の物体名を追加可能) 121 if data['number'] == '0': 122 data['number'] = 'Can' 123 elif data['number'] == '1': 124 data['number'] = 'Person' 125 elif data['number'] == '2': 126 data['number'] = 'Box' 127 else: 128 data['number'] = 'Bottle' 129 130 #座標の変更(非正規化) 131 data['x_coordinate'] = int(float(data['x_coordinate']) * im.shape[1]) 132 data['y_coordinate'] = int(float(data['y_coordinate']) * im.shape[0]) 133 data['width'] = int(float(data['width']) * im.shape[1]) 134 data['height'] = int(float(data['height']) * im.shape[0]) 135 coordinated_data['x_min'] = int(data['x_coordinate'] - (data['width']/2)) 136 coordinated_data['x_max'] = int(data['x_coordinate'] + (data['width']/2)) 137 coordinated_data['y_min'] = int(data['y_coordinate'] - (data['height']/2)) 138 coordinated_data['y_max'] = int(data['y_coordinate'] + (data['height']/2)) 139 140 #xmlへの書き込み開始 141 os.chdir('../../') 142 os.chdir('./labels/converted_to_xml') 143 144 #xmlにデータを書き込んでいる 145 tree = ET.parse(xml_name) 146 root = tree.getroot() 147 148 root.findall('filename')[0].text = im_name 149 root.findall('./*/width')[0].text = str(im.shape[1]) 150 root.findall('./*/height')[0].text = str(im.shape[0]) 151 root.findall('./*/depth')[0].text = str(im.shape[2]) 152 root.findall('./*/name')[object_counter].text = data['number'] 153 root.findall('./*/*/xmin')[object_counter].text = str(coordinated_data['x_min']) 154 root.findall('./*/*/ymin')[object_counter].text = str(coordinated_data['y_min']) 155 root.findall('./*/*/xmax')[object_counter].text = str(coordinated_data['x_max']) 156 root.findall('./*/*/ymax')[object_counter].text = str(coordinated_data['y_max']) 157 158 159 tree.write(xml_name) 160 print(xml_name) 161 os.chdir('../') 162 163 return data 164 165 166if __name__ == "__main__": 167 #txtデータの名前のリストを保存する 168 path = os.listdir('./test') 169 #xmlファイルを生成してtxtファイルの内容を書き込む 170 171 # xmlファイル生成 172 for i, name in enumerate(path): 173 name = name.replace('.txt', '.xml') 174 create_xml(name, 1) 175 read_txt(path[i]) 176

投稿2021/07/21 08:19

Saba3

総合スコア15

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

int32_t

2021/07/21 08:28

> for annotation in root.findall('object'): > object = ET.SubElement(annotation, 'object') このコードだと既存の<object>の中に<object>が入りますが、それでいいのですか?
guest

0

python

1 for annotation in root.findall('object'):

'object'ではなく'annotation'では。

もし <annotation>がルート要素であるなら、そもそも forfindall() は不要で、object = ET.SubElement(root, 'object') とすべきかと思います。

投稿2021/07/21 02:22

編集2021/07/21 04:52
int32_t

総合スコア21695

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Saba3

2021/07/21 03:38

annotatiaonに変更したのですが、追加のobjectが生成されませんでした。
Saba3

2021/07/21 03:38

失礼いたしました。annotationでした。
Saba3

2021/07/21 08:19

ご指摘ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問