GIFエンコーダーを作成する


9

はい、古き良きGIFです。汎用性が高く、特許を嫌い、制限(および特許)のために部分的に廃止されたGIFは、コアでは、カラーパレットと、LZWアルゴリズムを使用して圧縮されたパレットインデックス付き画像で構成されています。

あなたの仕事は、標準入力からASCII PPM形式( "P3"マジックナンバー)の画像を読み取り、GIF形式で同じ画像(同一のピクセル単位)を標準出力に書き込むプログラムを作成することです。出力はバイナリ形式、または各バイトが空白で区切られた0〜255の数値で表されるASCIIテキストのいずれかです。

入力画像は256色を超える色がないことが保証されています。

得点:

プログラムは3つのサンプル画像でテストされ、スコアは次のように計算されます。
プログラムサイズ+合計(出力サイズ-各サンプル画像の参照サイズ)
最も低いスコアが優先されます。

要件:

  • プログラムは、サンプル画像に限定されず、さまざまなサイズの類似した画像で機能する必要があります。たとえば、寸法を2の倍数に制限したり、ppmの最大色を255と想定したりできますが、それでもさまざまな入力画像で機能するはずです。
  • 出力は、任意の準拠プログラムで読み込むことができる有効なGIFファイルである必要があります(ASCII出力オプションを使用している場合は、バイナリに変換して戻します)。
  • 画像処理機能(組み込みまたはサードパーティ)を使用することはできません。プログラムには、関連するすべてのコードが含まれている必要があります。
  • プログラムは、無料で入手できるソフトウェアを使用してLinuxで実行できる必要があります。
  • ソースコードはASCII文字のみを使用する必要があります。

サンプル画像:

スコアリングに使用される3つのサンプル画像を次に示します。ppmファイルを含むzipアーカイブをダウンロードできます(そのページの上部にあるダウンロードボタンを使用)。または、以下のコマンドでImageMagickを使用して、下のpng画像から変換することもできます。

convert file.png -compress none file.ppm

確認のために、ppmファイルのMD5チェックサムも提供しています。

1.アンバー

amber.png

参照サイズ:38055
ppmのMD5チェックサム:d1ad863cb556869332074717eb278080

2. blueeyes

blueeyes.png

参照サイズ:
ppmの28638 MD5チェックサム:e9ad410057a5f6c25a22a534259dcf3a

3.ピーマン

peppers.png

参照サイズ:53586
ppmのMD5チェックサム:74112dbdbb8b7de5216f9e24c2e1a627


1
モデレーターのメモ:トピック外/古いコメントは削除されました。この質問のサンプル画像については、メタ参照してください。
ドアノブ

2番目の画像は次のように処理されたようです。websiteoptimization.com / speed / tweak / lossyは、LZWエンコーダーの微調整に対する感度の向上を説明しています。
nutki

1
「ソースコードはASCII文字のみを使用する必要があります。」—つまり、APLでこのチャレンジを行うことはできませんか?
FUZxxl 2015年

@FUZxxlはtrueですが、Jを使用できます。また、Aheuiを使用したり、GolfScript / CJamで基本変換トリックを実行したりすることもできません。
SEは悪であるためaditsuは終了

回答:


4

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

別のアプローチ。今回は、サイズ64xHのタイルに画像を保存しようとしています。これは仕様によると問題ありませんが、一部のソフトウェアでは最初のタイルまたはアニメーションのみが表示される場合があります。タイルは、より局所的な局所性のため、よりよく圧縮されます。私は2番目の画像に対しても通常の圧縮を行い、短くなったものを選択します。これにより画像が2回圧縮されるため、以前のソリューションと比較して2倍遅くなります。

#!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

私のコードにいくつかのバグがあるか、わずかな変更がサイズを参照に正確に縮小したため、2番目のイメージが特定のエンコーダー用に最適化されています。画像ごとに約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に非常に感銘を受けました。方法を組み合わせて、より低いスコアを取得できるかどうか疑問に思います。また、プログラムの機能について少し書いていただけますか?
SEが悪魔であるためaditsuが辞任2015

幅を64の倍数にする必要があることは少し極端に思えます...
SEが悪であるため、aditsuは終了

@aditsu、これは必須ではありません。幅が64の倍数でない場合、タイリング方法は試行されず、通常の圧縮が使用されます。もちろん、別の約100文字のコストで、最後のタイルを可変サイズにすることができました。
nutki

非常に遅く、タイル張りの画像はアニメーションとして表示されますが、よくできているので、実際に小さくできることは非常に印象的です。
SEが悪魔であるためaditsuが辞任2015

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.