本文爲博主原創,轉載請註明出處:https://blog.csdn.net/hitzijiyingcai/article/details/81636455
一、前言
在利用諸如Faster R-CNN等深度學習網絡進行目標檢測的時候一定需要訓練自己的數據集,一般有兩種方法:
按照VOC2007的格式修改自己的數據格式
根據自己的數據格式修改源碼
一般推薦第一種方法,因爲第一種方法比較簡單而且不容易出錯,在製作爲VOC2007格式的數據集之前,這裏可以下載原始VOC2007數據集:VOC2007數據集,來看看這個數據集到底是什麼樣的:
解壓VOC2007數據集後可以看到VOC2007文件夾下有以下5個文件夾:
Annotations文件夾
該文件下存放的是xml格式的標籤文件,每個xml文件都對應於JPEGImages文件夾的一張圖片。
JPEGImages文件夾
改文件夾下存放的是數據集圖片,包括訓練和測試圖片。
ImageSets文件夾
該文件夾下存放了三個文件,分別是Layout、Main、Segmentation。在這裏我們只用存放圖像數據的Main文件,其他兩個暫且不管。
SegmentationClass文件和SegmentationObject文件。
這兩個文件都是與圖像分割相關,跟本文無關,暫且不管。
1、Annotations
Annotations文件夾中存放的是xml格式的標籤文件,每一個xml文件都對應於JPEGImages文件夾中的一張圖片。xml文件的解析如下:
<annotation>
<folder>VOC2007</folder>
<filename>2007_000392.jpg</filename> //文件名
<source> //圖像來源(不重要)
<database>The VOC2007 Database</database>
<annotation>PASCAL VOC2007</annotation>
<image>flickr</image>
</source>
<size> //圖像尺寸(長寬以及通道數)
<width>500</width>
<height>332</height>
<depth>3</depth>
</size>
<segmented>1</segmented> //是否用於分割(在圖像物體識別中01無所謂)
<object> //檢測到的物體
<name>horse</name> //物體類別
<pose>Right</pose> //拍攝角度
<truncated>0</truncated> //是否被截斷(0表示完整)
<difficult>0</difficult> //目標是否難以識別(0表示容易識別)
<bndbox> //bounding-box(包含左下角和右上角xy座標)
<xmin>100</xmin>
<ymin>96</ymin>
<xmax>355</xmax>
<ymax>324</ymax>
</bndbox>
</object>
<object> //檢測到多個物體
<name>person</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>198</xmin>
<ymin>58</ymin>
<xmax>286</xmax>
<ymax>197</ymax>
</bndbox>
</object>
</annotation>
2、JPEGImages
JPEGImages 內部存放了PASCAL VOC所提供的所有的圖片信息,包括了訓練圖片和測試圖片
這些圖像的像素尺寸大小不一,但是橫向圖的尺寸大約在500*375左右,縱向圖的尺寸大約在375*500左右,基本不會偏差超過100。(在之後的訓練中,第一步就是將這些圖片都resize到300*300或是500*500,所有原始圖片不能離這個標準過遠。
3、 ImageSets
ImageSets存放的是每一種類型的challenge對應的圖像數據。
Layout下存放的是具有人體部位的數據(人的head、hand、feet等等,這也是VOC challenge的一部分)
Main下存放的是圖像物體識別的數據,總共分爲20類。
Segmentation下存放的是可用於分割的數據。
在這裏主要考察Main文件夾。
Main文件夾下包含了每個分類的train.txt、val.txt和trainval.txt。
這些txt中的內容都差不多如下:
000005 -1
000007 -1
000009 1
000016 -1
000019 -1
前面的表示圖像的name,後面的1代表正樣本,-1代表負樣本。
_train中存放的是訓練使用的數據
_val中存放的是驗證結果使用的數據
_trainval將上面兩個進行了合併
需要保證的是train和val兩者沒有交集,也就是訓練數據和驗證數據不能有重複,在選取訓練數據的時候 ,也應該是隨機產生的。
二、製作PASCAL VOC形式的數據集
製作自己的VOC2007格式數據集其實不需要上述那麼多內容,我們只要做三個部分即可:Annotations文件夾、JPEGImages文件夾、ImageSets文件夾下的Main文件。
step1:JPEGImages
參照原始VOC2007數據集的文件層次創建上述四個文件夾,也就是創建一個VOCdevkit文件夾,下面再創建Annotations、JPEGImages、ImageSets三個文件夾,最後在ImageSets文件夾下再創建一個Main文件夾。
創建好所有文件夾後,我們將自己的數據集圖片都放到JPEGImages文件夾下。按照習慣,我們將圖片的名字修改爲000001.jpg這種格式的(參照原始數據集圖片命名規則),統一命名方法,可以參考如下代碼:
MATLAB代碼:
%%
%圖片保存路徑爲:
%E:\image\car
%E:\image\person
%car和person是保存車和行人的文件夾
%這些文件夾還可以有多個,
%放在image文件夾裏就行
%該代碼的作用是將圖片名字改成000123.jpg這種形式
%%
clc;
clear;
maindir='E:\image\';
name_long=5; %圖片名字的長度,如000123.jpg爲6,最多9位,可修改
num_begin=1; %圖像命名開始的數字如000123.jpg開始的話就是123
subdir = dir(maindir);
n=1;
for i = 1:length(subdir)
if ~strcmp(subdir(i).name ,'.') && ~strcmp(subdir(i).name,'..')
subsubdir = dir(strcat(maindir,subdir(i).name));
for j=1:length(subsubdir)
if ~strcmp(subsubdir(j).name ,'.') && ~strcmp(subsubdir(j).name,'..')
img=imread([maindir,subdir(i).name,'\',subsubdir(j).name]);
imshow(img);
str=num2str(num_begin,'%09d');
newname=strcat(str,'.jpg');
newname=newname(end-(name_long+3):end);
system(['rename ' [maindir,subdir(i).name,'\',subsubdir(j).name] ' ' newname]);
num_begin=num_begin+1;
fprintf('當前處理文件夾%s',subdir(i).name);
fprintf('已經處理%d張圖片\n',n);
n=n+1;
pause(0.1);%可以將暫停去掉
end
end
end
end
python代碼:
import os
path = "E:\\image"
filelist = os.listdir(path) #該文件夾下所有的文件(包括文件夾)
count=0
for file in filelist:
print(file)
for file in filelist: #遍歷所有文件
Olddir=os.path.join(path,file) #原來的文件路徑
if os.path.isdir(Olddir): #如果是文件夾則跳過
continue
filename=os.path.splitext(file)[0] #文件名
filetype=os.path.splitext(file)[1] #文件擴展名
Newdir=os.path.join(path,str(count).zfill(6)+filetype) #用字符串函數zfill 以0補全所需位數
os.rename(Olddir,Newdir)#重命名
count+=1
強調兩點:第一點是圖片的格式,圖片需是JPEG或者JPG格式,其他格式需要轉換一下。第二點是圖片的長寬比,圖片長寬比不能太大或太小,這個參考原始VOC2007數據集圖片即可。
step2:Annotations
方法一:LabelImg
製作Annotations文件夾下所需要存放的xml文件需要藉助圖片標註工具LabelImg,在github上有源碼。關於它的使用,本人採用的是在Linux系統下的操作,Windows系統下的操作在這裏附上兩個鏈接需要的自行點開https://blog.csdn.net/zhyj3038/article/details/54923781/,https://blog.csdn.net/jesse_mx/article/details/53606897,而在Linux系統操作如下:
由於Ubuntu系統自帶python,這款軟件在Ubuntu環境下的安裝是最方便的。軟件要求python版本在2.6以上,同時需要PyQt和lxml的支持。
sudo apt-get install pyqt4-dev-tools #安裝PyQt4
sudo pip install lxml #安裝lxml,如果報錯,可以試試下面語句
sudo apt-get install python-lxml
接下來位下載github上的安裝包,下載地址爲https://github.com/tzutalin/labelImg,也可通過以下代碼直接獲取:
git clone https://github.com/tzutalin/labelImg.git
下載後進入下載目錄,copy到home目錄下解壓,解壓後進入目錄:
cd labelImg
pyrcc4 -o resources.py resources.qrc
python labelImg.py
即可打開界面:
此軟件的使用方法
修改默認的XML文件保存位置,使用快捷鍵“Ctrl+R”,改爲自定義位置,這裏的路徑一定不能包含中文,否則無法保存。
源碼文件夾中使用notepad++打開data/predefined_classes.txt,修改默認類別,比如改成person、car、motorcycle三個類別。
“Open Dir”打開圖片文件夾,選擇第一張圖片開始進行標註,使用“Create RectBox”或者“Ctrl+N”開始畫框,單擊結束畫框,再雙擊選擇類別。完成一張圖片後點擊“Save”保存,此時XML文件已經保存到本地了。點擊“Next Image”轉到下一張圖片。
標註過程中可隨時返回進行修改,後保存的文件會覆蓋之前的。
完成標註後打開XML文件,發現確實和PASCAL VOC所用格式一樣。
說明:每標註完一張圖片後進行保存,保存的xml文件名要與對應圖片名一致,大家可以參考原始VOC2007數據集中JPEGImages文件夾下圖片的命名和Annotations文件夾中的xml文件命名規則。
方法二:BBox-Label-Tool
進行數據標註的另一個工具即爲BBox-Label-Tool,此工具需要在python2.7下運行,且必須安裝PIL等包,可在github上下載source code源碼,該工具生成的標籤格式是:
classname類別名 x2min y2min x2max y2max
下載完成後裏面有一個main.py文件,但是直接運行會有一定的問題,以下爲修改過的代碼:
from __future__ import division
from Tkinter import *
import tkMessageBox
from PIL import Image, ImageTk
import os
import glob
import random
# colors for the bboxes
COLORS = ['red', 'blue', 'yellow', 'pink', 'cyan', 'green', 'black']
# image sizes for the examples
SIZE = 256, 256
classLabels = ['person', 'car', 'bike', 'motorcycle', 'truck', 'cyclist']
class LabelTool():
def __init__(self, master):
# set up the main frame
self.parent = master
self.parent.title("LabelTool")
self.frame = Frame(self.parent)
self.frame.pack(fill=BOTH, expand=1)
self.parent.resizable(width=False, height=False)
# initialize global state
self.imageDir = ''
self.imageList = []
self.egDir = ''
self.egList = []
self.outDir = ''
self.cur = 0
self.total = 0
self.category = 0
self.imagename = ''
self.labelfilename = ''
self.tkimg = None
# initialize mouse state
self.STATE = {}
self.STATE['click'] = 0
self.STATE['x'], self.STATE['y'] = 0, 0
# reference to bbox
self.bboxIdList = []
self.bboxId = None
self.bboxList = []
self.hl = None
self.vl = None
self.currentClass = ''
# ----------------- GUI stuff ---------------------
# dir entry & load
self.label = Label(self.frame, text="Image Dir:")
self.label.grid(row=0, column=0, sticky=E)
self.entry = Entry(self.frame)
self.entry.grid(row=0, column=1, sticky=W + E)
self.ldBtn = Button(self.frame, text="Load", command=self.loadDir)
self.ldBtn.grid(row=0, column=2, sticky=W + E)
# main panel for labeling
self.mainPanel = Canvas(self.frame, cursor='tcross')
self.mainPanel.bind("<Button-1>", self.mouseClick)
self.mainPanel.bind("<Motion>", self.mouseMove)
self.parent.bind("<Escape>", self.cancelBBox) # press <Espace> to cancel current bbox
self.parent.bind("s", self.cancelBBox)
self.parent.bind("a", self.prevImage) # press 'a' to go backforward
self.parent.bind("d", self.nextImage) # press 'd' to go forward
self.mainPanel.grid(row=1, column=1, rowspan=4, sticky=W + N)
# showing bbox info & delete bbox
self.lb1 = Label(self.frame, text='Bounding boxes:')
self.lb1.grid(row=1, column=2, sticky=W + N)
self.listbox = Listbox(self.frame, width=22, height=12)
self.listbox.grid(row=2, column=2, sticky=N)
self.btnDel = Button(self.frame, text='Delete', command=self.delBBox)
self.btnDel.grid(row=3, column=2, sticky=W + E + N)
self.btnClear = Button(self.frame, text='ClearAll', command=self.clearBBox)
self.btnClear.grid(row=4, column=2, sticky=W + E + N)
# select class type
self.classPanel = Frame(self.frame)
self.classPanel.grid(row=5, column=1, columnspan=10, sticky=W + E)
label = Label(self.classPanel, text='class:')
label.grid(row=5, column=1, sticky=W + N)
self.classbox = Listbox(self.classPanel, width=4, height=2)
self.classbox.grid(row=5, column=2)
for each in range(len(classLabels)):
function = 'select' + classLabels[each]
print
classLabels[each]
btnMat = Button(self.classPanel, text=classLabels[each], command=getattr(self, function))
btnMat.grid(row=5, column=each + 3)
# control panel for image navigation
self.ctrPanel = Frame(self.frame)
self.ctrPanel.grid(row=6, column=1, columnspan=2, sticky=W + E)
self.prevBtn = Button(self.ctrPanel, text='<< Prev', width=10, command=self.prevImage)
self.prevBtn.pack(side=LEFT, padx=5, pady=3)
self.nextBtn = Button(self.ctrPanel, text='Next >>', width=10, command=self.nextImage)
self.nextBtn.pack(side=LEFT, padx=5, pady=3)
self.progLabel = Label(self.ctrPanel, text="Progress: / ")
self.progLabel.pack(side=LEFT, padx=5)
self.tmpLabel = Label(self.ctrPanel, text="Go to Image No.")
self.tmpLabel.pack(side=LEFT, padx=5)
self.idxEntry = Entry(self.ctrPanel, width=5)
self.idxEntry.pack(side=LEFT)
self.goBtn = Button(self.ctrPanel, text='Go', command=self.gotoImage)
self.goBtn.pack(side=LEFT)
# example pannel for illustration
self.egPanel = Frame(self.frame, border=10)
self.egPanel.grid(row=1, column=0, rowspan=5, sticky=N)
self.tmpLabel2 = Label(self.egPanel, text="Examples:")
self.tmpLabel2.pack(side=TOP, pady=5)
self.egLabels = []
for i in range(3):
self.egLabels.append(Label(self.egPanel))
self.egLabels[-1].pack(side=TOP)
# display mouse position
self.disp = Label(self.ctrPanel, text='')
self.disp.pack(side=RIGHT)
self.frame.columnconfigure(1, weight=1)
self.frame.rowconfigure(10, weight=1)
# for debugging
## self.setImage()
## self.loadDir()
def loadDir(self, dbg=False):
if not dbg:
s = self.entry.get()
self.parent.focus()
self.category = int(s)
else:
s = r'D:\workspace\python\labelGUI'
## if not os.path.isdir(s):
## tkMessageBox.showerror("Error!", message = "The specified dir doesn't exist!")
## return
# get image list
self.imageDir = os.path.join(r'./Images', '%d' % (self.category))
self.imageList = glob.glob(os.path.join(self.imageDir, '*.png'))
if len(self.imageList) == 0:
print('No .PNG images found in the specified dir!')
return
# set up output dir
self.outDir = os.path.join(r'./Labels', '%d' % (self.category))
if not os.path.exists(self.outDir):
os.mkdir(self.outDir)
labeledPicList = glob.glob(os.path.join(self.outDir, '*.txt'))
for label in labeledPicList:
data = open(label, 'r')
if '0\n' == data.read():
data.close()
continue
data.close()
picture = label.replace('Labels', 'Images').replace('.txt', '.png')
if picture in self.imageList:
self.imageList.remove(picture)
# default to the 1st image in the collection
self.cur = 1
self.total = len(self.imageList)
self.loadImage()
print('%d images loaded from %s' % (self.total, s))
def loadImage(self):
# load image
imagepath = self.imageList[self.cur - 1]
self.img = Image.open(imagepath)
self.imgSize = self.img.size
self.tkimg = ImageTk.PhotoImage(self.img)
self.mainPanel.config(width=max(self.tkimg.width(), 400), height=max(self.tkimg.height(), 400))
self.mainPanel.create_image(0, 0, image=self.tkimg, anchor=NW)
self.progLabel.config(text="%04d/%04d" % (self.cur, self.total))
# load labels
self.clearBBox()
self.imagename = os.path.split(imagepath)[-1].split('.')[0]
labelname = self.imagename + '.txt'
self.labelfilename = os.path.join(self.outDir, labelname)
bbox_cnt = 0
if os.path.exists(self.labelfilename):
with open(self.labelfilename) as f:
for (i, line) in enumerate(f):
if i == 0:
bbox_cnt = int(line.strip())
continue
tmp = [int(t.strip()) for t in line.split()]
## print tmp
self.bboxList.append(tuple(tmp))
tmpId = self.mainPanel.create_rectangle(tmp[0], tmp[1], \
tmp[2], tmp[3], \
width=2, \
outline=COLORS[(len(self.bboxList) - 1) % len(COLORS)])
self.bboxIdList.append(tmpId)
self.listbox.insert(END, '(%d, %d) -> (%d, %d)' % (tmp[0], tmp[1], tmp[2], tmp[3]))
self.listbox.itemconfig(len(self.bboxIdList) - 1,
fg=COLORS[(len(self.bboxIdList) - 1) % len(COLORS)])
def saveImage(self):
with open(self.labelfilename, 'w') as f:
f.write('%d\n' % len(self.bboxList))
for bbox in self.bboxList:
f.write(' '.join(map(str, bbox)) + '\n')
print('Image No. %d saved' % (self.cur))
def mouseClick(self, event):
if self.STATE['click'] == 0:
self.STATE['x'], self.STATE['y'] = event.x, event.y
# self.STATE['x'], self.STATE['y'] = self.imgSize[0], self.imgSize[1]
else:
x1, x2 = min(self.STATE['x'], event.x), max(self.STATE['x'], event.x)
y1, y2 = min(self.STATE['y'], event.y), max(self.STATE['y'], event.y)
if x2 > self.imgSize[0]:
x2 = self.imgSize[0]
if y2 > self.imgSize[1]:
y2 = self.imgSize[1]
self.bboxList.append((self.currentClass, x1, y1, x2, y2))
self.bboxIdList.append(self.bboxId)
self.bboxId = None
self.listbox.insert(END, '(%d, %d) -> (%d, %d)' % (x1, y1, x2, y2))
self.listbox.itemconfig(len(self.bboxIdList) - 1, fg=COLORS[(len(self.bboxIdList) - 1) % len(COLORS)])
self.STATE['click'] = 1 - self.STATE['click']
def mouseMove(self, event):
self.disp.config(text='x: %d, y: %d' % (event.x, event.y))
if self.tkimg:
if self.hl:
self.mainPanel.delete(self.hl)
self.hl = self.mainPanel.create_line(0, event.y, self.tkimg.width(), event.y, width=2)
if self.vl:
self.mainPanel.delete(self.vl)
self.vl = self.mainPanel.create_line(event.x, 0, event.x, self.tkimg.height(), width=2)
if 1 == self.STATE['click']:
if self.bboxId:
self.mainPanel.delete(self.bboxId)
self.bboxId = self.mainPanel.create_rectangle(self.STATE['x'], self.STATE['y'], \
event.x, event.y, \
width=2, \
outline=COLORS[len(self.bboxList) % len(COLORS)])
def cancelBBox(self, event):
if 1 == self.STATE['click']:
if self.bboxId:
self.mainPanel.delete(self.bboxId)
self.bboxId = None
self.STATE['click'] = 0
def delBBox(self):
sel = self.listbox.curselection()
if len(sel) != 1:
return
idx = int(sel[0])
self.mainPanel.delete(self.bboxIdList[idx])
self.bboxIdList.pop(idx)
self.bboxList.pop(idx)
self.listbox.delete(idx)
def clearBBox(self):
for idx in range(len(self.bboxIdList)):
self.mainPanel.delete(self.bboxIdList[idx])
self.listbox.delete(0, len(self.bboxList))
self.bboxIdList = []
self.bboxList = []
def selectperson(self):
self.currentClass = 'person'
self.classbox.delete(0, END)
self.classbox.insert(0, 'person')
self.classbox.itemconfig(0, fg=COLORS[0])
def selectcar(self):
self.currentClass = 'car'
self.classbox.delete(0, END)
self.classbox.insert(0, 'car')
self.classbox.itemconfig(0, fg=COLORS[0])
def selectbike(self):
self.currentClass = 'bike'
self.classbox.delete(0, END)
self.classbox.insert(0, 'bike')
self.classbox.itemconfig(0, fg=COLORS[0])
def selectmotorcycle(self):
self.currentClass = 'motorcycle'
self.classbox.delete(0, END)
self.classbox.insert(0, 'motorcycle')
self.classbox.itemconfig(0, fg=COLORS[0])
def selecttruck(self):
self.currentClass = 'truck'
self.classbox.delete(0, END)
self.classbox.insert(0, 'truck')
self.classbox.itemconfig(0, fg=COLORS[0])
def selectcyclist(self):
self.currentClass = 'cyclist'
self.classbox.delete(0, END)
self.classbox.insert(0, 'cyclist')
self.classbox.itemconfig(0, fg=COLORS[0])
def prevImage(self, event=None):
self.saveImage()
if self.cur > 1:
self.cur -= 1
self.loadImage()
def nextImage(self, event=None):
self.saveImage()
if self.cur < self.total:
self.cur += 1
self.loadImage()
def gotoImage(self):
idx = int(self.idxEntry.get())
if 1 <= idx and idx <= self.total:
self.saveImage()
self.cur = idx
self.loadImage()
if __name__ == '__main__':
root = Tk()
tool = LabelTool(root)
root.mainloop()
main.py
使用方法:
(1) 在BBox-Label-Tool/Images目錄下創建保存圖片的目錄, 目錄以數字命名(BBox-Label-Tool/Images/1), 然後將待標註的圖片copy到1這個目錄下;
(2) 在BBox-Label-Tool目錄下執行命令 python main.py
(3) 在工具界面上, Image Dir 框中輸入需要標記的目錄名(比如 1), 然後點擊load按鈕, 工具自動將Images/1目錄下的圖片加載進來;需要說明一下, 如果目錄中的圖片已經標註過,點擊load時不會被重新加載進來.
(4) 該工具支持多類別標註, 畫bounding boxs框標定之前,需要先選定類別,然後再畫框.
(5) 一張圖片標註完後, 點擊Next>>按鈕, 標註下一張圖片, 圖片label成功後,會在BBox-Label-Tool/Labels對應的目錄下生成與圖片文件名對應的label文件.
使用界面如圖所示:
以上步驟生成的是txt文件,接下來要做的是將此label轉換成VOC數據格式
BBox-Label-Tool工具標註好的bounding box座標文件轉換成VOC數據格式的形式,轉換過程包括了兩個步驟:
(1)將BBox-Label-Tool下的txt格式保存的bounding box信息轉換成VOC數據格式下以xml方式表示;
(2)生成用於訓練的數據集和用於測試的數據集。
首先建立一個VOC2007文件夾,在其下面建立JPEGImages,Annotations,label文件夾,將前面生成的所有txt文件轉放到label文件夾下,並將所有的圖片轉移到JPEGImages文件夾下。
建立一個**.py文件,完成txt到xml轉換的腳本,放到和label文件夾同一目錄下,執行腳本Python **.py,生成xml,此py文件代碼如下:
import os
import sys
import cv2
from itertools import islice
from xml.dom.minidom import Document
labels = 'label'
imgpath = 'JPEGImages/'
xmlpath_new = 'Annotations/'
foldername = 'VOC2007'
def insertObject(doc, datas):
obj = doc.createElement('object')
name = doc.createElement('name')
name.appendChild(doc.createTextNode(datas[0]))
obj.appendChild(name)
pose = doc.createElement('pose')
pose.appendChild(doc.createTextNode('Unspecified'))
obj.appendChild(pose)
truncated = doc.createElement('truncated')
truncated.appendChild(doc.createTextNode(str(0)))
obj.appendChild(truncated)
difficult = doc.createElement('difficult')
difficult.appendChild(doc.createTextNode(str(0)))
obj.appendChild(difficult)
bndbox = doc.createElement('bndbox')
xmin = doc.createElement('xmin')
xmin.appendChild(doc.createTextNode(str(datas[1])))
bndbox.appendChild(xmin)
ymin = doc.createElement('ymin')
ymin.appendChild(doc.createTextNode(str(datas[2])))
bndbox.appendChild(ymin)
xmax = doc.createElement('xmax')
xmax.appendChild(doc.createTextNode(str(datas[3])))
bndbox.appendChild(xmax)
ymax = doc.createElement('ymax')
if '\r' == str(datas[4])[-1] or '\n' == str(datas[4])[-1]:
data = str(datas[4])[0:-1]
else:
data = str(datas[4])
ymax.appendChild(doc.createTextNode(data))
bndbox.appendChild(ymax)
obj.appendChild(bndbox)
return obj
def create():
for walk in os.walk(labels):
for each in walk[2]:
fidin = open(walk[0] + '/' + each, 'r')
objIndex = 0
for data in islice(fidin, 1, None):
objIndex += 1
data = data.strip('\n')
datas = data.split(' ')
if 5 != len(datas):
print 'bounding box information error'
continue
pictureName = each.replace('.txt', '.jpg')
imageFile = imgpath + pictureName
img = cv2.imread(imageFile)
imgSize = img.shape
if 1 == objIndex:
xmlName = each.replace('.txt', '.xml')
f = open(xmlpath_new + xmlName, "w")
doc = Document()
annotation = doc.createElement('annotation')
doc.appendChild(annotation)
folder = doc.createElement('folder')
folder.appendChild(doc.createTextNode(foldername))
annotation.appendChild(folder)
filename = doc.createElement('filename')
filename.appendChild(doc.createTextNode(pictureName))
annotation.appendChild(filename)
source = doc.createElement('source')
database = doc.createElement('database')
database.appendChild(doc.createTextNode('My Database'))
source.appendChild(database)
source_annotation = doc.createElement('annotation')
source_annotation.appendChild(doc.createTextNode(foldername))
source.appendChild(source_annotation)
image = doc.createElement('image')
image.appendChild(doc.createTextNode('flickr'))
source.appendChild(image)
flickrid = doc.createElement('flickrid')
flickrid.appendChild(doc.createTextNode('NULL'))
source.appendChild(flickrid)
annotation.appendChild(source)
owner = doc.createElement('owner')
flickrid = doc.createElement('flickrid')
flickrid.appendChild(doc.createTextNode('NULL'))
owner.appendChild(flickrid)
name = doc.createElement('name')
name.appendChild(doc.createTextNode('idaneel'))
owner.appendChild(name)
annotation.appendChild(owner)
size = doc.createElement('size')
width = doc.createElement('width')
width.appendChild(doc.createTextNode(str(imgSize[1])))
size.appendChild(width)
height = doc.createElement('height')
height.appendChild(doc.createTextNode(str(imgSize[0])))
size.appendChild(height)
depth = doc.createElement('depth')
depth.appendChild(doc.createTextNode(str(imgSize[2])))
size.appendChild(depth)
annotation.appendChild(size)
segmented = doc.createElement('segmented')
segmented.appendChild(doc.createTextNode(str(0)))
annotation.appendChild(segmented)
annotation.appendChild(insertObject(doc, datas))
else:
annotation.appendChild(insertObject(doc, datas))
try:
f.write(doc.toprettyxml(indent=' '))
f.close()
fidin.close()
except:
pass
if __name__ == '__main__':
create()
NB:除了這兩種方法,網上還有另一種適用於Windows系統的方法,大家根據需要自行查看:https://blog.csdn.net/sinat_30071459/article/details/50723212
step3:ImageSets
接下來爲製作ImageSets文件夾下Main文件夾中的4個文件(test.txt、train.txt、trainval.txt、val.txt)。
首先說明一下這四個文件到底是幹什麼用的:
test.txt:測試集
train.txt:訓練集
val.txt:驗證集
trainval.txt:訓練和驗證集
在原始VOC2007數據集中,trainval大約佔整個數據集的50%,test大約爲整個數據集的50%;train大約是trainval的50%,val大約爲trainval的50%。所以我們可參考以下代碼來生成這4個txt文件:
import os
import random
trainval_percent = 0.5
train_percent = 0.5
xmlfilepath = '/home/hqd/桌面/VOC2010/Annotations'
txtsavepath = '/home/hqd/桌面/VOC2010/ImageSets/Main'
total_xml = os.listdir(xmlfilepath)
num=len(total_xml)
list=range(num)
tv=int(num*trainval_percent)
tr=int(tv*train_percent)
trainval= random.sample(list,tv)
train=random.sample(trainval,tr)
ftrainval = open(txtsavepath+'/trainval.txt', 'w')
ftest = open(txtsavepath+'/test.txt', 'w')
ftrain = open(txtsavepath+'/train.txt', 'w')
fval = open(txtsavepath+'/val.txt', 'w')
for i in list:
name=total_xml[i][:-4]+'\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest .close()
注意:上述代碼中涉及到的路徑要寫全,另外各個數據集所佔比例根據實際數據集的大小調整比例。
至此,我們自己的VOC2007格式數據集就全部製作完成了。