使用TensorFlow读入一幅图片后,有时候我们需要将图片的尺寸进行一些修正,比如AlexNet所做的crop操作,或者某些网络需要固定的输入而将图片resize到固定的大小,等等。如果是针对类似numpy array这样的内容进行这些操作,可能大家都能手到擒来,但在TensorFlow里进行这样的操作需要一些tensor操作,这些操作有些反人类,而且调试不易。所以,本文我们就对这些东西进行一些总结,以便备忘,并方便后来者查阅。
1. Resize到固定尺寸大小
这个操作相对比较简单,直接使用tf.image.resize_images()
即可,如下:
image = tf.image.resize_images(image, [new_height, new_width])
但是这样的操作会破坏图片的原始比例,可能会影响后续的处理结果。所以,通常情况下,我们一般不再直接使用这种操作。
2. 通过padding操作把原始图片resize到一个固定大小,同时保持图片的原始比例
在保持图片的原始比例的同时,把原始图片resize到一个固定的大小,考虑到神经网络的输入要求,这个固定的大小通常是一个正方形。我们自然会想到将较长的边resize到这个固定大小,而将较小边的空余地方padding上值为0的像素,TensorFlow提供了这样的函数tf.image.pad_to_bounding_box可以直接进行操作。 该函数的介绍是这样的:
tf.image.pad_to_bounding_box(
image,
offset_height,
offset_width,
target_height,
target_width
)
可以看到,它需要5个参数,即原始图片tensor,在高和宽方向上各需要padding多少像素,以及padding之后的图片的目标高和宽。很显然,需要目标高宽大于原始图片的高和宽。 至于我们需要的操作,将原始图片resize到一个固定大小,则可以首先将图片resize到最大边与padding之后的目标大小相等,然后在小边上padding像素。 以一副高宽为300×500的图片resize到224×224的图片为例,如下: 首先将原始的300*500的image resize到[300*(224/500)]*224,即134*224,然后宽边不作处理,在高边的两侧需要各padding (224-134)/2和224-134-(224-134)/2,即45和45像素。 因此,函数tf.image.pad_to_bounding_box的5个参数如下:
- image为resize过后的大边为224的image;
- offset_height为45,offset_width为0;
- target_height和target_width都是224.
如此,即可将原来高宽为300*500的图片resize到224*224的图片,且保持了图片原来的原始比例。
3. Resize图片时,padding不同的像素值
如2中操作,在resize时的padding操作只能padding像素值为0的内容,但有时候我们想在图片的周围padding上ImageNet的Mean值,即[123.68, 116.779, 103.939]。这时候该怎么处理呢? 我在GitHub上TensorFlow的Repository里提了一个Feature Request Issue,但是并没有获得好的反馈,也没有人进行开发。因此,目前还是只能自己修改添加。具体我是这样处理的:
3.1 修改函数tf.image.pad_to_bounding_box,在里面加入一个constant_values参数,指定padding的数值;
具体可以参考这个函数的实现,该函数里是在这行代码进行的padding:
padded = array_ops.pad(image, paddings)
我们打开该pad函数,其定义如下:
def pad(tensor, paddings, mode=”CONSTANT”, name=None, constant_values=0)
可以看到,修改上面代码,传入一个constant_values即可:
padded = array_ops.pad(image, paddings, constant_values=constant_values)
3.2 为tf.image.pad_to_bounding_box传入constant_values值
需要注意的是,该函数修改后还是只能针对一个tensor padding同样的值,所以,如果我们想为不同的通道传入不同的值,还是需要一些trick:
- 将一个image Tensor的三个通道slice出来成三个Tensor
- 分别对三个Tensor使用不同的值进行padding
- 将三个padding过后的Tensor重新concat到一起
代码片段如下:
image_slices = []
for i in range(3):
img = image[…, i, tf.newaxis]
img = tf.image.pad_to_bounding_box(img, offset_height, offset_width,
image_size, image_size, constant_values=MEAN[i])
image_slices.append(img)
image = tf.concat(image_slices, 2)
代码参考
与本文相关的代码请参考:Resize-to-fixed-size-keeping-aspect-ratio