XML文件讀取與VOC數據集使用(pascal_voc_parser.py)

對源碼進行逐句解析,盡量說的很細緻。

歡迎各位看官捧場!

源碼地址:keras版本faster rcnn

想了解這篇文章的前後內容出門左拐:faster rcnn代碼理解-keras(目錄)

視頻目錄:深度學習一行一行敲faster rcnn-keras版(視頻目錄)

本章代碼流程

這章是關於--pascal_voc_parser.py的:get_data()函數

函數輸入:

def get_data(input_path)n

input_path:只需要給定到VOC所在的文件夾,不需要知道給定到具體的版本

如:input_path = F:/study_files/faster_rcnn/training_data/VOCdevkit

函數輸出:

return all_imgs, classes_count, class_mappingn

  1. all_imgs:其是一個list,每一條信息是以字典形式存儲包含了一張圖片的所有信息。

字典名稱包含:圖片的高度,寬度,路徑,和所處訓練集和框。其中bboxes: 其是一個list,每一條信息是以字典形式存儲包含了一個box的所有信息。有難度,類別,上下兩點的坐標。下面是一個示列:

[{height: 500, imageset: trainval,width: 486, filepath:

F:/study_files/faster_rcnn/training_data/VOCdevkitVOC2012JPEGImages2007_000027.jpg,

bboxes: [{x2: 349, y1: 101, class: person, y2: 351, difficult:False, x1: 174}]}]

2. classes_count:是一個字典,其存儲類別和其對應的總個數

{person: 2, horse: 1}

3. class_mapping:是一個字典,其對應每一個類別對應的編號

{person: 0, horse: 1}

【註:①該文件處理VOC2012更好,②它是遍歷Annotations文件夾得到所有信息】


代碼分析:

-------------------------------①------------------------------

all_imgs = []nn classes_count = {}nn class_mapping = {}nn visualise = Falsen # not using vOC2007, 2012 onlyn # data_paths = [os.path.join(input_path, s) for s in [VOC2007, VOC2012]]n data_paths = [os.path.join(input_path, s) for s in [VOC2012]]nn print(Parsing annotation files)n

設置一些變數存儲信息【註:data_paths = [os.path.join(input_path, s) for s in [VOC2012]]是遍歷列表中給的VOC訓練集】

for data_path in data_paths:nn annot_path = os.path.join(data_path, Annotations)n imgs_path = os.path.join(data_path, JPEGImages)n imgsets_path_trainval = os.path.join(data_path, ImageSets, Main, trainval.txt)n imgsets_path_test = os.path.join(data_path, ImageSets, Main, test.txt)nn trainval_files = []n test_files = []n

遍歷data_paths中給的VOC訓練集,並得到相應訓練集下的文件路徑【os.path.join(data_path, ImageSets, Main, test.txt):這是一個很重要的函數,其是將給定的路徑與名稱結合得到需要的路徑】

try:n with open(imgsets_path_trainval) as f:n for line in f:n trainval_files.append(line.strip() + .jpg)n except Exception as e:n print(e)nn try:n with open(imgsets_path_test) as f:n for line in f:n test_files.append(line.strip() + .jpg)n except Exception as e:n if data_path[-7:] == VOC2012:n # this is expected, most pascal voc distibutions dont have the test.txt filen passn else:n print(e)n

得到訓練與測試集圖片文件的名稱,這是為以後判斷圖片是屬於哪個集而準備的。

try:n 事件Anexcept Exception as e:n if 事件B:n passn else:n print(e)n

  1. 異常處理用try--except函數,如果事件A出現異常(比如要打開的文件不存在)則去執行except內的函數,而不是函數報錯
  2. if--else函數,如果data_path[-7:] == VOC2012的最後7為VOC2012則pass掉不報任何異常,否則列印出異常

with open(imgsets_path_trainval) as f:n

  1. open打開文件,默認為只讀模式
  2. 得到的是一個 _io.TextIOWrapper類

f = open(F:/study_files/faster_rcnn/training_data/VOCdevkit/VOC2012/ImageSets/Main/trainval.txt)nprint(f)nprint(type(f))n輸出:n<_io.TextIOWrapper name=F:/study_files/faster_rcnn/training_data/VOCdevkit/VOC2012/ImageSets/Main/trainval.txt mode=r encoding=cp936>n<class _io.TextIOWrapper>n

【註:python想得到數據的類型用type(),得到形狀用shape】

for line in f:n trainval_files.append(line.strip() + .jpg)n

  1. 按行取出文件對象類的文字
  2. line.strip()去除空格【python strip()函數 介紹】
  3. append() 方法用於在列表末尾添加新的對象【Python List append()方法】

-------------------------------②------------------------------

annots = [os.path.join(annot_path, s) for s in os.listdir(annot_path)]n

得到annot_path所有xml文件【註:os.listdir()列出該路勁下的所有文件的名稱】

idx = 0n for annot in annots:n try:n idx += 1nn et = ET.parse(annot)n element = et.getroot()nn element_objs = element.findall(object)n element_filename = element.find(filename).textn element_width = int(element.find(size).find(width).text)n element_height = int(element.find(size).find(height).text)n

開始遍歷xlm文件

  1. ET.parse(annot):讀取xml文件
  2. element = et.getroot():得到xml的根,所有根包含的屬性都可以從中得到
  3. 接下來就是得到所需要的信息【Python標準庫之xml.etree.ElementTree】

【註:需要引入專門的包來處理xml文件】

import xml.etree.ElementTree as ETn

if len(element_objs) > 0:n annotation_data = {filepath: os.path.join(imgs_path, element_filename), width: element_width,n height: element_height, bboxes: []}nn if element_filename in trainval_files:n annotation_data[imageset] = trainvaln elif element_filename in test_files:n annotation_data[imageset] = testn else:n annotation_data[imageset] = trainvaln

  1. 首先判讀要存在對象
  2. annotation_data存儲這張圖片的基本信息,路徑、寬度、高度、框、和所屬訓練集

if element_filename in trainval_files:n

這個是一個非常好的函數,判斷一個名稱是否在一個列表裡

for element_obj in element_objs:n class_name = element_obj.find(name).textn if class_name not in classes_count:n classes_count[class_name] = 1n else:n classes_count[class_name] += 1nn if class_name not in class_mapping:n class_mapping[class_name] = len(class_mapping)n

遍歷該圖片中的所有object對象

  1. find方法是十分重要的,其主要是得到xml相應標籤
  2. text是一個重要的屬性,其可以得到標籤對應的值。需要注意的是它是以文字類型存儲的

obj_bbox = element_obj.find(bndbox)n x1 = int(round(float(obj_bbox.find(xmin).text)))n y1 = int(round(float(obj_bbox.find(ymin).text)))n x2 = int(round(float(obj_bbox.find(xmax).text)))n y2 = int(round(float(obj_bbox.find(ymax).text)))n difficulty = int(element_obj.find(difficult).text) == 1n annotation_data[bboxes].append({class:class_name,x1:x1,x2:x2,y1:y1,y2:y2,difficult:difficulty})nall_imgs.append(annotation_data)n

得到邊框對象的兩點坐標,在向該張圖片信息annotation_data[bboxes]中添加bboxes信息,再將這條圖片信息


-------------------------------③------------------------------

if visualise:n img = cv2.imread(annotation_data[filepath])n for bbox in annotation_data[bboxes]:n cv2.rectangle(img, (bbox[x1], bbox[y1]), (bbox[x2], bbox[y2]), (0, 0, 255))n cv2.imshow(img, img)n cv2.waitKey(0)nnexcept Exception as e:n print(e)n continuen

是否需要讀一張圖片顯示一張圖片

  1. cv2.rectangle():四個輸入分別代表圖片、左上坐標、右下坐標、顏色
  2. cv2.imshow(img, img)、cv2.waitKey(0):顯示圖片這兩函數要一起用
  3. 異常處理是:列印出異常,結束這次循環開始下一次循環

附上一個xml文件示列:

<annotation>nt<folder>VOC2012</folder>nt<filename>2007_000033.jpg</filename>nt<source>ntt<database>The VOC2007 Database</database>ntt<annotation>PASCAL VOC2007</annotation>ntt<image>flickr</image>nt</source>nt<size>ntt<width>500</width>ntt<height>366</height>ntt<depth>3</depth>nt</size>nt<segmented>1</segmented>nt<object>ntt<name>aeroplane</name>ntt<pose>Unspecified</pose>ntt<truncated>0</truncated>ntt<difficult>0</difficult>ntt<bndbox>nttt<xmin>9</xmin>nttt<ymin>107</ymin>nttt<xmax>499</xmax>nttt<ymax>263</ymax>ntt</bndbox>nt</object>nt<object>ntt<name>aeroplane</name>ntt<pose>Left</pose>ntt<truncated>0</truncated>ntt<difficult>0</difficult>ntt<bndbox>nttt<xmin>421</xmin>nttt<ymin>200</ymin>nttt<xmax>482</xmax>nttt<ymax>226</ymax>ntt</bndbox>nt</object>nt<object>ntt<name>aeroplane</name>ntt<pose>Left</pose>ntt<truncated>1</truncated>ntt<difficult>0</difficult>ntt<bndbox>nttt<xmin>325</xmin>nttt<ymin>188</ymin>nttt<xmax>411</xmax>nttt<ymax>223</ymax>ntt</bndbox>nt</object>n</annotation>n

該函數使用示列:

from keras_frcnn.pascal_voc_parser import get_datanall_imgs, classes_count, class_mapping = get_data(F:/study_files/faster_rcnn/training_data/VOCdevkit)n其中visualise = Truen

歡迎關注公眾號:huangxiaobai880

https://www.zhihu.com/video/918791450202890240
推薦閱讀:

為什麼XML這麼笨重的數據結構仍在廣泛應用?
C# 讀取XML

TAG:XML | 深度学习DeepLearning | 源码阅读 |