Можно ли изменить размер изображения байтами, а не шириной и высотой? - PullRequest
0 голосов
/ 23 октября 2018

Я хотел уменьшить изображение до меньшего размера для удобства обмена и более быстрой загрузки.

Но я понял, что если я просто уменьшу размер на его h & w, это не поможет, потому что большой файл изображения может иметь меньшие значения h & w, а маленький файл изображения может иметь большие значения h & w, поэтому уменьшитеРазмер изображения за счет уменьшения его высоты и веса не всегда может уменьшить размер так, как я хотел.

Итак, теперь я получил размер байта, используя это:

import os
os.stat('myImage.jpg').st_size

Можно ли уменьшить размер изображения путем уменьшения его байта?И останется ли его соотношение?

1 Ответ

0 голосов
/ 23 октября 2018

Вот функция, которую я написал с 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.

...