作为标准数据集,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函数如下:

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文件,例如:

<annotation>
<filename>000001</filename>
<size>
<width>640</width>
<height>360</height>
</size>
<object>
<name>bird1</name>
<bndbox>
<xmax>469</xmax>
<xmin>429</xmin>
<ymax>244</ymax>
<ymin>228</ymin>
</bndbox>
</object>
</annotation>

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

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文件如下:

%该代码可以做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″?>
<annotation>
<folder>DAC</folder>
<filename>000001.jpg</filename>
<source>
<database>MyDatabase</database>
<annotation>DAC</annotation>
<image>flickr</image>
<flickrid>NULL</flickrid>
</source>
<owner>
<flickrid>NULL</flickrid>
<name>embedded</name>
</owner>
<size>
<width>640</width>
<height>360</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>bird1</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>429</xmin>
<ymin>228</ymin>
<xmax>469</xmax>
<ymax>244</ymax>
</bndbox>
</object>
</annotation>

 

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文件具体内容无关,函数中的相关百分比可以修改:

%%  
%该代码根据已生成的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即可。