From: Mischa POSLAWSKY Date: Fri, 26 Feb 2021 11:15:03 +0000 (+0100) Subject: codec: compare 7 image encoding formats X-Git-Tag: v1.13~119 X-Git-Url: http://git.shiar.nl/sheet.git/commitdiff_plain/433d74a7369648a5a005b8678d005d85fe1dfac1?ds=sidebyside codec: compare 7 image encoding formats An initial summary of current technologies following the completion of JPEG-XL. Started out with original research, but then encountered an article by Jon Sneyers with very similar results, so copied that instead: https://cloudinary.com/blog/time_for_next_gen_codecs_to_dethrone_jpeg --- diff --git a/codec.inc.pl b/codec.inc.pl new file mode 100644 index 0000000..a76b04d --- /dev/null +++ b/codec.inc.pl @@ -0,0 +1,391 @@ +use utf8; ++{ +codec => { + jpeg => { + name => 'JPEG', + available => 1992, + }, + png => { + name => 'PNG', + available => 1996, + }, + jp2k => { + name => 'JPEG 2000', + available => 2000, + }, + webp => { + name => 'WebP', + available => 2010, + }, + heic => { + name => 'HEIC', + available => 2015, + }, + avif => { + name => 'AVIF', + available => 2019, + }, + jxl => { + name => 'JPEG XL', + available => 2021, + }, +}, +feature => { + quality_photo => { + name => 'compression (photo)', + score => { + jpeg => 3, + png => 1, + jp2k => 4, + webp => 3, + heic => 5, + avif => 5, + jxl => 5, + }, + }, + quality_photo_1 => { + name => 'low fidelity', + score => { + jpeg => 2, + png => 1, + jp2k => 3, + webp => 3, + heic => 5, + avif => 5, + jxl => 4, + }, + }, + quality_photo_2 => { + name => 'medium fidelity', + score => { + jpeg => 3, + png => 1, + jp2k => 4, + webp => 3, + heic => 4, + avif => 5, + jxl => 5, + }, + }, + quality_photo_3 => { + name => 'high fidelity', + score => { + jpeg => 3, + png => 2, + jp2k => 4, + webp => 2, + heic => 3, + avif => 4, + jxl => 5, + }, + }, + quality_photo_ll => { + name => 'lossless', + score => { + jpeg => 1, + png => 2, + jp2k => 4, + webp => 3, + heic => 3, + avif => 3, + jxl => 5, + }, + }, + quality_art => { + name => 'compression (other images)', + score => { + jpeg => 2, + png => 3, + jp2k => 2, + webp => 4, + heic => 3, + avif => 4.5, + jxl => 5, + }, + }, + quality_art_2 => { + name => 'lossy non-photographic', + score => { + jpeg => 2, + png => 3, + jp2k => 2, + webp => 4, + heic => 3, + avif => 5, + jxl => 5, + }, + }, + quality_art_ll => { + name => 'lossless non-photographic', + score => { + jpeg => 1, + png => 4, + jp2k => 2, + webp => 5, + heic => 2, + avif => 3, + jxl => 5, + }, + }, + quality_art_mixed => { + name => 'mixed photo/nonphoto', + score => { + jpeg => 2, + png => 2, + jp2k => 2, + webp => 3, + heic => 3, + avif => 5, + jxl => 5, + }, + }, + speed => { + score => { + jpeg => 5, + png => 4, + jp2k => 3, + webp => 4, + heic => 3, + avif => 3, + jxl => 5, + }, + }, + speed_encode => { + name => 'single-core encode', + score => { + jpeg => 5, + png => 3, + jp2k => 4, + webp => 4, + heic => 3, + avif => 2, + jxl => 5, + }, + }, + speed_decode => { + name => 'single-core decode', + score => { + jpeg => 5, + png => 5, + jp2k => 4, + webp => 5, + heic => 3, + avif => 3, + jxl => 5, + }, + }, + speed_parallel => { + name => 'pararellizable', + score => { + jpeg => 2, + png => 2, + jp2k => 4, + webp => 2, + heic => 4, + avif => 4, + jxl => 5, + }, + }, + limits => { + score => { + jpeg => 3, + png => 4, + jp2k => 5, + webp => 2, + heic => 4, + avif => 4.5, + jxl => 5, + }, + }, + max_dimensions => { + name => 'maximum image dimensions', + data => { + jpeg => '65k²', # 2**16 + png => '2G²', # 2**31 + jp2k => '4G²', # 2**32 + webp => '16k²', # 2**14 + heic => '8k×4k+', # 2**13 + avif => '8k×4k+', + jxl => '1G²', # 2**30 + }, + }, + max_bitdepth => { + name => 'precision (max. bit depth)', + data => { + jpeg => 8, + png => 16, + jp2k => 38, + webp => 8, + heic => 10, + avif => 10, + jxl => 32, + }, + }, + color_444 => { + name => 'can do (lossy) 4:4:4', + score => { + jpeg => 'y', + png => 'y', + jp2k => 'y', + webp => 'n', + heic => 'n', + avif => 'y', + jxl => 'y', + }, + }, + hdr => { + name => 'wide gamut/HDR', + score => { + jpeg => 'n', + png => 'y', + jp2k => 'y', + webp => 'n', + heic => 'y', + avif => 'y', + jxl => 'y', + }, + }, + max_channels => { + name => 'maximum number of channels', + data => { + jpeg => 4, # cmyk + png => 4, # cmyk + jp2k => 2**15, + webp => 4, + heic => 5, + avif => 5, + jxl => 4099, + }, + }, + features => { + score => { + jpeg => 2, + png => 3, + jp2k => 4, + webp => 2, + heic => 4, + avif => 4, + jxl => 5, + }, + }, + animation => { + name => 'supports animation', + score => { + jpeg => 'n', + png => 'y', + jp2k => 'n', + webp => 'y', + heic => 'y', + avif => 'y', + jxl => 'y', + }, + data => { + jpeg => 'MJPEG', + png => 'APNG', + jp2k => 'MJP2', + }, + }, + progressive => { + name => 'progressive decoding', + score => { + jpeg => 4, + png => 2, + jp2k => 5, + webp => 'n', + heic => 'n', + avif => 'n', + jxl => 5, + }, + }, + alpha => { + name => 'alpha transparency', + score => { + jpeg => 'n', + png => 'y', + jp2k => 'y', + webp => 'y', + heic => 'y', + avif => 'y', + jxl => 'y', + }, + }, + depthmap => { + name => 'depth map', + score => { + jpeg => 'n', + png => 'n', + jp2k => 'n', + webp => 'n', + heic => 'y', + avif => 'y', + jxl => 'y', + }, + }, + overlays => { + name => 'overlays', + score => { + jpeg => 'n', + png => 'n', + jp2k => 'n', + webp => 'n', + heic => 'y', + avif => 'y', + jxl => 'y', + }, + }, + authoring => { + name => 'authoring workflow suitability', + score => { + jpeg => 2, + png => 3, + jp2k => 3, + webp => 2, + heic => 2, + avif => 2, + jxl => 5, + }, + }, + reencode => { + name => 'generation loss resilience', + score => { + jpeg => 4, + png => 'n/a', + jp2k => 3, + webp => 2, + heic => 3, + avif => 3, + jxl => 4, + }, + data => { + png => 'n/a', + }, + }, + compat_jpeg => { + name => 'lossless JPEG recompression', + score => { + jpeg => 0, + png => 'n', + jp2k => 'n', + webp => 'n', + heic => 'n', + avif => 'n', + jxl => 'y', + }, + data => { + jpeg => 'n/a', + }, + }, + royalties => { + name => 'royalty-free', + score => { + jpeg => 'y', + png => 'y', + jp2k => 'y', + webp => 'y', + heic => 'n', + avif => 'y', + jxl => 'y', + }, + }, +}, +} diff --git a/codec.plp b/codec.plp new file mode 100644 index 0000000..2cc454a --- /dev/null +++ b/codec.plp @@ -0,0 +1,50 @@ +<(common.inc.plp)><: + +Html({ + title => 'Codecs', + version => '1.0', + description => [ + ], + keywords => [qw' + '], + stylesheet => [qw'light circus dark red'], + data => [qw'codec.inc.pl'], +}); + +my $info = do 'codec.inc.pl'; +$info and %{$info} > 1 or Abort("cannot open operator include", 500, $@ // $!); + +my %BOOLSCORE = (y => 5, n => 1); +:> +

Image codecs

+ +
+ + +"; +print ''; + +print ''; +for my $feat (sort keys %{$info->{feature}}) { + my $featinfo = $info->{feature}->{$feat}; + printf ''; +} + +:>
feature +<: +my @codecs = sort { + $info->{codec}->{$a}->{available} <=> $info->{codec}->{$b}->{available} +} keys $info->{codec}->%*; + +print '', $_->{name} for @{$info->{codec}}{@codecs}; +print "\n
', $_->{available} for @{$info->{codec}}{@codecs}; +say '
%s', $featinfo->{name} // $feat; + printf('%s', + (map { $_ && $BOOLSCORE{$_} || $_ || 0 } $featinfo->{score}->{$_}), + $featinfo->{data}->{$_} // (map { + $BOOLSCORE{$_} ? ($_ eq 'y' ? '✔' : '✘') : '•' x ($_ - 1) + } $featinfo->{score}->{$_}), + ) for @codecs; + say '
+
+