Написать кодировщик GIF


9

Да, старый добрый GIF. Любимый за его универсальность, ненавистный за его патенты и частично устаревший из-за его ограничений (и патентов), GIF состоит, в основном, из цветовой палитры и изображения с индексом палитры, сжатых с использованием алгоритма LZW.

Ваша задача - написать программу, которая считывает изображение в формате ASCII PPM (магическое число «P3») со стандартного ввода и записывает то же изображение (идентично попиксельно) в формате GIF в стандартный вывод. Вывод может быть либо в двоичной форме, либо в виде текста ASCII, где каждый байт представлен числом от 0 до 255 (включительно), разделенным пробелом.

Входное изображение гарантированно не будет иметь более 256 различных цветов.

Подсчет очков:

Ваша программа будет протестирована на 3 образцах изображений, а ваш счет будет рассчитываться следующим образом:
размер программы + сумма (выходной размер - эталонный размер для каждого изображения образца)
Побеждает самый низкий показатель.

Требования:

  • Ваша программа должна работать с любыми подобными видами изображений разных размеров, и не ограничиваться образцами изображений. Вы можете, например, ограничить размеры, кратные 2, или предположить, что максимальный цвет ppm равен 255, но он все равно должен работать с широким спектром входных изображений.
  • Выходными данными должен быть действительный GIF-файл, который можно загрузить с помощью любой совместимой программы (после преобразования обратно в двоичный файл, если используется опция вывода ASCII).
  • Вы не можете использовать какие-либо функции обработки изображений (встроенные или сторонние), ваша программа должна содержать весь соответствующий код.
  • Ваша программа должна быть запущена в Linux с использованием свободно доступного программного обеспечения.
  • Исходный код должен использовать только символы ASCII.

Образцы изображений:

Вот 3 образца изображения, которые будут использоваться для оценки. Вы можете скачать zip-архив с файлами ppm (используйте кнопку загрузки в верхней части этой страницы). Или вы можете преобразовать их из изображений PNG ниже, используя ImageMagick с помощью следующей команды:

convert file.png -compress none file.ppm

Я также предоставляю контрольные суммы MD5 файлов ppm для подтверждения.

1. янтарь

amber.png

Контрольный размер: 38055 Контрольная
сумма MD5 ppm: d1ad863cb556869332074717eb278080

2. голубые

blueeyes.png

Контрольный размер: 28638 Контрольная
сумма MD5 ppm: e9ad410057a5f6c25a22a534259dcf3a

3. перец

peppers.png

Контрольный размер: 53586 Контрольная
сумма MD5 ppm: 74112dbdbb8b7de5216f9e24c2e1a627


1
Примечание модератора: не по теме / устаревшие комментарии удалены. Пожалуйста, ознакомьтесь с мета для обсуждения примеров изображений в этом вопросе.
Дверная ручка

Кажется, что второе изображение было обработано примерно так: websiteoptimization.com/speed/tweak/lossy , что объясняет лучшую степень сжатия и чувствительность к настройкам LZW-кодировщика.
nutki

1
«В исходном коде должны использоваться только символы ASCII». То есть, другими словами, нам не разрешено выполнять эту задачу в APL?
FUZxxl

@FUZxxl true, но вы можете использовать J. Вам также не разрешено использовать Aheui или выполнять базовые приемы преобразования в GolfScript / CJam.
Aditsu уйти, потому что SE зла

Ответы:


4

Perl, 515 + -2922 + 0 + -2571 = -4978

Другой подход. На этот раз я пытаюсь сохранить изображение в тайлах размером 64xH. Это хорошо согласно спецификациям, но некоторые программы могут показывать только первую плитку или анимацию. Плитка лучше сжимается благодаря лучшему пространственному расположению. Я все еще делаю обычное сжатие для второго изображения и выбираю то, что получилось короче. Так как это сжимает изображение дважды, это вдвое медленнее по сравнению с моим предыдущим решением.

#!perl -n0
sub r{$r.=1&"@_">>$_ for 0..log(@d|256)/log 2}
@k=/(\d+) (\d+)/;
@l=map{$$_||=(push(@t,split$"),++$i)}/\d+ \d+ \d+/g;
print+GIF89a,pack(vvCxxC768,@k,~8,@t);
sub v{($w,$h)=@_;for$k(0.."@k"/$w-1){
$k*=$w;$r='';@d=();@p=grep+($z++-$k)%"@k"<$w,@l;
$"=' ';$_="@p ";$"='|';while(/./){
r 256;%d=map{($_,$_-1)}@d=1..256;
$d{$&}=@d+2,r$d{$1},unshift@d,$&while@d<4095&&s/^(@d) (\d*)/$2/}
r 257;$_=pack"b*",$r;
$h.=pack"Cv4n(C/a)*",44,$k,0,$w,$k[1],8,/.{0,255}/gs
}$b=$h if!$b||length$b>length$h}
"@k"%64||v 64;v"@k";print"$b;"

Perl, 354 + 12 + 0 + -1 = 365 418 9521 51168 56639

Либо в моем коде есть какая-то ошибка, либо второе изображение оптимизировано для конкретного кодировщика, так как казалось бы незначительное изменение уменьшило размер до эталонного размера. Принимает около 30-х-60-х годов на изображение.

Гольф версия.

#!perl -n0
sub r{$r.=1&"@_">>$_ for 0..log(@d|256)/log 2}
@k=/(\d+) (\d+)/;
@p=map{$$_||=(push(@t,split$"),++$i)}/\d+ \d+ \d+/g;
$_="@p ";$"='|';while(/./){
r 256;%d=map{($_,$_-1)}@d=1..256;
$d{$&}=@d+2,r$d{$1},unshift@d,$&while@d<4095&&s/^(@d) (\d*)/$2/}
r 257;$_=pack"b*",$r;
print+GIF89a,pack(vvCxxC768,@k,~8,@t),
pack("Cx4vvn(C/a)*",44,@k,8,/.{0,255}/gs),';'

Единственное решение, которое может принять компрессор GIF, - это сброс словаря LZW. В общем, из-за того, как изображения для этой задачи были выбраны, наилучшим моментом для этого является каждый 4096 кодов, то есть момент, когда словарь переполняется. С таким ограничением словарь никогда не переполняется, что экономит пару байтов в реализации. Вот как это работает в деталях:

#!perl -n0
# function to add one codeword to the output stream @r.
# the current codeword length is based on the dictionary size/
sub r{push@r,map"@_">>$_,0..log(@d|256)/log 2}
# get the dimensions into @k
@k=/(\d+) (\d+)/;
# get pixel indexes to @p and palette to @t
@p=map{$$_||=(push(@t,split$"),++$i)}/\d+ \d+ \d+/g;
# convert index table into space separated string 
$_="@p ";$"='|';
# LZW encoder; while something to encode
while(/\S/){
# output reset code
r 256;
# reset code dictionary $d is the last code number,
# %d is the map of codes and @d list of codes
$d=257;%d=map{($_,$_-1)}@d=1..256;
# find codes using regexp, stop at dictionary overflow
while($d<4096&&s/^(@d) (\d*)/$2/){
unshift@d,$&;$d{$&}=++$d;r$d{$1}}}
# end LZW encoder; output end code
r 257;
# convert bit string @r to bytes $f
vec($f,$j++,1)=$_ for@r;
# output header up to the color table
print+GIF89a,pack(vvCvC768,@k,~8,0,@t),
# output rest of the header
pack(Cv4CC,44,0,0,@k,0,8),
# output the LZW compressed data $f slicing into sub-blocks
$f=~s/.{0,255}/chr(length$&).$&/egsr,';'

Perl, 394 + -8 + 0 + -12 = 374

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

#!perl -n0
sub r{$r.=1&"@_">>$_ for 0..log(@d|256)/log 2}
@k=/(\d+) (\d+)/;
@p=map{$$_||=(push(@t,split$"),++$i)}/\d+ \d+ \d+/g;
$_="@p ";$"='|';while(/./){
r 256;%d=map{($_,$_-1)}@d=1..256;
$d{$&}=@d+2,r$d{$1},unshift@d,$&while
(@d<4001||(/((@d) ){11}/,$&=~y/ //>12))&@d<4095&&s/^(@d) (\d*)/$2/}
r 257;$_=pack"b*",$r;
print+GIF89a,pack(vvCxxC768,@k,~8,@t),
pack("Cx4vvn(C/a)*",44,@k,8,/.{0,255}/gs),';'

Очень хорошо! Хотя это занимает намного больше, чем 30 секунд на изображение здесь. Я был очень впечатлен -30 от предыдущей версии, интересно, вы можете объединить методы и получить более низкий балл. Кроме того, не могли бы вы немного рассказать о том, что делает программа?
Aditsu уйти, потому что SE зла

Требование, чтобы ширина была кратна 64, кажется немного экстремальной ...
Адицу бросил, потому что SE - ЗЛО

@aditsu, не требуется, если ширина не кратна 64, метод листов не используется и используется обычное сжатие. Конечно, затратив еще ~ 100 символов, я мог бы сделать последний размер плитки разным.
Nutki

Очень медленно, и мозаичные изображения отображаются в виде анимации, но ... хорошо, впечатляет то, что вы действительно можете уменьшить их.
Адицу ушел, потому что SE это зло

2

CJam, счет 155 + 35306 + 44723 + 21518 = 101702

Просто тупая эталонная реализация. Это медленно, не делает никакого фактического сжатия, и это не игра в гольф.

"GIF89a":iS*SqN/S*S%1>:i3/:M0=2<256f{md\}S*:ZS247S0S0SM1>_|:PL*_,768\m0a*+S*S44S0S0S0S0SZS0S8SM1>Pf{\a#}254/256a*{512+2b1>W%}%:+8/{W%2b}%255/{_,S@S*S}/0S59
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.