在TensorFlow里把Image Resize到固定尺寸同时保持宽高比

在 2018-06-27 发布于 人工智能 下以来已有1,157人读过本文 | 0条评论 发表评论

使用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

发表评论

您的昵称 *

您的邮箱 *

您的网站