lovell / sharp

High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images. Uses the libvips library.
https://sharp.pixelplumbing.com
Apache License 2.0
29.31k stars 1.3k forks source link

Enhancement: add support for jpegli as jpeg encoder #4018

Open kurtextrem opened 8 months ago

kurtextrem commented 8 months ago

Feature request

What are you trying to achieve?

The JPEG XL team at Google has built yet another JPEG encoder, jpegli, which is both faster and better than even mozjpeg. It is based on lessons learned from guetzli and libjxl, and offers a very attractive trade-off: it is very fast, compresses better than WebP and even high-speed AVIF, while still producing good old JPEG files that are supported everywhere. - source: https://cloudinary.com/blog/jpeg-xl-and-the-pareto-front.

Why is it worth adding

Google did a visual study using jpegli, made public in 2024: https://github.com/google-research/google-research/tree/master/mucped23

jpegli likely lifts jpeg to the WebP quality, and far above in high quality - https://twitter.com/jyzg/status/1758436289181859995

There is no bait. Jpegli is a huge upside, 30–35 % more compression without decode slowdown, without compatibility nightmare. It just wörks. It continues on the trajectory of mozjpeg and guetzli but goes further in speed and density. - https://twitter.com/jyzg/status/1762239776667685361

The second and third deficiencies are not a big issues at high quality (libjpeg q85+), but reduces Jpegli's ability to compete in the lowest quality class, like q75-. Overall I believe jpegli is in its own class of jpeg codecs, combining the quality of guetzli while being faster than mozjpeg and allowing 10+ bits of dynamics for "8 bit" jpegs. I believe Jpegli and jpeg xl lead in the highest quality category (5 BPP). In mid quality (1.5 BPP) jpeg xl and AVIF are the leaders, Jpegli likely better than WebP. In low quality (0.5 BPP) AVIF will be great for graphics and indoor photos while JPEG XL can still outperform AVIF in nature photos. In low quality WebP can be better than jpegli. - https://news.ycombinator.com/item?id=36429397

Jpegli will lift traditional JPEG compression density by 25–30% and support 10+ bit HDR within the backward compatible 8 bit formalism. Jpegli is based on porting jpeg xl encoding strategies back to old jpeg. It is a whole new reimplementation, instead of tweaking old libs such as libjpeg, libjpeg-turbo or mozjpeg. Jpegli is API and ABI compatible with its usual alternatives like three mentioned before. - https://news.ycombinator.com/item?id=38644005

Further blog posts (from last year, could be that jpegli advanced by now):

How to integrate

Jpegli is included in the JXL repo: https://github.com/libjxl/libjxl/blob/main/lib/jpegli/README.md:

When building the parent libjxl project, two binaries, tools/cjpegli and tools/djpegli will be built, as well as a lib/jpegli/libjpeg.so.62.3.0 shared library that can be used as a drop-in replacement for the system library with the same name.

libvips discussion: https://github.com/libvips/libvips/discussions/3871

When you searched for similar feature requests, what did you find that might be related?

https://github.com/lovell/sharp/issues/2731

What would you expect the API to look like?

Probably an option like [options.mozjpeg] -> [options.jpegli].

What alternatives have you considered?

see above; jpegli is an alternative to mozjpeg, while being faster and achieving better quality.

lovell commented 8 months ago

My understanding is that the jpegli encoder discards more data from the blue part of the visual spectrum to achieve smaller files. To achieve this it uses a somewhat non-standard approach, converting RGB pixel values to a custom colour space (XYB) and storing them as if they were YCbCr, attaching an ICC profile with the relevant curves for a decoder to convert back again.

This means all decoders given such images would need to properly support ICC profiles, something that is still not the case today. This would also preclude the use of wider gamut RGB profiles such as P3.

as well as a lib/jpegli/libjpeg.so.62.3.0 shared library that can be used as a drop-in replacement for the system library

Those wishing to use this should be able to do so today with a globally-installed libvips, although I cannot provide any help or support.

If the prebuilt binaries were ever to include this, it would be instead of mozjpeg rather than in addition to. This means it would require that the upstream jpegli library have a "switch" to use standard libjpeg YCbCr colour encoding, a feature I don't see today.

kurtextrem commented 8 months ago

I got the info that Jpegli only uses XYB optionally nowadays, and all comparisons that Google has done were without the XYB ICC color space. jpegli does the same as other encoders, just quantized differently. i.e., no decoding compatibility burned.

Jyrki will also reply here as soon as he has the time.

lovell commented 8 months ago

Ah OK, happy to stand corrected, the documentation for jpegli is rather sparse and I might be mixing it up with bits of JPEG-XL.

georges-gomes commented 7 months ago

Today's Google announcement

https://opensource.googleblog.com/2024/04/introducing-jpegli-new-jpeg-coding-library.html

kurtextrem commented 7 months ago

For people following this issue, as far as I can tell, if you build your own libvips and replace the jpeg libs during building of libvips, you can make sharp use jpegli already (implicitly).

adityapatadia commented 7 months ago

How to replace JPEG lib? Can you give command to give while building libvips?

kurtextrem commented 7 months ago

I think something like this should do it:

sudo cp ./libjpeg.so.62.3.0 /usr/lib/ # make sure libjpeg.so.62.3.0 is the one from the jpegli build
sudo ldconfig

and then build libvips. not 100% sure though (and make sure to make a backup of the current libjpeg in case you try it outside of a docker container or so)

jyrkialakuijala commented 7 months ago

Jpegli is not using XYB by default. Most of the substantial savings are by variable dead zone quantization and more precise intermediate computations. Using XYB will give an additional ~10 % savings in mid-to-high quality.

lovell commented 7 months ago

@jyrkialakuijala Thanks for the info. Given jpegli is intended as a "drop-in replacement" for libjpeg(-turbo), is there any plan to add support for the jpeg_c_*_param_supported() extensibility framework functions, as provided by mozjpeg via https://github.com/mozilla/mozjpeg/pull/118? This would allow libvips and therefore sharp to switch jpegli features on/off in an ABI-compatible manner in the same way that we can currently switch mozjpeg features on/off.

szabadka commented 4 weeks ago

Jpegli features can currently be switched on/off with individual jpegli_* API functions, like jpegli_enable_adaptive_quantization. We are currently in the process of migrating Jpegli from the libjxl/libjxl repository to the google/jpegli repository and will not change the API until this is completed.

Please consider adding an issue to the google/jpegli repository so that we can consider this in the future.

kurtextrem commented 3 weeks ago

@lovell you probably have more context on what you'd need, could you open the issue there? (or if you don't have time, please lmk which features sharp would need, and then I'll happily open it)

lovell commented 3 weeks ago

I've opened https://github.com/google/jpegli/issues/45 to discuss this upstream in the jpegli repo.