Is there a way to speed up the Save method with PIL?

Question:

I have an API that saves an the image to S3 bucket and returns the S3 URL but the saving part of the PIL image is slow. Here is a snippet of code:

from PIL import Image
import io
import boto3

BUCKET = ''
s3 = boto3.resource('s3')

def convert_fn(args):
  pil_image = Image.open(args['path']).convert('RGBA')
  .
  .
  .
  in_mem_file = io.BytesIO()
  pil_image.save(in_mem_file, format='PNG') #<--- This takes too long
  in_mem_file.seek(0)
  s3.meta.client.upload_fileobj(
      in_mem_file,
      BUCKET,
      'outputs/{}.png'.format(args['save_name']),
      ExtraArgs={
          'ACL': 'public-read',
          'ContentType':'image/png'
      }
                               )

  return json.dumps({"Image saved in": "https://{}.s3.amazonaws.com/outputs/{}.png".format(BUCKET, args['save_name'])})

How can I speed up the upload?, Would it be easier to return the bytes?

The Image.save method is the most time consuming part of my script. I want to increase the performance of my app and I’m thinking that returning as a stream of bytes may be the fastest way to return the image.

Asked By: Diego Rodea

||

Answers:

Compressing image data to PNG takes time – CPU time. There might be a better performant lib to that than PIL, but you’d have to interface it with Python, and it still would take sometime.

"Returning bytes" make no sense – you either want to have image files saved on S3 or don’t. And the "bytes" will only represent an image as long as they are properly encoded into an image file, unless you have code to compose back an image from raw bytes.

For speeding this up, you could either create an AWS lambda project that will take the unencoded array, generate the png file and save it to S3 in an async mode, or, easier, you might try saving the image in an uncompressed format, that will spare you from the CPU time to compress PNG: try saving it as a .tga or .bmp file instead of a .png, but expect final files to be 10 to 30 times larger than the equivalent .PNGs.

Also, it is not clear from the code if this is in a web-api view, and you’d like to speedup the API return, and it would be ok if the image would be generated and uploaded in background after the API returns.

In that case, there are ways to improve the responsivity of your app, but we need to have the "web code": i.e. which framework you are using, the view function itself, and the calling to the function presented here.

Answered By: jsbueno