制作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这种格式的(参照原始数据集图片命名规则),统一命名方法,可以参考如下代码:
 


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)#重命名

强调两点:第一点是图片的格式,图片需是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
 

即可打开界面:

此软件的使用方法

修改默认的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文件命名规则。


以上步骤生成的是txt文件,接下来要做的是将此label转换成VOC数据格式

首先建立一个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()

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格式数据集就全部制作完成了。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值