Fri Jun 26 2026 20:00:00 GMT-0400 (北美东部夏令时间)

Сжатие изображений в Python: Pillow, WebP и mozjpeg

Сжимайте изображения JPEG, PNG и WebP в Python с помощью Pillow, cwebp и mozjpeg. Настройте качество, используйте циклы для целевого размера и быстрый пакетный скрипт.

Сжатие изображений в Python: Pillow, WebP и mozjpeg

Последнее обновление: June 27, 2026

У вас есть папка с JPEG размером с камеру, этап сборки, который отправляет их пользователям, и бюджет веса страницы, который постоянно растет. Сжатие этих файлов вручную в редакторе изображений не масштабируется. Это руководство показывает точный Python, который вам нужен для уменьшения размера файлов JPEG, PNG и WebP по расписанию, в CI-задаче или внутри обработчика загрузки.

Краткий ответ: как сжать изображения в Python?

Откройте изображение с помощью Pillow, а затем сохраните его с правильными флагами формата. Для фотографий сохраняйте в формате JPEG с параметрами quality=80, optimize=True и progressive=True. Для получения самых маленьких современных файлов сохраняйте в WebP с параметрами quality=80, method=6. Для плоской графики и скриншотов используйте PNG и передайте параметр optimize=True.

from PIL import Image

def compress_jpeg(src, dst, quality=80):
    img = Image.open(src)
    if img.mode in ("RGBA", "P"):
        img = img.convert("RGB")
    img.save(dst, "JPEG", quality=quality, optimize=True, progressive=True)

Эта функция из четырех строк уже сокращает большинство JPEG-файлов с телефонов и DSLR на 40–70 процентов. Остальная часть этого руководства добавляет цели по размеру, поддержку WebP, mozjpeg и пакетный обработчик, чтобы вы могли указать его на каталог и забыть о нем.

Что вам понадобится перед началом работы?

Вам потребуется Python 3.9 или новее и Pillow — поддержикованная форк PIL. Pillow обрабатывает JPEG, PNG и WebP "из коробки" на каждой распространенной платформе.

pip install Pillow

Для получения минимального объема вы также захотите использовать два командных кодировщика, которые может вызвать Python: cwebp из libwebp и cjpeg из mozjpeg.

brew install webp mozjpeg   # macOS; Linux: apt install webp, build mozjpeg

Проверьте назначение каждой части перед написанием кода:

  • Pillow считывает и записывает каждый формат и выполняет изменение размера.
  • cwebp создает самые компактные файлы WebP и предоставляет больше настроек, чем Pillow.
  • mozjpeg перекодирует JPEG, делая их меньше стандартных libjpeg, используемых Pillow.
  • Вспомогательный инструмент для отчетности, чтобы каждый запуск выводил количество сэкономленных байт.

Вот вспомогательный инструмент, используемый на протяжении всего этого руководства:

import os

def report(src, dst):
    before, after = os.path.getsize(src), os.path.getsize(dst)
    saved = (1 - after / before) * 100
    print(f"{before/1024:.0f} KB -> {after/1024:.0f} KB ({saved:.0f}% smaller)")

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

Как сжать JPEG с помощью Pillow?

Три флага, которые важны для JPEG: quality, optimize и progressive. quality контролирует, сколько деталей отбрасывает кодировщик. optimize=True выполняет второй проход для создания меньшей таблицы Хаффмана (Huffman table). progressive=True переупорядочивает файл таким образом, что он рисуется сверху вниз с низким разрешением в первую очередь, что кажется быстрее при медленном соединении и обычно немного уменьшает размер файла.

Ноутбук на деревянном столе с открытым исходным кодом в редакторе

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

Quality value Typical use What to expect
90-95 Hero images, print proofs Near-original, noticeably larger files
75-85 Most web photos The safe default, large savings
60-70 Thumbnails and previews Visible softening on fine detail
Below 50 Avoid for photos Blocky artifacts around edges

Одна ловушка сбивает почти всех: у JPEG нет альфа-канала. Если вы открываете скриншот PNG с прозрачностью и сохраняете его напрямую в JPEG, Pillow выдает ошибку cannot write mode RGBA as JPEG. Вызов convert("RGB") в предыдущей функции сначала "выравнивает" прозрачность на сплошном фоне, поэтому он выполняется перед каждым сохранением JPEG.

Сжатие PNG и преобразование в WebP

PNG является алгоритмом без потерь (lossless), поэтому вы не можете обменять качество на размер так, как это делаете с JPEG. Что вы можете сделать, так это запустить оптимизатор Pillow и, для всего, что действительно является фотографией, полностью изменить формат.

def compress_png(src, dst):
    Image.open(src).save(dst, "PNG", optimize=True, compress_level=9)

def to_webp(src, dst, quality=80):
    img = Image.open(src)
    img.save(dst, "WEBP", quality=quality, method=6)

method=6 указывает кодировщику WebP потратить больше времени на поиск меньшего файла. Это медленнее для каждого изображения, но стоит того на этапе сборки (build step). Для фотографии, которая начиналась как PNG размером 2 MB, WebP при качестве 80 обычно составляет менее 200 KB, выглядя при этом идентично в нормальном размере просмотра. По собственным измерениям Google, WebP примерно на 25–35% меньше, чем сопоставимый JPEG, согласно документации WebP.

Используйте эту таблицу, чтобы выбрать формат, а не угадывать:

Содержимое источника Лучший формат Почему
Фотография WebP или JPEG Плавные градиенты хорошо сжимаются в режиме потерь (lossy)
Логотип, иконка, плоский UI PNG или WebP lossless Резкие края остаются четкими
Скриншот с текстом PNG или lossless WebP Избегает размытого текста от проходов с потерями
Изображение, требующее прозрачности WebP или PNG JPEG не может хранить альфа-канал

Если вы все еще решаете между форматами для всего сайта, подробный обзор в WebP vs JPEG vs PNG освещает поддержку браузерами и компромиссы более глубоко.

Достижение целевого размера файла в цикле

«Сделать размер меньше 200 KB» — это распространенное требование от инструментов для электронной почты, маркетплейсов и лимитов загрузки CMS. Вы не можете выбрать число качества, которое достигнет заданного объема байт с первой попытки, потому что одно и то же качество дает очень разные размеры для сложной фотографии по сравнению с простой. Проходите циклом вниз от высокого качества до тех пор, пока не попадете в заданный лимит:

def compress_to_target(src, dst, target_kb=200, q=90, floor=35):
    img = Image.open(src).convert("RGB")
    while q >= floor:
        img.save(dst, "JPEG", quality=q, optimize=True, progressive=True)
        if os.path.getsize(dst) <= target_kb * 1024:
            return q
        q -= 5
    return q

Аргумент floor останавливает цикл до того, как качество упадет в область смазывания. Если изображение не может достичь целевого значения выше этого предела, функция все равно возвращает результат, чтобы вы могли записать это и изменить размер изображения вместо более сильного сжатия. Для фиксированных бюджетов байт, таких как 100 KB или 50 KB, работает тот же цикл; в руководстве по сжатию изображений без потери качества объясняется, когда изменение размера превосходит понижение качества.

Как сделать JPEG меньше с помощью mozjpeg и cwebp?

Pillow сохраняет JPEG с помощью стандартного кодировщика libjpeg. mozjpeg — это drop-in кодировщик от Mozilla, который создает более мелкие файлы при том же визуальном качестве, обычно на 5–15 процентов. Поскольку Pillow не включает его в себя, вызовите бинарник cjpeg через subprocess. cjpeg от mozjpeg надежно считывает PPM, поэтому сначала выровняйте изображение с помощью Pillow:

import subprocess

def mozjpeg(src, dst, quality=80):
    Image.open(src).convert("RGB").save("_tmp.ppm")
    subprocess.run(["cjpeg", "-quality", str(quality), "-progressive",
                    "-optimize", "-outfile", dst, "_tmp.ppm"], check=True)
    os.remove("_tmp.ppm")

Для WebP прямой вызов cwebp дает вам возможности, которые не предоставляет Pillow, такие как -m 6 для максимального усилия и -sharp_yuv для более чистых цветовых краев:

def cwebp(src, dst, quality=82):
    subprocess.run(["cwebp", "-q", str(quality), "-m", "6",
                    "-sharp_yuv", src, "-o", dst], check=True)

Вот как сравниваются эти опции, чтобы вы могли решить, что установить:

Tool Best for Trade-off
Pillow Общее скриптование, любой формат Не самый маленький JPEG
mozjpeg Самый маленький прогрессивный JPEG Дополнительный бинарник, цикл PPM
cwebp (libwebp) Наиболее точный контроль WebP Отдельный бинарник для установки
pillow-simd Более быстрое изменение размера на x86 серверах Сложнее в сборке и закреплении

Проект mozjpeg документирует свой кодировщик и эталонные тесты в репозитории mozjpeg repository, а полный список опций сохранения Pillow находится в Pillow image file formats reference.

Как быстро выполнить пакетное сжатие папки?

Одно изображение — это легко. Настоящая задача — каталог из сотен файлов. Измените размер всего, что шире, чем требует ваш макет, затем сжимайте и выполняйте работу по потокам (threads), чтобы папка из 500 изображений обрабатывалась за секунды, а не минуты.

Разработчик за рабочим местом с несколькими мониторами, пишущий код в наушниках

from pathlib import Path
from concurrent.futures import ThreadPoolExecutor

def batch(in_dir, out_dir, quality=80, max_width=1920):
    out = Path(out_dir); out.mkdir(parents=True, exist_ok=True)
    files = [p for p in Path(in_dir).iterdir()
             if p.suffix.lower() in {".jpg", ".jpeg", ".png"}]
    def work(p):
        img = Image.open(p)
        if img.width > max_width:
            h = round(img.height * max_width / img.width)
            img = img.resize((max_width, h), Image.LANCZOS)
        img.convert("RGB").save(out / f"{p.stem}.jpg", "JPEG",
                                quality=quality, optimize=True, progressive=True)
    with ThreadPoolExecutor() as pool:
        list(pool.map(work, files))
    return len(files)

Несколько практических замечаний по использованию этого в продакшене:

  1. Измените размер, прежде чем сжимать. Подача фотографии 6000 px в колонку шириной 1200 px приводит к потере байтов, которую никакие настройки качества не могут восстановить.
  2. Pillow освобождает GIL во время кодирования, поэтому здесь потоки обеспечивают реальное ускорение; для большинства папок вам не нужно использовать multiprocessing.
  3. Пишите в отдельную выходную директорию, чтобы неудачный запуск никогда не перезаписал ваши оригиналы.
  4. Используйте Image.LANCZOS для уменьшения масштаба; он лучше сохраняет резкость краев, чем фильтр по умолчанию.
  5. Логируйте каждый файл с помощью вспомогательной функции report и сохраняйте оригиналы, пока не просмотрите образец визуально.

Если ваша задача повторяющаяся, а не разовая, шаблоны в batch image processing охватывают очереди, повторные попытки и правила именования. Для запуска без кода над zip-архивом файлов batch tool и image converter обрабатывают изменения формата без скрипта.

Какой подход следует выбрать?

Подбирайте метод под задачу, а не всегда хватайтесь за самый мощный инструмент:

  • Изображения Web в процессе сборки: Pillow WebP с качеством 80 или cwebp с -m 6, если вам нужны последние проценты.
  • Строгий бюджет байтов: цикл compress_to_target, который переходит к изменению размера при достижении лимита.
  • Устаревшие конвейеры JPEG, которые нельзя изменить: перекодируйте вывод с помощью mozjpeg для бесплатной экономии.
  • Скриншоты и диаграммы: используйте PNG с optimize=True или без потерь WebP, если ваши браузеры это поддерживают.

Теория того, почему эти настройки работают, изложена в руководстве по сжатию изображений, где объясняется разница между lossy и lossless и то, как таблицы квантования влияют на результат.

Распространенные ошибки и способы их устранения

Несколько исключений вызывают большинство неудачных запусков:

  • cannot write mode RGBA as JPEG — вызовите img.convert("RGB") перед сохранением в JPEG.
  • Image.DecompressionBombError — Pillow защищает от очень больших изображений; повышайте лимит только для файлов, которым вы доверяете, с помощью Image.MAX_IMAGE_PIXELS = 200_000_000.
  • OSError: broken data stream — исходный файл обрублен; установите ImageFile.LOAD_TRUNCATED_IMAGES = True, чтобы спасти его, а затем воспользуйтесь возможностью повторной загрузки.
  • Выходной файл больше входного — вы снова сохранили уже сжатый JPEG; сжимайте из оригинала, а не из предыдущего экспорта.
  • Цвета выглядят блеклыми — источник CMYK был сохранен без преобразования; сначала вызовите convert("RGB").
from PIL import Image, ImageFile
Image.MAX_IMAGE_PIXELS = 200_000_000
ImageFile.LOAD_TRUNCATED_IMAGES = True

Детальное крупное изображение питона, лежащего на перфорированной поверхности.

Основной вывод

Pillow обрабатывает 90 percent сжатия изображений в Python с использованием трёх флагов JPEG и сохранения WebP. Используйте mozjpeg и cwebp, когда вам нужны максимально маленькие файлы; оборачивайте всё в цикл target-size, если задан бюджет байтов; и запускайте папку через потоковую пакетную функцию, когда увеличивается объем. Сохраняйте оригиналы, изменяйте размер перед сжатием и выводите экономию при каждом запуске, чтобы всегда знать, что на самом деле делает конвейер.

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

Обложка статьи «WebP Converter: Как преобразовать изображения в WebP (с реальными размерами)»

Wed Mar 18 2026 20:00:00 GMT-0400 (北美东部夏令时间)

WebP Converter: Как преобразовать изображения в WebP (с реальными размерами)

Преобразуйте изображения JPEG и PNG в WebP для уменьшения размера веб-файлов. Мы предлагаем реальные размеры, команду cwebp, методы на Python и в браузере, а также стратегию резервного копирования JPEG/PNG.