Easy Django Image Compression
This article written for completing PPL 2021 Individual Review competencies
Image Compression is one of important things when our software create a service to save images and serve image. Image Compression could decrease server storage usage and decrease user data usage and image load time on app, this could bring great user experience for the user. But how we are going to compress it in Python?
PNG vs JPG
Before we talk about how to compress the image, let’s talk about this two common type of image PNG and JPG.
PNG stands for Portable Network Graphics, it is a lossless compression file format, which makes it a common choice for use on the Web. PNG is a good choice for storing line drawings, text, and iconic graphics at a small file size. PNG support RGB and RGBA color model and PNG support transparency by set single color defined as transparent, and the other is to set an alpha channel.
JPEG stands for Joint Photographic Experts Group, It is a lossy compressed file format. This makes it useful for storing photographs at a smaller size than a BMP. JPEGs images were designed to make detailed photographic images as small as possible by removing information that the human eye won’t notice. PNG doesn’t support transparency and could’nt store RGBA color model. By converting a PNG file to JPEG you can save a lot of space, most of the time between 50–70%.
From this difference, we might conclude that JPEG is better to use rather than PNG as an image file format due to it’s lossy compression and could convert PNG image to a smaller size with human eye couldn’t (most of the time) see the difference. But it comes back to the app purpose to save an image, does the app need to preserve the best image quality or not.
In my Software Project that I currently working on in PPL course, we focusing on keeping photograph images, so we choose to save image with JPEG file format.
Python Image Compression
Before we talk how to save and compress uploaded image in Django, lets talk how to do it in plain Python. The library that we use is Pillow to process image. here are the code to convert any image to JPEG format and compress it.
Isn’t it easy? all you need to do is a few lines of code and you could optimize the image. In this code, I force convert the image to RGB color mode, color mode that JPEG support, auto-rotate the image according to image EXIF data using ImageOps, and save it to optimized/compressed JPEG image file according to quality percentage.
Image above are the comparison of the original image and the compressed image from the code above. The original image size is 1.8 MB and the compressed image file size is 440,9 kB. This a great compression without any significant quality loss. But the rate of compression is vary according to the original image, if the original image is already compressed, there might be no significant file size reduction.
It easy to compress Image in Python, but how are we going to process the image in Django models?
Image Compression Django Model
Processing image in Django models is quite easy, we only need to override the Django model class save method to process the image. here are the code on how to do it with comments.
It only need a few tweaks from the from the Python image compression code before. but it raises another problem, If we have multiple Django class models, doesn’t that means that means that we need to override every Django class models save method that contains ImageField to compress it? are there any other solution to avoid code repetition?
Well folks, one of the solution I propose is creating Django Custom ImageField.
Django Custom ImageField
Before we make the Custom ImageField, we need to see the source code of ImageField itself, here are the link to the source code. Below are the section of source code that we need to focus on to create Django Custom ImageField.
Django ImageField has 3 attributes class, attr_class, descriptor_class, and description_class. We’re only interested in attr_class, because attr_class is responsible for saving the image.
attr_class contains ImageFieldFile which inherits FieldFile class, the FieldFile class has a save method to save the file to the storage. We could override this function to process the image before saving it to the storage.
ImageField also has deconstruct method, this method could send variables to be accesed by attr_class by adding new key and value to kwargs, atrr_class could access the variable by calling self.field.key_inserted_to_kwargs.
With this knowledge we could create Custom ImageField by inherits the ImageField class. Lets call the Custom ImageField that we created as CompressedImageField.
With this code we could avoid code repetition from overriding Django model class method save
I thinks that’s all from me, i hope this article could help and happy coding! :D