Jpegoptim уменьшает размер JPEG-файлов из командной строки: без потери изображения он перестраивает таблицы Хаффмана, а при необходимости умеет повторно сжимать фотографии с заданным пределом качества или целевым размером. Утилита особенно полезна администраторам сайтов, разработчикам, системным инженерам и фотографам, которым нужно обрабатывать сотни или тысячи JPEG по одинаковым правилам, встраивать оптимизацию в shell-скрипты и CI-задачи либо освобождать место без ручного открытия каждого снимка в редакторе.
- Ретушь и удаление объектов
- Замена фона на фотографии
- Фильтры и 3D-LUT стили
Что именно делает Jpegoptim
Работа Jpegoptim строится вокруг уже существующего JPEG. Это не редактор фотографий и не конвертер форматов: программа не меняет композицию кадра, не удаляет объекты, не исправляет цвет, не масштабирует изображение и не превращает PNG, WebP или AVIF в JPEG. Ее задача уже: взять JPEG/JFIF, проверить его структуру, при заданных условиях оптимизировать кодирование и записать результат. Такая специализация важна для понимания почти всех параметров программы.
В режиме по умолчанию Jpegoptim выполняет lossless-оптимизацию. Он не меняет квантованные коэффициенты изображения, а ищет более компактное представление энтропийно закодированных данных, в частности строит оптимизированные таблицы Хаффмана. Поэтому визуальное содержимое не подвергается повторному квантованию. Файл при этом может стать меньше, но выигрыш зависит от того, насколько хорошо исходный JPEG уже был оптимизирован предыдущим кодировщиком.
Слово «без потерь» относится прежде всего к изображению. Оно не означает, что каждый байт исходного файла обязан сохраниться. Jpegoptim может перестроить служебные структуры JPEG, а политика метаданных зависит от параметров: распространенные сегменты EXIF, IPTC, ICC, XMP и комментарии поддерживаются, неизвестные сегменты по умолчанию могут быть отброшены, JFIF и Adobe-маркеры в отдельных случаях формируются библиотекой JPEG заново. Если требуется архивная побайтовая идентичность служебной части, одной формулировки «lossless» недостаточно — нужно отдельно выбрать политику метаданных и сохранить исходники.
Второй режим — повторное сжатие с потерями. Он включается не отдельной кнопкой, а такими параметрами, как --max и --size. Первый задает верхнюю границу фактора качества, второй просит уложиться примерно в определенный размер. Именно в этих режимах декодированное изображение может измениться. Поэтому команды, безопасные для безусловной пакетной обработки архива, и команды для агрессивного уменьшения веб-файлов следует считать разными рабочими процессами.
Jpegoptim работает с одним файлом, набором имен, маской оболочки, списком файлов и потоком. В актуальных сборках доступны параллельные рабочие процессы через --workers, чтение имен через --files-from и --files-stdin, выбор между обычным и прогрессивным JPEG, режим --auto-mode, повторные попытки сжатия --retry и детальное управление APP-маркерами. Наличие конкретной опции лучше проверять командой jpegoptim --help, если утилита установлена из системного репозитория: дистрибутив может поставлять не ту же версию, что находится в основном репозитории проекта.
Установка и начало работы
Для первого запуска важнее всего убедиться, что вызывается именно консольная утилита Jpegoptim, а не одноименный веб-сервис или сторонняя оболочка. После установки команда называется jpegoptim. Проверка версии и списка доступных параметров занимает несколько секунд и одновременно показывает, что исполняемый файл находится в PATH.
jpegoptim --version
jpegoptim --help
В Linux Jpegoptim обычно устанавливают из пакетного менеджера дистрибутива. В Debian и Ubuntu пакет обычно называется jpegoptim; в Fedora, Arch Linux и других системах имя пакета также чаще всего совпадает с именем утилиты. На macOS ее можно получить через распространенный пакетный менеджер. Для Windows существуют сборки проекта, однако набор возможностей конкретного бинарного файла стоит сверять по --help: параметры, завязанные на механизм параллельных процессов Unix, могут зависеть от платформы и способа сборки.
Перед первым изменением реальных фотографий разумно работать с копией каталога. По умолчанию Jpegoptim модифицирует исходный файл, если получает меньший результат. Это удобное поведение для автоматизации, но непривычное для пользователей графических редакторов: диалог «Сохранить как» не появится. Если исходник имеет ценность, сначала создайте резервную копию или используйте --dest для записи результата в отдельный каталог.
Для контрольного теста удобно взять один JPEG, записать его размер, выполнить оптимизацию без дополнительных параметров и снова проверить размер. В Linux это можно сделать любой утилитой просмотра файлов; главное — не оценивать результат только по округленным мегабайтам в файловом менеджере. Сам Jpegoptim выводит количество байт до и после обработки и процент изменения, поэтому для диагностики его строка результата полезнее грубого округления.
jpegoptim photo.jpg

Если файл уже хорошо оптимизирован, Jpegoptim может сообщить, что он пропущен, либо сохранить исходник без замены. Это нормальный результат, а не ошибка. Алгоритм не обязан уменьшать каждый JPEG. Дополнительная перезапись только ради того, чтобы команда «что-нибудь сделала», обычно не нужна: для принудительной записи существует --force, но применять его следует под конкретную задачу.
Первая оптимизация JPEG и расшифровка вывода
Обычный вызов выглядит минималистично: после команды перечисляются один или несколько файлов. Для каждого Jpegoptim печатает техническую информацию и статус. В типичной строке видны имя файла, размеры изображения, глубина, тип кодирования, обнаруженные маркеры, исходный и новый размер, процент изменения и слово вроде optimized или skipped. Формат вывода полезен человеку, но для парсинга в автоматизации лучше использовать предназначенные для этого режимы, а не разбирать цветной или подробный текст регулярными выражениями.

Статус optimized означает, что программа сформировала подходящий меньший результат и заменила исходный файл либо записала его в каталог назначения. skipped обычно означает, что оптимизация не дала результата, удовлетворяющего условиям: файл уже достаточно компактен, выигрыш ниже порога, целевой параметр не требует повторного сжатия или объект не подходит для обработки. Причину следует оценивать вместе с параметрами команды и диагностикой в stderr.
Размеры, например 1920x1080, Jpegoptim не меняет. Если задача — сделать изображение физически меньше по ширине и высоте, нужна другая программа до или после Jpegoptim. То же относится к повороту по пикселям, кадрированию, повышению резкости и цветокоррекции. Это принципиальное отличие от полноценного фоторедактора: Jpegoptim работает с JPEG-кодированием, а не с художественным содержанием.
При массовой работе полезно заранее решить, что считать успехом. Для lossless-прохода цель обычно проста: уменьшить файл там, где это возможно, не меняя изображение. Для lossy-прохода критерий сложнее: это может быть ограничение качества, примерный размер, экономия трафика или соблюдение лимита CMS. Jpegoptim предоставляет технические рычаги, но не оценивает художественную приемлемость результата за пользователя.
Lossless-режим: оптимизация без повторного квантования
Без --max и --size Jpegoptim работает в своем основном безопасном режиме: оптимизирует кодирование JPEG без изменения квантованных данных изображения. На практике это хороший первый проход для каталога, где нельзя позволить дополнительную деградацию. Особенно полезен он для файлов из камер, старых экспортов, CMS и программ, которые сохранили JPEG с неоптимальными таблицами Хаффмана.
Не стоит ожидать одинаковой экономии. У одного файла выигрыш может быть заметным, у другого — доли процента, у третьего — нулевым. Современные кодировщики часто уже выполняют энтропийную оптимизацию, поэтому повторный lossless-проход не обязан давать существенный эффект. Производственный скрипт должен считать «ничего не уменьшилось» корректным исходом, а не аварией.
Параметр --threshold позволяет не сохранять результат, если выигрыш слишком мал. Значение задается в процентах. Например, если организационно имеет смысл переписывать файл только при экономии хотя бы одного процента, можно использовать:
jpegoptim --threshold=1 photo.jpg
В сборках, где поддерживаются дробные значения порога, допустимы значения вроде 1.5. Это удобно для больших архивов: лишняя запись миллионов файлов с экономией в несколько байт может создавать ненужную нагрузку на хранилище, систему резервного копирования и механизмы синхронизации. Порог не делает алгоритм «сильнее»; он только определяет, считать ли полученный выигрыш достаточным.
--force действует противоположно осторожной политике: позволяет принудительно записывать результат даже тогда, когда обычные критерии не привели бы к замене исходника. Этот параметр нужен прежде всего для преобразований, где важен не размер сам по себе, например для принудительного перевода между normal и progressive, а не для повседневной lossless-оптимизации. Без причины включать --force во все команды не следует.
Термин lossless не отменяет необходимости проверять метаданные. Если каталог содержит оригиналы с GPS, авторскими полями, профилями цвета, нестандартными APP-сегментами или данными специализированного оборудования, сначала сделайте контроль на небольшой выборке. Jpegoptim оптимизирует JPEG, а не выполняет функцию архивного контейнера, гарантирующего сохранение любой неизвестной служебной структуры.
Ограничение качества через —max
Параметр --max=N включает режим с потерями и задает максимальный фактор качества от 0 до 100. Типичный веб-сценарий может выглядеть так:
jpegoptim --max=85 photo.jpg
Смысл --max часто понимают неправильно. Это не команда «уменьшить файл на 15%» и не универсальная шкала видимого качества. JPEG quality factor — параметр кодирования, связанный с таблицами квантования; одинаковое число не гарантирует одинаковую визуальную деградацию на разных фотографиях. Детализированный пейзаж, гладкий градиент и скриншот с текстом реагируют на повторное сжатие по-разному.
Если исходный JPEG уже закодирован с качеством ниже заданного предела, Jpegoptim не должен искусственно повышать его качество и увеличивать файл. Для таких изображений остается смысл lossless-оптимизации. Если исходное качество выше границы, изображение перекодируется с потерями. Именно поэтому при пакетном запуске с одним --max часть файлов может измениться сильнее, а часть — только слегка оптимизироваться.
Повторное JPEG-сжатие накопительно. Если фотография уже пережила несколько сохранений с потерями, новый --max не возвращает утраченное качество и может добавить артефакты. Для рабочих оригиналов разумнее применять lossy-проход один раз на производной копии, предназначенной для сайта, рассылки или другого конечного канала. Если есть RAW, TIFF или другой мастер-источник, лучше создавать веб-JPEG из него и лишь затем оптимизировать конечный файл.
Для выбора значения качества нет честного универсального числа. Практический процесс состоит из тестовой выборки, визуальной проверки в масштабе 100% и на реальном устройстве, а затем фиксации политики. Там, где критична типографика, мелкие линии или интерфейсные скриншоты, JPEG вообще может быть не лучшим исходным форматом. Jpegoptim не определяет семантику изображения и не умеет автоматически выбрать формат.
При использовании --max особенно важны резервные копии: команда по умолчанию работает с исходником. Для экспериментов безопаснее отделить вход от выхода:
mkdir -p optimized
jpegoptim --max=85 --dest=optimized photo.jpg
Так можно сравнить файлы и только потом заменить производственные версии. Но у --dest есть важная особенность: каталог назначения не является автоматической зеркальной копией. Если файл не был оптимизирован и Jpegoptim решил его не записывать, в целевом каталоге может не появиться соответствующего файла. Для полного зеркала сначала копируют набор, а затем оптимизируют копию, либо отдельно обрабатывают случаи skipped.
Целевой размер через —size
Когда ограничение задается не качеством, а размером файла, используется --size. Значение можно задавать как размер в килобайтах или как процент от исходного файла. Например:
jpegoptim --size=250 photo.jpg
jpegoptim --size=70% photo.jpg
В первом случае программа пытается получить файл примерно до заданного количества килобайт, во втором — примерно до указанной доли исходного размера. Это режим с потерями: для достижения цели Jpegoptim подбирает качество повторного кодирования. Слово «примерно» существенно. JPEG имеет дискретные параметры, метаданные занимают отдельное место, а связь между quality factor и итоговым числом байт нелинейна, поэтому точное попадание до одного байта не гарантируется.
--size полезен, когда внешняя система имеет ограничение на загрузку: например, изображение должно быть меньше условных 300 КБ. Но для строгого серверного лимита лучше оставить запас, а не ставить цель ровно на границе. Метаданные, иной набор библиотек или изменение режима progressive/normal могут дать небольшое отклонение. Скрипт должен после обработки проверить фактический размер и принять решение, а не считать сам факт успешного вызова гарантией.
Параметр --retry предназначен для lossy-режимов --max и --size. Он повторяет рекомпрессию, пока размер продолжает уменьшаться. Иногда это позволяет отыграть еще немного байт, но цена — заметно больше вычислений и дополнительная сложность анализа. В массовой обработке его имеет смысл включать только после теста на своем наборе, где дополнительная экономия действительно оправдывает CPU-время.
Сочетать --size с бездумным повторным запуском по тем же файлам не стоит. Целевая величина не означает, что каждый новый проход будет без потерь. Если процесс построен неправильно и JPEG снова перекодируется, можно получать накопительную деградацию. Надежная схема хранит мастер-копию отдельно и генерирует оптимизированную производную версию детерминированно.
При жесткой цели полезно сохранять протокол: имя, исходный размер, конечный размер, статус и выбранные параметры. Jpegoptim умеет формировать CSV-вывод, но собственный скрипт все равно должен проверять код завершения и факт появления ожидаемого файла. Так легче найти аномалии: фотографии с огромным ICC-профилем, поврежденные JPEG, необычные метаданные или изображения, для которых нужный размер достижим только ценой неприемлемого качества.
Метаданные: EXIF, ICC, XMP, IPTC и комментарии
Уменьшая JPEG для сайта, легко сфокусироваться только на пикселях и забыть, что заметную часть файла иногда занимают APP-маркеры. Jpegoptim умеет управлять распространенными типами метаданных. В обычном режиме он стремится сохранять известные сегменты EXIF, IPTC, ICC, XMP и комментарии, но неизвестные служебные сегменты не следует считать гарантированно сохраненными. Для архивной обработки сначала нужна проверка на реальных файлах из вашей камеры или редактора.
Параметр --strip-all удаляет поддерживаемые метаданные максимально агрессивно. Для веб-производных это может дать дополнительную экономию и убрать персональные сведения, например GPS из EXIF. Однако «удалить все» — не всегда безопасная политика. ICC-профиль может быть нужен для корректной цветопередачи, а EXIF Orientation — для правильной ориентации изображения в программах, которые отображают исходные пиксели с учетом этого тега.
Если JPEG физически хранится повернутым, а правильное положение задается только EXIF Orientation, удаление EXIF способно визуально «повернуть» фотографию у части потребителей. Jpegoptim сам не выполняет авторотацию пикселей. Поэтому перед --strip-all либо нормализуйте ориентацию отдельным инструментом, либо сохраните нужный сегмент. Именно такие детали отличают безопасный массовый скрипт от команды, которая случайно портит внешний вид части архива.
Вместо глобального удаления можно сочетать параметры --strip-... и --keep-.... Практический веб-вариант — убрать лишние метаданные, но сохранить ICC:
jpegoptim --strip-all --keep-icc photo.jpg
Если нужен EXIF, добавляют соответствующее сохранение. Аналогично можно управлять XMP, IPTC и COM. Точные имена опций следует проверять по --help установленной версии: пакет в стабильном дистрибутиве может отставать от возможностей текущей ветки проекта.
Параметр --strip-none, также описываемый как политика сохранения всех поддерживаемых маркеров, нужен в противоположной ситуации: служебная информация важнее экономии нескольких килобайт. Но даже здесь надо учитывать особенность библиотеки JPEG: JFIF и Adobe-маркеры могут не просто копироваться байт-в-байт, а создаваться заново в зависимости от декодера/кодировщика и цветового пространства. Поэтому «сохранить метаданные» и «получить побайтовый клон заголовков» — разные требования.
JFIF и JFXX имеют отдельные ключи сохранения и удаления в новых сборках. То же касается Adobe marker. Эти параметры нужны не большинству сайтов, а специализированным процессам, где структура APP-сегментов проверяется downstream-инструментами. Если такой зависимости нет, лучше не усложнять команду без необходимости.
Еще один класс данных находится после маркера EOI, то есть формально за концом JPEG. По умолчанию лишние данные после EOI отбрасываются. Параметр --save-extra позволяет сохранить их в поддерживаемых сценариях. Для стандартных фотографий хвост обычно не нужен, но в forensic-, архивных или нестандартных производственных процессах его удаление может быть нежелательным. При чтении изображения из stdin сохранение такого хвоста имеет ограничения, поэтому потоковый режим нужно тестировать отдельно.
Normal и progressive JPEG
JPEG может храниться в обычном последовательном режиме или как progressive. Progressive-файл при сетевой загрузке способен показываться поэтапно: сначала грубое изображение, затем уточнение. Но для Jpegoptim этот выбор важен еще и как вариант кодирования: один и тот же визуальный материал в normal и progressive может иметь немного разный итоговый размер.
По умолчанию современные версии Jpegoptim сохраняют исходный режим. Это разумно для консервативной оптимизации: утилита не меняет структуру без явного требования. Если нужно принудительно получить progressive, применяется --all-progressive; для обычного последовательного JPEG — --all-normal. В некоторых случаях для гарантированной конверсии приходится добавить --force, если новый вариант не меньше старого.
jpegoptim --all-progressive --force photo.jpg
Новые версии также предлагают --auto-mode: программа пробует варианты и выбирает тот, который дает меньший размер. Это удобно, когда тип кодирования не принципиален, а важен каждый байт. Цена — дополнительные вычисления, поскольку нужно сравнивать варианты. Перед включением этого режима в длинную серверную очередь стоит проверить конкретную сборку: в одной из стабильных веток проекта исправление зависания в --auto-mode появилось уже после релиза, поэтому для production важна не только поддержка ключа, но и конкретный исправленный код.
Выбирать progressive только потому, что он «современнее», не требуется. На реальном сайте значение имеют браузерное поведение, CDN, кэширование, целевой размер и общая стратегия изображений. Jpegoptim не меняет размеры кадра и не генерирует responsive-варианты; progressive — лишь одна характеристика конкретного JPEG.
Для экзотических сценариев проект умеет работать и с арифметическим кодированием, если соответствующая поддержка включена при сборке. Опции этого режима помечены как экспериментальные, а совместимость декодеров хуже привычного Huffman-кодирования. Для массовой веб-публикации это не тот параметр, который следует активировать ради теоретической экономии без тестов на всей цепочке доставки.
Каталог назначения, перезапись и сохранение атрибутов
--dest=DIR меняет модель работы: оптимизированный файл записывается в указанный каталог, а исходный остается на месте. Это один из самых полезных параметров для экспериментов и безопасных миграций.
mkdir -p optimized
jpegoptim --dest=optimized *.jpg
Критическое ограничение: Jpegoptim не является инструментом синхронизации каталогов. Если конкретный JPEG не удалось уменьшить и программа решает оставить его без изменений, в --dest он может не появиться. Поэтому нельзя считать каталог назначения полной копией входного набора. Для публикационного пайплайна либо сначала копируют весь набор, а затем оптимизируют копию на месте, либо обрабатывают статус каждого файла и отдельно копируют пропущенные.
--overwrite имеет смысл вместе с --dest: он разрешает заменять существующий файл в каталоге назначения. Без этого защита от случайной перезаписи помогает не затереть предыдущий результат. В автоматизации следует заранее решить, является ли повторный запуск идемпотентным и должен ли обновлять существующие производные файлы.
При обычной работе на месте Jpegoptim старается записывать оптимизированный результат через временный файл и затем заменять исходник. Такая стратегия безопаснее прямой записи, потому что сбой во время кодирования с меньшей вероятностью оставит половину файла. Однако она влияет на файловые атрибуты и hard link-семантику: путь может начать указывать на новый inode, а другие жесткие ссылки на прежний inode останутся с прежним содержимым.
Опция --preserve сохраняет время модификации файла. Это полезно для фотоколлекций, где mtime используется для сортировки, инкрементного копирования или проверки изменений. Но сохраненный mtime не означает, что файл не менялся: содержимое и контрольная сумма будут другими.
--preserve-perms предназначен для сохранения прав доступа и владельца за счет иной схемы записи. В руководстве прямо отмечается, что такой режим немного менее безопасен, чем обычная замена через временный файл: при прерывании процесса риск повреждения выше. Применять его стоит только там, где действительно важна сохранность атрибутов и поведение проверено на резервной копии.
Символические ссылки и специальные файлы Jpegoptim не рассматривает как обычные JPEG для перезаписи. Для рекурсивных каталогов это полезная защита, но скрипт не должен молча предполагать, что каждый путь, полученный от find, будет обработан. Логи и коды завершения важны особенно в каталогах, где рядом с фотографиями лежат ссылки, FIFO или другие объекты.
Пакетная обработка каталога
Самая простая пакетная команда использует подстановку оболочки:
jpegoptim *.jpg

Звездочка раскрывается самой оболочкой до запуска Jpegoptim. Это означает несколько практических ограничений. Маска обычно не рекурсивна, регистр расширения имеет значение в чувствительных к регистру файловых системах, а при огромном числе файлов можно упереться в системный предел длины списка аргументов. Для небольшого каталога команда удобна, для медиатеки на сотни тысяч файлов нужен более надежный механизм.

Для рекурсивной обработки на Unix-подобных системах часто используют find с -exec ... {} +. Эта форма собирает безопасные порции аргументов и корректно работает с пробелами в именах:
find ./images -type f ( -iname '*.jpg' -o -iname '*.jpeg' )
-exec jpegoptim --threshold=1 {} +
Такой запуск не требует парсить текстовый список имен через пробел. Если в имени встречается перевод строки, -exec также не ломает границы. Альтернатива — связка find -print0 и xargs -0, но она добавляет еще один процесс и требует аккуратно передавать параметры.
Актуальные версии Jpegoptim имеют собственный --files-from=FILE. В указанном файле размещается по одному пути на строку. Это удобно, когда список сформирован заранее системой сборки, CMS или SQL-выгрузкой. Если вместо имени передать специальное значение для stdin, список читается из потока. Отдельный --files-stdin также читает имена по строкам. Здесь есть принципиальная граница: строковый формат не способен без дополнительных соглашений представить имя файла, содержащее перевод строки. Для контролируемой медиатеки это обычно приемлемо, для произвольных пользовательских имен — нет.
Выборочная обработка нескольких конкретных файлов выполняется простым перечислением аргументов. Это удобнее маски, когда список короткий и формируется человеком:
jpegoptim header.jpg hero.jpg gallery-01.jpg
Если список огромный и получен программно, --files-from позволяет избежать ограничения ARG_MAX. При этом удобно хранить рядом манифест, с какими параметрами был обработан набор. Для повторяемой публикации это полезнее, чем команда, восстановленная по истории shell.
Параллельная обработка через —workers
--workers=N разрешает обрабатывать несколько файлов параллельно в сборках, где доступен соответствующий механизм процессов. По умолчанию Jpegoptim работает последовательно. Параллелизм может существенно повысить пропускную способность на многоядерном процессоре, но оптимальное число рабочих процессов зависит от типа накопителя, размера файлов, режима сжатия, свободной памяти и того, что еще делает сервер.
jpegoptim --workers=4 *.jpg
Число 4 не является рекомендацией для всех машин. На локальном SSD и восьмиядерном CPU оно может быть консервативным, на слабом VPS с сетевым диском — чрезмерным. Правильный подход: измерить типичную очередь с одним worker, затем увеличить параллелизм и следить за загрузкой CPU, I/O wait, латентностью диска и влиянием на веб-процессы. Пользовательский сайт не должен становиться медленным ради фоновой оптимизации.
В исходном коде предусмотрен верхний технический предел количества рабочих процессов, но использовать максимум не значит получить максимум скорости. JPEG-кодирование нагружает CPU, а чтение и запись тысяч мелких файлов — файловую систему. Слишком много workers увеличивает конкуренцию за кэш и диск, а выигрыш быстро насыщается.
На некоторых платформах или в сборках без Unix-механизмов fork/wait параметр может отсутствовать. Поэтому переносимый скрипт сначала проверяет поддержку в jpegoptim --help либо знает версию и платформу среды выполнения. Не стоит считать, что одинаковое имя пакета на Linux и Windows означает идентичный набор опций.
При параллельной обработке особенно важен формат логов. Строки разных рабочих процессов могут появляться в ином порядке, чем имена во входном списке. Если downstream-система сопоставляет результаты с файлами, она должна опираться на имя из самой записи, а не на номер строки. Для строгого машинного протокола стоит тестировать CSV-режим конкретной версии и не парсить декоративный человеческий вывод.
stdin, stdout и конвейеры
Jpegoptim может работать не только с путями файлов. --stdin читает один JPEG из стандартного ввода, а результат направляется в stdout. Это полезно в конвейерах, где изображение поступает от другой программы, временный файл нежелателен или обработка выполняется в контейнере.
cat input.jpg | jpegoptim --stdin > output.jpg
Ключевой риск — stdout содержит бинарный JPEG. Его нельзя смешивать с диагностическими сообщениями. При построении скрипта убедитесь, что служебный вывод идет в stderr и что оболочка не подставляет текст в поток. После выполнения следует проверить код завершения и валидность output.jpg, особенно если он сразу передается дальше.
--stdout позволяет вывести результат обработки обычного входного файла в стандартный вывод. Это удобно, если путь нужен только для чтения, а получатель — другая программа или объектное хранилище. Но опять же, автоматизация должна знать, что именно происходит, когда оптимизированная версия не оказывается меньше исходной: поведение потока следует проверять на установленной версии, а не строить критичный pipeline на предположении.
Потоковый режим имеет ограничения по сравнению с обычным файлом. Например, сохранение дополнительных данных после EOI через --save-extra не работает так же, как при полном файле. Некоторые свойства файловой системы — mtime, permissions, hard links — в stdin/stdout вообще неприменимы. Поэтому поток нельзя считать просто «тем же самым, только быстрее».
Не следует одновременно использовать режим одного изображения из stdin и режим чтения списка имен из того же stdin: это логически конфликтующие источники. Jpegoptim обнаруживает такие комбинации и прекращает работу. В сложном shell-процессе лучше явно развести дескрипторы или использовать файл-манифест.
Dry run, CSV, quiet и подробная диагностика
Перед массовым изменением полезен --noaction или короткая форма -n. Режим позволяет увидеть, как Jpegoptim оценивает файлы и какие действия планирует, не заменяя оригиналы. Он особенно полезен при первом внедрении --max, --size, --strip-all или сложной команды find.
jpegoptim --noaction --max=85 ./sample/*.jpg
Dry run не заменяет тестовый экспорт, если речь о lossy-качестве. Он подтверждает логику выбора файлов и параметров, но визуально оценить артефакты можно только на реально созданных копиях. Оптимальный процесс состоит из трех уровней: сначала noaction на наборе, затем реальная обработка копий и просмотр, затем production-запуск.
--csv переключает вывод в форму, удобнее пригодную для машинной обработки. Это не JSON и не стабильный API веб-сервиса, поэтому перед написанием долгоживущего парсера стоит сохранить пример вывода той версии, которая развернута в инфраструктуре. В логах полезно дополнительно фиксировать версию jpegoptim --version, чтобы после обновления можно было объяснить изменение формата или поведения.
--quiet уменьшает объем сообщений, а --verbose дает дополнительные детали. В интерактивной диагностике verbose удобен, в cron-задаче часто полезнее короткий журнал только с ошибками и итоговой статистикой. Полностью подавлять stderr опасно: тогда поврежденный файл, ошибка прав или отсутствие каталога назначения превращаются в незаметную потерю обработки.
Если система наблюдения анализирует exit code, сначала протестируйте сценарии «файл уже оптимален», «файл отсутствует», «нет прав на запись», «JPEG поврежден», «неизвестная опция». У разных CLI-программ понятия skipped и error различаются. Надежный скрипт не делает вывод только по наличию слова в stdout.
Локальная man-страница — лучший быстрый способ проверить синтаксис именно установленной сборки. Если примеры из интернета не совпадают с man jpegoptim или jpegoptim --help, приоритет должен быть у документации вашего бинарного файла. Для новых опций это особенно важно: системные репозитории обновляются с разной скоростью.
Поврежденные JPEG и параметр —nofix
JPEG из старых архивов, неудачных загрузок и оборванных копирований может содержать ошибки или предупреждения декодера. По умолчанию Jpegoptim способен обработать часть таких файлов и фактически переписать их в более чистом виде. Иногда это полезно, но для forensic- или архивной работы подобная «починка» может быть нежелательной: исходный дефект сам по себе является информацией.
--nofix говорит не оптимизировать файл, если при декодировании обнаружены ошибки или предупреждения, которые Jpegoptim иначе мог бы попытаться исправить. Для массовой медиатеки это хороший режим предварительной инвентаризации: подозрительные файлы попадают в отдельный список, а не незаметно меняются.
jpegoptim --nofix ./incoming/*.jpg
Если задача — восстановление, Jpegoptim не заменяет специализированный валидатор JPEG. Он может успешно переписать некоторые структуры, но не гарантирует восстановление утраченных данных изображения. Для ценных архивов сначала сохраняют оригинал, проверяют его отдельным инструментом и только потом принимают решение о перепаковке.
Наличие предупреждения также может объяснить неожиданные различия в размере. Если один файл после «без потерь» изменился сильнее остальных, полезно посмотреть verbose-вывод и метаданные: возможно, была отброшена лишняя хвостовая информация или нестандартный APP-сегмент, а не только оптимизированы таблицы Хаффмана.
Дополнительные данные после EOI
Стандартный JPEG заканчивается маркером EOI. Иногда после него остаются лишние байты: результат работы конкретного ПО, встроенный служебный блок, случайный мусор или данные, намеренно присоединенные к файлу. Обычный просмотрщик может их игнорировать, поэтому пользователь даже не знает об их существовании.
Jpegoptim по умолчанию не обязан сохранять такой хвост. Если он важен, используется --save-extra. Это узкоспециализированная опция: для обычной веб-публикации данные после EOI чаще воспринимаются как лишний вес, но в корпоративном или исследовательском процессе они могут иметь значение.
Перед массовым удалением стоит определить источник данных. Если хвост постоянно появляется после конкретной камеры или DAM-системы, лучше выяснить его назначение, чем просто считать мусором. В противном случае можно получить технически корректные JPEG, которые потеряли сведения, используемые внутренним процессом.
Потоковая обработка stdin ограничивает возможность сохранить extra data. Поэтому миграция из файловой схемы в streaming pipeline должна включать регрессионные тесты не только размеров и пикселей, но и необходимых служебных блоков.
Практический сценарий: подготовка фотографий для сайта
Для сайта Jpegoptim логично ставить ближе к концу цепочки: после кадрирования, изменения размеров, цветокоррекции и финального экспорта в JPEG. Если сначала оптимизировать большой исходник, а затем другой инструмент снова перекодирует его при ресайзе, эффект Jpegoptim будет потерян и появится лишний lossy-проход. Последовательность «редактирование → ресайз → экспорт JPEG → финальная оптимизация» обычно предсказуемее.
Первый безопасный шаг — lossless-проход. Он дает возможность оценить реальный резерв без ухудшения изображения:
find ./publish -type f ( -iname '*.jpg' -o -iname '*.jpeg' )
-exec jpegoptim --threshold=0.5 {} +
Если экономии недостаточно, на копии тестируют --max. Выбор качества должен опираться на контент. Портреты с мягким фоном, шумные ночные кадры, графика с текстом и архитектура с тонкими линиями ведут себя по-разному. Хорошая тестовая выборка содержит крайние случаи, а не только пять красивых фотографий из одной серии.
Следующий вопрос — метаданные. Для публичного сайта часто есть смысл убрать GPS и служебные сведения камеры, но профиль цвета может быть важен. Если пайплайн заранее приводит изображения к нужному цветовому пространству и проверяет отображение, политика может быть агрессивнее. Если нет — разумнее сохранить ICC и отдельно решить, что делать с EXIF Orientation.
После обработки следует проверять не только размер. Минимальный QA включает: успешное декодирование, ширину и высоту, ориентацию, корректный цвет на целевом браузере, отсутствие неожиданного удаления авторских сведений, а для lossy — визуальные артефакты. Jpegoptim решает только часть этой задачи.
При работе через CDN важно помнить, что CDN может сам выполнять трансформации изображения. Если CDN перекодирует JPEG в WebP/AVIF или меняет качество, локальная агрессивная рекомпрессия способна дать меньший выигрыш и двойную деградацию. В таком случае полезно сравнить стратегию «качественный исходный JPEG + трансформация на краю» с «жестко оптимизированный JPEG заранее» на реальном стеке.
Практический сценарий: Jpegoptim и WordPress
Jpegoptim не знает структуру базы WordPress и не является плагином медиатеки. Он видит только файлы. Поэтому при оптимизации уже существующего сайта нужно понимать, какие JPEG лежат в каталоге загрузок и какие из них являются отдельными производными размерами, созданными WordPress или темой. Изменение содержимого файла при сохранении имени не требует менять URL в базе, но каждый производный JPEG — отдельный объект и оптимизируется отдельно.
Безопасная схема начинается с резервной копии файлов и базы, хотя Jpegoptim базу не трогает. Затем выбирается небольшой каталог по месяцу, выполняется lossless-проход и проверяется фронтенд. После этого можно расширять диапазон. Запускать агрессивный --max сразу по всему uploads рискованно: там могут смешиваться фотографии, скриншоты, экспортированные макеты и изображения, уже сильно сжатые пользователями.
Пример рекурсивного lossless-прохода по каталогу загрузок может выглядеть так:
find ./wp-content/uploads -type f
( -iname '*.jpg' -o -iname '*.jpeg' )
-exec jpegoptim --threshold=1 {} +
Перед выполнением на production разумно убрать -exec и сначала вывести найденные пути, чтобы убедиться, что область поиска верна. Еще безопаснее работать на staging-копии. Команда не фильтрует файлы по принадлежности медиатеке WordPress, поэтому в каталоге могут попасться JPEG, которыми управляет сторонний плагин или процесс импорта.
Если после оптимизации используется файловый кэш, reverse proxy или CDN, содержимое по старому URL может оставаться закэшированным. Jpegoptim не выполняет purge. Нужно отдельно решить, требуется ли сброс кэша; при lossless-оптимизации визуально это незаметно, но экономия трафика начнет работать только после обновления кэшированных объектов.
Еще одна тонкость — резервные копии с дедупликацией. Массовая перезапись миллионов файлов даже с малой экономией меняет хэши и может резко увеличить объем первого инкрементального бэкапа после операции. Параметр --threshold помогает не переписывать JPEG ради ничтожного выигрыша. Для большой медиатеки это операционный фактор, а не только вопрос процентов сжатия.
Jpegoptim не создает WebP или AVIF и не подменяет решения для responsive images. Он может оставаться полезным как оптимизатор JPEG-версий, которые WordPress продолжает отдавать браузерам или хранить как fallback. Если задача — смена формата, нужна другая часть пайплайна.
Практический сценарий: сохранение исходников и выпуск производных файлов
Для фотостудии, каталога товаров или редакции лучше разделять мастер и delivery. Мастер-файлы хранятся без destructive-перезаписи, а публикационные JPEG создаются в отдельной структуре. В таком процессе Jpegoptim применяется к производным файлам и может работать агрессивнее, потому что источник для повторной генерации не потерян.
Например, после экспорта в каталог build/jpeg выполняется:
jpegoptim --max=85 --strip-all --keep-icc
--workers=4 build/jpeg/*.jpg
Это только пример структуры команды, а не универсальная политика качества. Значение 85, необходимость удаления EXIF и число workers должны быть выбраны после теста. Важна архитектура: команда применяется к копии, которую можно пересоздать.
Если публикационная система требует фиксированный потолок размера, вместо --max используется --size и пост-проверка размера. Если часть файлов не укладывается без заметных артефактов, лучше изменить размеры изображения или требования канала, чем бесконечно снижать JPEG quality.
Манифест сборки может хранить входной хэш, параметры Jpegoptim и выходной хэш. Тогда при изменении политики качества легко определить, какие производные файлы надо пересобрать. Такой подход делает CLI-инструмент частью воспроизводимого конвейера, а не одноразовой ручной команды.
Списки файлов и безопасные имена
Имена с пробелами — обычный случай и сами по себе не проблема, если shell-команда построена правильно. Проблема возникает, когда пути превращают в текст и затем снова делят по пробелам. Конструкция for f in $(find ...) для фотоколлекции ненадежна. Лучше использовать find -exec, нуль-терминированные списки для xargs -0 или собственный --files-from в контролируемой системе имен.
Пример с нуль-терминированным потоком:
find ./images -type f -iname '*.jpg' -print0 |
xargs -0 jpegoptim --threshold=1
Так корректно передаются пробелы, табуляции и переводы строк. Однако если вы хотите воспользоваться именно --files-stdin Jpegoptim, он ожидает имена по строкам, а не NUL. Это разные протоколы; подменять один другим нельзя.
--files-from=list.txt хорош для воспроизводимых задач: список можно просмотреть, сохранить как артефакт сборки и повторно использовать. Формирующая его программа должна записывать один путь на строку и не добавлять декоративные столбцы. Если имена контролируются системой и переводы строк в них запрещены, формат прост и надежен.
Для путей, начинающихся с дефиса, безопаснее передавать абсолютный путь или путь с префиксом ./, чтобы имя не выглядело как опция. Это универсальная привычка для Unix CLI и помогает избегать неоднозначности даже в программах с поддержкой разделителя опций.
Порог экономии и стоимость массовой перезаписи
На одном файле разница между экономией 0,1% и 1% кажется несущественной. На миллионах объектов появляется второй фактор: сама запись. Перезапись меняет метаданные файловой системы, контрольные суммы, результаты синхронизации, инкрементальные резервные копии и кэш. Поэтому технически достижимое уменьшение не всегда операционно выгодно.
--threshold позволяет ввести минимальный процент улучшения. Для архива на сетевом хранилище можно сначала прогнать --noaction, собрать распределение выигрыша и решить, где проходит разумная граница. Это лучше, чем выбирать порог по ощущению.
Если файлов мало, порог можно не задавать. Если файлов много и резервная система дорого реагирует на каждое изменение, порог становится инструментом управления I/O и бэкапом. Jpegoptim не знает цену записи в вашей инфраструктуре, поэтому решение остается внешним.
Порог не относится к визуальному качеству. Он оценивает экономию размера. Файл с lossy-перекодированием может пройти порог и при этом оказаться визуально неприемлемым. Для --max и --size нужен отдельный QA качества.
Как читать skipped и почему файл не уменьшился
Самый частый вопрос после первого запуска: почему Jpegoptim «ничего не сделал». У этого есть несколько нормальных причин. JPEG мог быть уже оптимизирован; новый Huffman-код не дал меньшего файла; заданный --threshold оказался выше полученного выигрыша; исходное качество уже ниже --max; файл не соответствует JPEG; объект является ссылкой или специальным файлом; нет прав на запись; при --nofix найдено предупреждение декодера.
Начинать диагностику следует с одного файла и verbose-вывода, а не с добавления --force. Принудительная запись скрывает полезный сигнал: если результат больше, значит выбранная трансформация сама по себе не экономит место. --force нужен, когда важен формат структуры, а не потому, что команда «обязана» уменьшить файл.
Если с --dest целевой файл отсутствует, это тоже может быть следствием skipped, а не аварии. Проверьте строку результата и логи. Для полного дерева назначения предварительно копируйте оригиналы или программно обрабатывайте пропуски.
Если --max не уменьшает часть изображений, не надо автоматически снижать значение для всей коллекции. Возможно, эти файлы уже сильнее сжаты. Для них новый lossy-проход принесет мало экономии и больше риска артефактов.
Производительность без выдуманных цифр
Скорость Jpegoptim зависит от размера и количества JPEG, процессора, JPEG-библиотеки, режима сжатия, типа накопителя, метаданных и числа workers. Поэтому универсальная цифра «N фотографий в секунду» не имеет смысла без стенда. Lossless-оптимизация обычно дешевле сложного перебора вариантов, а --auto-mode и --retry увеличивают объем работы.
Для измерения собственного пайплайна берут репрезентативный набор: маленькие веб-миниатюры, большие фотографии камеры, progressive и normal, файлы с крупным EXIF/ICC. Затем отдельно измеряют один worker и несколько workers. Важно смотреть не только wall-clock, но и среднюю загрузку CPU, I/O wait и влияние на другие сервисы.
На сервере с сайтом лучше ограничить параллелизм так, чтобы оптимизация не вытесняла PHP, базу данных и системные процессы. На выделенной batch-машине можно использовать больше ядер. Jpegoptim не имеет встроенного понимания приоритета веб-трафика; это задача scheduler, nice/ionice, контейнерных лимитов или оркестратора.
Если файлов очень много и каждый мал, накладные расходы на запуск процессов и файловую систему могут стать значимыми. Собственный --workers или батчирование аргументов через find -exec ... {} + уменьшает стоимость по сравнению с запуском отдельного jpegoptim для каждого файла в цикле shell.
При --retry важно измерять дополнительную экономию, а не только время. Если повторные проходы экономят считанные байты, а CPU-время растет многократно, для production это плохой обмен. Параметр существует для случаев, где каждый байт действительно важен.
Работа с цветом и ориентацией
Jpegoptim не выполняет цветокоррекцию и не преобразует профиль в sRGB. Если входной JPEG использует ICC-профиль, визуальная корректность зависит от того, сохранен ли профиль и поддерживает ли его потребитель. Удаление ICC ради экономии способно изменить отображение цвета. Для профессиональной фотографии профиль сначала приводят к требуемому цветовому пространству специализированным инструментом, а уже затем решают, можно ли убрать ICC.
С ориентацией аналогично. EXIF Orientation — инструкция о том, как повернуть пиксельную матрицу при отображении. Если удалить EXIF без физической авторотации, некоторые изображения окажутся боком или вверх ногами. Jpegoptim не обязан анализировать и применять этот тег. Поэтому политика приватности «strip EXIF» должна быть связана с предварительной нормализацией ориентации.
IPTC и XMP могут содержать подписи, авторство, ключевые слова и сведения о правах. Публичный сайт иногда хочет удалить технические поля камеры, но сохранить copyright. Грубый --strip-all не различает бизнес-ценность полей. Если метаданные являются частью редакционного процесса, лучше управлять ими до Jpegoptim и затем выбрать точные keep/strip-опции.
Комментарий COM также можно сохранять или удалять отдельно. Он редко нужен обычному сайту, но может использоваться внутренним ПО. Перед массовой чисткой метаданных полезно инвентаризировать несколько сотен реальных файлов и выяснить, какие сегменты встречаются.
Что дает —auto-mode и когда его не включать
--auto-mode решает узкую задачу: сравнивает варианты режима JPEG и выбирает меньший. Это не «автоматическое качество» и не визуальный анализ. Он не решает, насколько сильными могут быть артефакты, не меняет размеры изображения и не выбирает WebP/AVIF.
Преимущество режима — избавление от жесткого правила «все progressive» или «все normal», если единственный критерий — размер. Недостаток — больше вычислений. На миллионе миниатюр дополнительные пробные кодирования могут стоить дороже, чем сэкономленные килобайты. На небольшом наборе крупных изображений компромисс может быть другим.
Есть и эксплуатационный нюанс версий. Поддержка --auto-mode появилась в новых релизах, а затем разработчик исправлял сценарий, где режим мог зациклиться. Если этот ключ нужен в unattended-процессе, проверьте точную сборку и прогоните timeout-тест на разнообразном наборе. Не переносите параметр в cron только потому, что он есть в статье или man-странице новой версии.
Если нужна предсказуемая структура файла, проще выбрать --all-progressive или --all-normal. Если нужна минимизация размера без требования к режиму, --auto-mode уместнее после проверки версии.
Когда нужен —retry
--retry относится к режимам с потерями. Он повторяет попытки рекомпрессии, пока размер продолжает уменьшаться. Это не дополнительная lossless-магия и не способ гарантировать точный --size. Его смысл — выжать еще немного размера ценой дополнительных проходов.
Для одиночной фотографии время часто не важно, но в batch-задаче эффект умножается. Поэтому сравнивают два набора: одинаковые --max/--size с retry и без него. Смотрят суммарный размер, CPU-время и визуальное качество. Если выигрыш мал, параметр исключают.
Retry не следует использовать как компенсацию плохо выбранной архитектуры. Если лимит размера недостижим без слишком низкого качества, правильнее уменьшить геометрические размеры, изменить требования или выбрать иной формат, а не заставлять JPEG многократно перекодироваться.
Примеры рабочих команд
Без потерь для одного файла
jpegoptim photo.jpg
Минимальный безопасный по пикселям сценарий. Метаданные все равно следует учитывать отдельно.
Проверка без записи
jpegoptim --noaction photo.jpg
Подходит для первичной оценки и отладки выбора файлов.
Сохранить только заметный выигрыш
jpegoptim --threshold=1.5 photo.jpg
Полезно на больших хранилищах, где массовая перезапись ради долей процента нежелательна.
Ограничить качество
jpegoptim --max=85 photo.jpg
Lossy-режим. Число выбирается после визуального теста на собственном контенте.
Попытаться уложиться в долю исходного размера
jpegoptim --size=70% photo.jpg
Lossy-режим с приблизительной целью.
Сохранить результат отдельно
mkdir -p optimized
jpegoptim --dest=optimized photo.jpg
Не забывайте, что skipped-файлы могут отсутствовать в каталоге назначения.
Удалить метаданные, оставить ICC
jpegoptim --strip-all --keep-icc photo.jpg
Перед этим проверьте EXIF Orientation и требования к авторским метаданным.
Сделать progressive
jpegoptim --all-progressive --force photo.jpg
--force нужен, если структурная конверсия важнее того, оказался ли файл меньше.
Рекурсивный lossless-проход
find ./images -type f ( -iname '*.jpg' -o -iname '*.jpeg' )
-exec jpegoptim --threshold=1 {} +
Для production сначала проверьте выборку путей без -exec.
Обработка заранее подготовленного списка
jpegoptim --files-from=jpeg-list.txt --max=85
Список должен содержать по одному пути на строку; необычные имена с переводами строк требуют другой схемы.
Параллельная очередь
jpegoptim --workers=4 --max=85 *.jpg
Число workers подбирается по нагрузке, а наличие опции проверяется на целевой сборке.
Типичные ошибки и их устранение
| Симптом | Вероятная причина | Что проверить |
|---|---|---|
| Файл не стал меньше | JPEG уже оптимизирован | Статус, threshold, режим без force |
| В dest нет файла | Результат был skipped | Лог и правило копирования исходников |
| Фото повернулось после публикации | Удален EXIF Orientation | Авторотацию до strip или keep-exif |
| Изменился цвет | Удален нужный ICC-профиль | Сохранение ICC и цветовой процесс |
| Команда не знает —workers | Старая или иная сборка | jpegoptim —version и —help |
| Список ломается на пробелах | Неверное разбиение shell | find -exec или xargs -0 |
| Процесс слишком грузит сервер | Слишком много workers | CPU, I/O wait, лимиты ресурсов |
| Файл после —max выглядит хуже | Повторное lossy-сжатие | Мастер-копию и выбранное качество |
| Размер выше строгого лимита | —size дает приблизительную цель | Пост-проверку и запас по размеру |
| Неизвестные данные исчезли | Политика маркеров/extra data | keep-параметры и —save-extra |
Ошибка «unknown option» почти всегда означает несовпадение версии или сборки. Не надо заменять ключ на похожий по случайному совету. Сначала откройте jpegoptim --help и man-страницу на этой машине. Особенно это касается --auto-mode, --retry, JFIF-параметров, списков файлов и параллельной обработки — они добавлялись не одновременно.
Если команда пишет permission denied, определите, на каком этапе не хватает прав: чтение входного JPEG, создание временного файла в каталоге, переименование, запись в --dest или сохранение атрибутов. Запуск от root как универсальное решение плох тем, что маскирует неверную модель прав и увеличивает последствия ошибки пути.
Если после прерывания обнаружен временный или backup-файл, не удаляйте его автоматически до проверки. Некоторые режимы записи используют резервную копию. Сначала сравните содержимое и убедитесь, какой файл является целым.
Если скрипт внезапно зависает только с --auto-mode, зафиксируйте версию Jpegoptim и проверьте, не относится ли сборка к версии с известным исправленным циклом. Для production можно временно выбрать явный normal/progressive или использовать сборку, где исправление присутствует, после регрессионного теста.
Почему Jpegoptim не заменяет графический редактор
Специализация Jpegoptim делает его удобным в автоматизации, но одновременно определяет ограничения. Он не показывает изображение на холсте, не имеет кистей, слоев, масок, кривых, удаления объектов, пластики, ретуши кожи, замены фона или текстовых надписей. Даже базовое изменение ширины и высоты не относится к его задаче.
Если нужно подготовить одну фотографию вручную, сначала лучше выполнить художественную работу в редакторе, экспортировать JPEG с нужными размерами и только затем применять Jpegoptim как финальный технический проход. В обратном порядке финальный экспорт редактора почти наверняка заново закодирует JPEG и сведет оптимизацию на нет.
Это же объясняет, почему сравнивать Jpegoptim с фоторедактором только по числу функций бессмысленно. Для shell-скрипта отсутствие GUI — сознательная форма работы, а не «недостающая кнопка». Для пользователя, которому нужно убрать объект или заменить фон, напротив, Jpegoptim вообще не решает задачу.
Сравнение с аналогами
Jpegoptim удобно сравнивать не с абстрактными «оптимизаторами», а с инструментами, которые пересекаются с ним по конкретным операциям: FotoMASTER как ручной фоторедактор, jpegtran как инструмент lossless-преобразований JPEG, MozJPEG как альтернативный JPEG-кодировщик и jpeg-recompress как утилита, подбирающая степень сжатия по метрикам визуального качества. У каждого другой уровень работы с изображением.
Jpegoptim и ФотоМАСТЕР
ФотоМАСТЕР рассчитан на визуальное редактирование: в нем есть ретушь, удаление нежелательных объектов, замена фона, фильтры и 3D-LUT-стили. Это задачи, которых в Jpegoptim нет принципиально. Пользователь видит фотографию и вручную принимает художественные решения. Jpegoptim, напротив, удобен там, где уже готовые JPEG надо одинаково оптимизировать из командной строки и обработать большой набор без GUI.
Эти программы могут находиться в одной цепочке, но на разных этапах. Сначала в ФотоМАСТЕР выполняют коррекцию и экспорт, затем Jpegoptim может оптимизировать полученный JPEG. Если после Jpegoptim снова сохранить фотографию из редактора с JPEG-перекодированием, финальный технический проход придется повторить.
Jpegoptim и jpegtran
jpegtran работает с JPEG-коэффициентами и особенно полезен для lossless-трансформаций: поворота, отражения, транспонирования и некоторых вариантов кадрирования без полного декодирования и повторного квантования. Он также умеет оптимизировать энтропийное кодирование и переводить JPEG в progressive. Jpegoptim не предоставляет такой набор геометрических трансформаций; его сильная сторона — простая оптимизация файлов и режимы ограничения качества/размера.
Если задача — повернуть JPEG на 90 градусов без дополнительной потери, jpegtran подходит лучше. Если нужно рекурсивно уменьшать готовые JPEG и при необходимости ограничивать quality factor или размер, Jpegoptim дает более прямой интерфейс. В одном скрипте инструменты могут дополнять друг друга.
Jpegoptim и MozJPEG
MozJPEG — модифицированный JPEG-кодировщик на базе libjpeg-turbo, ориентированный на более эффективное сжатие. Он включает оптимизации progressive scan, trellis-квантование и другие техники, которые требуют полноценного кодирования. Его утилиты cjpeg и jpegtran дают более низкоуровневый контроль, а сам проект часто используют как библиотеку в приложениях и системах сборки.
Jpegoptim проще, когда вход уже JPEG и нужен понятный финальный проход. MozJPEG интереснее, когда вы контролируете этап кодирования из исходного изображения и готовы платить временем CPU за дополнительные методы оптимизации. Это не взаимозаменяемые команды с одинаковой семантикой.
Jpegoptim и jpeg-recompress
jpeg-recompress перекодирует JPEG, подбирая качество с опорой на метрики визуального сходства. Такой подход ориентирован на баланс размера и воспринимаемого качества, а не на фиксированный --max или приблизительный --size. Он полезен, если автоматизированный выбор качества важнее простоты и скорости.
Jpegoptim выигрывает простотой и предсказуемостью: пользователь явно задает верхнюю границу качества или целевой размер и может вообще остаться в lossless-режиме. jpeg-recompress выполняет более тяжелый анализ и по самой идее работает как lossy-рекомпрессор. Выбор зависит от того, нужна ли гарантированная сохранность пиксельных данных или автоматический поиск компромисса.
| Инструмент | Основная задача | Lossless JPEG | Lossy-контроль | Ручное редактирование |
|---|---|---|---|---|
| Jpegoptim | Оптимизация готовых JPEG | Да, по умолчанию | —max, —size | Нет |
| ФотоМАСТЕР | Визуальная правка фотографии | Не основная задача | Через экспорт | Да |
| jpegtran | Lossless-трансформации JPEG | Да | Не меняет quality как рекомпрессор | Нет |
| MozJPEG | Эффективное JPEG-кодирование | jpegtran для трансформаций | Да, через кодировщик | Нет |
| jpeg-recompress | Подбор lossy-сжатия по метрикам | Не основной сценарий | Да, автоматизированный подбор | Нет |
Что Jpegoptim не умеет
Поддерживаемый рабочий формат — JPEG/JFIF. PNG, GIF, TIFF, HEIC, WebP и AVIF требуют других инструментов. Переименование image.png в image.jpg не превращает его в JPEG, а расширение .jpeg или .jpg само по себе не гарантирует корректное содержимое.
Jpegoptim не меняет геометрические размеры. Если исходник 6000×4000 слишком велик для страницы, сначала нужен ресайз. Часто именно уменьшение разрешения дает значительно большую экономию, чем тонкая оптимизация энтропийного кодирования.
Jpegoptim не делает визуальный анализ сцены. --max=85 не означает «качество на 85% по человеческому восприятию», а --size=70% не гарантирует одинаковую визуальную деградацию всех файлов. Для автоматической perceptual-оптимизации нужны другие подходы.
Нет собственного GUI. Команды, логи и параметры — основной интерфейс. Это достоинство для серверной автоматизации и ограничение для человека, который хочет видеть ползунок качества и сравнение до/после.
Jpegoptim не управляет CMS, не очищает CDN, не создает записи медиатеки, не обновляет HTML и не переписывает URL. Все это должен делать внешний процесс.
Проверка результата после оптимизации
Для lossless-прохода полезно проверять три уровня. Первый — файл декодируется без ошибок. Второй — размеры изображения и ожидаемые служебные свойства не изменились. Третий — политика метаданных соблюдена. Сравнение только размера недостаточно: уменьшенный, но поврежденный JPEG не является успехом.
Для lossy-прохода добавляется визуальная проверка. Автоматически можно контролировать пиксельные размеры, размер файла, наличие профиля и декодируемость, но приемлемость артефактов зависит от контента. На этапе подбора параметров нужен просмотр крайних примеров: волосы, трава, текст, резкие контуры, градиенты неба, высокое ISO.
Если обрабатывается сайт, финальная проверка должна происходить в браузере через тот же URL и CDN, который увидит пользователь. Это ловит ошибки кэширования, MIME-типа и цепочки трансформаций, которые сама команда jpegoptim не видит.
Для больших архивов разумно сохранить случайную выборку «до/после» и лог версии инструмента. Тогда после обновления Jpegoptim или JPEG-библиотеки можно обнаружить изменение поведения до того, как новая политика пройдет по всему хранилищу.
Стратегия для автоматизации и CI
В CI Jpegoptim лучше применять к артефактам сборки, а не к исходникам в репозитории, если команда может менять файлы lossy. Это делает процесс воспроизводимым: исходное изображение остается стабильным, оптимизированный артефакт генерируется заново из известного входа.
Скрипт должен фиксировать версию Jpegoptim и параметры. Если runner обновился и получил новую системную версию, выходные байты могут измениться даже при той же команде из-за библиотеки JPEG или исправлений. Для строгой воспроизводимости версию закрепляют в контейнере или образе сборки.
Проверка CI может запрещать слишком большие JPEG. В этом случае Jpegoptim выполняет оптимизацию, а отдельный шаг проверяет лимит. Не стоит ожидать, что --size заменит assert: цель приблизительная, и pipeline должен явно упасть, если фактический файл все еще превышает допустимый размер.
При параллельной сборке избегайте ситуации, когда два процесса одновременно оптимизируют один путь. Jpegoptim не предназначен быть системой блокировок. Разделите набор файлов между jobs или создавайте уникальные каталоги артефактов.
Для pull request удобно публиковать отчет: суммарный размер до/после, список наиболее изменившихся файлов и количество skipped. Это позволяет видеть эффект без ручного чтения сотен строк. Сам Jpegoptim дает базовые данные, а агрегирование делает внешняя оболочка.
Безопасный shell-сценарий для большой медиатеки
Перед выполнением любого массового скрипта лучше отделить фазу поиска от фазы записи. Сначала сохраните список путей и посчитайте количество:
find ./media -type f ( -iname '*.jpg' -o -iname '*.jpeg' )
-print > jpeg-list.txt
wc -l jpeg-list.txt
Такой простой список подходит, только если имена в вашей системе не содержат переводов строк. Для произвольных Unix-имен используйте нуль-терминированную схему и не передавайте ее в строковый --files-from.
Затем выполните dry run на небольшой выборке, а не на всей медиатеке. После проверки запускайте рабочую команду с логом. Если важна доступность сайта, ограничьте приоритет процесса и workers средствами ОС.
После окончания подсчитайте обработанные ошибки и выборочно проверьте файлы. Не удаляйте резервную копию в ту же минуту. Сначала дождитесь прохождения приложения, CDN и планового бэкапа.
Для lossy-сценария надежнее никогда не оптимизировать master in-place. Пусть Jpegoptim работает только с каталогом производных файлов. Тогда любое изменение параметров можно откатить полной регенерацией.
Когда использовать только lossless
Только lossless-режим оправдан, когда изображения уже опубликованы и нельзя менять визуальные данные; когда это мастер-копии без альтернативного источника; когда файлы многократно проходили JPEG-сохранение и новая деградация недопустима; когда политика организации запрещает lossy-трансформацию; либо когда экономия места вторична по отношению к сохранности.
В таком режиме чаще всего достаточно стандартного вызова, --threshold, при необходимости политики метаданных и осторожного параллелизма. --max, --size и --retry не используются.
Если lossless-выигрыш невелик, это не повод считать Jpegoptim бесполезным. Возможно, исходные файлы уже хорошо закодированы. В таком случае дальнейшая заметная экономия требует решения на другом уровне: уменьшить геометрические размеры, изменить формат или разрешить lossy-сжатие.
Когда оправдан lossy-режим
Lossy-перекодирование оправдано для производных веб-файлов, которые можно пересоздать; для каналов с жестким лимитом размера; для архива превью, где качество вторично; для пользовательских загрузок, если политика сервиса заранее допускает нормализацию и повторное сжатие.
Ключевое условие — не считать одно значение качества универсальным. Политика должна быть проверена на типичном контенте. Если сервис принимает и фотографии, и схемы с мелким текстом, возможно, им нужны разные правила или разные форматы.
Lossy-проход следует выполнять после ресайза. Сжимать 24-мегапиксельный JPEG до низкого quality, а потом уменьшать его до 1200 пикселей — хуже, чем сначала качественно сделать ресайз, затем один раз закодировать финальное разрешение.
Версии и переносимость команд
Основные идеи Jpegoptim стабильны давно: lossless-оптимизация, ограничение качества, каталог назначения, управление метаданными. Но новые опции добавлялись постепенно. --workers, чтение списков файлов, --keep-jfif/--strip-jfif, --retry, --save-extra и --auto-mode могут отсутствовать в старых пакетах.
Поэтому инструкция для инфраструктуры должна начинаться с минимально поддерживаемой версии, а не с предположения «jpegoptim есть». Если разворачиваются разные Linux-дистрибутивы, контейнеры и macOS-ноутбуки, один и тот же скрипт стоит прогнать на каждой целевой платформе.
Проект может быть собран с обычной libjpeg, libjpeg-turbo или совместимой библиотекой. Это влияет на производительность и отдельные возможности. Технически одинаковый ключ не гарантирует побайтово одинаковый результат между всеми сборками. Для кэшей, основанных на хэше результата, это надо учитывать.
Если критична воспроизводимость, фиксируйте не только Jpegoptim, но и образ окружения. Если критична только визуальная эквивалентность и размер, достаточно приемочных тестов после обновления.
Как избежать повторного lossy-сжатия
Самая опасная ошибка автоматизации — запускать один и тот же lossy-проход по уже обработанным файлам без понимания происхождения JPEG. Команда с --max не является магически идемпотентной во всех сценариях: после изменения другим редактором, CMS или ресайзером файл снова может оказаться кандидатом на рекомпрессию. Поэтому pipeline должен различать мастер, промежуточный файл и конечный артефакт.
Надежный вариант — каждый раз строить конечный JPEG из неизменяемого источника в чистом каталоге сборки. Если это невозможно, храните признак обработки вне самого JPEG: манифест с хэшем, состояние задания в базе или версию артефакта. Ориентироваться только на имя файла или дату недостаточно, особенно если используется --preserve.
При миграции старой медиатеки сначала выполняйте lossless-проход. Затем lossy-политику применяйте только к тем файлам, для которых есть резервная или исходная версия. Так можно остановить эксперимент, изменить --max и получить результат заново без накопления артефактов.
Если downstream-сервис после Jpegoptim все равно перекодирует JPEG, оценивать качество надо после всей цепочки. Хорошо выглядящий промежуточный файл может ухудшиться на следующем этапе. Оптимизация каждого звена по отдельности не гарантирует оптимальный конечный результат.
Практический чек-лист перед массовым запуском
- Определите, должны ли пиксели остаться lossless.
- Сделайте резервную копию или работайте с производными файлами.
- Проверьте
jpegoptim --versionи--help. - Выберите тестовую выборку из разных типов изображений.
- Проверьте EXIF Orientation и необходимость ICC.
- Решите, нужны ли XMP, IPTC и комментарии.
- Сначала выполните
--noaction. - Для lossy создайте реальные копии и просмотрите их.
- Не считайте
--destзеркальной копией автоматически. - Подберите
--workersпо CPU и диску. - Сохраните лог и параметры запуска.
- После выполнения проверьте декодирование и фактический размер.
Этот порядок не делает процесс сложнее ради формальностей. Он закрывает реальные риски Jpegoptim: in-place изменение файлов, удаление метаданных, несовпадение опций между версиями, приблизительный размер в lossy-режиме и влияние массовой перезаписи на инфраструктуру.
Сочетания параметров и логика приоритетов
В Jpegoptim важно различать параметры, которые меняют способ кодирования, и параметры, которые только управляют условиями записи. --max и --size переводят задачу в lossy-область. --threshold после этого не отменяет потери, а лишь запрещает сохранять результат, если выигрыш слишком мал. --force не повышает качество и не делает алгоритм эффективнее — он снимает часть защитного решения «не записывать невыгодный результат».
Так, команда jpegoptim --max=85 --threshold=2 photo.jpg означает: при необходимости перекодировать JPEG не выше выбранной границы качества, но не заменять исходник, если экономия не достигла двух процентов. Это не эквивалентно lossless-проходу с порогом. Если файл был перекодирован для оценки, сохранение зависит от условия, однако сам смысл --max остается lossy.
--retry имеет смысл только рядом с --max или --size. В чистом lossless-сценарии повторять рекомпрессию нечего. Аналогично --auto-mode отвечает за выбор normal/progressive, а не за величину quality factor. Смешение этих понятий часто приводит к командам, которые потребляют больше CPU, но не решают исходную задачу.
Опции --all-progressive и --all-normal задают противоположные требования, поэтому их нельзя рассматривать как два независимых улучшения. --auto-mode также конкурирует с явным выбором режима: либо пользователь фиксирует структуру, либо разрешает программе выбрать меньший вариант. Скрипт конфигурации должен генерировать одну понятную политику, а не собирать все флаги одновременно.
Метаданные образуют отдельный слой. Например, --strip-all --keep-icc означает агрессивную очистку с исключением для ICC; это не связано с lossless/lossy-режимом пикселей. Благодаря такому разделению можно сначала сформулировать четыре независимых решения: как сжимать изображение, какой режим JPEG нужен, что делать с метаданными и при каких условиях записывать результат.
| Вопрос | Основные параметры | Что они не решают |
|---|---|---|
| Менять ли качество | —max, —size | Метаданные и размеры кадра |
| Какой режим JPEG | —all-progressive, —all-normal, —auto-mode | Визуальную приемлемость |
| Что оставить в APP-сегментах | —strip-*, —keep-* | Цветовое преобразование |
| Когда сохранять результат | —threshold, —force | Резервное копирование |
| Куда записывать | —dest, —overwrite | Полное зеркалирование каталога |
| Как распределить нагрузку | —workers | Приоритет процессов ОС |
Такая декомпозиция облегчает ревью shell-скрипта. По одной строке можно понять, где сознательно разрешены потери, какие метаданные исчезнут и почему результат будет либо заменен, либо пропущен.
Точная политика метаданных для разных задач
Для публичной веб-фотографии часто хочется одновременно убрать приватные EXIF-поля и сохранить корректный цвет. В этом случае сначала проверяют, не зависит ли ориентация от EXIF, затем сохраняют ICC. Если ориентация уже физически нормализована, политика может быть жестче. Если нет, массовый strip должен быть отложен до нормализации.
Для фотобанка или редакционного архива требования обратные: EXIF, IPTC и XMP могут иметь ценность как каталогизационные данные, а несколько килобайт экономии не оправдывают их потерю. Здесь правильнее сохранить известные метаданные и отдельно решить судьбу неизвестных APP-сегментов. Jpegoptim не является DAM-системой и не проверяет смысл каждого поля.
Для e-commerce изображения товара могут содержать ICC, но GPS и параметры камеры обычно не нужны. Для юридически значимых фотографий или материалов с доказательственным значением любые изменения служебной части могут быть недопустимы даже при неизменных пикселях; такие оригиналы лучше не обрабатывать на месте вообще.
Комментарий COM иногда содержит подпись кодировщика или технический текст. Его удаление обычно не влияет на отображение, но перед массовой политикой стоит проверить, не использует ли организация комментарий как внутренний маркер. То же относится к XMP: визуально JPEG останется тем же, а workflow-метка может исчезнуть.
ICC-профиль особенно важен потому, что его удаление может изменить не байты «вокруг картинки», а реальный визуальный результат на color-managed устройствах. Если исходник не в sRGB и профиль исчезает, браузер или редактор может трактовать числовые значения цвета иначе. Jpegoptim не конвертирует пространство; он только сохраняет или удаляет профиль согласно политике.
JFIF и Adobe marker требуют отдельной осторожности. Их присутствие может зависеть от JPEG-библиотеки, и некоторые маркеры при перекодировании формируются заново. Поэтому тест «сравнить только наличие сегмента» недостаточен для архивной задачи. Если downstream-программа зависит от точного содержимого маркера, сравнивают структуру до и после на реальных файлах.
Если цель — приватность, не стоит ограничиваться предположением, что --strip-all очищает вообще любой возможный секрет. Нестандартные данные могут находиться в необычных сегментах или после EOI. Для чувствительного процесса нужна независимая проверка метаданных и содержимого, а Jpegoptim рассматривается как один этап очистки, не как DLP-система.
Файловая система, hard links и атомарность
Поведение при записи важно на серверах не меньше, чем коэффициент сжатия. В обычном режиме Jpegoptim формирует временный результат и затем заменяет путь, если результат подходит. Такая схема уменьшает риск оставить частично записанный JPEG при падении процесса, но может изменить inode. Если к исходному inode существовали другие hard links, они продолжают указывать на старое содержимое, тогда как обработанный путь — на новое.
Для большинства веб-каталогов это незаметно, потому что hard links используются редко. В хранилищах с дедупликацией на уровне ссылок или в системах развертывания такой эффект принципиален. Перед массовой оптимизацией стоит проверить stat на тестовом наборе и понять, не основана ли экономия места на общих inode.
Символические ссылки обрабатываются консервативно и не должны восприниматься как обычные входные файлы. Если дерево медиатеки построено из symlink, скрипт может формально найти путь, но Jpegoptim его пропустит. Решение должно быть явным: либо находить реальные файлы, либо не оптимизировать такое дерево.
На файловой системе с copy-on-write изменение каждого JPEG может резко увеличить фактически занятое место в снапшотах, даже если текущая версия стала меньше. Аналогично инкрементальный бэкап видит тысячи новых хэшей. Поэтому перед запуском следует оценивать не только текущий du, но и стоимость снапшотов и резервных копий.
--preserve сохраняет время модификации, но не отменяет факта изменения данных. Инструменты синхронизации, которые полагаются исключительно на mtime и размер, могут вести себя иначе, чем системы по контрольным суммам. Если требуется гарантированно доставить новую версию, не следует рассчитывать на изменение времени как единственный сигнал.
--preserve-perms меняет стратегию записи ради сохранения прав и владельца. Этот режим требует особенно надежной резервной копии, потому что прямое изменение файла менее защищено от прерывания, чем создание нового файла и rename. Он нужен не для «лучшего качества», а только если файловые атрибуты нельзя восстановить обычной заменой.
Каталог --dest должен существовать и быть доступен для записи. Если он находится на другом filesystem или сетевом ресурсе, профиль I/O отличается от in-place-обработки. Большое число workers может упереться в латентность сети, а не в CPU. Параллелизм всегда подбирают на той же архитектуре, где будет production.
Диагностика большого пакетного запуска
Когда обрабатывается несколько файлов, человеческого просмотра строк достаточно. Когда файлов сотни тысяч, нужна агрегированная диагностика. Минимальный отчет должен ответить на пять вопросов: сколько файлов найдено, сколько реально оптимизировано, сколько пропущено, сколько завершилось ошибкой и насколько изменился суммарный объем.
До запуска полезно сохранить исходную статистику отдельной командой, а не пытаться восстановить ее после изменения файлов. После запуска сравнивают количество путей и суммарный размер. Для lossy-политики дополнительно выделяют файлы с крупнейшим процентом уменьшения: именно они чаще всего требуют визуальной выборочной проверки.
CSV-режим Jpegoptim помогает собрать машинно читаемые строки, но парсер следует привязать к протестированной версии. Даже если формат выглядит очевидно, не стоит строить долгоживущий production-контракт на непроверенных позициях колонок. Внешняя оболочка может также сохранять имя файла до вызова, exit status и размер через stat.
Ошибки прав, поврежденные JPEG и несуществующие пути должны попадать в отдельный список для повторной обработки. Не следует повторять всю очередь из-за десятка неудачных файлов: это лишняя запись и, в lossy-сценарии, потенциально повторная рекомпрессия. Хороший pipeline отделяет успешные результаты от retry-очереди.
Если процесс был прерван, нельзя предполагать, что «первые N строк» соответствуют уже обработанным именам, особенно с --workers. Порядок вывода параллельных задач не гарантирует исходный порядок. Продолжение лучше строить по фактическим файлам и журналу статусов.
Для строгой автоматизации можно хранить манифест с хэшем исходника. Если текущий хэш уже соответствует успешно обработанной записи, повтор не нужен. Если файл изменился после предыдущего запуска, он снова проходит pipeline. Такой подход защищает от двойной lossy-рекомпрессии лучше, чем проверка даты.
Периодические задания должны иметь ограничение времени и ресурсов. Если редкий JPEG вызывает необычно долгую работу, cron не должен накопить несколько одновременно работающих копий. Системные механизмы блокировки и timeout находятся вне Jpegoptim, но для надежного сервиса они обязательны.
Как выбирать между —max и —size
--max удобен, когда нужна единая политика качества: например, все публикационные JPEG не должны кодироваться выше выбранного quality factor. Размеры файлов при этом будут различаться в зависимости от разрешения и сложности изображения. Такой подход обычно проще для галереи, где нет жесткого лимита в килобайтах.
--size нужен, когда ограничение выражено именно в объеме. Он подбирает качество ради приблизительной цели, поэтому два визуально похожих файла могут получить разные параметры кодирования. При слишком жестком лимите сложный кадр будет сжат сильнее, чем простой. Это нормальное следствие постановки задачи.
Если внешний API требует «не больше 500 КБ», целиться ровно в 500 неразумно. Оставляют запас и после Jpegoptim проверяют фактическое число байт. Если результат все равно больше, скрипт либо уменьшает геометрические размеры отдельным инструментом, либо сообщает об ошибке. Бесконечный цикл повторного JPEG-сжатия — плохая стратегия.
Если важна одинаковая визуальная планка, а размеры могут плавать, --max понятнее. Если важен транспортный лимит, --size ближе к задаче. Если одновременно нужны жесткий размер и высокая визуальная однородность, одного Jpegoptim может быть недостаточно: понадобится более сложный адаптивный pipeline и контроль качества.
Параметры можно тестировать на копиях одного набора и строить распределение размеров. Например, сравнить lossless, --max=90, --max=85 и целевой процент. Но итог нельзя выбирать только по сумме мегабайт: крайние визуальные артефакты важнее среднего значения.
Проверка перед обновлением Jpegoptim
Обновление CLI-инструмента в production стоит воспринимать как изменение кодировщика. Даже если командная строка не менялась, исправления, новая JPEG-библиотека или иная логика выбора режима могут изменить выходной файл. Для CDN это может означать новые хэши, а для reproducible build — расхождение артефактов.
Перед обновлением сохраните небольшую регрессионную коллекцию: normal и progressive JPEG, файл с ICC, EXIF Orientation, XMP/IPTC, уже оптимизированный JPEG, поврежденный пример и файл с лишними данными после EOI, если такие встречаются. Запустите старую и новую версии с теми же параметрами и сравните статусы.
Отдельно проверьте опции, которые добавлялись недавно. Для --auto-mode важны исправления зависания; для --retry — влияние на время; для --workers — устойчивость параллельного запуска; для --files-from — формат реального списка. Такой тест дешевле, чем откат после обработки всего хранилища.
Если система не требует новых функций, обновление все равно может быть полезно из-за исправлений, но оно должно проходить обычный change-management. Jpegoptim работает с пользовательскими файлами на запись, поэтому «маленькая CLI-утилита» не означает нулевой риск обновления.
Итоговый рабочий подход
Jpegoptim наиболее предсказуем, когда используется как последний технический этап для уже подготовленного JPEG. Для консервативной оптимизации достаточно стандартного lossless-режима и, при больших объемах, порога экономии. Для веб-производных можно добавить --max или --size, но только после визуального теста и с сохраненным мастер-источником.
Параметры метаданных следует выбирать отдельно от параметров качества: экономия, приватность, цветовой профиль и ориентация — разные требования. --strip-all удобен, но не должен становиться шаблонной командой без проверки ICC и EXIF Orientation.
В пакетных задачах надежность обеспечивают правильный выбор файлов, --noaction, логи, --threshold, ограниченный параллелизм и явная проверка результата. Новые возможности вроде --workers, --files-from, --auto-mode и --retry полезны, если конкретная установленная версия их поддерживает и они оправданы нагрузкой.
Главное ограничение Jpegoptim одновременно является его сильной стороной: утилита не пытается быть фоторедактором. Она делает узкую операцию над JPEG и хорошо встраивается в автоматизацию. Ретушь, ресайз, смена формата, управление медиатекой и художественный контроль остаются за другими инструментами, а Jpegoptim отвечает за финальную оптимизацию тех JPEG, которые уже готовы к хранению или публикации.
