Сжатие GIF через gzip
Эксперимент мой сжатию несжатых ГИФ, по сути, провалился — если статические изображения иногда оказываются меньше (или сравнимы) с PNG, то с анимированными изображениями, ради которых всё и затевалось, всё плохо, я не сумел подобрать условия, при которых новоявленный метод обгоняет обычные соптимизированные ГИФы.
Зато я попутно выяснил, что ГИФ, вопреки моим ожиданиям, иногда жмётся gzip, на первой попавшейся анимации я получил выигрыш порядка 9%. Так что вот он метод ещё больше утоптать файлы ГИФ:
#!/bin/sh
which -s defluff
if [ $? -eq 0 ]; then
gifsicle -O3 "$1" | gzip -9nc | defluff 2>&- > "$2".temp
else
gifsicle -O3 "$1" | gzip -9nc > "$2".temp
fi
size=( $(ls -l "$2".temp) )
# срезаем заголовок gzip и контрольную сумму
let 'size=size[4]-10-8'
dd if="$2".temp of="$2" bs=1 skip=10 count=$size 2>&-
rm -f "$2".temp
В настройки Апача надо добавить новое расширение файла:
AddEncoding deflate .gif-def
AddType image/gif .gif-def
Для браузера вместо gzip я указываю метод deflate — по факту это тот же gzip, но без заголовка и контрольной суммы, что позволяет сэкономить 18 байт, не бог весть сколько, конечно, но почему бы их не убрать, спрашивается.
Утилита defluff , которая у меня упоминается — это оптимизатор кодов Хаффмана для gzip, PNG и zip, иногда даёт выигрыш в размере.
Иногда такой ГИФ занимает меньше ПНГ, например, вот результат по одной из картинок с моего сайта ( http://bolknote.ru/imgs/2011.11.06.png ):
ПНГ сжат при помощи imgo ( https://github.com/imgo/imgo ).
забавно
экономим на байтах переданных по сети, но напрягаем броузер на распаковку
Комментарий для TATAPuH:
Он быстрее распаковывает, чем информация по сети приходит.
Комментарий для Евгения Степанищева:
Видимо пропущено «вот».
Комментарий для hayk.livejournal.com:
Спасибо!
Оптимизация в самом формате (GIF) хороша для многих типичных случаев анимации — часто или передаётся разница между кадрами или даже маленький кадр размером значительно меньше всего изображения (а в нём опять-таки может быть лишь только разница с предыдущим состоянием).
Такая оптимизация чем-то похожа на сжатие JPEG, MP3 — она явно подразумевает определённую специфику данных (картинка, 2d, часто изменяется не всё изображение, а его часть) и алгоритм сжатия общего назначения как правило сможет тягаться со специализированным.
Но это лишь «как правило» — есть несколько типичных случаев, с которыми GIF справляется из рук вон плохо. Приведу примеры:
Комментарий для masterspammer.livejournal.com:
Я детально знаю как устроена анимация в GIF. А описанные случаи попробую, может получится что-то.
GIF одинаковые кадры хорошо жмёт — он просто оставит прозрачным одинаковые места.
Комментарий для Евгения Степанищева:
Имел в виду три картинки, которые повторяются так, что одинаковые подряд не идут — например, 1-2-3-1-2-3-1-2-3-1-2-3-4-5-6, при этом все «1» — совершенно одинаковые, равно как и все «2» и «3».
Комментарий для masterspammer.livejournal.com:
А, ну попробую. От безнадёги я сейчас что угодно попробую :)
Комментарий для masterspammer.livejournal.com:
Ну, первый выигрыш :)
Создал анимацию, у которой вот именно так кадры идут (кадры отличаются несильно), в итоге:
просто GIF — 8,6КБ
несжатый, сжатый через gzip — 4КБ
GIF, сжатый gzip — 3,3КБ
т. е. несжатый, но сжатый через gzip всё-таки проигрывает сжатому просто GIF, но явно выигрывает у обычного GIF.
Сейчас попробую ещё сильно отличающиеся кадры.
Комментарий для masterspammer.livejournal.com:
На сильно отличающихся соседних кадрах сжатие сжатого ГИФа — 67КБ, сжатие несжатого — 626КБ, исходный — 626КБ.
Т. е. сжатый несжатый ГИФ можно хоронить в пользу сжатого обычного, т. е. вот этой утилитой: http://bolknote.ru/all/3713
Комментарий для Евгения Степанищева:
Чтоб разница была мало чтоб были сильно отличающиеся соседние кадры (от этого просто GIF жмётся плохо), нужно ещё чтоб были сильно похожие НЕсоседние кадры (это будет жать ZIP).
Ну и более общий случай варианта 1. — кадры, совпадающиеся по пикселям, но отличающиеся палитрой (только нужно проследить чтоб конвертор не привёл бы их к общей палитре).
Комментарий для masterspammer.livejournal.com:
Я так и сделал. Взял четыре кадра. Все сильно отличаются и сделал 1+2+1+2+1+2+1+2+1+2+1+2+1+2+3+4.
Это сложнее, надо будет «вручную» сделать.
1+2+1+2+1+2+1+2+1+2+1+2+1+2 может быть относительно хорошо сжато самим GIF — у кадра есть параметр — что с ним делать после показа — а можно его как оставлять, так и удалять и если я правильно помню эту особенность, то последовательность превратится в
1+2+2+2+2+2+2+2, причём все «2» кроме последнего после показа удаляются, а последний — оставляется.
А что присходит в случае (из тех-же 4-х кадров)
1+2+3+1+2+3+1+2+3+1+2+3+1+2+3+4?
Комментарий для masterspammer.livejournal.com:
Я уж не помню где я взял те кадры, взял другие в этом порядке.
В случае gzipped uncompressed GIF проигрыш 172КБ, в случае DEFLATEd GIF выигрыш в 600 байт.
Комментарий для Евгения Степанищева:
Уже ничего не понимаю. Можно их увидеть?
Комментарий для masterspammer.livejournal.com:
Вот: http://zalil.ru/33674961
Комментарий для Евгения Степанищева:
Да... весело оно всё. Взял я GIMP, который с GIF работает честно (не делает ничего неявного).
Сначала я открыл файл и не увидел там явного криминала (чего-то такого, что жмёт сам GIF), но файл не жмётся. Мало ли что — может кадры одинаковы на вид, но, например, если два цвета в палитре местами поменять, то бинарно всё будет уже неравно.
Потом я сделал почти точную копию файла — удалил все слои кроме 4-х уникальных, а потом снова сделал как было, скопировав их. Не жмётся. Решил что как-то неявно файлы оптимизировал GIMP (такого не замечал, но мало ли какая там проруха на старуху).
Наконец, взял GIF с 3-мя кадрами и просто сделал склейку его 5 раз подряд (он конечно уже не GIF, но отличия с точки зрения архиватора минимальны). Не жмётся, ага! Дело значит в самом GZIPе.
Беру RAR — жмётся всё примерно в 5 раз (как и должно было быть).
Возникает догадка — может просто расстояние между схожими фрагментами слишком большое, чтоб GZ заметил и/или сами фрагменты слишком велики, чтоб войти в его словарь.
Уменьшаю ту же картинку что на втором шаге до 80x60 (3 слоя), размножаю слои по 6 раз (чтоб не бояться, что разные слои сожмутся по-разному) — жмётся GZIPом в 5 раз.
Получается, что для достаточно больших картинок GZIP не справляется. Параметры -1 ... -9 не влияют. Если у модуля апача есть настройки, касающиеся размера словаря, можно с ними поиграть.
Для небольших картинок GZIP несжатого GIFа (а иногда — как в этом примере — и сжатого) работает и так.
Комментарий для Евгения Степанищева:
P.S. Вот http://zalil.ru/33675627 обычный (сжатый) GIF дополнительно сжатый GZIPом.
FadeIn/FadeOut тоже можно использовать так (оба сжатия).
Картинки со сдвигами — имеет смысл только GZIP (сжатие LZW скроет схожесть кадров).
Может и этот файл лучше сжимать только GZIPом — не проверял.
Комментарий для Евгения Степанищева:
Да, а сжатый обычный получается лучше сжатого несжатого когда несжатый кадр слишком велик (в словарь не лезет), а сжатый уже не слишком. То есть когда сжатый 500к, то не лезет никак, а когда
то скорее всего сжатый кадр в словарь ещё лезет, а несжатый — нет.
Хм... а есть ли у GZIPа вообще словарь? У LZW он точно есть...
Комментарий для masterspammer.livejournal.com:
Я не очень-то разбираюсь в методе DEFLATE, но вот что, похоже, значит параметр 1…9 у gzip:
http://pastebin.com/ajxdkwGr
Комментарий для masterspammer.livejournal.com:
Попробовал пережать один из файлов Bzip2 (алгоритм Барроуза—Уилера), со словарём 9МБ и 10 проходами:
исходный — 259КБ ( http://zalil.ru/33676213 )
исходный, сжатый bzip2 — 126КБ
несжатый — 851КБ
несжатый, сжатый bzip2 — 129КБ
таки да, размер словаря имеет значение, но у DEFLATE этот параметр отсутствует.
Комментарий для Евгения Степанищева:
А при уменьшении размеров картинки где-то до 50*50 что происходит?
Как я понимаю, тогда должен начать работать и GZIP?
Комментарий для masterspammer.livejournal.com:
Не знаю, в обед попробую, вчера уже спать хотелось.
Комментарий для masterspammer.livejournal.com:
Забыл написать, что попробовал. Вот что вышло:
исходный (кадры из предыдущего моего комментария, ужаты до 50×38) — 17КБ
он же, сжатый DEFLATE — 6,6КБ
сжатый gzip неупакованный GIF — 7,2КБ