###概要
物体検出アルゴリズムSSDを使い、自分で用意した画像で物体検出を行うことを考えています。
物体検出アルゴリズム(SSD:Single Shot MultBox Detecter)を学習させてみるの記事を参考に、SSDプログラムや教師データを自分の環境にクローンし、訓練用プログラムやテスト用プログラムは両方とも私の環境下で動作することを確認しました。
次に、自分で用意した教師データで物体検出をしようと物体認識用データセット作成支援ツール(Faster R-CNNとかSSDとか)の記事を参考に、用意した画像ファイルから画像のどこに何が写っているのかの情報が格納されているアノテーションデータ(xmlファイル)を作成しました。
そうして作成したxmlファイル群はSSDに渡す際に1つのpklファイルにまとめる必要があるので、ssd_kerasのPASCAL_VOC/get_data_from_XML.pyを実行したところ、以下のようなエラーが出てきてしまい、pklファイルが作成できませんでした。
自分で用意した画像ファイルとそれらから作成したxmlファイルへのパスはそれぞれ、ssd_keras/Image_data/JPEGImagesとssd_keras/Image_data/Annotationsです。
Traceback (most recent call last): File "/Users/user/research/ssd_keras/PASCAL_VOC/get_data_from_XML.py", line 108, in <module> data = XML_preprocessor('Image_data/Annotations/').data File "/Users/user/research/ssd_keras/PASCAL_VOC/get_data_from_XML.py", line 13, in __init__ self._preprocess_XML() File "/Users/user/research/ssd_keras/PASCAL_VOC/get_data_from_XML.py", line 18, in _preprocess_XML tree = ElementTree.parse(self.path_prefix + filename) File "/Users/user/opt/anaconda3/lib/python3.8/xml/etree/ElementTree.py", line 1202, in parse tree.parse(source, parser) File "/Users/user/opt/anaconda3/lib/python3.8/xml/etree/ElementTree.py", line 595, in parse self._root = parser._parse_whole(source) xml.etree.ElementTree.ParseError: not well-formed (invalid token): line 1, column 0
また、このエラーを吐いたget_data_from_XML.pyはこちらです。ちなみに自分で変えた部分は、self.num_classesに代入する数と、検出させたいクラスの指定、最後の2行の3点のみです。
Python
1### get_data_from_XML.py 2 3import numpy as np 4import os 5from xml.etree import ElementTree 6 7class XML_preprocessor(object): 8 9 def __init__(self, data_path): 10 self.path_prefix = data_path 11 self.num_classes = 6 12 self.data = dict() 13 self._preprocess_XML() 14 15 def _preprocess_XML(self): 16 filenames = os.listdir(self.path_prefix) 17 for filename in filenames: 18 tree = ElementTree.parse(self.path_prefix + filename) 19 root = tree.getroot() 20 bounding_boxes = [] 21 one_hot_classes = [] 22 size_tree = root.find('size') 23 width = float(size_tree.find('width').text) 24 height = float(size_tree.find('height').text) 25 for object_tree in root.findall('object'): 26 for bounding_box in object_tree.iter('bndbox'): 27 xmin = float(bounding_box.find('xmin').text)/width 28 ymin = float(bounding_box.find('ymin').text)/height 29 xmax = float(bounding_box.find('xmax').text)/width 30 ymax = float(bounding_box.find('ymax').text)/height 31 bounding_box = [xmin,ymin,xmax,ymax] 32 bounding_boxes.append(bounding_box) 33 class_name = object_tree.find('name').text 34 one_hot_class = self._to_one_hot(class_name) 35 one_hot_classes.append(one_hot_class) 36 image_name = root.find('filename').text 37 bounding_boxes = np.asarray(bounding_boxes) 38 one_hot_classes = np.asarray(one_hot_classes) 39 image_data = np.hstack((bounding_boxes, one_hot_classes)) 40 self.data[image_name] = image_data 41 42 def _to_one_hot(self,name): 43 one_hot_vector = [0] * self.num_classes 44 if name == 'logo': 45 one_hot_vector[0] = 1 46 elif name == 'header': 47 one_hot_vector[1] = 1 48 elif name == 'navi_botton1': 49 one_hot_vector[2] = 1 50 elif name == 'navi_botton2': 51 one_hot_vector[3] = 1 52 elif name == 'eyecatch': 53 one_hot_vector[4] = 1 54 elif name == 'phone_icon': 55 one_hot_vector[5] = 1 56 else: 57 print('unknown label: %s' %name) 58 59 return one_hot_vector 60 61## example on how to use it 62import pickle 63 64data = XML_preprocessor('Image_data/Annotations/').data 65pickle.dump(data,open('Image_data.pkl','wb')) 66
教師データをThe PASCAL Visual Object Classes Homepage(※Chromeでは見れないようです。)からダウンロードしてget_data_from_XML.pyを実行させれば問題なくpklファイルが生成されるようです。ですので上記の方法で作成した自作xmlファイルを読み込む際に問題が起きていると思われます。
この方法で自作xmlファイルからpklファイルを生成する際には何が問題となっているのでしょうか。
ここで行き詰まってしまっているのでご教授をお願い致します。
(追記)
自作教師データのテストということで、少ないですが10個ほどの自作xmlファイルを作成しており、それらをpklファイルにまとめ、SSDに渡そうと考えております。その自作xmlファイルの内1つの内容を下記に共有いたします。
xml
1<?xml version="1.0" encoding="UTF-8"?> 2<annotation> 3 <folder>XXX</folder> 4 <filename>2021-09-08 15.13.29.jpg</filename> 5 <source> 6 <database>XXX</database> 7 <annotation>XXX</annotation> 8 <image>XXX</image> 9 <flickrid>XXX</flickrid> 10 </source> 11 <owner> 12 <flickrid>XXX</flickrid> 13 <name>?</name> 14 </owner> 15 <size> 16 <width>2880</width> 17 <height>1637</height> 18 <depth>3</depth> 19 </size> 20 <segmented>0</segmented> 21 <object> 22 <name>logo</name> 23 <pose>Unspecified</pose> 24 <truncated>0</truncated> 25 <difficult>1</difficult> 26 <bndbox> 27 <xmin>340</xmin> 28 <ymin>60</ymin> 29 <xmax>720</xmax> 30 <ymax>140</ymax> 31 </bndbox> 32 </object> 33 <object> 34 <name>header</name> 35 <pose>Unspecified</pose> 36 <truncated>0</truncated> 37 <difficult>1</difficult> 38 <bndbox> 39 <xmin>0</xmin> 40 <ymin>0</ymin> 41 <xmax>2880</xmax> 42 <ymax>180</ymax> 43 </bndbox> 44 </object> 45 <object> 46 <name>navi_botton1</name> 47 <pose>Unspecified</pose> 48 <truncated>0</truncated> 49 <difficult>1</difficult> 50 <bndbox> 51 <xmin>1340</xmin> 52 <ymin>60</ymin> 53 <xmax>1500</xmax> 54 <ymax>120</ymax> 55 </bndbox> 56 </object> 57 <object> 58 <name>navi_botton1</name> 59 <pose>Unspecified</pose> 60 <truncated>0</truncated> 61 <difficult>1</difficult> 62 <bndbox> 63 <xmin>1560</xmin> 64 <ymin>60</ymin> 65 <xmax>1700</xmax> 66 <ymax>120</ymax> 67 </bndbox> 68 </object> 69 <object> 70 <name>navi_botton1</name> 71 <pose>Unspecified</pose> 72 <truncated>0</truncated> 73 <difficult>1</difficult> 74 <bndbox> 75 <xmin>1740</xmin> 76 <ymin>60</ymin> 77 <xmax>1880</xmax> 78 <ymax>120</ymax> 79 </bndbox> 80 </object> 81 <object> 82 <name>navi_botton1</name> 83 <pose>Unspecified</pose> 84 <truncated>0</truncated> 85 <difficult>1</difficult> 86 <bndbox> 87 <xmin>1940</xmin> 88 <ymin>60</ymin> 89 <xmax>2140</xmax> 90 <ymax>120</ymax> 91 </bndbox> 92 </object> 93 <object> 94 <name>navi_botton1</name> 95 <pose>Unspecified</pose> 96 <truncated>0</truncated> 97 <difficult>1</difficult> 98 <bndbox> 99 <xmin>2180</xmin> 100 <ymin>60</ymin> 101 <xmax>2280</xmax> 102 <ymax>120</ymax> 103 </bndbox> 104 </object> 105 <object> 106 <name>navi_botton1</name> 107 <pose>Unspecified</pose> 108 <truncated>0</truncated> 109 <difficult>1</difficult> 110 <bndbox> 111 <xmin>2340</xmin> 112 <ymin>60</ymin> 113 <xmax>2480</xmax> 114 <ymax>120</ymax> 115 </bndbox> 116 </object> 117 <object> 118 <name>navi_botton2</name> 119 <pose>Unspecified</pose> 120 <truncated>0</truncated> 121 <difficult>1</difficult> 122 <bndbox> 123 <xmin>2660</xmin> 124 <ymin>0</ymin> 125 <xmax>2860</xmax> 126 <ymax>180</ymax> 127 </bndbox> 128 </object> 129 <object> 130 <name>eyecatch</name> 131 <pose>Unspecified</pose> 132 <truncated>0</truncated> 133 <difficult>1</difficult> 134 <bndbox> 135 <xmin>0</xmin> 136 <ymin>180</ymin> 137 <xmax>2880</xmax> 138 <ymax>740</ymax> 139 </bndbox> 140 </object> 141</annotation> 142
###やったこと
エラー文で検索をかけて調べてみて、同じような質問(Python xml.etree.ElementTree.ParseEroorについて)をされている方がいらっしゃいました。しかし、その方とは些か状況が違うようで、ssd_kerasに".DS_Store"ファイルはありませんでしたし、VOCのデータセットからでは問題なくpklファイルが生成できています。(私がダウンロードしたのはVOC2007ではなくVOC2012でしたが。)
また、自前の画像ファイルと作成したxmlファイル名のそれぞれに半角スペースが入っていたので消してみましたが状況は変わりませんでした。
他にも、XML中に実体参照していない & がある場合、ElementTree.parse で失敗するが、文字列置換で強引に修復のような記事も目を通しましたが正直よく分かりませんでした。
(追記)
xmlファイルのエラーチェックができるサイトというものをご共有いただきましたので上で共有させて頂いたxmlファイルを10個全てのエラーチェックをしてみたところ、いずれも「No errors were found」と出力されたのでxmlファイル自体には問題なさそうです。
また、AnacondaでPython3系での環境下のSpyderでもget_data_from_XML.pyを実行してみましたが、以下に示すように同じようなエラーが出てくるようです。
File "<string>", line unknown ParseError: not well-formed (invalid token): line 1, column 0
###環境
PC:MacOS BigSur11.6
プロセッサ:3.1 GHz デュアルコアIntel Core i5
また、SSDのネットワークの方の実行環境はAnacondaのSpyderやJupyter Notebookを使用していますが、get_data_from_XML.pyの実行時はVScodeを使用しています。VScodeで使用しているPythonのバージョンは2.7.16でした。
回答1件
あなたの回答
tips
プレビュー