作为标准数据集,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即可。