Определимся со стоящей перед нами задачей: сжать предназначенный для публикации в интернете JPEG (JPG) файл без потери качества.

Для начала разберёмся, за счёт чего мы вообще можем сжать и без того сжатый файл? Ведь всем известно, что JPEG-файл почти невозможно компрессировать обычными арифметическими алгоритмами, такими как ZIP или RAR.

Существуют три способа:

  • Применить другой способ энтропийного кодирования (прогрессивного арифметического сжатия).
  • Удалить всю лишнюю метаинформацию (EXIF, комментарии, ICC-профили).
  • Удалить вшитый в файл эскиз (thumbnail) - уменьшенную копию изображения.

Можно применить каждый способ по-отдельности, а можно и все вместе. Вот только встроенный эскиз является составной частью EXIF, а значит при удалении метаданных удаляется и он сам.

После длительного изучения различных инструментов, выбор остановился на двух, наиболее качественных и функциональных программах: jpegtran и jhead. Их можно установить и использовать как в Linux, так и в Windows.

Первая утилита входит в библиотеку libjpeg - набор программ, разработанные Independent JPEG Group и предназначенные для работы с JPEG-изображениями. В частности, jpegtran позволяет выполнять ряд операций над jpg-файлом без потери качества. Вторая программа, jhead - это разработка канадского парня Матиаса Уандела (Matthias Wandel), с помощью которой мы сможем работать с метаинформацией, зашитой в JPEG-файлы.

Для эксперимента были взяты два различающихся набора файлов.

Первый - это мой личный каталог images, в котором собрано большое количество совершенно различных JPEG-файлов. Разных размеров, происхождения и типов. В общем, это не однотипные фотографии одного фотоаппарата. Суммарный размер - 52 Мб. Файлов 132 штуки. Средний размер файла - 390 Кб.

Второй набор - собрание фотографий одного из сайта, поддерживаемых мной. 804 штуки общим размером 43 Мб. То есть, 53 Кб на одно фото, в среднем.

Перекодировать будем следующим образом.

Только перекодирование, с сохранением EXIF и эскиза:

for i in *;
    do jpegtran -optimize -progressive -copy all -outfile "with_exif/$i" "$i";
done

Перекодирование без сохранения EXIF:

for i in *;
    do jpegtran -optimize -progressive -outfile "without_exif/$i" "$i";
done

Удалим эскизы, но сохраним EXIF в перекодированных файлах:

do jhead -dt *

Хорошо, перейдём к делу.

Перекодируем JPEG-файлы первого тестового набора - каталога images.

$ du -s originals/ exif/ without_thumb/ without_exif/
52508   originals/
49280   exif/
48892   without_thumb/
48588   without_exif/

Таким образом, если мы удалим вообще всю EXIF-информацию, то общий размер уменьшается на 5 Мб. Что довольно неплохо. Если оставить всё, даже EXIF с thumbnail-ами, то мы всё равно выигрываем 4 Мб. На ровном месте, без потери качества. Неплохо? Вполне. А теперь посмотрим дальше.

Теперь, в качестве тестового набора фотографий, возьмём фотоколлекцию сайта.

$ du -s photo exif without_thumb without_exif
43288   photo
26536   exif
24520   without_thumb
23900   without_exif

Впечатляет! Простейшим перекодированием мы получаем сжатие почти в два раза! В это трудно поверить, но подвоха я не нашёл. А если ещё удалить EXIF, то мы выйграем дополнительные 3 Мб на 804 файла.

А теперь нужно разобраться, нужны ли нам EXIF-метатеги?

В них содержится такая важная информация, как параметры, дата и, возможно, координаты места съёмки. Когда фотографий становится несколько тысяч - это существенное подспорье для организации каталога и поиска по нему. Поэтому безусловно, они крайне нужны для организации каталога фотографий.

Ну, а в интернете? Неужели посетитель будет смотреть каким фотоаппаратом и с какой выдержкой и диафрагмой автор делал свою фотографию? Сомневаюсь.

Последняя надежда на секцию Comment метаданных фотографии, в которую мы можем вписать ключевые слова фотографии и имя автора для указания копирайта. Но, обращают ли внимание на эту секцию поисковые системы? Утвердительного ответа на этот вопрос я так и не нашёл.

И последнее.

Чтобы убедиться в том, что после всех этих преобразований, изображение осталось нетронутым, относительно оригинала, произведём следующие операции.

С помощью программы djpeg, из поставки libjpeg, декодируем JPEG-файл в формат RAW:

$ djpeg noexif.jpg > noexif.raw
$ djpeg origin.jpg > origin.raw

И определим, совпадают ли оба RAW-файла побайтово:

$ md5sum *.raw
6deb36a0b017cc7c41025e7a24e8f30e  noexif.raw
6deb36a0b017cc7c41025e7a24e8f30e  origin.raw

Как видим, содержимое файлов идентично, а значит, не было утрачено ни единого бита графической информации и с исходной задачей мы справились.

Дополнительная информация: