Сжатие 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, иногда даёт выигрыш в размере.
11 августа 2012 11:47

bolk (bolknote.ru)
11 августа 2012, 16:39

Иногда такой ГИФ занимает меньше ПНГ, например, вот результат по одной из картинок с моего сайта (http://bolknote.ru/imgs/2011.11.06.png):
$ ll gifs-def/2011.11.06.png.gif-def
-rw-r--r-- 1 bolk staff 21270 11 авг 17:33 gifs-def/2011.11.06.png.gif-def
ll pngs/2011.11.06.png
-rw-r--r-- 1 bolk staff 24749 11 авг 17:36 pngs/2011.11.06.png
ПНГ сжат при помощи imgo (https://github.com/imgo/imgo).

TATAPuH (инкогнито)
12 августа 2012, 08:26

забавно
экономим на байтах переданных по сети, но напрягаем броузер на распаковку

bolk (bolknote.ru)
12 августа 2012, 08:53, ответ предназначен TATAPuH

Он быстрее распаковывает, чем информация по сети приходит.

<wj4 (hayk.livejournal.com)
12 августа 2012, 09:28, ответ предназначен bolk (bolknote.ru):

что он метод
Видимо пропущено «вот».

bolk (bolknote.ru)
12 августа 2012, 10:17, ответ предназначен <wj4 (hayk.livejournal.com):

Спасибо!

masterspammer (masterspammer.livejournal.com)
13 августа 2012, 09:14

Оптимизация в самом формате (GIF) хороша для многих типичных случаев анимации - часто или передаётся разница между кадрами или даже маленький кадр размером значительно меньше всего изображения (а в нём опять-таки может быть лишь только разница с предыдущим состоянием).

Такая оптимизация чем-то похожа на сжатие JPEG, MP3 - она явно подразумевает определённую специфику данных (картинка, 2d, часто изменяется не всё изображение, а его часть) и алгоритм сжатия общего назначения как правило сможет тягаться со специализированным.

Но это лишь "как правило" - есть несколько типичных случаев, с которыми GIF справляется из рук вон плохо. Приведу примеры:

1. "Проявление"/"Угасание", FadeIn/FadeOut - в предельном варианте когда данные не меняются, а меняеться лишь палитра, GIF даст размер_кадра*число_кадров, а zip - примерно размер_кадра+малое_значение*число кадров. Тут даже сжатый GIF сожмётся ZIPом хорошо (если данные о пикселях реально совпадают).

2. Сдвиг картинки. Пиксели те же, но чуть сдвинуты в сторону (MPEG тут умнее).

3. Чередование (как я писал) нескольких повторяющихся или похожих последовательностей картинок; пример: машина едет, рука тянется к рычагу, снова машина едет, рука тянет рычаг, едет машина, дверь (гаражная) начинает опускаться, стредка спидометра движется вправа... и так далее.

4. Последовательность из 3-х и более одинаковых картинок (например, фазы движения чего-то). Часто (например, когда после десяти повторений пяти фаз бега (50 картинок) следует прыжок) нельзя оставить всего 3 картинки.

bolk (bolknote.ru)
13 августа 2012, 09:21, ответ предназначен masterspammer.livejournal.com:

Я детально знаю как устроена анимация в GIF. А описанные случаи попробую, может получится что-то.
Последовательность из 3-х и более одинаковых картинок (например, фазы движения чего-то).
GIF одинаковые кадры хорошо жмёт — он просто оставит прозрачным одинаковые места.

masterspammer (masterspammer.livejournal.com)
13 августа 2012, 09:33, ответ предназначен bolk (bolknote.ru):

GIF одинаковые кадры хорошо жмёт — он просто оставит прозрачным одинаковые места.
Имел в виду три картинки, которые повторяются так, что одинаковые подряд не идут - например, 1-2-3-1-2-3-1-2-3-1-2-3-4-5-6, при этом все "1" - совершенно одинаковые, равно как и все "2" и "3".

bolk (bolknote.ru)
13 августа 2012, 10:34, ответ предназначен masterspammer.livejournal.com:

А, ну попробую. От безнадёги я сейчас что угодно попробую :)

bolk (bolknote.ru)
13 августа 2012, 10:41, ответ предназначен masterspammer.livejournal.com:

Ну, первый выигрыш :)

Создал анимацию, у которой вот именно так кадры идут (кадры отличаются несильно), в итоге:
просто GIF — 8,6КБ
несжатый, сжатый через gzip — 4КБ
GIF, сжатый gzip — 3,3КБ

т.е. несжатый, но сжатый через gzip всё-таки проигрывает сжатому просто GIF, но явно выигрывает у обычного GIF.

Сейчас попробую ещё сильно отличающиеся кадры.

bolk (bolknote.ru)
13 августа 2012, 10:47, ответ предназначен masterspammer.livejournal.com:

На сильно отличающихся соседних кадрах сжатие сжатого ГИФа — 67КБ, сжатие несжатого — 626КБ, исходный — 626КБ.

Т.е. сжатый несжатый ГИФ можно хоронить в пользу сжатого обычного, т.е. вот этой утилитой: http://bolknote.ru/2012/08/11/~3713

masterspammer (masterspammer.livejournal.com)
13 августа 2012, 10:51, ответ предназначен bolk (bolknote.ru):

Чтоб разница была мало чтоб были сильно отличающиеся соседние кадры (от этого просто GIF жмётся плохо), нужно ещё чтоб были сильно похожие НЕсоседние кадры (это будет жать ZIP).

Ну и более общий случай варианта 1. - кадры, совпадающиеся по пикселям, но отличающиеся палитрой (только нужно проследить чтоб конвертор не привёл бы их к общей палитре).

bolk (bolknote.ru)
13 августа 2012, 14:34, ответ предназначен masterspammer.livejournal.com:

Чтоб разница была мало чтоб были сильно отличающиеся соседние кадры (от этого просто GIF жмётся плохо), нужно ещё чтоб были сильно похожие НЕсоседние кадры (это будет жать ZIP).
Я так и сделал. Взял четыре кадра. Все сильно отличаются и сделал 1+2+1+2+1+2+1+2+1+2+1+2+1+2+3+4.
Ну и более общий случай варианта 1. - кадры, совпадающиеся по пикселям, но отличающиеся палитрой (только нужно проследить чтоб конвертор не привёл бы их к общей палитре).
Это сложнее, надо будет «вручную» сделать.

masterspammer (masterspammer.livejournal.com)
13 августа 2012, 14:51

Я так и сделал. Взял четыре кадра. Все сильно отличаются и сделал 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?

bolk (bolknote.ru)
13 августа 2012, 15:07, ответ предназначен masterspammer.livejournal.com:

1+2+3+1+2+3+1+2+3+1+2+3+1+2+3+4?
Я уж не помню где я взял те кадры, взял другие в этом порядке.
В случае gzipped uncompressed GIF проигрыш 172КБ, в случае DEFLATEd GIF выигрыш в 600 байт.

masterspammer (masterspammer.livejournal.com)
13 августа 2012, 16:30, ответ предназначен bolk (bolknote.ru):

Уже ничего не понимаю. Можно их увидеть?

bolk (bolknote.ru)
13 августа 2012, 16:56, ответ предназначен masterspammer.livejournal.com:

Вот: http://zalil.ru/33674961

masterspammer (masterspammer.livejournal.com)
13 августа 2012, 20:00, ответ предназначен bolk (bolknote.ru):

Да... весело оно всё. Взял я GIMP, который с GIF работает честно (не делает ничего неявного).

Сначала я открыл файл и не увидел там явного криминала (чего-то такого, что жмёт сам GIF), но файл не жмётся. Мало ли что - может кадры одинаковы на вид, но, например, если два цвета в палитре местами поменять, то бинарно всё будет уже неравно.

Потом я сделал почти точную копию файла - удалил все слои кроме 4-х уникальных, а потом снова сделал как было, скопировав их. Не жмётся. Решил что как-то неявно файлы оптимизировал GIMP (такого не замечал, но мало ли какая там проруха на старуху).

Наконец, взял GIF с 3-мя кадрами и просто сделал склейку его 5 раз подряд (он конечно уже не GIF, но отличия с точки зрения архиватора минимальны). Не жмётся, ага! Дело значит в самом GZIPе.

Беру RAR - жмётся всё примерно в 5 раз (как и должно было быть).

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

Уменьшаю ту же картинку что на втором шаге до 80x60 (3 слоя), размножаю слои по 6 раз (чтоб не бояться, что разные слои сожмутся по-разному) - жмётся GZIPом в 5 раз.

Получается, что для достаточно больших картинок GZIP не справляется. Параметры -1 ... -9 не влияют. Если у модуля апача есть настройки, касающиеся размера словаря, можно с ними поиграть.

Для небольших картинок GZIP несжатого GIFа (а иногда - как в этом примере - и сжатого) работает и так.

masterspammer (masterspammer.livejournal.com)
13 августа 2012, 20:05, ответ предназначен bolk (bolknote.ru):

P.S. Вот http://zalil.ru/33675627 обычный (сжатый) GIF дополнительно сжатый GZIPом.

FadeIn/FadeOut тоже можно использовать так (оба сжатия).

Картинки со сдвигами - имеет смысл только GZIP (сжатие LZW скроет схожесть кадров).

Может и этот файл лучше сжимать только GZIPом - не проверял.

masterspammer (masterspammer.livejournal.com)
13 августа 2012, 20:13, ответ предназначен bolk (bolknote.ru):

Да, а сжатый обычный получается лучше сжатого несжатого когда несжатый кадр слишком велик (в словарь не лезет), а сжатый уже не слишком. То есть когда сжатый 500к, то не лезет никак, а когда
На сильно отличающихся соседних кадрах сжатие сжатого ГИФа — 67КБ, сжатие несжатого — 626КБ, исходный — 626КБ.
то скорее всего сжатый кадр в словарь ещё лезет, а несжатый - нет.

Хм... а есть ли у GZIPа вообще словарь? У LZW он точно есть...

bolk (bolknote.ru)
13 августа 2012, 22:32, ответ предназначен masterspammer.livejournal.com:

Я не очень-то разбираюсь в методе DEFLATE, но вот что, похоже, значит параметр 1…9 у gzip:

bolk (bolknote.ru)
13 августа 2012, 22:50, ответ предназначен masterspammer.livejournal.com:

Попробовал пережать один из файлов Bzip2 (алгоритм Барроуза—Уилера), со словарём 9МБ и 10 проходами:
исходный — 259КБ (http://zalil.ru/33676213)
исходный, сжатый bzip2 — *126КБ*
несжатый — 851КБ
несжатый, сжатый bzip2 — 129КБ

таки да, размер словаря имеет значение, но у DEFLATE этот параметр отсутствует.

masterspammer (masterspammer.livejournal.com)
14 августа 2012, 09:29, ответ предназначен bolk (bolknote.ru):

А при уменьшении размеров картинки где-то до 50*50 что происходит?

Как я понимаю, тогда должен начать работать и GZIP?

bolk (bolknote.ru)
14 августа 2012, 10:01, ответ предназначен masterspammer.livejournal.com:

Не знаю, в обед попробую, вчера уже спать хотелось.

bolk (bolknote.ru)
14 августа 2012, 13:56, ответ предназначен masterspammer.livejournal.com:

Забыл написать, что попробовал. Вот что вышло:

исходный (кадры из предыдущего моего комментария, ужаты до 50×38) — 17КБ
он же, сжатый DEFLATE — 6,6КБ
сжатый gzip неупакованный GIF — 7,2КБ

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)