11

I got a requirement to compress any uploaded images less than 500kb in file size, I have searched on google and all I can see is:

 >>> foo = foo.resize((160,300),Image.ANTIALIAS)
 >>> foo.save("path\\to\\save\\image_scaled.jpg",quality=95)

If I go with this approach I will have to check if the image is less than 500kb after compress, if not then go for lower quality and size.

Is there a better way to do it?

1
  • 1
    A better way to do it is to binary search the quality. So start with 50% quality, then check the size, if it's too small then try 75%, else try 25%, etc. You'll get as close as possible to 500KB, you might additionally set some additional parameters, eg. minimum quality or size tolerance. You should be able to zero in to the correct compression level in at most 7 iterations. Commented Nov 15, 2012 at 23:34

2 Answers 2

11

JPEG compression is not predictable beforehand. The method you described, compress & measure & try again, is the only way I know.

You can try compressing a number of typical images with different quality settings to get an idea of the optimum starting point, plus a way of guessing how changes to the setting will affect the size. That will get you to zero in on the optimum size without too many iterations.

You can also pass a file-like object to the save function that doesn't bother to write to disk, just counts the bytes. Once you've determined the best settings then you can save it again to an actual file.

Edit: Here's an implementation of a suitable byte counting file object. Just check size after the save.

class file_counter(object):
    def __init__(self):
        self.position = self.size = 0

    def seek(self, offset, whence=0):
        if whence == 1:
            offset += self.position
        elif whence == 2:
            offset += self.size
        self.position = min(offset, self.size)

    def tell(self):
        return self.position

    def write(self, string):
        self.position += len(string)
        self.size = max(self.size, self.position)

Edit 2: Here's a binary search using the above to get the optimal quality in the smallest number of attempts.

def smaller_than(im, size, guess=70, subsampling=1, low=1, high=100):
    while low < high:
        counter = file_counter()
        im.save(counter, format='JPEG', subsampling=subsampling, quality=guess)
        if counter.size < size:
            low = guess
        else:
            high = guess - 1
        guess = (low + high + 1) // 2
    return low
Sign up to request clarification or add additional context in comments.

Comments

3

Guess I will provide my code here so it might be helpful someone having the same problem

class PhotoField(forms.FileField, object):

    def __init__(self, *args, **kwargs):
        super(PhotoField, self).__init__(*args, **kwargs)
        self.help_text = "Images over 500kb will be resized to keep under 500kb limit, which may result in some loss of quality"

    def validate(self,image):
        if not str(image).split('.')[-1].lower() in ["jpg","jpeg","png","gif"]:
            raise ValidationError("File format not supported, please try again and upload a JPG/PNG/GIF file")

    def to_python(self, image):
        limit = 500000
        img = Image.open(image.file)
        width, height = img.size
        ratio = float(width) / float(height)
        quality = 100
        while len(image.file.read()) > limit:
            width -= 100
            quality -= 10
            height = int(width / ratio)
            img.resize((width, height), Image.ANTIALIAS)
            img.save(image.file.name, "JPEG", quality=quality)
            image.file = open(image.file.name)
            # reset the file pointer to the beginning so the while loop can read properly
            image.file.seek(0)
        return image

http://james.lin.net.nz/2012/11/19/django-snippet-reduce-image-size-during-upload/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.