When we are using convolutional neural networks, most of the time, we need to fix the input image size to feed it to the network. The usual practice is to resize the input image to the given size (the image aspect ratio is no longer kept) and then crop a fixed size patch randomly from the resized image. This practice may work well for image classification where fine details may not be necessary. But for Image retrieval, we want to keep the image aspect ration unchanged. In this post, I will summarize ways to resize an image to square shape with padding and keep its aspect ratio.

The main idea is to first resize the input image so that its maximum size equals to the given size. Then we pad the resized image to make it square. A number of packages in Python can easily achieves this.

Using PIL

PIL is a popular image processing package in Python. We can use either Image module or the ImageOps module to achieve what we want.

Resize and pad with Image module

First we create a blank square image, then we paste the resized image on it to form a new image. The code is:

from PIL import Image, ImageOps

desired_size = 368
im_pth = "/home/jdhao/test.jpg"

im = Image.open(im_pth)
old_size = im.size  # old_size[0] is in (width, height) format

ratio = float(desired_size)/max(old_size)
new_size = tuple([int(x*ratio) for x in old_size])
# use thumbnail() or resize() method to resize the input image

# thumbnail is a in-place operation

# im.thumbnail(new_size, Image.ANTIALIAS)

im = im.resize(new_size, Image.ANTIALIAS)
# create a new image and paste the resized on it

new_im = Image.new("RGB", (desired_size, desired_size))
new_im.paste(im, ((desired_size-new_size[0])//2,
                    (desired_size-new_size[1])//2))

new_im.show()

Resize and pad with ImageOps module

The PIL ImageOps module has a expand which will add borders to the 4 side of an image. We need to calculate the padding length in 4 side of the resized image before applying this method.

delta_w = desired_size - new_size[0]
delta_h = desired_size - new_size[1]
padding = (delta_w//2, delta_h//2, delta_w-(delta_w//2), delta_h-(elta_h//2))
new_im = ImageOps.expand(im, padding)

new_im.show()

Using OpenCV

In OpenCV, we have copyMakeBorder which is handy in making borders. The full code to resize and pad an image is as follows:

import cv2

desired_size = 368
im_pth = "/home/jdhao/test.jpg"

im = cv2.imread(im_pth)
old_size = im.shape[:2] # old_size is in (height, width) format

ratio = float(desired_size)/max(old_size)
new_size = tuple([int(x*ratio) for x in old_size])

# new_size should be in (width, height) format

im = cv2.resize(im, (new_size[1], new_size[0]))

delta_w = desired_size - new_size[1]
delta_h = desired_size - new_size[0]
top, bottom = delta_h//2, delta_h-(delta_h//2)
left, right = delta_w//2, delta_w-(delta_w//2)

color = [0, 0, 0]
new_im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT,
    value=color)

cv2.imshow("image", new_im)
cv2.waitKey(0)
cv2.destroyAllWindows()

I have upload the whole script to GitHub and you can download it here.

References