Библиотека изображений Python (PIL), как сжать изображение до желаемого размера файла?

Я получил требование сжать любые загруженные изображения размером менее 500 КБ, я искал в Google, и все, что я вижу, это:

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

Если я воспользуюсь этим подходом, мне придется проверить, не превышает ли размер изображения 500 КБ после сжатия, а если нет, то перейти к более низкому качеству и размеру.

Есть ли лучший способ сделать это?

 Lie Ryan16 нояб. 2012 г., 00:34
Лучший способ сделать это - бинарный поиск по качеству. Поэтому начните с 50% качества, затем проверьте размер, если онслишком мало, тогда попробуйте 75%, иначе попробуйте 25% и т. д.Получите как можно ближе к 500 КБ, вы можете дополнительно установить некоторые дополнительные параметры, например. минимальный допуск на качество или размер. Вы должны быть в состоянии сосредоточиться на правильном уровне сжатия не более чем за 7 итераций.

Ответы на вопрос(2)

я приведу свой код здесь, так что может быть полезно, кто-то с такой же проблемой

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/

Решение Вопроса

который вы описали, сжать и измерить и попробуйте еще раз, это единственный способ, которым я знаю.

Вы можете попробовать сжать ряд типичных изображений с различными настройками качества, чтобы получить представление об оптимальной начальной точке, а также способ угадать, как изменения настроек повлияют на размер. Это поможет вам найти оптимальный размер без слишком большого количества итераций.

Вы также можете передать файловый объект вsave функция, которая незаписывать на диск, просто считает байты. Однажды ты'После того, как вы определили лучшие настройки, вы можете снова сохранить их в реальном файле.

Изменить: здесьs реализация подходящего объекта файла подсчета байтов. Просто проверьsize после сохранения.

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)

Редактировать 2: Здесь 'бинарный поиск с использованием вышеупомянутого, чтобы получить оптимальныйquality в наименьшем количестве попыток.

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

Ваш ответ на вопрос