Skip to content

Image Resize Tutorial

Albert Suarez edited this page Jan 23, 2019 · 4 revisions

Overview

The goal of this tutorial is to demonstrate how to properly resize images before sending them to our Computer Vision API service.

Resizing Images

In general, sending an image at full resolution is suboptimal and unnecessary. A lot of modern cameras have 4k or even 5k (or higher) native resolution, but there is not really a need to send an image with a resolution higher than 800x600 for multiple reasons:

  • networking performance - larger files take longer to transfer across the internet
  • processing performance - larger images take more time to process
  • cost - sending large files at high volumes can consume a lot of bandwidth, which can potentially incur high costs from your ISP / service provider

Example Code

We have provided all the code in our GitHub repository if you want to jump directly to the sample code. Alternatively, if you'd like to follow along, please clone this project:

git clone https://github.com/restbai/sdk.git

Once the SDK / tutorial code has been cloned, please refer to the restb/examples/resize_tutorial.py file to follow along.

Tutorial

For example purposes, the first thing we will do is download and load a test image to resize:

# 1. first download and load image
response = requests.get(url='https://demo.restb.ai/images/demo/demo-2.jpg')
image = Image.open(BytesIO(response.content))
print("Original image dimensions: {}".format(image.size))

Just like with our Python SDK Tutorial, we will leverage the requests.py library to send HTTP requests. In addition, we now utilize the Pillow implementation of PIL for generic image processing, including resizing and saving images. The above sample code simply downloads a test image from Restb.ai and loads it with the Pillow library.

Then all we need to do is invoke our SDK's resize function:

# 2. then invoke the API resize function
resized_image = resize_image(image)
print("New resized dimensions: {}".format(resized_image.size))

That's all you need to do to resize images using our SDK. However, if you are using a language not covered by our SDK, let's break down what the api.resize_image() function actually does:

def resize_image(image):
    # calculate new dimensions
    x, y = image.size
    small = min(x, y)
    factor = small / 600 if small > 600 else 1.0
    resize_dimensions = int(x / factor), int(y / factor)

    # resize (using LANCZOS resampling/interpolation)
    image = image.resize(resize_dimensions, resample=Image.LANCZOS)
    return image

The first thing it does is calculate new dimensions to resize to. At a minimum, the smallest edge of an image (could be width or height) should ideally be approximately 600 pixels. To do this, the code first finds the smallest edge:

small = min(x, y)

It then calculates a factor (divisor) to divide by in order to derive the final resolution, while retaining aspect ratio. It also doesn't actually decrease the image size unless the smallest edge is smaller than 600 pixels:

factor = small / 600 if small > 600 else 1.0

Finally, we call the Image.resize() function with our new target dimensions. The most important detail here is that we are using Lanczos Interpolation / Resampling. This is critical for retaining image quality and details during the Image.resize() operation. This has a potentially high impact on our Computer Vision API service, so it's important to specify the algorithm directly.

One additional factor to consider when resizing images is how to encode the image before sending it to our API service. Our general recommendation is to re-encode the image as a high quality JPEG before sending it. The api.resize_image_path() function in our SDK specifies best practices when saving an image:

def resize_image_path(image_load_path, image_save_path):
    # load image
    img = Image.open(image_load_path)

    # handle resize
    resized_img = resize_image(img)

    # save the image by re-encoding as JPEG
    resized_img.save(fp=image_save_path, format='JPEG', quality=90)

The most important take-away with this function is the Image.save() call; we specify that it should be saved as a JPEG, with a quality level of 90 (out of 100). This represents a good overall compromise between image quality and image size; the higher the quality specified, the larger the file size. If you specify a quality value too high (e.g. anything over 95), the resulting saved image can be even larger than the original source image without actually being of higher quality.