Schreiben Sie einen GIF-Encoder


9

Ja, das gute alte GIF. GIF wird wegen seiner Vielseitigkeit geliebt, wegen seiner Patente gehasst und aufgrund seiner Einschränkungen (und Patente) teilweise überholt. Es besteht im Kern aus einer Farbpalette und einem palettenindexierten Bild, das mit dem LZW-Algorithmus komprimiert wurde.

Ihre Aufgabe besteht darin, ein Programm zu schreiben, das ein Bild im ASCII- PPM- Format (magische Zahl "P3") von der Standardeingabe liest und dasselbe Bild (Pixel für Pixel identisch) im GIF-Format in die Standardausgabe schreibt. Die Ausgabe kann entweder in binärer Form oder als ASCII-Text erfolgen, wobei jedes Byte durch eine durch Leerzeichen getrennte Zahl zwischen 0 und 255 (einschließlich) dargestellt wird.

Das Eingabebild hat garantiert nicht mehr als 256 verschiedene Farben.

Wertung:

Ihr Programm wird an 3 Beispielbildern getestet und Ihre Punktzahl wird wie folgt berechnet:
Programmgröße + Summe (Ausgabegröße - Referenzgröße für jedes Beispielbild) Die
niedrigste Punktzahl gewinnt.

Bedarf:

  • Ihr Programm muss mit ähnlichen Arten von Bildern unterschiedlicher Größe arbeiten und darf nicht auf die Beispielbilder beschränkt sein. Sie können beispielsweise die Abmessungen auf ein Vielfaches von 2 beschränken oder davon ausgehen, dass die maximale ppm-Farbe 255 beträgt, dies sollte jedoch mit einer Vielzahl von Eingabebildern funktionieren.
  • Die Ausgabe muss eine gültige GIF-Datei sein, die mit jedem kompatiblen Programm geladen werden kann (nach der Konvertierung in eine Binärdatei, wenn die ASCII-Ausgabeoption verwendet wird).
  • Sie können keine Bildverarbeitungsfunktionen (integriert oder von Drittanbietern) verwenden. Ihr Programm muss den gesamten relevanten Code enthalten.
  • Ihr Programm muss unter Linux mit frei verfügbarer Software ausgeführt werden können.
  • Der Quellcode darf nur ASCII-Zeichen verwenden.

Beispielbilder:

Hier sind die 3 Beispielbilder, die für die Bewertung verwendet werden. Sie können ein Zip-Archiv mit den ppm-Dateien herunterladen (verwenden Sie den Download-Button oben auf dieser Seite). Oder Sie können sie mit ImageMagick mit dem folgenden Befehl aus den folgenden PNG-Bildern konvertieren:

convert file.png -compress none file.ppm

Ich stelle auch die MD5-Prüfsummen der ppm-Dateien zur Bestätigung zur Verfügung.

1. Bernstein

amber.png

Referenzgröße: 38055
MD5-Prüfsumme von ppm: d1ad863cb556869332074717eb278080

2. Blueeyes

blueeyes.png

Referenzgröße: 28638
MD5-Prüfsumme von ppm: e9ad410057a5f6c25a22a534259dcf3a

3. Paprika

peppers.png

Referenzgröße: 53586
MD5-Prüfsumme von ppm: 74112dbdbb8b7de5216f9e24c2e1a627


1
Anmerkung des Moderators: Off-Topic / veraltete Kommentare entfernt. Weitere Informationen zu den Beispielbildern in dieser Frage finden Sie im Meta .
Türknauf

Es scheint, dass das zweite Bild mit so etwas behandelt wurde: websiteoptimization.com/speed/tweak/lossy , was ein besseres Komprimierungsverhältnis und eine bessere Empfindlichkeit für die LZW-Encoder- Optimierungen erklären würde.
Nutki

1
"Der Quellcode darf nur ASCII-Zeichen verwenden." - Mit anderen Worten, wir dürfen diese Herausforderung in APL nicht ausführen?
FUZxxl

@FUZxxl stimmt, aber Sie können J verwenden. Sie dürfen Aheui auch nicht verwenden oder Basiskonvertierungstricks in GolfScript / CJam ausführen.
Aditsu beendet, weil SE am

Antworten:


4

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

Ein anderer Ansatz. Dieses Mal versuche ich, das Bild in Kacheln der Größe 64xH zu speichern. Dies ist gemäß den Spezifikationen in Ordnung, aber einige Software zeigt möglicherweise nur die erste Kachel oder eine Animation an. Die Fliesen werden aufgrund der besseren räumlichen Lokalität besser komprimiert. Ich mache auch für das zweite Bild immer noch normale Komprimierung und wähle, was kürzer ist. Da das Bild dadurch zweimal komprimiert wird, ist es im Vergleich zu meiner vorherigen Lösung doppelt so langsam.

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

Entweder gibt es einen Fehler in meinem Code oder das zweite Bild ist für einen bestimmten Encoder optimiert, da scheinbar unbedeutende Änderungen die Größe auf die Referenz genau reduzierten. Dauert ungefähr 30s-60s pro Bild.

Golfversion.

#!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),';'

Die einzige Entscheidung, die ein GIF-Kompressor treffen kann, ist, wann das LZW-Wörterbuch zurückgesetzt werden muss. Im Allgemeinen ist aufgrund der Auswahl der Bilder für diese Aufgabe der beste Moment dafür jeder 4096-Code, der der Moment ist, in dem das Wörterbuch überlaufen würde. Mit einer solchen Begrenzung läuft das Wörterbuch nie über, was ein paar Bytes in der Implementierung spart. So funktioniert es im Detail:

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

Das Hinzufügen einer Heuristik zum Erraten des Rücksetzpunkts verbessert die Komprimierung ein wenig, reicht jedoch nicht aus, um den zusätzlichen Code zu rechtfertigen:

#!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),';'

Sehr schön! Obwohl es hier viel mehr als 30 Sekunden pro Bild dauert. Ich war ziemlich beeindruckt von der -30 aus der vorherigen Version. Ich frage mich, ob Sie die Methoden kombinieren und eine niedrigere Punktzahl erzielen können. Könnten Sie auch ein wenig darüber schreiben, was das Programm tut?
Aditsu beendet, weil SE am

Die Breite auf ein Vielfaches von 64 zu setzen, scheint ein bisschen extrem zu sein ...
aditsu beendet, weil SE am

@aditsu, Es ist nicht erforderlich, wenn die Breite nicht ein Vielfaches von 64 ist, wird die Kachelmethode nicht ausprobiert und es wird eine regelmäßige Komprimierung verwendet. Natürlich könnte ich für weitere ~ 100 Zeichen die letzte Kachel variabel machen.
Nutki

Sehr langsam und die gekachelten Bilder werden als Animationen angezeigt, aber gut gemacht, es ist ziemlich beeindruckend zu sehen, dass Sie sie wirklich verkleinern können.
Aditsu beendet, weil SE

2

CJam, Punktzahl 155 + 35306 + 44723 + 21518 = 101702

Nur eine blöde Referenzimplementierung. Es ist langsam, macht keine wirkliche Kompression und es wird nicht Golf gespielt.

"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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.