Сегодня мы поговорим про то, как сжать изображение в Python. Вы узнаете, как уменьшить размер файла, сжимая и изменяя размер изображения с помощью библиотеки Pillow.
Сжатие изображения — это процесс уменьшения веса картинки без ухудшения ее качества. Есть много онлайн-инструментов, которые предлагают эту услугу. Большинство из них являются отличным вариантом, если вы хотите быстро и надежно уменьшить вес изображения. Но не всегда это лучшее решение. Поэтому в этой статье мы расскажем, как уменьшать размер файла изображения в Python с помощью библиотеки Pillow.
Кроме того, вы можете свободно использовать код из этого руководства. Например, вы можете создать вокруг него API для пакетного уменьшения размеров изображений вместо использования стороннего API, который будет стоить вам денег.
Мы сделали код для данного урока максимально гибким. Вы можете сжать изображение и изменить его размер с коэффициентом масштабирования или точной шириной и высотой. Более того, вы также можете указать желаемое качество.
Начало работы
Приступим! Для начала нужно установить библиотеку Pillow:
$ pip install Pillow
Теперь откройте новый файл Python и импортируйте нашу библиотеку следующим образом:
import os from PIL import Image
Прежде чем углубиться в сжатие изображений, давайте напишем функцию для получения размера файла в удобном для нас формате:
def get_size_format(b, factor=1024, suffix="B"): """ Scale bytes to its proper byte format e.g: 1253656 => '1.20MB' 1253656678 => '1.17GB' """ for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]: if b < factor: return f"{b:.2f}{unit}{suffix}" b /= factor return f"{b:.2f}Y{suffix}"
А теперь создадим нашу основную функцию для сжатия изображений. Выглядеть она будет следующим образом:
def compress_img(image_name, new_size_ratio=0.9, quality=90, width=None, height=None, to_jpg=True): # load the image to memory img = Image.open(image_name) # print the original image shape print("[*] Image shape:", img.size) # get the original image size in bytes image_size = os.path.getsize(image_name) # print the size before compression/resizing print("[*] Size before compression:", get_size_format(image_size)) if new_size_ratio < 1.0: # if resizing ratio is below 1.0, then multiply width & height with this ratio to reduce image size img = img.resize((int(img.size[0] * new_size_ratio), int(img.size[1] * new_size_ratio)), Image.ANTIALIAS) # print new image shape print("[+] New Image shape:", img.size) elif width and height: # if width and height are set, resize with them instead img = img.resize((width, height), Image.ANTIALIAS) # print new image shape print("[+] New Image shape:", img.size) # split the filename and extension filename, ext = os.path.splitext(image_name) # make new filename appending _compressed to the original file name if to_jpg: # change the extension to JPEG new_filename = f"{filename}_compressed.jpg" else: # retain the same extension of the original image new_filename = f"{filename}_compressed{ext}" try: # save the image with the corresponding quality and optimize set to True img.save(new_filename, quality=quality, optimize=True) except OSError: # convert the image to RGB mode first img = img.convert("RGB") # save the image with the corresponding quality and optimize set to True img.save(new_filename, quality=quality, optimize=True) print("[+] New file saved:", new_filename) # get the new image size in bytes new_image_size = os.path.getsize(new_filename) # print the new size in a good format print("[+] Size after compression:", get_size_format(new_image_size)) # calculate the saving bytes saving_diff = new_image_size - image_size # print the saving percentage print(f"[+] Image size change: {saving_diff/image_size*100:.2f}% of the original image size.")
Какая-то гигантская функция, которая делает много всего. Жуть, не правда ли? Но не переживайте. Сейчас мы рассмотрим ее подробнее и всё станет понятно.
[python_ad_block]Итак:
- Мы используем метод
Image.open()
для загрузки изображения в память. Далее мы получаем размер файла изображения с помощью функцииos.path.getsize()
. Мы делаем это для того, чтобы позже иметь возможность сравнить этот размер с размером нового сгенерированного файла. - Если значение
new_size_ratio
меньше1.0
, то необходимо изменить размер. Это число находится в диапазоне от 0 до 1 и умножается на ширину и высоту исходного изображения, чтобы получить изображение с более низким разрешением. Это подходящий параметр, если вы хотите еще больше уменьшить размер изображения. Вы также можете установить его на 0,95 или 0,9, чтобы уменьшить размер изображения с минимальными изменениями разрешения. - Если значение
new_size_ratio
равно1.0
, но заданы ширина и высота, мы изменяем размер до этих новых значений ширины и высоты. Убедитесь, что они меньше исходной ширины и высоты! - Если для
to_jpg
установлено значениеTrue
, мы меняем расширение исходного изображения на JPEG. Это значительно уменьшит размер изображения, особенно для изображений PNG. Если возникнет ошибкаOSError
, преобразование формата изображения в RGB решит данную проблему. - И наконец, мы используем метод
save()
для записи оптимизированного изображения. Мы передаем в качестве параметров имя нового файла, желаемое качество и optimize со значениемTrue
. После этого мы получаем размер нового изображения. Его мы сравниваем с размером исходного изображения, который получили в самом начале.
Сжатие изображений на примерах
Теперь, когда у нас есть основная функция, давайте воспользуемся модулем argparse
, чтобы интегрировать ее с аргументами командной строки. Сделаем это следующим образом:
if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description="Simple Python script for compressing and resizing images") parser.add_argument("image", help="Target image to compress and/or resize") parser.add_argument("-j", "--to-jpg", action="store_true", help="Whether to convert the image to the JPEG format") parser.add_argument("-q", "--quality", type=int, help="Quality ranging from a minimum of 0 (worst) to a maximum of 95 (best). Default is 90", default=90) parser.add_argument("-r", "--resize-ratio", type=float, help="Resizing ratio from 0 to 1, setting to 0.5 will multiply width & height of the image by 0.5. Default is 1.0", default=1.0) parser.add_argument("-w", "--width", type=int, help="The new width image, make sure to set it with the `height` parameter") parser.add_argument("-hh", "--height", type=int, help="The new height for the image, make sure to set it with the `width` parameter") args = parser.parse_args() # print the passed arguments print("="*50) print("[*] Image:", args.image) print("[*] To JPEG:", args.to_jpg) print("[*] Quality:", args.quality) print("[*] Resizing ratio:", args.resize_ratio) if args.width and args.height: print("[*] Width:", args.width) print("[*] Height:", args.height) print("="*50) # compress the image compress_img(args.image, args.resize_ratio, args.quality, args.width, args.height, args.to_jpg)
Здесь мы создали парсер аргументов командной строки и добавили параметры, которые уже обсуждали выше.
Теперь воспользуемся нашим скриптом. Вы можете получить пример изображения по этой ссылке.
Для начала применим наш скрипт без каких-либо параметров. Это будет выглядеть так:
$ python compress_image.py sample-satellite-images.png
И вот какой результат мы получим:
================================================== [*] Image: sample-satellite-images.png [*] To JPEG: False [*] Quality: 90 [*] Resizing ratio: 1.0 ================================================== [*] Image shape: (953, 496) [*] Size before compression: 425.65KB [+] New file saved: sample-satellite-images_compressed.png [+] Size after compression: 379.25KB [+] Image size change: -10.90% of the original image size.
Вес изображения уменьшен с 425,65 КБ до 379,25 КБ. Это примерно 11%. Не много, но всё-таки. Далее попробуем передать -j
для преобразования нашего изображения из формата PNG в JPEG:
$ python compress_image.py sample-satellite-images.png -j
Запустим наш код и получим следующий результат:
================================================== [*] Image: sample-satellite-images.png [*] To JPEG: True [*] Quality: 90 [*] Resizing ratio: 1.0 ================================================== [*] Image shape: (953, 496) [*] Size before compression: 425.65KB [+] New file saved: sample-satellite-images_compressed.jpg [+] Size after compression: 100.07KB [+] Image size change: -76.49% of the original image size.
Примечание. Получить образец данного изображения вы можете здесь.
Это просто фантастика! Как мы видим, вес уменьшился на 76,5%! Теперь немного уменьшим качество с помощью такой команды:
$ python compress_image.py sample-satellite-images.png -j -q 75
И получим следующий вывод:
================================================== [*] Image: sample-satellite-images.png [*] To JPEG: True [*] Quality: 75 [*] Resizing ratio: 1.0 ================================================== [*] Image shape: (953, 496) [*] Size before compression: 425.65KB [+] New file saved: sample-satellite-images_compressed.jpg [+] Size after compression: 64.95KB [+] Image size change: -84.74% of the original image size.
Около 85% уменьшения файла без изменения исходного разрешения изображения! Впечатляет, не правда ли? Что ж, давайте попробуем умножить ширину и высоту изображения на 0,9:
$ python compress_image.py sample-satellite-images.png -j -q 75 -r 0.9
И вот что у нас получится:
================================================== [*] Image: sample-satellite-images.png [*] To JPEG: True [*] Quality: 75 [*] Resizing ratio: 0.9 ================================================== [*] Image shape: (953, 496) [*] Size before compression: 425.65KB [+] New Image shape: (857, 446) [+] New file saved: sample-satellite-images_compressed.jpg [+] Size after compression: 56.94KB [+] Image size change: -86.62% of the original image size.
Теперь последний штрих. Давайте установим точные значения ширины и высоты:
$ python compress_image.py sample-satellite-images.png -j -q 75 -w 800 -hh 400
Запустим и получим такой результат:
================================================== [*] Image: sample-satellite-images.png [*] To JPEG: True [*] Quality: 75 [*] Resizing ratio: 1.0 [*] Width: 800 [*] Height: 400 ================================================== [*] Image shape: (953, 496) [*] Size before compression: 425.65KB [+] New Image shape: (800, 400) [+] New file saved: sample-satellite-images_compressed.jpg [+] Size after compression: 49.73KB [+] Image size change: -88.32% of the original image size.
Потрясающе! Размер изображения уменьшился на 88%! Это прекрасный результат!
Заключение
Итак, теперь вы знаете, как сжать изображение в Python! Мы рассмотрели примеры и смогли уменьшить вес картинки на 88% без ухудшения разрешения. Невероятно! Что ж, теперь вы можете попробовать настроить параметры в соответствии с вашими потребностями.
Мы надеемся, что эта статья была для вас полезна, а данный скрипт будет удобным в использовании и поможет вам в ваших дальнейших проектах!
Полную версию кода можно получить по следующей ссылке.
Перевод статьи «How to Compress Images in Python».