为目标检测制作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文件的解析如下:

  
    VOC2007                             
    2007_000392.jpg                               //文件名  
                                                               //图像来源(不重要)  
        The VOC2007 Database  
        PASCAL VOC2007  
        flickr  
      
                                                   //图像尺寸(长宽以及通道数)                        
        500  
        332  
        3  
      
    1                                   //是否用于分割(在图像物体识别中01无所谓)  
                                                               //检测到的物体  
        horse                                         //物体类别  
        Right                                         //拍摄角度  
        0                                   //是否被截断(0表示完整)  
        0                                   //目标是否难以识别(0表示容易识别)  
                                                           //bounding-box(包含左下角和右上角xy坐标)  
            100  
            96  
            355  
            324  
          
      
                                                               //检测到多个物体  
        person  
        Unspecified  
        0  
        0  
          
            198  
            58  
            286  
            197  
          
      
 

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上有源码:https://github.com/tzutalin/labelImg。关于它的使用,本人采用的是在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

即可打开界面:

为目标检测制作PASCAL VOC2007格式的数据集_第1张图片

此软件的使用方法

  • 修改默认的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上下载源码https://github.com/puzzledqs/BBox-Label-Tool,该工具生成的标签格式是:

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("", self.mouseClick)
        self.mainPanel.bind("", self.mouseMove)
        self.parent.bind("", self.cancelBBox)  # press  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文件.

使用界面如图所示:

为目标检测制作PASCAL VOC2007格式的数据集_第2张图片

以上步骤生成的是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格式数据集就全部制作完成了。

NB:本人在网上还看到另一个教程是关于制作数据集的,但是本人实践的时候遇到了一些问题导致没有运行成功,在这里附上链接https://blog.csdn.net/Best_Coder/article/details/76577544?locationNum=8&fps=1,希望实践成功的小伙伴前来评论指导。

你可能感兴趣的:(为目标检测制作PASCAL VOC2007格式的数据集)