How to convert an image to a specific file size?

960 Views Asked by At

I am working with Pillow, Django, and django-imagekit.

I am looking to be able to have a profile picture model field (probably using the ProcessedImageField class from imagekit) that will take any image, convert to JPEG, crop it to 150x150, and make its file size 5KB.

The first two are easy:

profile_picture = imagekit.models.ProcessedImageField(upload_to=get_profile_picture_file_path,
                                                      format='JPEG',
                                                      processors=[ResizeToFill(height=150, width=150)]
                                                      )

But how can I ensure the file size is 5KB? I could use something like the options={'quality': 60} parameter in ProcessedImageField, but that seems to be only relative to the original file size (to my knowledge).

Solutions don't have to use django-imagekit, but that would be preferred.

2

There are 2 best solutions below

2
On

Maybe in such way. Check the size of image after uploaded and remove it or decrease more in overridden save method:

class Images(models.Model):
    profile_picture = imagekit.models.ProcessedImageField(upload_to=get_profile_picture_file_path,
                                                  format='JPEG',
                                                  processors=[ResizeToFill(height=150, width=150)]
                                                  )

    def save(self, force_insert=False, force_update=False, using=None,
             update_fields=None):

        if os.stat(get_profile_picture_file_path + "/" + self.profile_picture.new_name).st_size > max_size:
            do_something_further_image_processing_to_decrease_size

        super(Images, self).save()
0
On

I used to have a similar issue, so I decided to optimize the images using OS tools (jpegoptim, optipng, etc) called from django after save the model using signals (you can do overriden save method too). This tools optimize and eliminate metadata from your images. In the other hand you could study the average compression ratio and size for jpg files of 150x150 and try to guess the best quality to setup check this: (jpeg compression ratio)

This is my code for optimize files after save them, I'm using easy thumbnails library which provide me signals after save:

@receiver(saved_file)
def optimize_file(sender, fieldfile, **kwargs):
    optimize(fieldfile.path)


# thumbnail optimization
@receiver(thumbnail_created)
def optimize_thumbnail(sender, **kwargs):
    optimize(sender.path)

def optimize(path):
    """
    install image utilities
    apt-get install jpegoptim optipng pngcrush advancecomp
    :param path:
    :return:
    """
    # taken from trimage (http://trimage.org/)
    runString = {
        ".jpeg": u"jpegoptim -f --strip-all '%(file)s' ; chmod 644 '%(file)s'",
        ".jpg": u"jpegoptim -f --strip-all '%(file)s' ; chmod 644 '%(file)s'",
        ".png": u"optipng -force -o7 '%(file)s' && advpng -z4 '%(file)s' && pngcrush -rem gAMA -rem alla -rem cHRM -rem iCCP -rem sRGB -rem time '%(file)s' '%(file)s.bak' && mv '%(file)s.bak' '%(file)s' ; chmod 644 '%(file)s'"
    }

    ext = splitext(path)[1].lower()
    if ext in runString:
        subprocess.Popen(runString[ext] % {'file': path}, shell=True)