6

I need to send a picture to another place, where it requires picture size must be less than 512kb.
I use PIL to deal with my picture which download from internet. so I don't know what will be the next picture's size, code like this:

from PIL import Image
picture_location = '/var/picture/1233123.jpg'
compressed_picture_location = '/var/picture/1233123_compressed.jpg'
im = Image.open(picture_location)
quality = 75
im.save(compressed_picture_location, quality=quality)
im.save()

The problem is that the compressed picture's file size is not 75% or 75%*75% of the origin picture, so I have to compress it, stat it, compressed again, I can't select a suitable quality value.

Is there other way to solve this problem? Please help or try to give some ideas how to achieve this.

Mohamed Thasin ah
  • 10,754
  • 11
  • 52
  • 111
guerbai
  • 325
  • 4
  • 11
  • Because compression is a set of discrete processes, the only way I can see is to write a script that brute-forces it. You can't tell what it will be before you start without doing it. I can pop a script below if you like. – AER Mar 06 '18 at 06:58
  • Unfortunately, that's how JPEG works. You can perhaps speed up repeated trials by using a `FileIO` object rather than writing to disk every time, although I wouldn't expect that to be much faster due to disk caching. – kindall Mar 06 '18 at 06:58
  • See here for example: https://opensource.com/life/15/2/resize-images-python – Marichyasana Mar 06 '18 at 07:31
  • Any updates on your progress? – AER Mar 08 '18 at 00:07
  • I found that the oss service my company using provide the compressed picture with a specific postfix,by using it I solve this problem. :p But in technique method, I still can't find a clean resolve. – guerbai Mar 09 '18 at 02:04
  • This should work well for you https://stackoverflow.com/a/52281257/2836621 – Mark Setchell Sep 24 '18 at 10:15

2 Answers2

2

When you're changing the quality using PIL, the dimensions of the image does not change, it just changes the quality of the image using JPEG compression. By default the value is at 80, and thus changing the value to 75 will not reduce the size by much, you can change the value to about 60 without loosing a lot of picture quality. The value reduces logarithmically so if you want the exact math, you can read about JPEG compression. https://math.dartmouth.edu/archive/m56s14/public_html/proj/Marcus_proj.pdf

2

Unfortunately this is a property of JPEG compression and there is no strict inverse function (you can calculate it mathematically if you want large scale efficiency).

However, you can inefficiently brute force the calculation and get it close with the following. If you don't want the same size, just change the quality to a scaling factor and it should work similarly.

import os
from PIL import Image

def compress_under_size(size, file_path):
    '''file_path is a string to the file to be custom compressed
    and the size is the maximum size in bytes it can be which this 
    function searches until it achieves an approximate supremum'''

    quality = 90 #not the best value as this usually increases size

    current_size = os.stat(file_path).st_size

    while current_size > size or quality == 0:
        if quality == 0:
            os.remove(file_path)
            print("Error: File cannot be compressed below this size")
            break

        compress_pic(file_path, quality)
        current_size = os.stat(file_path).st_size
        quality -= 5


def compress_pic(file_path, qual):
    '''File path is a string to the file to be compressed and
    quality is the quality to be compressed down to'''
    picture = Image.open(file_path)
    dim = picture.size

    picture.save(file_path,"JPEG", optimize=True, quality=qual) 

    processed_size = os.stat(file_path).st_size

    return processed_size

for file in os.listdir("pics"):
    try:
        pic = f"./pics/{file}"
        compress_under_size(10000,pic)
    except Exception:
        pass
rspeer
  • 3,539
  • 2
  • 25
  • 25
AER
  • 1,549
  • 19
  • 37