4 分钟阅读

作为标准数据集,voc-2007 是衡量图像分类识别能力的基准,faster-rcnn,yolo -v1, yolo-v2都以此数据集作为演示样例。 数据集的组成架构如下:

  • Annotations —目标真值区域
  • ImageSets —-类别标签
  • JPEGImages —–图像
  • SegmentationClass
  • SegmentationObjec

其中,Annotations为存放xml文件的文件夹,ImageSets为存放类别标签的文件夹,JPEGImages为存放jpg图片的文件夹。制作数据集主要就是制作这三个文件夹,并将其与原数据集中对应文件夹替换即可。

1.图片命名及存放

建议按VOC2007那样,如”000001.jpg”这种形式。至于图片格式,代码里是jpg。 图片命名推荐使用工具Total Commander来批量重命名。 然后将所有的图片移动到同一个文件夹内,即JPEGImages文件夹。原本图片就在一个文件夹内的可以忽略这一步。而原本数据集中的图片按类分别存放在不同的文件夹内,即图片存放结构为 根文件夹-分类文件夹-图片 ,提供Java函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    public static void main(String[] args) {
     String path = ""; // 根文件夹路径
     File f = new File(path);
     if (!f.exists()) {
     System.out.println(path + " not exists");
     return;
     }

    File fa[] = f.listFiles();
     String file,target,filetar;
     target = "";//图片要移动到的位置
     for (int i = 0; i < fa.length; i++) {
     File fs = fa[i];
     if (fs.isDirectory()) {
     String pathx = ""+fs.getName(); // 分类文件夹路径
     File fx = new File(pathx);
     if (!fx.exists()) {
     return;
     }

    File fax[] = fx.listFiles();
     for (int j = 0; j < fax.length; j++) {
     File fsx = fax[j];
     if(fsx.getName().endsWith(".jpg")){
     file = pathx+"\\";
     filetar = target;
     file += fsx.getName();
     filetar += fsx.getName();
     File srcFile = new File(file);
     File targetFile = new File(filetar);
     try {
     InputStream in = new FileInputStream(srcFile);
     OutputStream out = new FileOutputStream(targetFile);
     byte[] bytes = new byte[1024];
     int len = -1;
     while((len=in.read(bytes))!=-1)
     {
     out.write(bytes, 0, len);
     }
     in.close();
     out.close();
     } catch (FileNotFoundException e) {
     e.printStackTrace();
     } catch (IOException e) {
     e.printStackTrace();
     }
     }
     }
     System.out.println(fs.getName() + " [目录]");
     }
     }

    }

2.统计目标包围框信息

将图片中所框的目标信息保存到txt里,如下:

000001.jpg bird1 429 228 469 244 000002.jpg bird1 423 220 460 236 000003.jpg bird1 418 212 450 228

前面是图片名,中间是目标类别,最后是目标的包围框坐标(左上角和右下角坐标)。 如果原数据集中图片附带xml文件,例如:

000001 640 360 bird1 469 429 244 228

其中包含包围框坐标信息,将上述信息提取出来并存放到txt文件中即可。提取信息并存到txt文件中的Java代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
    public class Dom4jParseXmlDemo {
     String num[] = new String[4];
     int i;

    public void parseXml02(String File,String name,String dir) {
     i = 0;
     num[0] = "0";
     num[1] = "0";
     num[2] = "0";
     num[3] = "0";
     String str = name.substring(0, name.length()-4);
     try {
     SAXReader reader = new SAXReader();
     Document document = reader.read(new File(File));
     Element root = document.getRootElement();
     Element element = root.element("object");
     Element element1 = element.element("bndbox");
     Element element2 = element1.element("xmin");
     num[1] = (String)element2.getData();
     Element element3 = element1.element("ymin");
     num[3] = (String)element3.getData();
     Element element4 = element1.element("xmax");
     num[0] = (String)element4.getData();
     Element element5 = element1.element("ymax");
     num[2] = (String)element5.getData();

    } catch (Exception e) {
     e.printStackTrace();
     }

    str += ".jpg "+dir+" "+num[1] + " " + num[3] + " " + num[0] + " " + num[2]+ "\n";
     try

    {

    FileWriter fw = new FileWriter("",true);//txt文件路径

    fw.write(str);

    fw.close();

    }

    catch (IOException e)

    {

    e.printStackTrace();

    }
     }

     public static void main(String[] args) {
     Dom4jParseXmlDemo demo = new Dom4jParseXmlDemo();
     String path = ""; // 根文件夹路径
     File f = new File(path);
     if (!f.exists()) {
     System.out.println(path + " not exists");
     return;
     }

    File fa[] = f.listFiles();
     String file;
     for (int i = 0; i < fa.length; i++) {
     File fs = fa[i];
     if (fs.isDirectory()) {
     String pathx = ""+fs.getName(); // 分类文件夹路径
     File fx = new File(pathx);
     if (!fx.exists()) {
     return;
     }

    File fax[] = fx.listFiles();
     for (int j = 0; j < fax.length; j++) {
     File fsx = fax[j];
     if(fsx.getName().endsWith(".xml")){
     file = pathx+"\\";
     file += fsx.getName();
     demo.parseXml02(file,fsx.getName(),fs.getName());
     }
     }
     System.out.println(fs.getName() + " [目录]");
     }
     }

    }

若xml中缺少相关的坐标信息,则文本文件中坐标信息为0。

3.做xml

按照第2步得到的txt生成符合要求的xml文件。 MATLAB文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
    %该代码可以做voc2007数据集中的xml文件,
    clc;
    clear;
    %注意修改下面四个变量
    imgpath='';%图像存放文件夹路径
    txtpath='';%txt文件路径
    xmlpath_new='';%修改后的xml保存文件夹路径
    foldername='DAC';%xml的folder字段名

    fidin=fopen(txtpath,'r');

    while ~feof(fidin)
         tline=fgetl(fidin);
         str = regexp(tline, ' ','split');
         filepath=[imgpath,str{1}];
         img=imread(filepath);
         [h,w,d]=size(img);
         rectangle('Position',[str2double(str{3}),str2double(str{4}),str2double(str{5})-str2double(str{3}),str2double(str{6})-str2double(str{4})],'LineWidth',4,'EdgeColor','r');
         Createnode=com.mathworks.xml.XMLUtils.createDocument('annotation');
         Root=Createnode.getDocumentElement;%根节点
         node=Createnode.createElement('folder');
         node.appendChild(Createnode.createTextNode(sprintf('%s',foldername)));
         Root.appendChild(node);
         node=Createnode.createElement('filename');
         node.appendChild(Createnode.createTextNode(sprintf('%s',str{1})));
         Root.appendChild(node);
         source_node=Createnode.createElement('source');
         Root.appendChild(source_node);
         node=Createnode.createElement('database');
         node.appendChild(Createnode.createTextNode(sprintf('MyDatabase')));
         source_node.appendChild(node);
         node=Createnode.createElement('annotation');
         node.appendChild(Createnode.createTextNode(sprintf('DAC')));
         source_node.appendChild(node);
         node=Createnode.createElement('image');
         node.appendChild(Createnode.createTextNode(sprintf('flickr')));
         source_node.appendChild(node);
         node=Createnode.createElement('flickrid');
         node.appendChild(Createnode.createTextNode(sprintf('NULL')));
         source_node.appendChild(node);
         owner_node=Createnode.createElement('owner');
         Root.appendChild(owner_node);
         node=Createnode.createElement('flickrid');
         node.appendChild(Createnode.createTextNode(sprintf('NULL')));
         owner_node.appendChild(node);

         node=Createnode.createElement('name');
         node.appendChild(Createnode.createTextNode(sprintf('embedded')));
         owner_node.appendChild(node);
         size_node=Createnode.createElement('size');
         Root.appendChild(size_node);

         node=Createnode.createElement('width');
         node.appendChild(Createnode.createTextNode(sprintf('%s',num2str(w))));
         size_node.appendChild(node);

         node=Createnode.createElement('height');
         node.appendChild(Createnode.createTextNode(sprintf('%s',num2str(h))));
         size_node.appendChild(node);

         node=Createnode.createElement('depth');
         node.appendChild(Createnode.createTextNode(sprintf('%s',num2str(d))));
         size_node.appendChild(node);

         node=Createnode.createElement('segmented');
         node.appendChild(Createnode.createTextNode(sprintf('%s','0')));
         Root.appendChild(node);
         object_node=Createnode.createElement('object');
         Root.appendChild(object_node);
         node=Createnode.createElement('name');
         node.appendChild(Createnode.createTextNode(sprintf('%s',str{2})));
         object_node.appendChild(node);

         node=Createnode.createElement('pose');
         node.appendChild(Createnode.createTextNode(sprintf('%s','Unspecified')));
         object_node.appendChild(node);

         node=Createnode.createElement('truncated');
         node.appendChild(Createnode.createTextNode(sprintf('%s','0')));
         object_node.appendChild(node);

         node=Createnode.createElement('difficult');
         node.appendChild(Createnode.createTextNode(sprintf('%s','0')));
         object_node.appendChild(node);

         bndbox_node=Createnode.createElement('bndbox');
         object_node.appendChild(bndbox_node);

         node=Createnode.createElement('xmin');
         node.appendChild(Createnode.createTextNode(sprintf('%s',num2str(str{3}))));
         bndbox_node.appendChild(node);

         node=Createnode.createElement('ymin');
         node.appendChild(Createnode.createTextNode(sprintf('%s',num2str(str{4}))));
         bndbox_node.appendChild(node);

         node=Createnode.createElement('xmax');
         node.appendChild(Createnode.createTextNode(sprintf('%s',num2str(str{5}))));
         bndbox_node.appendChild(node);

         node=Createnode.createElement('ymax');
         node.appendChild(Createnode.createTextNode(sprintf('%s',num2str(str{6}))));
         bndbox_node.appendChild(node);
         %保存xml%
         lastname=str{1};
         tempname=strrep(lastname,'.jpg','.xml');
         xmlwrite(tempname,Createnode);
         fprintf('%s\n',tempname);

    end
    fclose(fidin);

生成的xml文件如下:

<?xml version=”1.0” encoding=”utf-8”?> DAC 000001.jpg MyDatabase DAC flickr NULL </source> NULL embedded 640 360 3 0 bird1 Unspecified 0 0 429 228 469 244

4.生成ImageSets\Main里的四个txt文件

在ImageSets里新建文件夹,命名为Main。Main里面包含4个txt文件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%。 相关MATLAB函数如下,这4个txt文件的生成是根据Annotations文件夹中的xml文件名称生成的,和xml文件具体内容无关,函数中的相关百分比可以修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
    %%  
    %该代码根据已生成的xml,制作VOC2007数据集中的trainval.txt;train.txt;test.txt和val.txt  
    %trainval占总数据集的50%,test占总数据集的50%;train占trainval的50%,val占trainval的50%;  
    %上面所占百分比可根据自己的数据集修改,如果数据集比较少,test和val可少一些  
    %%  
    %注意修改下面四个值  
    xmlfilepath='';  //Annotations文件夹所在位置
    txtsavepath='';  //路径定位到Main文件夹下
    trainval_percent=0.5;%trainval占整个数据集的百分比,剩下部分就是test所占百分比  
    train_percent=0.5;%train占trainval的百分比,剩下部分就是val所占百分比  
      
      
    %%  
    xmlfile=dir(xmlfilepath);  
    numOfxml=length(xmlfile)-2;
      
      
    trainval=sort(randperm(numOfxml,floor(numOfxml*trainval_percent)));  
    test=sort(setdiff(1:numOfxml,trainval));  
      
      
    trainvalsize=length(trainval);%trainval的大小  
    train=sort(trainval(randperm(trainvalsize,floor(trainvalsize*train_percent))));  
    val=sort(setdiff(trainval,train));  
      
      
    ftrainval=fopen([txtsavepath 'trainval.txt'],'w');  
    ftest=fopen([txtsavepath 'test.txt'],'w');  
    ftrain=fopen([txtsavepath 'train.txt'],'w');  
    fval=fopen([txtsavepath 'val.txt'],'w');  
      
      
    for i=1:numOfxml  
        if ismember(i,trainval)  
            fprintf(ftrainval,'%s\n',xmlfile(i+2).name(1:end-4));  
            if ismember(i,train)  
                fprintf(ftrain,'%s\n',xmlfile(i+2).name(1:end-4));  
            else  
                fprintf(fval,'%s\n',xmlfile(i+2).name(1:end-4));  
            end  
        else  
            fprintf(ftest,'%s\n',xmlfile(i+2).name(1:end-4));  
        end  
    end  
    fclose(ftrainval);  
    fclose(ftrain);  
    fclose(fval);  
    fclose(ftest);

最后直接替换掉voc2007数据集中的Annotations、ImageSets和JPEGImages即可。