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

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

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

Если я пойду с таким подходом, мне нужно будет проверить, менее ли после сжатия сжатие изображения менее 500 кб, если нет, то идите для более низкого качества и размера.

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

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

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

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

Редактирование: вот реализация подходящего байтового счетного файла. Просто проверьте 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