Viết một bộ mã hóa GIF


9

Vâng, GIF cũ tốt. Được yêu thích vì tính linh hoạt của nó, ghét các bằng sáng chế và một phần bị lỗi thời do những hạn chế (và bằng sáng chế), GIF bao gồm, ở lõi, của một bảng màu và một hình ảnh được lập chỉ mục bảng màu được nén bằng thuật toán LZW.

Nhiệm vụ của bạn là viết một chương trình đọc một hình ảnh ở định dạng ASCII PPM (số ma thuật "P3") từ đầu vào tiêu chuẩn và ghi cùng một hình ảnh (pixel-by-pixel) theo định dạng GIF vào đầu ra tiêu chuẩn. Đầu ra có thể ở dạng nhị phân hoặc văn bản ASCII với mỗi byte được biểu thị bằng một số từ 0 đến 255 (đã bao gồm), được phân tách bằng khoảng trắng.

Hình ảnh đầu vào được đảm bảo không có quá 256 màu khác nhau.

Ghi điểm:

Chương trình của bạn sẽ được kiểm tra trên 3 hình ảnh mẫu và điểm của bạn sẽ được tính như sau:
kích thước chương trình + tổng (kích thước đầu ra - kích thước tham chiếu cho mỗi hình ảnh mẫu)
Điểm thấp nhất sẽ thắng.

Yêu cầu:

  • Chương trình của bạn phải hoạt động với mọi loại hình ảnh tương tự có kích cỡ khác nhau và không giới hạn ở hình ảnh mẫu. Ví dụ, bạn có thể giới hạn kích thước là bội số của 2 hoặc giả sử rằng màu tối đa ppm là 255, nhưng nó vẫn hoạt động với nhiều hình ảnh đầu vào.
  • Đầu ra phải là một tệp GIF hợp lệ có thể được tải với bất kỳ chương trình tuân thủ nào (sau khi chuyển đổi trở lại thành nhị phân nếu sử dụng tùy chọn đầu ra ASCII).
  • Bạn không thể sử dụng bất kỳ chức năng xử lý hình ảnh nào (tích hợp hoặc bên thứ ba), chương trình của bạn phải chứa tất cả các mã có liên quan.
  • Chương trình của bạn phải được chạy trong Linux bằng phần mềm có sẵn miễn phí.
  • Mã nguồn chỉ được sử dụng các ký tự ASCII.

Hình ảnh mẫu:

Dưới đây là 3 hình ảnh mẫu sẽ được sử dụng để chấm điểm. Bạn có thể tải xuống một kho lưu trữ zip với các tệp ppm (sử dụng nút tải xuống ở đầu trang đó). Hoặc bạn có thể chuyển đổi chúng từ các hình ảnh png bên dưới, sử dụng ImageMagick với lệnh sau:

convert file.png -compress none file.ppm

Tôi cũng đang cung cấp tổng kiểm MD5 của các tệp ppm để xác nhận.

1. hổ phách

amber.png

Kích thước tham chiếu:
tổng kiểm tra 38055 MD5 của ppm: d1ad863cb556869332074717eb278080

2. blueeyes

blueeyes.png

Kích thước tham chiếu: 28638
MD5 tổng kiểm tra ppm: e9ad410057a5f6c25a22a534259dcf3a

3. ớt

ớt.png

Kích thước tham chiếu: 53586
MD5 tổng kiểm tra ppm: 74112dbdbb8b7de5216f9e24c2e1a627


1
Người điều hành lưu ý: Đã xóa chủ đề / bình luận lỗi thời. Vui lòng xem meta để thảo luận về các hình ảnh mẫu trong câu hỏi này.
Doorknob

Có vẻ như hình ảnh thứ hai được xử lý bằng một cái gì đó như thế này: websiteoptimization.com/speed/tweak/lossy sẽ giải thích tỷ lệ nén tốt hơn và độ nhạy với các điều chỉnh bộ mã hóa LZW.
nutki

1
Mã nguồn phải chỉ sử dụng các ký tự ASCII. - - nói cách khác, chúng ta không được phép thực hiện thử thách này trong APL?
FUZxxl

@FUZxxl đúng, nhưng bạn có thể sử dụng J. Bạn cũng không được phép sử dụng Aheui hoặc thực hiện các thủ thuật chuyển đổi cơ sở trong GolfScript / CJam.
aditsu nghỉ việc vì SE là EVIL

Câu trả lời:


4

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

Cách tiếp cận khác. Lần này tôi đang cố gắng lưu hình ảnh trong các ô có kích thước 64xH. Điều này tốt theo thông số kỹ thuật, nhưng một số phần mềm có thể chỉ hiển thị ô đầu tiên hoặc hình động. Các viên gạch nén tốt hơn vì địa phương không gian tốt hơn. Tôi vẫn thực hiện nén bình thường cho hình ảnh thứ hai và chọn bất cứ thứ gì ngắn hơn. Vì điều này nén hình ảnh hai lần, nó chậm gấp đôi so với giải pháp trước đây của tôi.

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

Có một số lỗi trong mã của tôi hoặc hình ảnh thứ hai được tối ưu hóa cho một bộ mã hóa cụ thể vì sự thay đổi dường như không đáng kể đã giảm kích thước chính xác đến tham chiếu. Mất khoảng 30 giây 60 mỗi hình ảnh.

Phiên bản chơi gôn.

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

Quyết định duy nhất mà máy nén GIF có thể đưa ra là khi đặt lại từ điển LZW. Nói chung vì làm thế nào các hình ảnh cho nhiệm vụ này được chọn thời điểm tốt nhất để thực hiện là mỗi 4096 mã là thời điểm mà từ điển sẽ tràn ra. Với giới hạn như vậy, từ điển không bao giờ tràn ra giúp tiết kiệm một vài byte trong quá trình thực hiện. Đây là cách nó hoạt động chi tiết:

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

Thêm một heuristic để đoán điểm đặt lại giúp cải thiện việc nén một chút nhưng không đủ để biện minh cho mã bổ sung:

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

Rất đẹp! Mặc dù phải mất hơn 30 giây cho mỗi hình ảnh ở đây. Tôi khá ấn tượng với -30 từ phiên bản trước, tôi tự hỏi liệu bạn có thể kết hợp các phương pháp và đạt điểm thấp hơn không. Ngoài ra, bạn có thể viết một chút về những gì chương trình làm?
aditsu nghỉ việc vì SE là EVIL 17/03/2015

Yêu cầu chiều rộng là bội số của 64 có vẻ hơi cực ...
aditsu bỏ vì SE là EVIL

@aditsu, Không bắt buộc, nếu chiều rộng không phải là bội số của 64 thì phương pháp ốp lát không được thử và nén thông thường được sử dụng. Tất nhiên với chi phí ~ 100 ký tự khác, tôi có thể tạo kích thước biến ô cuối cùng.
nutki

Rất chậm, và các hình ảnh lát gạch hiển thị dưới dạng hình động, nhưng .. được thực hiện tốt, thật ấn tượng khi thấy bạn thực sự có thể làm cho chúng nhỏ hơn.
aditsu nghỉ việc vì SE là EVIL

2

CJam, điểm 155 + 35306 + 44723 + 21518 = 101702

Chỉ là một thực hiện tham khảo ngu ngốc. Nó chậm, không thực hiện bất kỳ nén thực tế nào và nó không được đánh gôn.

"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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.