Вот функция, которую я написал с PIL.Он выполняет некоторое итеративное изменение размера и сжатие JPEG изображения, чтобы затем посмотреть на полученный размер файла и сравнить его с целевым значением, угадав следующую лучшую комбинацию ширины / высоты из отношения отклонения размера (в основном, своего рода P-контроллер).
Он использует io.BytesIO, который выполняет все операции по изменению размера в памяти, поэтому на самом деле есть только один доступ для чтения и один доступ к файлам на диске.Кроме того, с помощью этого грубого подхода вы можете изменить формат целевого файла, скажем, PNG, и он будет работать "из коробки".
from PIL import Image
import os
import io
def limit_img_size(img_filename, img_target_filename, target_filesize, tolerance=5):
img = img_orig = Image.open(img_filename)
aspect = img.size[0] / img.size[1]
while True:
with io.BytesIO() as buffer:
img.save(buffer, format="JPEG")
data = buffer.getvalue()
filesize = len(data)
size_deviation = filesize / target_filesize
print("size: {}; factor: {:.3f}".format(filesize, size_deviation))
if size_deviation <= (100 + tolerance) / 100:
# filesize fits
with open(img_target_filename, "wb") as f:
f.write(data)
break
else:
# filesize not good enough => adapt width and height
# use sqrt of deviation since applied both in width and height
new_width = img.size[0] / size_deviation**0.5
new_height = new_width / aspect
# resize from img_orig to not lose quality
img = img_orig.resize((int(new_width), int(new_height)))
limit_img_size(
"test.jpg", # input file
"test_with_limited_size.jpg", # target file
50000, # bytes
tolerance = 5 # percent of what the file may be bigger than target_filesize
)
РЕДАКТИРОВАТЬ:
С "в памяти"Я имел в виду, что когда он save
s img
до buffer
в цикле, он сохраняет его в объекте BytesIO
, который является не файлом на диске, а в памяти.И из этого объекта я могу затем определить размер файла (который является просто длиной этого буфера данных) без фактического сохранения его в файл.В конце концов, возможно, именно так вы и ожидаете, что это сработает, но я видел слишком много кодов, которые теряют производительность при сохранении файлов на диске из-за недостатка знаний о io.BytesIO
.
в Python.окончательный результат будет сохранен в файл - и все, где вы хотите.Попробуйте использовать абсолютное имя файла для img_target_filename
.