爲目標檢測製作PASCAL VOC2007格式的數據集

本文爲博主原創,轉載請註明出處: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格式數據集就全部製作完成了。

發佈了11 篇原創文章 · 獲贊 6 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章