Replies: 9 comments
-
Encoding is quite memory intensive with default settings. Lower efforts/higher speeds have a much reduced memory usage: |
Beta Was this translation helpful? Give feedback.
-
Isn't speed 7 the default? Even at speed 1 it hits the ceiling, though, just needs a bit less pagefile swapping on a 16 gb system. And decoding these images peaks at over 13 gb (!) memory usage, too. Meanwhile JPEG manages with less than 2.5 gb and TIFF/PNG mostly just peak corresponding to the image size (about 1.4 gb). I might have to check with CLI tools instead of Irfanview/XnViewMP for a better comparison. |
Beta Was this translation helpful? Give feedback.
-
Both cjxl encode and djxl decode use full float buffers to represent images. That already means 12 or 16 bytes per pixel instead of 3 or 4 bytes per pixel, i.e. 4x the uncompressed size (if the original is 8-bit). On top of that, the encoder is storing all the ANS tokens (for histogram computation/context clustering and because encode is LIFO in ANS), which in case of lossless is another 8 bytes per token, i.e. 24 or 32 bytes per pixel. So for 8-bit images, lossless libjxl encode will need at least 12x the uncompressed image size (the good news is that for 16-bit images, it's 'only' 6x and for 32-bit images, it's 'only' 3x). A specialized encoder that is optimized for low bit depth (say at most 14-bit) could probably easily cut that in half. An encoder that doesn't try to do global optimization could do things in constant memory, at the cost of some compression density. If lossless compression is what you're after, you can try the As for the decoder: The API does support decoding to non-full buffers (pixel callback that gives you a row of a tile at a time), or decoding to uint8 or uint16 buffers instead of float ones, so it's certainly possible to avoid such high memory consumption — this is what the chrome and firefox integrations do at the moment. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the explanation! My numbers were for lossy compression on 8 bit RGB (non A) images, though (squeezing that 1.35 gb image down to 25-35 mb depending on speed settings). I cannot fully test the Chrome integration, because Chrome refuses to display the test image at all, it turns to light gray from medium gray once it's finished decoding, but it doesn't show anything except the solid gray. During "decoding" Chrome peaks at around 3.9 gb for the 1.35 gb image, which is less compared to decoding a PNG version, but double as much compared to decoding a JPG version. I will have to install the FF nightly to test that one. Is there a technical reason why cjxl and djxl use full float buffers even for 8 bit images? Are websites supposed to fallback to JPEG or WebP for mobile devices with limited memory then? |
Beta Was this translation helpful? Give feedback.
-
Doing everything with full float buffers is convenient for implementation, but it does quadruple the memory needed compared to using uint8. We already have decode options that are more memory-friendly (you can decode to uint8 buffers or use the callback to decode even without a full frame buffer). Chrome not showing the test image could be a bug in chrome: could you share the image that is a valid jxl but chrome doesn't show it properly? cjxl and djxl are just example applications so they're not really aiming for being memory optimized. It would be useful to have encode options in the api to reduce memory needs, but this hasn't been a priority to implement so far. |
Beta Was this translation helpful? Give feedback.
-
Here is the test image: |
Beta Was this translation helpful? Give feedback.
-
FWIW, the excessive peak memory usage during encoding can be reproduced using libjxl 0.6.1 via the Python imagecodecs package. Decoding to uint8 buffer looks reasonable: import psutil
from imagecodecs import jpegxl_decode, jpegxl_encode
with open('briarstone_asylumt1X.jxl', 'rb') as fh:
image = jpegxl_decode(fh.read())
print(f'image size: {image.size / 2**30:.3f} GB')
print(f'decode: {psutil.Process().memory_info().peak_wset / 2**30:.3f} GB')
encoded = jpegxl_encode(image)
print(f'encode: {psutil.Process().memory_info().peak_wset / 2**30:.3f} GB') Output on Python 3.9 for Windows 10:
|
Beta Was this translation helpful? Give feedback.
-
Things should have improved significantly since version 0.10. Moving this to Discussions now. |
Beta Was this translation helpful? Give feedback.
-
Lossless JPEG transcoding with -j 1 still uses an extreme amount of memory (on win64 with cjxl.exe). 94 megapixels, 50 MB uses over 5 gigs of RAM. Encoding with -j 0 uses only about 800 MB. The program should do piece-wise processing of the input when not aiming for maximum compression ratio to be scalable for large images. Perhaps this could be linked to the effort parameter. |
Beta Was this translation helpful? Give feedback.
-
First of all: I did not test libjxl directly, but through its implementation in Irfanview and XnViewMP.
Describe the bug
Unfortunately memory consumption goes through the roof for JXL encoding and decoding.
Encoding a 19200 x 25200 px image of 1.35 gb uncompressed TIFF size uses more than my 16 gb for encoding and thus needs to make heavy use of the pagefile to finish.
Decoding the image peaks at over 12 gb, while decoding the same as TIFF or PNG only hits a peak of about 1.35 gb and JPEG a peak of about 2.3 gb.
To Reproduce
Encode/Decode (uncompressed) 1.35 gb large image file (19200 x 25200 px).
Expected behavior
Should not use over 10 times the memory size of the original image to encode, maybe 2-3 times max. Should not use close to 10 times the memory size to decode.
Screenshots
https://i.imgur.com/oB6Nc6N.png
Environment
Beta Was this translation helpful? Give feedback.
All reactions