感谢作者:http://imzack.me/post/training-rcnn-using-my-own-dataset.html
之前已经写了一篇安装 R-CNN 的文章了,现在就要使用自己的数据集训练 R-CNN 了。
这篇文章记录了我用人脸数据集来训练 R-CNN,并且使用训练后的 R-CNN 模型检测出图片中的人脸的过程。
注意:虽然下述内容在我的电脑上测试通过,但是由于此文章是我在做毕业设计期间撰写的,时间紧迫,未能考虑到所有情况,仅是做记录使用,因此下述内容不一定适用于每个人,如果发现错误,请求助于其他博客,敬请留意!
构建数据集
查看 R-CNN 的 demo 程序的源代码 rcnn_demo.m
发现,有两个已经训练好的模型可供使用,它们分别是使用 PASCAL VOC 2007 数据集和 ImageNet ILSVRC 2013 数据集训练好的模型,前者可识别 20 个类型,后者可识别 200 个类型。然而这 220 个类型并不包括人脸,所以需要自己训练。
下载数据集
我下载的是香港中文大学多媒体实验室的 CelebA 数据集,这是一个人脸数据集,一共有 20 多万张图片。官方也提供了某度盘的链接。以下是我下载的文件:
1. 有环境的图片(In-The-Wild Images)
在度盘的 Img/img_celeba.7z
文件夹中。由于图片非常多,这个压缩包很大,所以作者进行了分卷压缩,有 14 个文件(img_celeba.7z.001
到 img_celeba.7z.014
),一共 9 个多 G。我在 Windows 用度盘客户端下的,不然速度会时不时地归零。下载下来后也在 Windows 下的命令提示符中进行分卷的合并:
copy /b img_celeba.7z.* img_celeba.7z
很慢啊,因为太大了。合并好了会生成一个新的压缩包 img_celeba.7z
,然后解压。
2. 标注信息(Bounding Box Annotations)
训练和测试的时候,需要告诉程序一张图片哪个地方才是人脸,所以需要标注框,标注信息提供了每张图片中人脸标注框的坐标。在度盘的 Anno/list_bbox_celeba.txt
,直接下载即可。
转换数据集
其实作者提供了使用自己的数据集训练 R-CNN 的教程,但是写得很粗略,主要就是需要准备三个函数:
- 返回描述图片和类型的结构体(参考
imdb/imdb_from_voc.m
) - 返回感兴趣区域(即标注框)的结构体(参考
imdb/roidb_from_voc.m
) - 提供评估功能(参考
imdb/imdb_eval_voc.m
)
也可以把自己的数据集转换成 PASCAL VOC 的格式,这样就可以重用作者的代码了,我就是使用的这种方式。
数据集结构
我以前有下过 PASCAL VOC 2007 数据集,先用 tree -d
命令看下目录结构吧:
接下来仿照此结构,构建自己的数据集。
先把 rcnn/datasets/VOCdevkit2007
链接到存放 CelebA 数据集的目录(我是 ~/pascal_voc/celeba/VOCdevkit
):
$ ln -sf ~/pascal_voc/celeba/VOCdevkit ~/rcnn/datasets/VOCdevkit2007
再在 VOCdevkit
下添加一些文件夹,结构如下:
VOCdevkit ├── results │ └── VOC2007 │ └── Main ├── VOC2007 │ ├── Annotations │ ├── ImageSets │ │ └── Main │ └── JPEGImages └── VOCcode
下面是每个文件夹的说明:
VOC2007/Annotations
存放JPEGImages
文件夹中图片的标注信息,与图片文件同名且一一对应,为xml
格式。VOC2007/ImageSets/Main
存放四个txt
文件:train.txt
、val.txt
、trainval.txt
、test.txt
,分别定义训练、验证、训练和验证、测试四种数据集中的图片,这些文件里的每一行文本都是一个 image identifier,其实也就是图片的文件名。VOC2007/JPEGImages
存放所有图片文件。VOCcode
存放加载、处理数据集的代码。直接将 PASCAL VOC 2007 数据集中的VOCdevkit/VOCcode
文件夹复制到这里就行。results/VOC2007/Main
测试的时候会在此文件夹下临时存放一个文件,没有这个路径的话会报错:
Error using fprintf Invalid file identifier. Use fopen to generate a valid file identifier.
准备图片
PASCAL VOC 2007 数据集中包含 20 类图片,但是我只需要检测一类,就是人脸,所以到底使用多少张图片合适呢?我自己写了个 Python 脚本,读取 Annotations
文件夹中的 xml 文件分析了一下每个类型在所有图片中出现的次数,结果如图:
感觉每个类型出现的次数也不多,那我就选 CelebA 数据集中前 1000 张图片吧,应该够用了。把 CelebA 数据集中文件名为 000001.jpg
到 001000.jpg
的图片放到 VOCdevkit/VOC2007/JPEGImages
里。
准备标注文件
PASCAL VOC 2007 数据集的标注文件为 xml 格式的,以 000002.xml
为例:
<annotation> <folder>VOC2007</folder> <!-- 图片的文件名 --> <filename>000002.jpg</filename> <source> <database>The VOC2007 Database</database> <annotation>PASCAL VOC2007</annotation> <image>flickr</image> <flickrid>329145082</flickrid> </source> <owner> <flickrid>hiromori2</flickrid> <name>Hiroyuki Mori</name> </owner> <!-- 图片宽、高、深度 --> <size> <width>335</width> <height>500</height> <depth>3</depth> </size> <segmented>0</segmented> <object> <name>train</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <!-- 标注框信息 --> <bndbox> <xmin>139</xmin> <ymin>200</ymin> <xmax>207</xmax> <ymax>301</ymax> </bndbox> </object> </annotation>
CelebA 提供的标注信息都存放在 list_bbox_celeba.txt
中。此文本除 1、2 行外,其余行均为各图片的标注信息,格式为
列数 | 内容 | 例子 |
---|---|---|
1 | 图片文件名 | 000002.jpg |
2 | 标注框左上角x坐标 | 72 |
3 | 标注框左上角y坐标 | 94 |
4 | 标注框宽 | 221 |
5 | 标注框高 | 306 |
需要利用这些信息来构建 xml 格式的标注文件,当然,手动转换是不现实的,所以我写了个 Python 脚本来转换,使用了 PASCAL VOC 2007 数据集中的标注文件 000002.xml
作为模板,仅仅将模板中的 filename
、size
、bndbox
元素修改了就行,其他的不动。代码如下:
import xml.etree.cElementTree as ET import cv2 # 使用000002.xml作为模板,因为它只有一个object元素 template_file = '/home/zack/pascal_voc/2007/VOCdevkit/VOC2007/Annotations/000002.xml' # 生成的xml文件保存的路径 target_dir = '/home/zack/pascal_voc/celeba/VOCdevkit/VOC2007/Annotations/' # 所有图片的路径 image_dir = '/home/zack/pascal_voc/celeba/VOCdevkit/VOC2007/JPEGImages/' # 给出所有标注信息的文件,由CelebA提供 bbox_file = '/home/zack/celeba/list_bbox_celeba.txt' # 图片数量 image_num = 1000 bboxes = open(bbox_file, 'r') for bbox in bboxes.readlines()[2 : image_num + 2]: bb_info = bbox.split() image_file = bb_info[0] x_1 = int(bb_info[1]) y_1 = int(bb_info[2]) width = int(bb_info[3]) height = int(bb_info[4]) tree = ET.parse(template_file) root = tree.getroot() # filename root.find('filename').text = image_file # size sz = root.find('size') im = cv2.imread(image_dir + image_file) sz.find('width').text = str(im.shape[0]) sz.find('height').text = str(im.shape[1]) sz.find('depth').text = str(im.shape[2]) # object obj = root.find('object') obj.find('name').text = 'face' bb = obj.find('bndbox') bb.find('xmin').text = str(x_1) bb.find('ymin').text = str(y_1) bb.find('xmax').text = str(x_1 + width) bb.find('ymax').text = str(y_1 + height) xml_file = image_file.replace('jpg', 'xml') tree.write(target_dir + xml_file) print xml_file bboxes.close() print 'Done'
执行以上脚本后,所有的标注框文件就已经生成了。如果出现 ImportError: No module named cv2
的错误,执行以下命令安装 OpenCV 的 Python 接口:
$ sudo apt-get install python-opencv`
准备数据集配置文件
配置文件即 ImageSets/Main
下的四个文件,定义四种数据集,这四种数据集中的图片会被分别用来做训练、验证、训练和验证、测试。我看了看 PASCAL VOC 2007 中四个数据集的分配,发现每个数据集中的图片应该是随机抽取的,但需要满足一定的条件: trainval
和 test
约为总图片数的一半,但不重合,即选出其中一个数据集的图片后,另一个数据集的图片就是选剩下的图片了,而 train
和 val
约占 trainval
的一半,同样不重合。所以我也用这个比例来制作这四个数据集吧。依然写了个 Python 脚本(Python 真是太好用了