MXNet im2rec.py使用教程

im2rec.py是MXNet提供的一个将图片转为rec文件的工具。 当训练数据包含大量图片的时候,一次性将所有数据载入内存很容易导致out of memory。使用rec文件和MXNet提供的 ImageRecordIter或ImageIter迭代器,可以按批次读取数据到内存,且读取图片更加简单方便。本文将介绍.lst,.rec文件的生成、多标签文件的处理,ImageRecordIter和ImageIter迭代器的使用以及比较。

Platform: Ubuntu 16.04
MXNet: 0.11.0

制作.lst文件

首先需要下载mxnet,找到tools文件夹的im2rec.py文件,我的是

/home/username/mxnet/tools/im2rec.py

然后需要准备图片数据,放入test_img文件夹中。

提示:最好将图片统一size,避免使用ImageRecordIter时将图片裁剪。

执行命令

python /home/username/mxnet/tools/im2rec.py --list=1 my_test test_img

--list=1表示制作.lst而不是.rec文件。
my_test是.lst的文件名。
test_img是图片所在文件夹。
更多参数设置参考文档。

运行完毕后打开my_test.lst,格式如下

1	0.000000	xxxx.jpg
0	0.000000	xxxx.jpg
...

每列之间都是\t分隔。

第一列数字是编号,第二列是标签,第三列是文件名。

此处因为只有一个图片文件夹,因此其标签分类只有一个,即0。现实情况是多个文件夹存放不同分类的图片,im2rec会根据不同文件夹生成不同的标签。例如Caltech 101数据集:

生成的.lst文件为

3073	6.000000	airplanes/image_0684.jpg
7167	69.000000	okapi/image_0017.jpg
6153	52.000000	ibis/image_0073.jpg
7761	81.000000	scissors/image_0005.jpg
7792	81.000000	scissors/image_0036.jpg
...

6对应的分类为airplanes。详见【MXNet官方教程5】Iterators-加载数据。

如果图片为多标签数据,需要手动修改第二列。参见本文后面部分。

制作.rec文件

有了.lst文件后,直接生成.rec文件:

python /home/username/mxnet/tools/im2rec.py --num-thread=4 my_test.lst test_img

更多参数参考文档。

在当前目录下生成了my_test.idxmy_test.rec两个文件。接下来就可以从.idx和.rec文件中读取图片了。

使用ImageRecordIter加载数据

在【MXNet官方教程5】Iterators-加载数据中介绍过使用方法,这里再解释一下。

data_iter = mx.image.ImageRecordIter(batch_size=4, 
							  resize=30,
							  data_shape=(3, 30, 60),# depth,height,width
                              path_imgrec="./xxx/my_test.rec",
                              path_imgidx="./xxx/my_test.idx" )
data_iter.reset()
batch = data_iter.next()
data = batch.data[0]
for i in range(4):
    plt.subplot(1,4,i+1)
    plt.imshow(data[i].asnumpy().astype(np.uint8).transpose((1,2,0)))
plt.show()

data_shape:图片的size,前文中提到最好将图片size统一。如果不统一,ImageRecordIter将按照data_shape对图片裁剪,可能丢失图片信息。
resize:当图片短边小于data_shape时,resize成给定大小。
更多参数配置参考文档。

一个错误

what():  [15:45:50] src/io/image_aug_default.cc:300: Check failed: 
static_cast(res.rows) >= param_.data_shape[1] && 
static_cast(res.cols) >= param_.data_shape[2] 
input image size smaller than input shape

原因是resize值小于图片实际长度。

多标签图片.rec制作

一些场景比如目标检测下,一张图片有多个标签(bbox)。需要修改.lst文件为以下格式:

46330	1  0.1    0.1    0.3    0.3	xxxx.jpg
213723	2  1.1    1.1    1.3    1.3	xxxx.jpg
173090	3  2.1    2.1    2.3    2.3	xxxx.jpg
...

每列之间都是\t分隔。

其中,第2-6列为标签数据(比如一个class标签和4个bbox标签)。

参考脚本:

with open('my_test.lst', 'w+') as f:  
    for i in range(3):  
        f.write(  
            str(i) + '\t' +  
            # idx        
            str(i) + '\t' +  
            # class  
            str((i / 10)) + '\t' + str((i / 10)) + '\t' + str(((i + 3) / 10)) + '\t' +str(((i + 3) / 10)) + '\t' +  
            # xmin, ymin, xmax, ymax  
            'xxxx.jpg\n'
            # image path
        )

然后执行命令生成.rec文件:

python /home/username/mxnet/tools/im2rec.py --num-thread=4 --pack-label=1 my_test.lst test_img

多了一个--pack-label=1参数。

使用ImageRecordIter:

data_iter = mx.image.ImageRecordIter(batch_size=4, 
							  resize=30,
							  label_width=5,
							  data_shape=(3, 30, 60),# depth,height,width
                              path_imgrec="./xxx/my_test.rec",
                              path_imgidx="./xxx/my_test.idx" )
data_iter.reset()
batch = data_iter.next()
img, labels = batch.data[0], batch.label[0]
print(labels)

多了一个label_width配置。

使用ImageIter加载数据

相比ImageRecordIter,ImageIter既能读原图数据,也能读rec数据。在ImageRecordIter中对于数据的预处理操作都是固定的,不好修改,但ImageIter却可以非常灵活地添加各种预处理操作。

data_iter = mx.image.ImageIter(
        batch_size=4,
        data_shape=(3, 30, 60),
        label_width=5,
        path_imglist="./xxx/my_test.lst",
        path_root='/xxx',
        shuffle=True
    )

用ImageIter读取原图与ImageRecordIter读取rec比较:

  • ImageRecordIter生成.rec文件需要耗费较多时间和空间。当数据集变化时,ImageIter只需要重新生成.lst,简单快捷。
  • 读取速度上ImageRecordIter大概是ImageIter的100倍(自测)。

因此当数据修改频繁时,可使用ImageIter测试效果。确定好数据集后,生成一次rec文件可以大大提高训练速度。

一些问题

  1. 使用rec文件训练的模型在测试中表现很差
    训练过程中,训练集和验证集都得到了比较高的准确率。训练好模型之后,输入图片进行测试,结果比验证集都要差很多。即使是用验证集的数据,效果也很差。为什么同样的图片会一个准一个不准呢?问题只可能出在图片的加载上。在测试时,使用的是opencv进行读取。debug图片数据,发现和rec中读取的图片有细微差别。后来找到原因是opencv读取图片是BGR模式,而MXNet从rec中读取的是RGB,导致这个问题。

    解决方法是调整opencv的图片格式:

    img = cv2.imread(imgpath)
    b, g, r = cv2.split(img)
    img = cv2.merge([r, g, b])
    

参考:
https://github.com/leocvml/mxnet-im2rec_tutorial

你可能感兴趣的:(mxnet)