matthewwithanm / pilkit

Utilities and processors built for, and on top of PIL
BSD 3-Clause "New" or "Revised" License
196 stars 54 forks source link

Excessive memory usage of Resize* processors #44

Closed ykiu closed 3 years ago

ykiu commented 4 years ago

Hi. I've been using pilkit via django-imagekit and they have been helping me a lot!

Recently I noticed my django app consumes weirdly large amount of memory when handling requests with some jpeg photos. The high memory usage turned out to be caused by this line of code, which adds an alpha channel to address this antialiasing issue.

When fed with a 2.8 MB image of sizes 4000x3000, the following Resize.process() call consumed 157 MiB of RAM:

from PIL import Image
from pilkit import processors
img = Image.open('path/to/image')
processors.Resize(100, 100).process(img)  # 157 MiB of RAM usage

When I removed img = img.convert('RGBA') from inside Resize.process(), the above code consumed only 65 MiB.

Since the original antialiasing issue seems to affect only GIF images, why don't we just skip the RGBA conversion for non-GIF images? (i.e. adding an if statement)

Would love to hear your thoughts. Thanks!

vstoykov commented 3 years ago

Hi! Thank you for your report and apologies for the late reply.

I'm very glad that django-imagekit and pilkit are helping you a lot. I also hope that you are still using it despite the problem that you are experiencing. In the issue you linked above is mentioned that for the JPEG images it is working correctly. I did not got it if for PNG the problem also occurs or there is ok. If the problem with the ugly resize is happening only for GIF images and we add the alpha channel only for them this sounds as a very good improvement.

If you have the time would it be possible to prepare a patch that can address the issue?

ykiu commented 3 years ago

Hi, thanks for taking this up.

Yes, I'm still using this library! I created a copy of the Resize processor and removed the img = img.convert('RGBA') line😀

As you suggested, it is not clear if the problem occurs for PNG images as well. I'll check if there is a reasonable way to distinguish images subject to ugly resize and those that are not, hopefully next weekend. I'll let you know how it goes.

Thank you.

vstoykov commented 3 years ago

Hi @ykiu. Just to ping you up, did you have time to do your investigation?

ykiu commented 3 years ago

Apologies for being silent for a while. I just opened PR #49, which addresses this issue.

It looks like it is those images that have palettes that get ugly when resized. Non-palette (i.e. regular RGB or RGBA) images are immune to ugly resizing.

To make a good-looking thumbnail out of a palette image, we need to resolve the palette before resizing. This is already achieved in pilkit and PR #49 adds some optimizations to it:

  1. Skip the conversion to RGB/RGBA if the image does not have a palette (i.e. already in RGB/RGBA).
  2. If the image does have a palette, convert the image based on the target mode of the palette. That is, if the palette maps to RGB colors, convert the image to RGB; If the palette maps to RGBA, convert the image to RGBA.

Hope to hear your thoughts. Thanks!