In Python, it's possible to open an image in one format and save it to another using Pillow, also known as Python's Image Library (PIL). This means we can open a PNG and save it as a JPG or PNG with just Python. However, normally examples to do this show how to do it using files: you open from a filepath, then save to a filepath. Sometimes we might want to avoid this step if, for example, we want to preview how the image will look like before actually saving it.
To do this, we can simply save the image to a buffer in memory instead of to a filepath, then open it from the buffer to see how it looks like. Saving will encode the image using the compression algorithm, and opening will decode it.
from io import BytesIO
from PIL import Image
def convert_in_memory(im: Image, *args, **kwargs) -> Image:
# create a buffer in memory
buffer = BytesIO()
# save to the buffer in memory
im.save(buffer, *args, **kwargs)
# open from the buffer in memory
new_im = Image.open(buffer)
return new_im
# the buffer is automatically freed
# after it goes out of scope
# because it's Python
png_image = Image.open('/home/me/Pictures/example.png')
jpeg_preview = convert_in_memory(png_image, 'jpeg', quality=80)
webp_preview = convert_in_memory(png_image, 'webp', lossless=True)
# if we want to save a preview afterwards
# we have to use the original image or it will re-encode
# right:
png_image.save('/home/me/Pictures/converted.jpg', 'jpeg', quality=80)
# wrong:
jpg_preview.save('/home/me/Pictures/converted.jpg')
Doing this you won't need to use a temporary file to get a preview.
Getting the File Size
To get the file size of the converted image without saving it to disk, you can use the buffer where we saved the file to like this1:
size_in_bytes = buffer.getbuffer().nbytes
We can make this human-readable very easily:
def get_file_size(value: int) -> str:
if value < 1000:
format = '%dB'
else:
value /= 1024
if value < 1000:
format = '%0.1fKiB'
else:
value /= 1024
format = '%0.2fMiB'
return format % (value,)
print("The file size is " + get_file_size(size_in_bytes))
Observations
Currently Pillow doesn't support some settings in some image codecs. Notably, it's missing the sharpyuv
setting for the WebP codec, which is useful to make WebP less blurry.
There is a pull request to add it [github.com/python-pillow/Pillow/pull/1263]. It was created in 2015.
ImageMagick and GIMP do have this ability, so one thing that you can do is create a temporary file then use subprocess
to convert the image using them.
References
- https://stackoverflow.com/a/26827410 (accessed 2024-10-03) ↩︎
Leave a Reply