GIF kodlayıcı yazma


9

Evet, eski güzel GIF. Çok yönlülüğü için sevilen, patentleri için nefret eden ve sınırlamaları (ve patentleri) nedeniyle kısmen eskimiş olan GIF, özünde bir renk paletinden ve LZW algoritması kullanılarak sıkıştırılmış bir palet indeksli görüntüden oluşur.

Göreviniz , standart girdiden ASCII PPM biçiminde ("P3" sihirli numarası) bir görüntüyü okuyan ve aynı görüntüyü (piksel piksel) aynı çıktıyı standart çıktıya yazan bir program yazmaktır . Çıktı ikili formda olabilir veya her bayt boşlukla ayrılmış 0 ile 255 (dahil) arasında bir sayı ile temsil edilen ASCII metninde olabilir.

Giriş görüntüsünün 256'dan fazla farklı renge sahip olmaması garanti edilir.

puanlama:

Programınız 3 örnek görüntü üzerinde test edilecek ve puanınız şu şekilde hesaplanacaktır:
program boyutu + toplamı (çıktı boyutu - her örnek görüntü için referans boyutu)
En düşük puan kazanır.

Gereksinimler:

  • Programınız, çeşitli boyutlardaki benzer görüntülerle çalışmalı ve örnek görüntülerle sınırlı olmamalıdır. Örneğin, boyutları 2'nin katları olarak sınırlandırabilir veya ppm maksimum rengin 255 olduğunu varsayabilirsiniz, ancak yine de çok çeşitli giriş görüntüleri ile çalışması gerekir.
  • Çıktı, uyumlu bir programla yüklenebilecek geçerli bir GIF dosyası olmalıdır (ASCII çıktı seçeneği kullanılıyorsa ikili dosyaya dönüştürüldükten sonra).
  • Görüntü işleme işlevlerini (yerleşik veya üçüncü taraf) kullanamazsınız, programınız ilgili tüm kodu içermelidir.
  • Programınız Linux'ta serbestçe kullanılabilen yazılımlar kullanılarak çalıştırılabilir olmalıdır.
  • Kaynak kod yalnızca ASCII karakterleri kullanmalıdır.

Örnek görüntüler:

Puanlama için kullanılacak 3 örnek resim. Ppm dosyalarıyla bir zip arşivi indirebilirsiniz (bu sayfanın üst kısmındaki indirme düğmesini kullanın). Veya aşağıdaki komutla ImageMagick'i kullanarak bunları aşağıdaki png resimlerinden dönüştürebilirsiniz:

convert file.png -compress none file.ppm

Ayrıca onay için ppm dosyalarının MD5 sağlama toplamlarını sağlıyorum.

1. sarı

amber.png

Referans boyutu: 38055
ppm MD5 sağlama toplamı: d1ad863cb556869332074717eb278080

2. blueeyes

blueeyes.png

Referans boyutu: 28638
ppm MD5 sağlama toplamı: e9ad410057a5f6c25a22a534259dcf3a

3. biber

peppers.png

Referans boyutu: 53586 PP5
ppm sağlama toplamı: 74112dbdbb8b7de5216f9e24c2e1a627


1
Moderatör notu: Konu dışı / eski yorumlar kaldırıldı. Bu sorudaki örnek görüntüler hakkında tartışma için lütfen meta'ya bakın .
Kapı tokmağı

Görünüşe bakılırsa ikinci görüntü, daha iyi sıkıştırma oranını ve LZW kodlayıcı ayarlarına duyarlılığı açıklayan websiteoptimization.com/speed/tweak/lossy gibi bir şeyle tedavi edildi .
nutki

1
“Kaynak kodu yalnızca ASCII karakterleri kullanmalıdır.” - Yani, başka bir deyişle, APL'de bu zorluğu gerçekleştirmemize izin verilmiyor mu?
FUZxxl

@FUZxxl true, ancak J kullanabilirsiniz. Aheui kullanmanıza veya GolfScript / CJam'da temel dönüştürme hileleri yapmanıza da izin verilmez.
aditsu bıraktı çünkü SE EVIL

Yanıtlar:


4

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

Başka bir yaklaşım. Bu kez görüntüyü 64xH boyutlarındaki fayanslarda kaydetmeye çalışıyorum. Bu, özelliklere göre iyidir, ancak bazı yazılımlar yalnızca ilk kutucuğu veya animasyonu gösterebilir. Fayanslar daha iyi boşluklu konum nedeniyle daha iyi sıkıştırır. İkinci resim için de normal sıkıştırma yapıyorum ve daha kısa olanı seçiyorum. Bu, görüntüyü iki kez sıkıştırdığından, önceki çözümümle karşılaştırıldığında iki kat daha yavaştır.

#!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 = 365418 9521 51168 56639

Kodumda bir hata var veya ikinci görüntü belirli bir kodlayıcı için optimize edildi, çünkü görünüşte önemsiz bir değişiklik referansı tam olarak küçültdü. Görüntü başına yaklaşık 30 ila 60 saniye sürer.

Golfçü versiyon.

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

Bir GIF kompresörünün alabileceği tek karar, LZW sözlüğünün ne zaman sıfırlanacağıdır. Genel olarak, bu görevin resimlerinin nasıl seçildiğinden dolayı, bunun için en iyi an, sözlüğün taştığı an olan her 4096 kodudur. Böyle bir sınırlama ile sözlük asla taşmaz ve bu da uygulamada birkaç bayt tasarrufu sağlar. Ayrıntılı olarak şu şekilde çalışır:

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

Sıfırlama noktasını tahmin etmek için bir buluşsal yöntem eklemek, sıkıştırmayı biraz geliştirir, ancak ekstra kodu haklı çıkarmak için yeterli değildir:

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

Çok hoş! Her ne kadar burada görüntü başına 30 saniyeden fazla sürüyor. Önceki sürümdeki -30'dan oldukça etkilendim, yöntemleri birleştirip daha düşük bir puan elde edip edemeyeceğinizi merak ediyorum. Ayrıca, programın ne yaptığı hakkında biraz yazabilir misiniz?
aditsu bıraktı çünkü SE EVIL

Genişliğin 64'ün katları olmasını gerektiren biraz aşırı gibi görünüyor ...
aditsu çıkın çünkü SE EVIL

@aditsu, Gerekli değildir, genişlik 64'ün katı değilse döşeme yöntemi denenmez ve düzenli sıkıştırma kullanılır. Tabii başka bir ~ 100 karakter pahasına son kiremit değişken boyutu yapabilir.
nutki

Çok yavaş ve döşenmiş görüntüler animasyon olarak gösterilir, ancak .. aferin, onları gerçekten daha küçük yapabileceğinizi görmek oldukça etkileyici.
aditsu bıraktı çünkü SE EVIL

2

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

Sadece aptal bir referans uygulaması. Yavaştır, gerçek sıkıştırma yapmaz ve golf oynamaz.

"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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.