JPEG XL

Info

rules 57
github 35276
reddit 647

JPEG XL

tools 4225
website 1655
adoption 20712
image-compression-forum 0

General chat

welcome 3810
introduce-yourself 291
color 1414
photography 3435
other-codecs 23765
on-topic 24923
off-topic 22701

Voice Channels

General 2147

Archived

bot-spam 4380

libjxl

jonnyawsom3
2025-03-09 06:51:33
I set djxl to use level 1 zlib IIRC, so oxipng is doing the same without filtering, or any of the JXL en/decoding
2025-03-09 08:49:11
I'm working on some Resampling tweaks and noticed this... There's 3 different types? `DownsampleImage2_Iterative`, `DownsampleImage2_Sharper` and `DownsampleImage` The comment at the top says regular should be used if shaper isn't fixed, and the comment I've replaced (also tagged `lode`) said iterative was too slow for squirrel and it should be raised, which I did. Testing seems to show roughly equal visual quality between iterative and sharper, so I made it effort 11 since lossy does nothing there currently, but how does regular compare to the other two?
2025-03-09 08:53:57
Iterative seems to retain the original details of the image better, but the lower resolution makes it look like a sharpen filter with ringing artifacs on non-photo content. Combined with the 2x slowdown (10x slowdown at effort 7) and 3x memory increase, I considered disabling it entirely or making it an experimental flag
2025-03-09 12:40:38
Well, I got all 3 downsampling methods. Orginal, Downsample, Sharper, Iterative
spider-mario
2025-03-09 01:20:49
last one seems best to me
2025-03-09 01:21:18
similar sharpness as the third image (second downsampled) but with slightly less aliasing
jonnyawsom3
2025-03-09 01:43:10
It scores around 1 point higher in ssimulacra, but varies image to image. Particularly on non-photo, the last downsampling method causes severe ringing
2025-03-09 01:43:51
Iterative seems to retain the original details of the image better, but the lower resolution makes it look like a sharpen filter with ringing artifacs on non-photo content. Combined with the 2x slowdown (10x slowdown at effort 7) and 3x memory increase, I considered disabling it entirely or making it an experimental flag
2025-03-09 01:49:30
For now, I set the last method to enable only at effort 11 lossy, since it's a relatively large slowdown for highly varied results (And e11 does nothing different to e10 otherwise). If/when it's sped up and the TODOs are cleared, it can be re-enabled at standard effort settings
2025-03-09 01:54:06
I actually wanted to ask. I know distance is capped to 25 in cjxl, but I also know you can override it and set whatever you want using the library/a dev build. I got resampling 4 to look competitive at distance 25 compared to resampling 2, but should I add this to compensate for values above 25 `2.5 + ((Distance - 25) * 0.25)`, or just explicitly set the value since it's the cap?
2025-03-09 01:55:25
~~If I had a cracked build I could set resampling 8 too~~
2025-03-09 03:31:51
Scrapped the resampling 4, made anything below 1440p far too blurry to be useful. Noticed one or two images that actually had significantly higher bpp and quality than no resampling, but it was an issue with the current settings too so I didn't bother tweaking it and breaking the other images
2025-03-09 04:42:31
bpp is a smidge higher, but it's faster, more memory efficient and scores a lot better
monad
2025-03-09 06:06:06
comparing to git head is probably safer. adding bits to increase fidelity is totally unremarkable (assuming ssimu2 captures reality in this context), so the compelling thing would be the speedup if density doesn't diverge from expectation in general
spider-mario
2025-03-09 06:11:22
having curves of bpp vs score might make it easier to compare two versions without having to match the bpp exactly
jonnyawsom3
2025-03-09 06:26:31
That's git head at distance 10, my PR at distance 10, and git head manually set to resample with the same parameters as my PR for the speed/memory comparison
2025-03-09 06:28:26
The slight bpp and SSIMULACRA2 difference with the same parameters is from me disabling the iterative downsampling at effort 7, which gave the speed and memory improvements
monad
2025-03-09 06:32:33
hm, version/commit hash indicates differently
jonnyawsom3
monad hm, version/commit hash indicates differently
2025-03-09 06:38:17
Have some hashes, fresh out the workflow
2025-03-09 06:49:40
The problem is the image content changes the effectiveness and the internal butter scores. So on some the bpp is higher and on others it's lower, but overall the quality is higher than the bytes otherwise
2025-03-09 06:52:26
I was originally using `3 + ((Distance - 10) * 0.25)` to calculate the resampled distances, but on some images it was half the bpp and lower quality. So I raised it to `2.5 + ((Distance - 10) * 0.25)` which seemed to be a good middleground
Traneptora
I was originally using `3 + ((Distance - 10) * 0.25)` to calculate the resampled distances, but on some images it was half the bpp and lower quality. So I raised it to `2.5 + ((Distance - 10) * 0.25)` which seemed to be a good middleground
2025-03-10 11:53:16
this is just distance / 4
jonnyawsom3
Traneptora this is just distance / 4
2025-03-10 12:03:35
If you input `-d 15`, it does 15 - 10, then 5 / 4, followed by 2.5 + 1.25 to give a final result of `--resampling 2 -d 3.75` instead. This usually results in equal or lower bpp, but at higher quality. Some images it's too low, some it's too high, but I thought I'd aim for the higher end.
Traneptora
If you input `-d 15`, it does 15 - 10, then 5 / 4, followed by 2.5 + 1.25 to give a final result of `--resampling 2 -d 3.75` instead. This usually results in equal or lower bpp, but at higher quality. Some images it's too low, some it's too high, but I thought I'd aim for the higher end.
2025-03-10 12:04:42
15 / 4 = 3.75
2025-03-10 12:05:28
expand it out. `(distance - 10) * 0.25 = distance * 0.25 - 10 * 0.25` and `10 * 0.25 = 2.5`
2025-03-10 12:05:41
so you're just taking `distance * 0.25` and then subtracting and adding `2.5`
jonnyawsom3
2025-03-10 12:07:14
Right, yeah. Because 2.5 is already 1/4 of 10. Didn't catch that since lowering from 3 was a last minute decision
2025-03-10 12:08:34
So if it is the right value, that can just be changed to distance / 4, but if it's any other value then it probably needs the equation
Traneptora
So if it is the right value, that can just be changed to distance / 4, but if it's any other value then it probably needs the equation
2025-03-10 12:08:56
if you want to add 3 instead then change it to distance/4 + 0.5
2025-03-10 12:09:01
still much simpler
2025-03-10 12:09:11
and notably, much more readable code
jonnyawsom3
2025-03-10 12:10:55
The current/original line was `p->butteraugli_distance = 6 + ((p->butteraugli_distance - 20) * 0.25);` so I just assumed there was a reason for it... Thanks for spotting that
2025-03-10 12:25:31
Added to the PR, thanks again
2025-03-10 12:56:39
This is the worst case I've had so far. 0.340 bpp on the left, 0.438 bpp with the PR in the middle. It seems to focus more on the whole image than things like text. Going to 3 instead of 2.5 reduces it to 0.358 bpp, but I'd have to test more images to see if it's worse for other types of content
2025-03-10 01:08:55
It's essentially a balance of photo vs non-photo. 2.5 means it aligns with difficult photos, but overshoots on non-photo. 3 means it's equal on non-photo but undershoots on photos Guess I'll add 0.25 to put it in the middle
Demiurge
This is the worst case I've had so far. 0.340 bpp on the left, 0.438 bpp with the PR in the middle. It seems to focus more on the whole image than things like text. Going to 3 instead of 2.5 reduces it to 0.358 bpp, but I'd have to test more images to see if it's worse for other types of content
2025-03-10 03:10:10
The first image has unacceptable quality imo
2025-03-10 03:10:21
The other two are slightly better
2025-03-10 03:10:41
I don't think the first image is usable quality at all
jonnyawsom3
2025-03-10 03:10:49
Yeah, it seems like butteraugli is falling flat on it's face for some reason
Demiurge
2025-03-10 03:11:07
It's a useless and ruined image
2025-03-10 03:11:35
The other two are more acceptable and usable
2025-03-10 03:13:20
The one in the middle looks the least distorted
jonnyawsom3
2025-03-10 03:14:04
For now, I've changed the PR *yet again* to use a setting between the last two. Distance 2.75 at 0.380 bpp. 40 higher than main, but 60 lower than the second image. In my opinion the best quality without drastically changing size between distance 9.9 and 10
2025-03-10 03:45:03
15% higher bpp for 20% higher score in the worst case. On a random photo it's -7% and 11%, generally with visual inspection looking better than SSIMULACRA2 scores anyway. This is the quality 0 to 18 range, so really people shouldn't be going this low anyway... But at least if they do, they get usable results now
2025-03-10 03:45:43
In the end, as with most things in cjxl, it depends on the image :P
Demiurge
2025-03-11 12:21:31
Which pr
jonnyawsom3
Demiurge Which pr
2025-03-11 05:06:31
https://github.com/libjxl/libjxl/pull/4147
Demiurge
2025-03-11 05:19:33
This doesn't change anything except when distance >=10?
2025-03-11 05:20:15
I hope people aren't actually compressing images at libjxl distance 10...
2025-03-11 05:21:43
0.5 to 1.5 is the recommended range I think.
2025-03-11 05:22:21
I would even be in favor of making those the hard minimum/maximum distance values
2025-03-11 05:22:46
But I'm radical like that
2025-03-11 05:24:33
I think software should be designed to not give people a big red "DO NOT PRESS" button since they're always GUNNA press it thinking they know better. 😂
2025-03-11 05:25:09
People who know better can always fork or make a pull request
jonnyawsom3
Demiurge This doesn't change anything except when distance >=10?
2025-03-11 05:30:09
Yeah, at that point resampling is competitive, which says a lot about what kinda quality you're using... But it is better, usually smaller and multiple times faster to encode
Demiurge
2025-03-11 05:30:46
And decode.
jonnyawsom3
2025-03-11 05:31:07
Surprisingly not in my minor testing
Demiurge
2025-03-11 05:31:19
But nothing would be lost either if distance values were just hard clamped to 0.5-1.5
2025-03-11 05:32:18
At that point I would say libjxl is not adequate or competitive there
2025-03-11 05:32:51
Unless it were to receive a lot of specific tuning
jonnyawsom3
2025-03-11 05:32:56
It's mostly people comparing it to AVIF, which unfortunately is quite often. The PR actually increases quality at distance 25 compared to main, but otherwise the images are unusable
Demiurge
It's mostly people comparing it to AVIF, which unfortunately is quite often. The PR actually increases quality at distance 25 compared to main, but otherwise the images are unusable
2025-03-11 05:33:10
All the more reason to clamp the values
2025-03-11 05:33:31
To prevent bogus automated comparisons that are done without actual visual confirmation
2025-03-11 05:33:51
Like the comparison the avif team did at absurdly bad quality settings
jonnyawsom3
2025-03-11 05:34:11
That does raise a point....
Demiurge
2025-03-11 05:34:38
You made me realize that it's actually a serious issue and now I strongly suggest clamping the values unless MORON_MODE=1
2025-03-11 05:34:58
And change it from "expert options..."
2025-03-11 05:35:18
It's not for experts, it's for crazy people 😂
jonnyawsom3
Demiurge The one in the middle looks the least distorted
2025-03-11 05:35:19
The best looking images actually scored lowest, probably due to the DCT noise you love so much, caused by the resampling forcing it to use smaller blocks
Demiurge
The best looking images actually scored lowest, probably due to the DCT noise you love so much, caused by the resampling forcing it to use smaller blocks
2025-03-11 05:36:00
What metric?
jonnyawsom3
2025-03-11 05:36:35
SSIMULACRA2, I can rub some butter on it when I have my computer on
Demiurge
2025-03-11 05:36:50
I just choose the image that has the most discernible features and doesn't look like someone smeared their fingers all over an oil painting
jonnyawsom3
2025-03-11 05:37:31
It's ironic. Resampling the image to half the resolution brings *back* fine details
Demiurge
2025-03-11 05:38:19
Yeah I don't trust ssimu2 at all sadly, it sounds exciting when I first heard of it but it loves zeroing out crucial image data and visual features
jonnyawsom3
2025-03-11 05:38:29
0.8 has the same resampling gains, just with both scoring higher than main
Demiurge
2025-03-11 05:39:30
It's not a good sign when a codec performs a lot worse or better with resampling.
2025-03-11 05:40:10
These are good ways to stress test the encoder though I guess
The best looking images actually scored lowest, probably due to the DCT noise you love so much, caused by the resampling forcing it to use smaller blocks
2025-03-11 05:41:51
DCT noise only looks bad if there are obvious block boundaries and if that noise doesn't blend into the image behind it.
jonnyawsom3
2025-03-11 05:42:02
The biggest thing I noticed was chroma noise at high distances. Large banding areas of yellow across flat surfaces, so while resampling was sometimes a dubious fidelity improvement, it removed the most distracting artifacts
2025-03-11 05:43:02
Tends to distribute bits across the entire image instead of focusing on the 'attention points' like the UI in the game screenshot above
Demiurge
2025-03-11 05:43:38
Imagine an image codec that looks like an analog picture getting static interference when the compression is too strong 😂
2025-03-11 05:44:19
That would kinda be a cool idea. The artifacts would be honest, easy to tell when they are there and easy to see underneath them
2025-03-11 05:44:44
Just like the stated goals of jxl
jonnyawsom3
2025-03-11 05:44:46
Oh, you reminded me. I have a natural example of an image aligning with the DCT coefficients
2025-03-11 05:45:35
The roof of my church image has a single block that remains perfectly sharp even at distance 25 or intensity targets like 1 (IIRC)
Demiurge
2025-03-11 05:47:33
Jon I think spoke a great deal about how dangerous and undesirable deceptive artifacts like blurring and smearing can be in avif for example. But shortly afterwards, libaom became a lot less blurry and libjxl became a lot more blurry, so the roles were reversed.
2025-03-11 05:52:31
It's a little embarrassing especially considering how the creator of avif is trying to wield his influence over Chromium to prevent jxl from coexisting and competing with his avif and webp
2025-03-11 05:54:17
libaom made some real strides in the psychovisual quality
jonnyawsom3
Well, I got all 3 downsampling methods. Orginal, Downsample, Sharper, Iterative
2025-03-11 04:15:36
<@207980494892040194>
K
2025-03-12 09:08:40
TagStudio (a tagging software) is trying to support jxl but there is some incompatible at the moment. If anyone like to help out feel free! Or just want to use this software in general. PS. I'm just an user, so dont bother asking me any technical questions. Here is the git: https://github.com/TagStudioDev/TagStudio
2025-03-12 09:10:11
And here is their discord https://discord.gg/hRNnVKhF2G
K TagStudio (a tagging software) is trying to support jxl but there is some incompatible at the moment. If anyone like to help out feel free! Or just want to use this software in general. PS. I'm just an user, so dont bother asking me any technical questions. Here is the git: https://github.com/TagStudioDev/TagStudio
2025-03-12 09:12:01
Or should this be posted in <#794206170445119489>
Xarvex
2025-03-12 09:13:00
Hi, TagStudio maintainer, to correct the above: there’s no incompatibility with JXL, there was a small build issue related to a [Pillow](<https://pypi.org/project/pillow>) plugin used for JXL that’s now been fixed. Not at all related to libJXL itself.
K
2025-03-12 09:16:23
PS. It was all a ruse to advertise for more contribute so version 9.6 will be e release*yesterday*
Xarvex
2025-03-12 09:16:36
Please, no
2025-03-12 09:20:16
We have nothing to do with the above, nor condone it. Please disregard it and I hope everyone is enjoying their day. Sorry for the disturbance
jonnyawsom3
2025-03-12 09:57:53
Regardless, good to know it has support. I don't think we were aware of it before
Xarvex
2025-03-12 10:01:24
yes, we make use of [pillow-jpegxl-plugin](https://github.com/Isotr0py/pillow-jpegxl-plugin) which seems to be the same thing hydrus uses according to https://discord.com/channels/794206087879852103/803574970180829194/1335062020269150208
2025-03-12 10:14:33
one thing im a tad wary on is how things will look in an eventual rust rewrite, but thats my issue to tackle, it does look though that with this plugin being part rust it obviously does work. im just confused because i know mozilla is waiting on the completion of jxl_rs
Demiurge
2025-03-12 04:47:08
libjxl can benefit a great deal from a rewrite or major overhaul. Regardless of Rust or not.
Xarvex
2025-03-12 05:10:29
Interesting, so some of this is being propagated by libjxl at the “top” needing some rework?
Demiurge
2025-03-13 02:36:02
It's not something to be wary of. The codebase is showing its age. It was originally 2 separate codecs written by different people, merged together into one. So the seams and stitches are showing.
2025-03-13 02:36:28
What I mean is, a rewrite would be more concise and simpler to maintain.
Xarvex
2025-03-13 02:41:28
gotcha, i see what youre saying
Meow
2025-03-13 03:20:04
That's why some people consider jxl-rs as the ultimate solution
Xarvex
2025-03-13 03:23:51
for some reason i glanced over the mention of it being a reimplementation
2025-03-13 03:24:20
when i was looking at the pillow plugin i kinda assumed it was a wrapper for the C lib
2025-03-13 03:24:30
i see now thats not the case
username
2025-03-13 03:32:17
huh? but it is? wait what are y'all even talking about?
Xarvex
2025-03-13 03:37:42
jxl-rs being a reimplementation rather than a wrapper
username
2025-03-13 03:44:10
the pillow plugin isn't using jxl-rs. jxl-rs isn't even ready for use
Xarvex
2025-03-13 03:48:28
i dont believe i said it did, if i gave such an appearance i apologize for the confusion
username
2025-03-13 03:49:16
ah it's fine \👍 also sorry if I came across as rude
Xarvex
2025-03-13 03:50:07
no worries at all!
A homosapien
2025-03-13 04:11:12
jpegxl-rs is the wrapper, jxl-rs is the reimplementation
2025-03-13 04:11:21
I got confused as well
2025-03-13 04:11:31
the names are very similar
Xarvex
2025-03-13 04:14:15
Working on TagStudio is the reason I ever heard of JXL, so I’m trying to figure out all this stuff haha
Demiurge
2025-03-13 07:08:12
Hey that's cool, it's nice to have more tools for viewing and modifying and converting metadata
2025-03-13 07:08:18
Other than exiftool
jonnyawsom3
2025-03-13 08:43:26
It should be noted, jxl-rs is only a decoder, and jxl-oxide is an existing rust decoder too
Demiurge
2025-03-13 12:51:40
It's likely to expand to encoding later. Decoding is the hard part
2025-03-13 12:52:23
And decoding happens more often than encoding
Hello71
2025-03-14 07:49:34
is libjxl_extras_codec.a supposed to be installed?
Demiurge
2025-03-14 08:26:41
No
intelfx
2025-03-15 07:36:05
Hey. (Apologies if this channel is not for general peanut gallery questions, redirect me if so.) Is it expected that progressive lossless encoding (not JPEG→XL reconstruction!) with `cjxl` can be very slow for large inputs? Input: ``` $ magick identify /path/to/input/jpeg /path/to/input/jpeg JPEG 24316x18544 24316x18544+0+0 8-bit sRGB 142.977MiB 0.000u 0:00.001 ``` Command line: ``` cjxl -v -v -e 7 -j 0 -p /path/to/input/jpeg /path/to/output/jxl ``` Cumulative CPU time is 2h08:33 so far (on a ~4.5 GHz Zen 3 desktop chip), and what seems to be a ~18 GiB working set.
jonnyawsom3
intelfx Hey. (Apologies if this channel is not for general peanut gallery questions, redirect me if so.) Is it expected that progressive lossless encoding (not JPEG→XL reconstruction!) with `cjxl` can be very slow for large inputs? Input: ``` $ magick identify /path/to/input/jpeg /path/to/input/jpeg JPEG 24316x18544 24316x18544+0+0 8-bit sRGB 142.977MiB 0.000u 0:00.001 ``` Command line: ``` cjxl -v -v -e 7 -j 0 -p /path/to/input/jpeg /path/to/output/jxl ``` Cumulative CPU time is 2h08:33 so far (on a ~4.5 GHz Zen 3 desktop chip), and what seems to be a ~18 GiB working set.
2025-03-15 08:13:42
Could you try with effort 4 instead? Progressive lossless has density bugs at effort 5+, along with disabling chunked encoding, causing patches to be enabled at effort 5+ (very expensive on large images), IIRC
monad
2025-03-15 08:23:29
2h still seems excessive, are you hitting swap?
intelfx
monad 2h still seems excessive, are you hitting swap?
2025-03-15 08:24:55
I did, briefly, at one point, but that was really tiny portion of total runtime
Could you try with effort 4 instead? Progressive lossless has density bugs at effort 5+, along with disabling chunked encoding, causing patches to be enabled at effort 5+ (very expensive on large images), IIRC
2025-03-15 08:26:54
yup, `-e 4` finished the entire batch in 3 minutes _and_ generated 20% smaller files
Quackdoc
2025-03-15 08:29:08
I never even touch the -p flag anymore I have had results that are just too funky
intelfx
2025-03-15 08:29:32
funky, as in...?
Quackdoc
2025-03-15 08:30:30
one time I had a file size that was 8x the size of using `--progressive_dc` instead. another time it made it take 3x longer to decode
intelfx
2025-03-15 08:31:25
what does `--progressive_{ac,dc}` mean?
jonnyawsom3
2025-03-15 08:33:22
The issue is the chunked encoding I mentioned. Usually it disallows progressive files due to requiring the TOC at the start of the file, to contain data only written at the end. Or something to that effect
Quackdoc
2025-03-15 08:34:33
iirc progressive_dc encodes a second small second image that has a good amount of progressiveness it adds very little file size and is IMO one of cjxls best features
intelfx
2025-03-15 08:37:29
what exactly do "DC" and "AC" even mean in this context?
jonnyawsom3
2025-03-15 08:37:34
https://github.com/libjxl/libjxl/issues/3823
intelfx what does `--progressive_{ac,dc}` mean?
2025-03-15 08:45:35
DC is the 1:8 scale image of lossy encoding, usually the first preview before the rest of the file. Progressive DC can turn that into another lossy image, with a second DC, now 1:64 of the original image for further progressive steps. Progressive AC is how the remaining data is ordered. There are 2 modes, the QAC which simply sends the least significant bits last for a more blocky image. And 'regular' AC, which has a rougher initial image but better quality passes, if that makes sense
intelfx
DC is the 1:8 scale image of lossy encoding, usually the first preview before the rest of the file. Progressive DC can turn that into another lossy image, with a second DC, now 1:64 of the original image for further progressive steps. Progressive AC is how the remaining data is ordered. There are 2 modes, the QAC which simply sends the least significant bits last for a more blocky image. And 'regular' AC, which has a rougher initial image but better quality passes, if that makes sense
2025-03-15 09:12:40
I tried to read whatever docs I could find on the conceptual overview of the format and encoder, but I still can't understand what exactly "DC" and "AC" are
_wb_
2025-03-15 10:05:22
DC = direct component, AC = alternating component; if you do a DCT (or other types of frequency transforms) you end up with one coefficient representing the block average (this is the DC) and a bunch of other coefficients representing 'waves' at various frequencies
2025-03-15 10:08:04
in the jxl spec we use the terminology LF and HF instead (low frequency / high frequency) since we mix DC and AC coefficients a bit to harmonize variable block size data into a low frequency part corresponding to an 1:8 image (in case of the old JPEG which only uses DCT8x8, that's just the DC) and a high frequency part which is the bulk of the data corresponding to the remaining image data.
Demiurge
2025-03-16 06:19:40
DCT is not used for lossless
2025-03-16 06:20:03
Wasn't the question about lossless?
2025-03-16 06:35:33
Progressive lossless is not well tested or optimized afaik
2025-03-18 06:40:08
2025-03-18 06:40:09
Is this a bug others can reproduce? Maybe an issue on github can be opened.
jonnyawsom3
2025-03-18 05:30:04
A random thought, would multithreaded extra channel and layer decoding require a format extension? I'm not sure if the decoder can tell if they reference other channels/frames beforehand (Using `-E` or blend modes)
2025-03-18 11:50:29
https://github.com/libjxl/libjxl/issues/4156
2025-03-18 11:50:38
.....what?
2025-03-18 11:51:28
They want to completely replace the encoding methods so it beats ZPAQ
A homosapien
2025-03-19 01:19:59
Breaking the spec now would be one of the worst things to do as adoption is ramping up
2025-03-19 01:20:34
Also isn't ZPAQ like super slow and single threaded? That would be unusable for larger images imo
TheBigBadBoy - 𝙸𝚛
2025-03-19 07:11:19
Breaking the spec *bc some dude asked chatgpt how to beat zpaq* <:KekDog:805390049033191445>
_wb_
2025-03-19 07:34:59
Everyone is free to fork libjxl and make a research prototype thing out of it, but no way the spec is going to change in breaking ways.
monad
A homosapien Also isn't ZPAQ like super slow and single threaded? That would be unusable for larger images imo
2025-03-19 07:36:46
it's not single-threaded, but the CM is slow and aging. obviously density improvements are possible for jxl, but the particular comparison is amusing
Demiurge
_wb_ Everyone is free to fork libjxl and make a research prototype thing out of it, but no way the spec is going to change in breaking ways.
2025-03-19 08:15:24
It could still be possible to make small, breaking changes, as long as existing old files will always work, but if future files would change in breaking ways, you just keep it disabled in the encoder until no one is using an old enough decoder anymore for it to be an issue any longer.
2025-03-19 08:17:06
You can add experimental features to the encoder but keep them disabled indefinitely until there's literally no one using an old enough decoder
2025-03-19 08:18:16
It would have to be important enough to be worth it, but if it's something you really think is worth it, I don't think you should feel like your hands are completely tied
2025-03-19 08:18:52
It's still pretty early into the life of "next gen alien technology from the future."
2025-03-19 08:20:34
You wouldn't even have to announce it or make a big deal about it. It could be something done completely behind the scenes. Adding support to the decoder and the spec first, and waiting until everyone updates before changing the encoder.
_wb_
2025-03-19 08:47:41
even such breaking changes are quite undesirable, it is really hard to tell when "no one is using an old decoder anymore" so it really complicates interoperability. If there's a really good reason to do it, it can of course be considered, but something like just some compression improvement is not enough of a reason imo...
A homosapien
2025-03-19 09:01:02
Isn't the AVIF team using the "added to decoder/disabled in encoder" method for lossless YCoCg support?
Demiurge
_wb_ even such breaking changes are quite undesirable, it is really hard to tell when "no one is using an old decoder anymore" so it really complicates interoperability. If there's a really good reason to do it, it can of course be considered, but something like just some compression improvement is not enough of a reason imo...
2025-03-19 11:03:15
At this point in time, it's not THAT difficult to tell, yet, at this stage.
2025-03-19 11:04:32
And you can always wait a few years AFTER the spec+decoders have all been updated and you THINK everyone already switched, BEFORE allowing the encoder to generate a new bitstream that wouldn't work with an old decoder.
2025-03-19 11:05:33
I kinda doubt there's an important issue that's big enough to warrant this methodology. Since the spec was so thoughtfully designed to begin with. I'm just saying that if there WAS, I wouldn't want you feeling as if your hands were completely tied.
2025-03-19 11:07:13
But I think all the "hindsight regrets" were all pretty minor, weren't they?
2025-03-19 11:07:39
Just minor little things that could make things slightly more neat and cool
2025-03-19 11:08:30
Probably not big enough to warrant a new incompatible bitstream structure.
2025-03-19 11:09:07
Or maybe, small and harmless enough to be added and co-exist with the existing one?
2025-03-19 11:10:22
For use in the distant future, when there don't exist any incompatible decoders anymore.
spider-mario
2025-03-20 08:33:32
> CMYK-tailored predictors optimized for print-oriented content. that would be jxl’s killer feature, for sure
jonnyawsom3
2025-03-20 02:01:25
MA trees and dots/patches should be able to do half toning and crosshatching, ect
2025-03-21 03:15:16
Seems to be causing a lot of failures https://github.com/libjxl/libjxl/pull/4132
2025-03-21 03:15:32
https://github.com/libjxl/libjxl/issues/4157
2025-03-21 03:15:34
https://github.com/libjxl/libjxl/issues/4159
A homosapien
2025-03-21 03:21:58
Bring it up it in <#848189884614705192>?
jonnyawsom3
2025-03-21 03:25:20
Equal visibility here and Jon is already in the PR, just thought I'd make the rest of you aware
_wb_
2025-03-21 06:59:52
<@1346460706345848868> can you take a look? Looks like you have an edge case not handled
2025-03-21 10:13:06
Some int arithmetic was replaced by float arithmetic in RebalanceHistogram(), I bet this is causing some shenanigans. Ideally that function should just never return false and that template option `minimize_error_of_sum` should not be needed, but if that's too tricky then at least it should never return false when `minimize_error_of_sum==true`. I guess it's hard to test this exhaustively since the number of possible inputs is too huge and it probably only fails in rare cases, so I suppose we need to use brains and math and stuff.
Demiurge
2025-03-21 01:33:24
Oh no! Not brains and math and stuff! Anything but that!
Melirius
_wb_ <@1346460706345848868> can you take a look? Looks like you have an edge case not handled
2025-03-22 01:04:40
Yeah, will take a look.
_wb_ Some int arithmetic was replaced by float arithmetic in RebalanceHistogram(), I bet this is causing some shenanigans. Ideally that function should just never return false and that template option `minimize_error_of_sum` should not be needed, but if that's too tricky then at least it should never return false when `minimize_error_of_sum==true`. I guess it's hard to test this exhaustively since the number of possible inputs is too huge and it probably only fails in rare cases, so I suppose we need to use brains and math and stuff.
2025-03-22 01:46:23
I tried to find a mathematically correct way to rebalance any histogram, but due to the increase of low frequencies to 1/2048 and coarsing by `SmallestIncrementNonzero` it got too complicated quickly and I have not succeed.
2025-03-22 04:45:30
Fix is ready https://github.com/libjxl/libjxl/pull/4160
2025-03-24 05:07:49
I've put an experimental version of rebalance that cannot produce invalid histograms, testing it now on a large corpus of images
Lilli
2025-03-24 05:27:14
Hello there! I wanted to integrate the format into my program on my raspi. It works great, but I noticed an recurring artifact present in the images. I am using my raspi as an intermediate where I compress rather large TIFF images (>200Mo, 16bits) of astronomical observations into jxl. The images are split into bits, so there's no problem there (even though I'd like to make it a tiled jxl, I did not figure that out yet). My problem is: - blue boxes appearing around stars (close to dirac) See below a few images, one with lower quality (d=0.085) and one with good quality (d=0.001), and the stock image. These are closeups to see the issue, and the histogram has been way stretched since it's 16bits. The original tiff is there: https://we.tl/t-Ri6XFzZtkH I noticed that when using cjxl PNG->JXL, this artifact is not present. When exporting to regular PNG from my solf, I also do not experience this. Only when using libJXL. Note that I load the tiff in a `uint16_t*`, which I pass directly to libjxl using ` JxlPixelFormat pixel_format = {image.getChannels(), JXL_TYPE_UINT16, JXL_NATIVE_ENDIAN, 0};` Here are my settings: ``` basic_info.uses_original_profile = JXL_TRUE; basic_info.exponent_bits_per_sample = 0; color_encoding.color_space = JXL_COLOR_SPACE_RGB; color_encoding.white_point = JXL_WHITE_POINT_E; color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR; color_encoding.primaries = JXL_PRIMARIES_SRGB; color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL; ``` When passing the same buffer to PNG, no artifact, so there is something in my settings I suppose. Where does this artifact come from, and what kind of settings are likely to modify it? I have tried changing the distance and the effort with no luck.
2025-03-24 05:27:41
Should I create an issue on git?
RaveSteel
2025-03-24 05:48:03
Is lossy a requirement?
Lilli
2025-03-24 05:59:30
I need about 10 - 12x compression ratio, it will be hard to get with lossy I presume. Being on embedded device, I also cannot use process-intensive operation, the processor isn't that great. This 300Mo file gets compressed in about 20seconds on target, which is about twice as fast as jp2.
spider-mario
Lilli Hello there! I wanted to integrate the format into my program on my raspi. It works great, but I noticed an recurring artifact present in the images. I am using my raspi as an intermediate where I compress rather large TIFF images (>200Mo, 16bits) of astronomical observations into jxl. The images are split into bits, so there's no problem there (even though I'd like to make it a tiled jxl, I did not figure that out yet). My problem is: - blue boxes appearing around stars (close to dirac) See below a few images, one with lower quality (d=0.085) and one with good quality (d=0.001), and the stock image. These are closeups to see the issue, and the histogram has been way stretched since it's 16bits. The original tiff is there: https://we.tl/t-Ri6XFzZtkH I noticed that when using cjxl PNG->JXL, this artifact is not present. When exporting to regular PNG from my solf, I also do not experience this. Only when using libJXL. Note that I load the tiff in a `uint16_t*`, which I pass directly to libjxl using ` JxlPixelFormat pixel_format = {image.getChannels(), JXL_TYPE_UINT16, JXL_NATIVE_ENDIAN, 0};` Here are my settings: ``` basic_info.uses_original_profile = JXL_TRUE; basic_info.exponent_bits_per_sample = 0; color_encoding.color_space = JXL_COLOR_SPACE_RGB; color_encoding.white_point = JXL_WHITE_POINT_E; color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR; color_encoding.primaries = JXL_PRIMARIES_SRGB; color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL; ``` When passing the same buffer to PNG, no artifact, so there is something in my settings I suppose. Where does this artifact come from, and what kind of settings are likely to modify it? I have tried changing the distance and the effort with no luck.
2025-03-24 06:03:44
how sure are you that you have absolutely no over/underflow in the values you pass?
Lilli
2025-03-24 06:05:14
Images come from the TIFF. They are in uint16_t, so I believe over/underflow are not possible there
spider-mario how sure are you that you have absolutely no over/underflow in the values you pass?
2025-03-24 06:10:50
I added a little more details to make sure this is clear 🙂 Thank you for asking clarifications!
Quackdoc
2025-03-24 06:14:52
I wonder if there is a significant difference between modular and vardct with this issue
spider-mario
2025-03-24 06:28:15
I wonder if perhaps underflow occurs because of the conversion to linear sRGB
2025-03-24 06:28:42
does the problem still occur if you change the white point from E to D65?
2025-03-24 06:28:55
(E is a bit of an unusual choice)
2025-03-24 06:29:45
also, which decoder produced the result with the green square?
jonnyawsom3
Lilli Hello there! I wanted to integrate the format into my program on my raspi. It works great, but I noticed an recurring artifact present in the images. I am using my raspi as an intermediate where I compress rather large TIFF images (>200Mo, 16bits) of astronomical observations into jxl. The images are split into bits, so there's no problem there (even though I'd like to make it a tiled jxl, I did not figure that out yet). My problem is: - blue boxes appearing around stars (close to dirac) See below a few images, one with lower quality (d=0.085) and one with good quality (d=0.001), and the stock image. These are closeups to see the issue, and the histogram has been way stretched since it's 16bits. The original tiff is there: https://we.tl/t-Ri6XFzZtkH I noticed that when using cjxl PNG->JXL, this artifact is not present. When exporting to regular PNG from my solf, I also do not experience this. Only when using libJXL. Note that I load the tiff in a `uint16_t*`, which I pass directly to libjxl using ` JxlPixelFormat pixel_format = {image.getChannels(), JXL_TYPE_UINT16, JXL_NATIVE_ENDIAN, 0};` Here are my settings: ``` basic_info.uses_original_profile = JXL_TRUE; basic_info.exponent_bits_per_sample = 0; color_encoding.color_space = JXL_COLOR_SPACE_RGB; color_encoding.white_point = JXL_WHITE_POINT_E; color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR; color_encoding.primaries = JXL_PRIMARIES_SRGB; color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL; ``` When passing the same buffer to PNG, no artifact, so there is something in my settings I suppose. Where does this artifact come from, and what kind of settings are likely to modify it? I have tried changing the distance and the effort with no luck.
2025-03-24 08:55:36
`basic_info.uses_original_profile` should be false for lossy, as XYB is used internally instead of RGB
spider-mario
2025-03-24 09:17:05
oh, yeah, missed that
2025-03-24 09:17:40
it also means that what I said doesn’t apply
jonnyawsom3
2025-03-24 10:09:20
...why does that exist? Lossy only works with it set to false, and lossless isn't lossless unless it's true, right?
spider-mario
2025-03-24 10:14:30
it does seem to be a source of a lot of confusion
2025-03-24 10:16:06
I know too little about this application to say whether this might be one of the instances in which there might be a case to be made for it
CrushedAsian255
...why does that exist? Lossy only works with it set to false, and lossless isn't lossless unless it's true, right?
2025-03-24 10:20:52
Lossy modular may find a need for it
jonnyawsom3
2025-03-24 10:39:29
Lossy modular still uses XYB interally (by default)
A homosapien
spider-mario it does seem to be a source of a lot of confusion
2025-03-25 12:01:54
I assume its because Jpeg XL is the first of its kind, where color information is part of the encoding and decoding process. Every other image format has optional color space information defined in an icc profile. Very few are aware that Jpeg XL handles all color internally.
0xC0000054
A homosapien I assume its because Jpeg XL is the first of its kind, where color information is part of the encoding and decoding process. Every other image format has optional color space information defined in an icc profile. Very few are aware that Jpeg XL handles all color internally.
2025-03-25 12:35:14
AVIF also has per-frame color information through the CICP metadata.
A homosapien
2025-03-25 12:44:50
You can opt out of all color management in AVIF with cicp 2/2/2 (Not that's a good idea). Jpeg XL *has* to be color managed every step of the way. We should really have a big fat warning with red text in the docs saying, "DO NOT USE LOSSY COMPRESSION WITH THIS OPTION ON". RGB compression is a sin.
username
A homosapien You can opt out of all color management in AVIF with cicp 2/2/2 (Not that's a good idea). Jpeg XL *has* to be color managed every step of the way. We should really have a big fat warning with red text in the docs saying, "DO NOT USE LOSSY COMPRESSION WITH THIS OPTION ON". RGB compression is a sin.
2025-03-25 12:51:02
I presume RGB lossy could be made to not be *horrible* as I assume it's just the encoder not being tuned/setup for it at all really, though it doesn't make much sense to put effort towards fixing it up currently since if you are doing lossy then XYB is the way to go for most use cases. But yeah with how many people keep mistakenly using RGB lossy it should probably be pointed out in the docs.
Quackdoc
A homosapien You can opt out of all color management in AVIF with cicp 2/2/2 (Not that's a good idea). Jpeg XL *has* to be color managed every step of the way. We should really have a big fat warning with red text in the docs saying, "DO NOT USE LOSSY COMPRESSION WITH THIS OPTION ON". RGB compression is a sin.
2025-03-25 12:52:29
rgb compression is probably a lot better then xyb if you want a fast decode option
2025-03-25 12:52:37
that can be 4 xD
2025-03-25 12:56:17
I wonder whats faster, jxl-oxide or yuvxyb https://cdn.discordapp.com/emojis/1113499891314991275?size=64
CrushedAsian255
username I presume RGB lossy could be made to not be *horrible* as I assume it's just the encoder not being tuned/setup for it at all really, though it doesn't make much sense to put effort towards fixing it up currently since if you are doing lossy then XYB is the way to go for most use cases. But yeah with how many people keep mistakenly using RGB lossy it should probably be pointed out in the docs.
2025-03-25 12:58:54
im assuming the loss accured through rgb->xyb->rgb roundtrip is significantly lower than the actual lossy nature of the codec
username
A homosapien I assume its because Jpeg XL is the first of its kind, where color information is part of the encoding and decoding process. Every other image format has optional color space information defined in an icc profile. Very few are aware that Jpeg XL handles all color internally.
2025-03-25 01:01:31
maybe a better way to put it would be that with JPEG XL color management is apart of the **full** pipeline and not something that can be ignored like in other formats. Jon/wb worded it nicely here: https://discord.com/channels/794206087879852103/794206170445119489/1281531968688750664 Also this blog post from Kampidh showcases the kind of negatives that come with not treating color management as first class: https://saklistudio.com/miniblog/modern-image-format-linear-color-analysis/ <@833420862334042152> sorry for the direct ping but Discord doesn't seem to have a way to reply to multiple messages at the same time.
username maybe a better way to put it would be that with JPEG XL color management is apart of the **full** pipeline and not something that can be ignored like in other formats. Jon/wb worded it nicely here: https://discord.com/channels/794206087879852103/794206170445119489/1281531968688750664 Also this blog post from Kampidh showcases the kind of negatives that come with not treating color management as first class: https://saklistudio.com/miniblog/modern-image-format-linear-color-analysis/ <@833420862334042152> sorry for the direct ping but Discord doesn't seem to have a way to reply to multiple messages at the same time.
2025-03-25 01:01:46
animated examples from that blog post: https://saklistudio.com/miniblog/blog-img/jxlvother/1bpp-anim.webp https://saklistudio.com/miniblog/blog-img/jxlvother/06bpp-anim.webp
Demiurge
2025-03-25 02:00:43
Using the vips api might be easier <@1353777338843795608>
2025-03-25 02:01:03
Compared to libjxl directly
jonnyawsom3
CrushedAsian255 im assuming the loss accured through rgb->xyb->rgb roundtrip is significantly lower than the actual lossy nature of the codec
2025-03-25 02:19:58
IIRC the transformation is fully reversible
CrushedAsian255
IIRC the transformation is fully reversible
2025-03-25 03:12:07
I’m thinking like floating point math weirdness
jonnyawsom3
`basic_info.uses_original_profile` should be false for lossy, as XYB is used internally instead of RGB
2025-03-25 03:12:23
Maybe it should be changed to `basic_info.uses_xyb_internally` or something similar. Since the output *is* the original profile, just not used for the internal compression
CrushedAsian255 I’m thinking like floating point math weirdness
2025-03-25 03:14:58
32float can represent up to 24int with exact precision, so for any normal 8int or 16int input it shouldn't be an issue
Demiurge
Lilli Hello there! I wanted to integrate the format into my program on my raspi. It works great, but I noticed an recurring artifact present in the images. I am using my raspi as an intermediate where I compress rather large TIFF images (>200Mo, 16bits) of astronomical observations into jxl. The images are split into bits, so there's no problem there (even though I'd like to make it a tiled jxl, I did not figure that out yet). My problem is: - blue boxes appearing around stars (close to dirac) See below a few images, one with lower quality (d=0.085) and one with good quality (d=0.001), and the stock image. These are closeups to see the issue, and the histogram has been way stretched since it's 16bits. The original tiff is there: https://we.tl/t-Ri6XFzZtkH I noticed that when using cjxl PNG->JXL, this artifact is not present. When exporting to regular PNG from my solf, I also do not experience this. Only when using libJXL. Note that I load the tiff in a `uint16_t*`, which I pass directly to libjxl using ` JxlPixelFormat pixel_format = {image.getChannels(), JXL_TYPE_UINT16, JXL_NATIVE_ENDIAN, 0};` Here are my settings: ``` basic_info.uses_original_profile = JXL_TRUE; basic_info.exponent_bits_per_sample = 0; color_encoding.color_space = JXL_COLOR_SPACE_RGB; color_encoding.white_point = JXL_WHITE_POINT_E; color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR; color_encoding.primaries = JXL_PRIMARIES_SRGB; color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL; ``` When passing the same buffer to PNG, no artifact, so there is something in my settings I suppose. Where does this artifact come from, and what kind of settings are likely to modify it? I have tried changing the distance and the effort with no luck.
2025-03-25 04:33:13
lossy image formats try to approximate low "perceptual" distortion but this TIFF is raw sensor data with no hints about how to interpret the brightness values, so there's no way of knowing what type of distortion is "perceptible" or not. I see you added some color encoding info with the libjxl API, but... there's still no way of telling how bright the pixels are, and therefore how much distortion is acceptable or not. I think that weird square you're getting might be a separate bug though.
2025-03-25 05:09:41
Also, lossless is about 1/3rd of the size of the TIFF and it's extremely fast on effort 1 and 2. Higher effort doesn't compress the file any smaller.
Lilli
spider-mario also, which decoder produced the result with the green square?
2025-03-25 08:12:07
Thank you all for your excellent responses! I chose E because my images are mostly black and I thought this would better suit my data, but I will try with D65. I used multiple decoders: decoded the jxl directly in gimp, also tried decoding it through my own means with libjxl and converted to TIFF.
`basic_info.uses_original_profile` should be false for lossy, as XYB is used internally instead of RGB
2025-03-25 08:12:58
I did not understand this, thank you !
2025-03-25 08:22:31
Okay, so I tested with `basic_info.uses_original_profile = JXL_FALSE;` And the squares vanished! I did not try to put the white point to D65 as it seems the issue is now resolved. If this is actually a bug with `uses_original_profile`, I can provide data and code to reproduce it. I will further experiment, but everything seems ok now
Demiurge lossy image formats try to approximate low "perceptual" distortion but this TIFF is raw sensor data with no hints about how to interpret the brightness values, so there's no way of knowing what type of distortion is "perceptible" or not. I see you added some color encoding info with the libjxl API, but... there's still no way of telling how bright the pixels are, and therefore how much distortion is acceptable or not. I think that weird square you're getting might be a separate bug though.
2025-03-25 08:25:30
I see. Is there a way to give better hints to the encoder ?
_wb_
2025-03-25 08:51:06
The most important thing is to get the encoder to see the image in the same way as a human. If you decode to png, it should be a png you can view in a browser or other standard png viewer without adjustments. One of the causes of bad lossy compression is doing things like storing 12-bit RGB values in a 16-bit PNG with the padding in the most significant bits instead of in the least significant bits, which means you're effectively making the image near-black and the encoder will compress it too aggressively.
2025-03-25 08:54:49
at d=1 the image should be more or less visually lossless when viewed without zooming, if that's not the case then something is not right
Lilli
2025-03-25 08:57:51
I see, this seems close to what I am doing, but I do need the large dynamic. Stars are bright, nebulaes aren't ! This is an intermediate format for transfer over wifi (because fast compression, fast decompression and good image quality) The image is supposed to be processed after, on a more powerful device, so we need as little artifacts as possible but still in linear. It's not an image meant to be viewed right away, it doesn't have any gamma applied to it yet!
_wb_
2025-03-25 09:02:21
I understand, but if in the postprocessing you're going to increase brightness a lot in the darker regions, then probably it's a good idea to give the encoder an image that is closer to the brightest it may get after processing rather than letting it think it's very dark
Lilli
2025-03-25 09:11:05
So I would get better compression performance if I pre-gamma it as I understand. That could be done, performing a simple gamma is relatively reversible.
2025-03-25 09:12:17
This is the input
2025-03-25 09:12:43
and this is the processed output
2025-03-25 09:12:49
same region
_wb_ I understand, but if in the postprocessing you're going to increase brightness a lot in the darker regions, then probably it's a good idea to give the encoder an image that is closer to the brightest it may get after processing rather than letting it think it's very dark
2025-03-25 09:29:59
I will try to spread the dynamic a bit better, and report back with the results if that's of any interest to you 🙂
jonnyawsom3
2025-03-25 09:42:37
Some light related reading <https://github.com/libjxl/libjxl/issues/314#issuecomment-880620569>
Lilli
2025-03-25 09:47:05
Good read thanks !
2025-03-25 03:54:07
I come to report my findings. I applied a moderately complex gamma. A simple exponent wouldn't do, so I had to use a curve (for which I do not have the exact values, I did it on gimp) I then fed this "proper" image into my lib using jxl and noticed a few things: - the "-d" parameter now behaves as expected, a value of about 0.9 - 0.8 yields similar compression rate (x12) to previously 0.065 - the compression is worse in places of high intensity, as in, the center of the galaxy is badly compressed, whereas it was fine on the unprocessed input You can see clearly the blocks in the output that had a gamma on it already. Should the parameter given to `transfer_function` be something else than LINEAR in this case?
2025-03-25 04:04:13
2025-03-25 04:05:43
In summary, in my use case so far, it seems that using the pipeline TIFF->JXL (->gamma for display) is better than TIFF -> gamma -> JXL (-> Ungamma if necessary)
spider-mario
2025-03-25 04:08:54
it might be that using a higher-than-default `intensity_target` is a justifiable alternative in your case
2025-03-25 04:09:41
except with PQ images, `intensity_target=N` means that (1, 1, 1) in the image has a luminance of `N` cd/m²
2025-03-25 04:09:46
the default is 255
Lilli
2025-03-25 04:12:30
Ah! Even for a 16bits image?
spider-mario
2025-03-25 04:44:12
yes, that 255 doesn’t have _that_ much to do with 2⁸ − 1
2025-03-25 04:44:34
it’s numerically equal to it but these days, you can treat that as a coincidence
2025-03-25 04:45:12
maybe there is less of a temptation to relate it to that if it’s written as 255.0
Demiurge
2025-03-25 09:17:14
You sure you can't just compress the 300M tiff to a 117M lossless jxl?
2025-03-25 09:17:37
It's even faster than lossy compression.
spider-mario maybe there is less of a temptation to relate it to that if it’s written as 255.0
2025-03-25 09:19:46
Maybe just change the default to 300? Then there's even less confusion. And I agree it's very common for people to be confused about the uses_original_profile thing, something should be done to prevent it from being misused by developers.
2025-03-25 09:20:12
Making the libjxl api less confusing to use is very important
2025-03-25 09:20:31
And reducing the chances of mistakes.
CrushedAsian255
Demiurge Maybe just change the default to 300? Then there's even less confusion. And I agree it's very common for people to be confused about the uses_original_profile thing, something should be done to prevent it from being misused by developers.
2025-03-25 09:34:47
Intensity target is in nits correct?
_wb_
2025-03-25 09:58:40
Yes
2025-03-25 09:59:26
And the default value is in the spec, you can of course signal any value but the value you get by not signaling anything is 255
Demiurge
2025-03-25 10:25:40
I need to read the spec more thoroughly some day...
Quackdoc
2025-03-25 10:29:09
need tirr's jxl document <:PepeHands:808829977608323112>
damian101
Quackdoc rgb compression is probably a lot better then xyb if you want a fast decode option
2025-03-26 01:14:20
no, yuv rgb conversion is very fast
username I presume RGB lossy could be made to not be *horrible* as I assume it's just the encoder not being tuned/setup for it at all really, though it doesn't make much sense to put effort towards fixing it up currently since if you are doing lossy then XYB is the way to go for most use cases. But yeah with how many people keep mistakenly using RGB lossy it should probably be pointed out in the docs.
2025-03-26 01:15:06
no, not really, isolating luminance into its own channel is crucial
Quackdoc
no, yuv rgb conversion is very fast
2025-03-26 01:15:41
A) it still takes time, and when you want the lowest possible decode speed, its a waste of time B) xyb to rgb iirc it a bit more involved then yuv to rgb
2025-03-26 01:15:53
or maybe im thinking of xyb to yuv
2025-03-26 01:16:00
in any case, its extra operations
damian101
Quackdoc A) it still takes time, and when you want the lowest possible decode speed, its a waste of time B) xyb to rgb iirc it a bit more involved then yuv to rgb
2025-03-26 01:16:44
It's never a waste of time, because encoding in YUV is way more efficient, and chroma subsampling gives a performance boost way beyond the minuscule speed boost of skipping YUV<->RGB conversion
Quackdoc
2025-03-26 01:17:00
wrong
2025-03-26 01:17:08
if I need faster decode, i need faster decode
A homosapien
2025-03-26 01:17:12
If it's good enough for jpegli, it's good enough for us
damian101
Quackdoc A) it still takes time, and when you want the lowest possible decode speed, its a waste of time B) xyb to rgb iirc it a bit more involved then yuv to rgb
2025-03-26 01:17:18
you were talking about XYB to RGB?
Quackdoc
2025-03-26 01:17:26
yes
damian101
2025-03-26 01:17:33
well that changes things
2025-03-26 01:17:35
but still
Quackdoc
2025-03-26 01:17:40
even then yuv to rgb still takes time
damian101
2025-03-26 01:17:54
directly encoding in RGB is awfully inefficient
Quackdoc
2025-03-26 01:18:16
sure
2025-03-26 01:18:22
no one said differently
A homosapien
2025-03-26 01:20:21
For the fastest possible decode speed just use ppm
Quackdoc
2025-03-26 01:24:14
some comression still needed https://cdn.discordapp.com/emojis/867794291652558888?size=64
A homosapien
2025-03-26 01:26:18
But then it's not the fastest decode speed possible as you said
2025-03-26 01:26:27
Come on. You're basically asking for magic
jonnyawsom3
2025-03-26 02:29:05
PPM with filesystem compression
Demiurge
Quackdoc if I need faster decode, i need faster decode
2025-03-26 04:23:19
If you use libjpeg-turbo you don't need to ever worry about decode speed. And chroma subsampling is a significant computational reduction too, meaning speed boost. You would probably gain much more speed than the negligible, absolutely marginal amount of time yuv conversion costs...
Quackdoc even then yuv to rgb still takes time
2025-03-26 04:25:03
Unless you actually measured the difference in time it actually takes, saying this does not matter at all. Premature ejaculation is the root of all evil.
Quackdoc
2025-03-26 04:33:47
chroma subsampling is actually not super cheap if you want a high quality upsample
jonnyawsom3
Quackdoc if I need faster decode, i need faster decode
2025-03-26 04:37:48
You already said you don't care about encoding efficency, so low quality upsampling shouldn't matter
Quackdoc
2025-03-26 04:38:11
it matters a shit ton in an NLE,
Lilli
spider-mario except with PQ images, `intensity_target=N` means that (1, 1, 1) in the image has a luminance of `N` cd/m²
2025-03-26 09:43:19
I increased it to a 1000, now I need to have d=3 to get x12 compression. The image is still worse than the input without an applied gamma in critical parts. I will keep the original image mostly black as it's still the easiest for me (no gamma - ungamma procedure) and no parameter setup. I might tweak the intensity to bias it toward compressing the noise but apart from that, I'm satisfied of the lib and happy with the results 🙂 Thank you all for your support and insights!
jonnyawsom3
Lilli I increased it to a 1000, now I need to have d=3 to get x12 compression. The image is still worse than the input without an applied gamma in critical parts. I will keep the original image mostly black as it's still the easiest for me (no gamma - ungamma procedure) and no parameter setup. I might tweak the intensity to bias it toward compressing the noise but apart from that, I'm satisfied of the lib and happy with the results 🙂 Thank you all for your support and insights!
2025-03-26 09:50:48
If I understand correctly, I'd actually suggest trying an intensity target of 10,000 on the original very dark image. That should give all areas roughly equal quality regardless of brightness. In the end, what works, works. So feel free to stick with your own solution. Could definitely do with a 'Range extender' option that doesn't crush blacks and over smoothen whites
Lilli
2025-03-26 09:59:33
I see ! Okay I will try that 🙂 Thank you Maybe this use case is a bit too specific, it's hard to ask for a format to be that versatile.
jonnyawsom3
2025-03-26 10:08:33
As you've proved by lowering the distance to 0.1 and 0.001, it is possible, just not very easily currently. We've had a few people doing satellite/multispectral imagery, along with people using it for photography where they want to change exposure after. There's even multiple issues on Github about this exact problem, but no easy way to fix it other than changing the input, using the intensity target workaround or lowering the distance drastically as you did
spider-mario
Lilli I increased it to a 1000, now I need to have d=3 to get x12 compression. The image is still worse than the input without an applied gamma in critical parts. I will keep the original image mostly black as it's still the easiest for me (no gamma - ungamma procedure) and no parameter setup. I might tweak the intensity to bias it toward compressing the noise but apart from that, I'm satisfied of the lib and happy with the results 🙂 Thank you all for your support and insights!
2025-03-26 10:24:17
oh, right, I meant without the gamma
2025-03-26 10:24:30
just the same as you were doing before but with the higher intensity_target on top
Demiurge
2025-03-26 10:43:56
Hell yeah. +1 for "range extender mode" for raw sensor data!
Lilli
spider-mario just the same as you were doing before but with the higher intensity_target on top
2025-03-26 11:54:52
I see! yes then what you suggested did what it was supposed to do. In the end what I'm doing is trying different combinations of distance and intensity target to keep the compression ratio the same, and checking which one suits the requirements best (more compression on noise, less on signal) Thank you for the pointer in the right direction
CrushedAsian255
Lilli I see ! Okay I will try that 🙂 Thank you Maybe this use case is a bit too specific, it's hard to ask for a format to be that versatile.
2025-03-26 12:40:26
> it's hard to ask for a format to be that versatile Well, given enough tuning (or a custom encoder), JXL can be that format!
Lilli
2025-03-27 12:20:43
I'm noticing that depending on the images, I'm not getting the same compression (which makes sense). I know there is a way to specify a target compression with cjxl, but how to do it from libjxl?
spider-mario
2025-03-27 12:30:50
which `cjxl` option are you thinking of? I don’t believe we still have it nowadays
jonnyawsom3
2025-03-27 12:43:06
target size is in benchmark_xl only
Lilli
2025-03-27 12:47:43
I see :/
2025-03-27 12:51:06
Is there a way to achieve that anyway?
jonnyawsom3
2025-03-27 12:55:06
IIRC it was just a trial and error binary search for the nearest match to a certain threshold
spider-mario
2025-03-27 01:08:55
indeed
Lilli
2025-03-27 02:16:54
I see, so it compresses the whole thing several times? Argh. JPEG2000's quality parameter was quite consistent in giving the same compression ratio on all my images. Sounds like I should just hope for the best then :/
HCrikki
2025-03-27 02:24:32
it was never consistent or made sense for other formats, just a best effort that worked because it tried quality % until it approached the kilobyte number/ratio you asked for
2025-03-27 02:25:18
jxl should do these approximations a lot quicker and since it identifies the most detailled parts of images can tune compression more agressively for those regions (without redoing compression for the rest or the full image - or the opposite, if youd prefer preserving those details)
jonnyawsom3
2025-03-27 02:26:42
The other, much more jank way, would be progressive encoding and then truncating the file. But only jxl-oxide can decode that currently. That's essentially what JPEG2000 did. There's no compression ratio target since images can have vastly different requirements, and we don't want people shooting themselves in the foot when a simple image is wasting a lot of data and complex images look unrecognisable
2025-03-27 02:27:57
You could try `--disable_perceptual_optimizations` which might also remove the need for `--intensity_target` but memory usage and encode speed will suffer drastically
Lilli
2025-03-27 03:18:44
Oh! interesting... Speed and memory usage are both very important because it's time sensitive and on embedded device... So I'll just hope for the best 🙂
_wb_
2025-03-27 03:27:10
Keeping compression ratio constant means the same thing as having wildly inconsistent quality. There are few use cases where that is useful, notable exception being cases where there is a hard filesize limit imposed and you want to fully use it even if it is not needed.
jonnyawsom3
Lilli Oh! interesting... Speed and memory usage are both very important because it's time sensitive and on embedded device... So I'll just hope for the best 🙂
2025-03-27 03:34:35
A bit of a far fetch, but I don't suppose you're trying to do JXL encoding on a satellite before downlinking?
Lilli
2025-03-27 03:47:05
No, it's astrophoto sensor data
2025-03-27 03:48:44
but onboard memory and processing capabilities are busy doing something else so these images should have as little impact as possible.
_wb_ Keeping compression ratio constant means the same thing as having wildly inconsistent quality. There are few use cases where that is useful, notable exception being cases where there is a hard filesize limit imposed and you want to fully use it even if it is not needed.
2025-03-27 04:01:23
Yes the filesize isn't a strictly speaking hard requirement, but transfer time should be low for transfer stability reasons. Having a bit of a swing is expected, but basically what I'd like is "at least this much compression rate", then I have to roll with the quality that goes with it. In all likelihood, It'll be easiest to compress images with less noise, and what I want to compress is precisely the bottom of the histogram, the noise. In any case, if we find that we need a bit more quality, then we'll increase the quality.
Demiurge
Lilli Yes the filesize isn't a strictly speaking hard requirement, but transfer time should be low for transfer stability reasons. Having a bit of a swing is expected, but basically what I'd like is "at least this much compression rate", then I have to roll with the quality that goes with it. In all likelihood, It'll be easiest to compress images with less noise, and what I want to compress is precisely the bottom of the histogram, the noise. In any case, if we find that we need a bit more quality, then we'll increase the quality.
2025-03-27 08:54:53
It sounds like a custom encoder could be more than 100 times more effective in speed/quality for what you want, rather than stock standard libjxl
2025-03-27 09:01:13
libjxl doesn't have the capability to target a specific average bitrate, and I don't think the quantizer is designed to compress a really, really dark image with a faint signal peeking through the noise
2025-03-27 09:05:19
The libjxl devs also said they are afraid of making drastic changes to the lossy encoder cuz they're afraid of regressions and the lack of alternative encoders if they mess things up. Which is very poor reasoning in my opinion but whatevers.
Lilli
2025-03-28 08:51:02
I see :/ For sure a custom encoder would be miles better, but I don't have the knowledge nor the time. The devs could always hide the implementation behind an opt-in flag, so that doesn't seem very reasonable to say they're afraid of regressions ._.
jonnyawsom3
2025-03-28 09:25:17
If transfer stability is an issue, this may be of interest too https://github.com/libjxl/libjxl/pull/4062
2025-03-28 09:28:09
Another idea... How much of the 16bit range is actually used? Maybe you could bitshift the image to raise the levels, similar to the [prior example](<https://github.com/libjxl/libjxl/issues/314#issuecomment-880620569>) where a PNG was incorrectly padded
Lilli
2025-03-28 09:49:57
The entire range except for the first ~200 values (dark offset of the sensor)
2025-03-28 09:50:55
Interesting, this streaming option. I think it's gonna be just a file transfert though, and not stream-decoded, but having the option is great for a later iteration
Lilli The entire range except for the first ~200 values (dark offset of the sensor)
2025-03-28 09:51:36
It's just the middle part of the image is quite empty as nebulas are faint but stars are bright :/
2025-03-28 09:51:52
(middle as in "histogram space", i.e. the middle values)
2025-03-28 10:56:20
By the way, is there a standardized way to compress an image tile by tile? I couldn't really find information about that, except adding several frames.
_wb_
2025-03-28 12:01:42
There's a chunked encode api
Lilli
2025-03-28 12:37:37
I assume it is:`JxlChunkedFrameInputSource` I thought it was for streaming but maybe that's the same thing. Is this transparent for the decoder?
Demiurge
2025-03-28 02:24:51
You still get just a normal jxl file in the end
Melirius
2025-03-29 08:58:37
Interesting, this new balancing histogram approach improves compression of large JPEG files up to 5 %. And it restores normal size sequence from effort 7 to 9: files become smaller, instead of paradoxical growth before between 7 and 8.
2025-03-29 09:01:34
I think it is the result of getting closer to theoretically best histogram on nearly flat histos..
_wb_
2025-03-29 09:03:33
Nice!
2025-03-29 09:04:51
5% is a huge amount, are you sure?
Melirius
2025-03-29 09:05:09
Yep, I was surprised as well
2025-03-29 09:05:36
2025-03-29 09:05:49
Default effort
2025-03-29 09:06:42
Left is before - right after
2025-03-29 09:06:55
Note 70MPix result
2025-03-29 09:10:27
There is a jump from slight improvement of 2-3*10^-3 % to percents between 52 and 70 MPix size
jonnyawsom3
2025-03-29 09:11:07
Now if only I had enough RAM for 70MP Transcoding xD
Melirius
2025-03-29 09:13:43
The problem to get the best histo is interesting, but I suspect is NP-complete
lonjil
2025-03-29 09:14:53
CrushedAsian255
Melirius The problem to get the best histo is interesting, but I suspect is NP-complete
2025-03-29 09:15:55
What is the “best” histogram even mean
Melirius
CrushedAsian255 What is the “best” histogram even mean
2025-03-29 09:19:09
For the most compact encoding we need to prescribe frequencies of tokens is exact correspondence with their real frequency, but it is not possible with finite accuracy (1/4096 currently). So we need to choose a histogram "quite close" to the theoretically best with given accuracy of each bin. This is achieved by maximizing entropy of the histogram
CrushedAsian255
Melirius For the most compact encoding we need to prescribe frequencies of tokens is exact correspondence with their real frequency, but it is not possible with finite accuracy (1/4096 currently). So we need to choose a histogram "quite close" to the theoretically best with given accuracy of each bin. This is achieved by maximizing entropy of the histogram
2025-03-29 09:20:09
So basically optimising for the quantisation of the histogram?
Melirius
CrushedAsian255 So basically optimising for the quantisation of the histogram?
2025-03-29 09:20:57
Yes, taking into account balancing histogram technique from the standard
jonnyawsom3
2025-03-29 09:21:07
Is there any speed or memory penalty to this?
Melirius
Is there any speed or memory penalty to this?
2025-03-29 09:21:48
Very small, usually only several improvement steps are taken
jonnyawsom3
2025-03-29 09:22:18
Nice, worth checking
CrushedAsian255
2025-03-29 09:22:26
Why can’t the histogram just increased the accuracy
Tirr
2025-03-29 09:23:34
12-bit precision is from the spec
Melirius
CrushedAsian255 Why can’t the histogram just increased the accuracy
2025-03-29 09:23:38
It is possible by the standard (up to 16 bit, IFAICR) - but then you loose space on encoding the histograms themselves
Tirr
2025-03-29 09:24:08
ah 12-bit is for ANS, I think it's something like 15-bit for prefix code?
2025-03-29 09:24:19
not very sure though
_wb_
2025-03-29 09:24:28
I will have to revisit https://github.com/libjxl/libjxl/pull/4130 after the better histogram balancing
CrushedAsian255
2025-03-29 09:24:31
Ah so the histogram also is signalled. Is it one histogram column for each hybrid uint configuration?
Tirr
2025-03-29 09:25:09
distributions are clustered, then there's one histogram and hybrid uint config for each cluster
2025-03-29 09:26:02
where there are at most 256 clusters
CrushedAsian255
2025-03-29 09:26:33
I mean for inside the histogram the histogram is predicting of the each type of uint?
_wb_
2025-03-29 09:27:11
ANS histogram precision is 12-bit. Histogram signaling itself is a bit complicated.
Melirius
2025-03-29 09:27:48
What is puzzling me - that threshold of improvement, I suspect 64MiPix, but had not investigated yet
CrushedAsian255
2025-03-29 09:28:06
As in like does the histogram store the probables of each hybrid uint type?
Melirius
CrushedAsian255 As in like does the histogram store the probables of each hybrid uint type?
2025-03-29 09:29:06
No, first best uint and histo accuracy is chosen, and then stored
CrushedAsian255
2025-03-29 09:29:19
what?
2025-03-29 09:29:43
isn’t the uint a type of value in the clustering of the accurate histogram, and then it quantises the bit depth?
2025-03-29 09:29:53
Then clustering?
Melirius
CrushedAsian255 what?
2025-03-29 09:30:08
Depending on effort, different uints and accuracies are probed
CrushedAsian255
2025-03-29 09:30:42
Like each cluster of 256 clusters gets its own histogram
2025-03-29 09:31:17
I don’t mean the encoding I am thinking of how this theory work of the how the theoretically stored and how the abstract data types
2025-03-29 09:31:28
Like I am trying to understand the fundamental theorem here
2025-03-29 09:31:45
Does the histogram store the probabilities of each hybrid uint?
2025-03-29 09:32:08
Like hybrid uint config 1 is 53/4096 and config 2 is 124/4096
Melirius
CrushedAsian255 Like I am trying to understand the fundamental theorem here
2025-03-29 09:33:56
As far as I got it, each hybrid uint provides different separation between literal bits and remainder bits of each value to be encoded. Then literals should be compressed the best possible way - that is where histogram of literals distribution comes into play
2025-03-29 09:34:37
Each uint produces different histogram and different number of remainder bits
jonnyawsom3
2025-03-29 09:35:01
On the topic of histograms, is there a reason LZ77 gets disabled in certain cases? I'm trying to debug why cjxl is outputting completely uncompressed files with certain parameters, and keep finding switches that set LZ77 to kNone I'd have expected that worst case it's a en/decode speed hit
_wb_
2025-03-29 09:42:01
The hybriduint config is per (clustered) context. The config defines how a symbol is split into a token and raw bits. E.g. in a typical config, symbol values 0..15 get their own tokens (with no raw bits) while for higher symbol values, the token represents only the exponent and some of the most significant bits, while the least significant bits are raw bits. This allows not needing a huge number of tokens even if the range of the symbols is large, while still getting most of the benefit of entropy coding — the raw bits are not entropy coded but since they are least significant bits that are usually pretty noisy, entropy coding wouldn't help anyway. The histograms represent the probability of each token value.
2025-03-29 09:46:36
LZ77 is pretty much orthogonal to all of this: if enabled, a range of tokens is added to represent lz77 (distance,length) copy instructions. The main reason why libjxl doesn't use it much is because it generally doesn't help much for the type of data we are encoding (DCT coefficients, or in case of lossless, prediction residuals with variable predictors), while it is expensive on the encoder side to do the search.
jonnyawsom3
On the topic of histograms, is there a reason LZ77 gets disabled in certain cases? I'm trying to debug why cjxl is outputting completely uncompressed files with certain parameters, and keep finding switches that set LZ77 to kNone I'd have expected that worst case it's a en/decode speed hit
2025-03-29 09:52:41
The reason I ask is lossless faster_decoding 3 and 4 are meant to use LZ77 instead of an MA tree, but 3 and 4 disable LZ77...
2025-03-29 09:55:00
Not to mention the switches seem flipped in cjxl but I'll dig into that later. I'm slowly working on a PR to fix both faster_decoding and progressive lossless. Thanks for the insight around it, I suppose using a fixed predictor makes it slightly more worthwhile
CrushedAsian255
_wb_ The hybriduint config is per (clustered) context. The config defines how a symbol is split into a token and raw bits. E.g. in a typical config, symbol values 0..15 get their own tokens (with no raw bits) while for higher symbol values, the token represents only the exponent and some of the most significant bits, while the least significant bits are raw bits. This allows not needing a huge number of tokens even if the range of the symbols is large, while still getting most of the benefit of entropy coding — the raw bits are not entropy coded but since they are least significant bits that are usually pretty noisy, entropy coding wouldn't help anyway. The histograms represent the probability of each token value.
2025-03-29 10:10:57
Oh I get it, I thought a hybrid uint config was a singular set of (exponent,implied bits), what you are called a token. So now it makes sense the histogram is storing the probability of each token?
2025-03-29 10:11:15
And the hybrid config is stored along side
_wb_
2025-03-29 10:11:52
yes, every context has a hybriduint config plus a histogram for the tokens
CrushedAsian255
2025-03-29 10:13:16
So the first 2^split tokens are literals and then from there the tokens store (exponent-split, msb’s / lsb’s) ?
_wb_
2025-03-29 10:14:22
yep
CrushedAsian255
2025-03-29 10:14:42
ah that makes so much sense now! tanks
Traneptora
The reason I ask is lossless faster_decoding 3 and 4 are meant to use LZ77 instead of an MA tree, but 3 and 4 disable LZ77...
2025-03-29 06:44:58
RLE is still just lz77, it just changes the search algorithm to essentially only look back one symbol
2025-03-29 06:45:30
it keeps reading until it sees a different symbol and then it dumps a L/D pair
CrushedAsian255 So the first 2^split tokens are literals and then from there the tokens store (exponent-split, msb’s / lsb’s) ?
2025-03-29 06:46:47
yup, smaller tokens are literals, otherwise what it does it the token splits into a higher order half and a lower order half
2025-03-29 06:46:50
with RAW bits in the middle
2025-03-29 06:47:15
how many of those most-significant-bits and least-significant-bits are in the token itself and not residue is determined by the hybrid-uint-config
2025-03-29 06:49:18
for example, 4-0-0 means 0-15 are literals, and 16+ are hybrids. 16+ have 16 subtracted
2025-03-29 06:49:55
`lsb_in_token = 0` means that 0 bits of the lower-order of the final result, are actually part of the token
2025-03-29 06:50:23
`msb_in_token = 0` means that 0 bits of the higher-order of the final result are actually part of the token
2025-03-29 06:50:30
both 0 means the entire symbol is residue
2025-03-29 06:51:42
in this case, the value encoded by the token tells us how many bits of residue to read
2025-03-29 06:52:27
or rather, that value - 16
2025-03-29 06:52:28
but yea
2025-03-29 06:52:42
so for a 4-0-0 config there's a fasttrack
2025-03-29 06:52:53
`token < 16 ? token : readBits(token - 16)`
jonnyawsom3
Traneptora RLE is still just lz77, it just changes the search algorithm to essentially only look back one symbol
2025-03-29 06:53:00
Yeah, but look at the last line of the second image. If speed tier is >=3, and modular mode is used, it's set to kNone instead of kRLE or kLZ77
Traneptora
2025-03-29 06:53:37
I think that may be intentional since RLE helps very little when you have modular predictions
2025-03-29 06:54:03
unless the MA tree is perfect, it makes more sense to disable lz77 than to try to go for rle
2025-03-29 06:54:07
overhead not worth it
jonnyawsom3
2025-03-29 06:56:09
In the first image, speed tier >=3 disables MA trees and predictors too
2025-03-29 06:57:04
So it causes a completely uncompressed JXL even larger than the image in memory
Traneptora
2025-03-29 07:00:32
it also disdables MA trees and predictors?
2025-03-29 07:00:42
well disable MA tree I assume means use a dummy one
2025-03-29 07:00:44
you can't disble both at once
2025-03-29 07:01:16
I wonder if we just hardcode gradiant predictor and call it a day if that's easier
2025-03-29 07:01:33
or at least, west only
username
2025-03-29 07:01:42
isn't there a "none" predictor?
Traneptora
2025-03-29 07:01:47
zero, but yea
2025-03-29 07:01:54
I figure disabling it means always predicting zero
2025-03-29 07:02:03
which would allow you to just encode the actual value as a residue
2025-03-29 07:02:21
that said I don't see much harm in using west-only for higher modes
2025-03-29 07:02:29
doesn't require fast memory access
2025-03-29 07:02:40
simdable across lines
2025-03-29 07:02:40
etc.
jonnyawsom3
2025-03-29 10:43:16
Any of the first 5 predictors are fast, since they're standalone while the others are combinations of directions, using 6 directions for AvgAll. Fixed MA trees are also fast. Effort 2 decode is almost as fast as faster decoding 4 already. Lots of room for improvement, so gonna run some tests and see what works. At the very least, remove that if statement so it stops outputting uncompressed files
CrushedAsian255
So it causes a completely uncompressed JXL even larger than the image in memory
2025-03-29 10:47:53
Does it also disable entropy encoding?
jonnyawsom3
CrushedAsian255 Does it also disable entropy encoding?
2025-03-29 10:55:44
Seems like it
Melirius
_wb_ I will have to revisit https://github.com/libjxl/libjxl/pull/4130 after the better histogram balancing
2025-03-30 09:51:26
Heh, that size improvement is not my histogram - it is your extending counters in `ComputeCoeffOrder`
_wb_
2025-03-30 09:55:43
Oh, right, I suppose those counters would overflow for large jpegs
Melirius
_wb_ Oh, right, I suppose those counters would overflow for large jpegs
2025-03-30 10:13:25
Yeah, I applied only extension (even without new buckets structure) and it gives this size drop
_wb_ Oh, right, I suppose those counters would overflow for large jpegs
2025-03-30 10:17:51
BTW, do you think it will worth it to apply more sophisticated algo to find best coefficient order? Now it is simple zero counts histogram, and it can be prone to coefficients correlations
_wb_
2025-03-30 10:24:47
Maybe?
Melirius
2025-03-30 10:27:16
That is a variant of patching consecutive ones property problem, that is unfortunately again NP-complete, but some heuristics can be applied
Demiurge
2025-03-31 01:23:24
2025-03-31 01:23:27
webp definitely retains a lot more detail here. I know libjxl is not meant to be optimized for this low quality setting, but maybe it's indicative of a more underlying problem with the encoder's visual decisions?
jonnyawsom3
2025-03-31 07:53:56
I've got some theorys on it, for now my resampling PR seems to compensate for it, but v0.8 also improves with the resampling for much higher quality results. So something can be changed
Demiurge
2025-03-31 08:22:51
I think there are more fundamental problems with the encoder that affect both the low-quality and high-quality range.
2025-03-31 08:28:30
Only a few very specific issues are specific to a certain quality range. Most changes have a similar effect on all quality levels.
2025-03-31 08:44:39
There are lots of possible techniques and optimizations that are much more useful at low bitrates and completely useless at higher bitrates, but libjxl does not do anything like that since it's optimized for high bitrates, so that's not relevant here. Even without any optimizations for low bitrates, libjxl is performing a lot worse than it should there.
2025-03-31 08:46:34
The most important issue is in the amount of psychovisual info preserved in the DCT transform itself
2025-03-31 08:46:49
Always will be, always has been
Lilli
2025-03-31 03:21:33
I could not make the chunked API work. Is there an example somewhere, where it is used? I could not find one after looking for quite a while. :/ I set up `JxlChunkedFrameInputSource` with callbacks, which I then feed to `JxlEncoderAddChunkedFrame(frame_settings, true, chunked)` This essentially replaces the call to `JxlEncoderAddImageFrame(frame_settings, &pixel_format, image_data, image_data_size)`
2025-03-31 03:22:14
for reference here are my callbacks: ```c++ struct ChunkingState { const uint8_t* image; uint32_t width; uint32_t offset; // Current offset in the image data uint32_t channels; }; void get_color_channels_pixel_format(void* opaque, JxlPixelFormat* pixel_format) { const auto* state = static_cast<ChunkingState*>(opaque); pixel_format->num_channels = state->channels; pixel_format->data_type = JXL_TYPE_UINT16; pixel_format->endianness = JXL_NATIVE_ENDIAN; pixel_format->align = 0; } const void* get_color_channel_data_at(void* opaque, size_t xpos, size_t ypos, size_t xsize, size_t ysize, size_t* row_offset) { const auto* state = static_cast<ChunkingState*>(opaque); const uint8_t* image = state->image; // Calculate the starting position in the image data size_t start = ypos * state->width * state->channels + xpos * state->channels; *row_offset = state->width * state->channels; // Return a pointer to the requested data return image + start; } void release_buffer(void* opaque, const void* buf) { } ```
jonnyawsom3
Traneptora I think that may be intentional since RLE helps very little when you have modular predictions
2025-03-31 06:10:17
Well, with <@207980494892040194>'s help, enabling LZ77 fixes both faster decoding and progressive lossless (Somewhat)
2025-03-31 06:11:27
Need to do some more tweaking, but promising for just commenting out 3 lines
2025-03-31 06:23:21
Worst case, it is faster *de*coding, so slower encode should be a decent trade-off. We'll see what we can figure out though
2025-03-31 06:29:04
I did want to check, <@794205442175402004> I noticed libjxl has a function called `MakeFixedTree`. I always thought the fixed tree was an effort 1 and 2 specific thing, with the decoder using a lookup table to skip most of the MA traversal. Are fixed trees a more universal concept, signalled globally and traversed once then re-used or something similar? Wondering if using the fixed tree for faster decoding is a good idea, or if it doesn't apply to other decoders and could be broken (Much slower) if the tree is changed in future inside libjxl
2025-03-31 06:29:32
Hopefully that makes sense
_wb_
2025-03-31 07:00:07
Effort 1 uses no tree at all (fixed tree with single node, I guess), MakeFixedTree is used for effort 2 and 3. But it is also used for some of the modular-coded stuff in VarDCT, including LF, iirc.
Tirr
2025-03-31 07:13:49
some MA trees have structure that can be fused into single lookup table to speed up tree traversal (which jxl-oxide also does), I think that's what MakeFixedTree is for
_wb_
2025-03-31 07:16:09
These fixed trees do have that property, since they're using a single split property
2025-03-31 07:18:25
But the tree traversal speedup also works for non-fixed trees iirc, if it locally has a subtree that uses a single decision property
jonnyawsom3
2025-03-31 07:19:14
Okay, so it is a specialised speedup rather than a general rule, good to know
2025-03-31 07:22:27
We'll probably have to test the faster decoding parameters on djxl, Oxide and Latte to make sure it's not an isolated improvement. Don't want to inadvertently create files that are *only* faster in libjxl
Tirr
2025-03-31 07:26:09
"textbook" MA tree traversal can be very very slow since it's super branchy and (maybe) cache unfriendly. I'd expect decoders that are advanced enough to implement at least some of the optimization techniques like lookup table thing
_wb_
2025-03-31 07:33:05
I think it's indeed reasonable to assume that decoders will implement fast paths. There is still room to do more btw. I am dreaming of some kind of JIT compilation of MA trees. It's just that those are dreams that are nightmares for people worrying about security surface, understandably.
2025-03-31 07:35:05
(I think it can be done without serious security issues, termination can be trivially assured so it is very different from JIT compilation of some Turing complete language, but I can still understand some nervousness)
Demiurge
2025-04-01 05:10:45
An image codec with self modifying code...
2025-04-01 05:16:34
I think it's inherently unsafe for executable memory to be modified based on untrusted input. Web browsers get away with it by using a separate process with the OS kernel enforcing restrictions on what that process is allowed to access.
2025-04-01 05:18:42
There's no cross platform way to do that
2025-04-01 05:19:12
Which is a shame because it really should be the norm
2025-04-01 05:28:38
Plan9 has process namespaces where each process has a restricted view of the system. And instead of linking binary code into a single process, multiple processes can communicate using the same read and write commands as everything else, if you want shared libraries that are isolated in their own restricted process. But other operating systems do not make it easy to write secure code. Therefore software is always going to be insecure by design until it's no longer difficult to write secure code. Nobody except web browsers want to write platform specific code for each platform they want to support having a sandboxed JIT
_wb_
2025-04-01 05:43:40
The 'programming language' would be just MA trees, so no loops, no writes, no reads besides to well defined previously decoded pixels, etc. Something very different from something like JavaScript.
Tirr
2025-04-01 05:58:01
Demiurge
2025-04-01 05:59:53
But it's pretty inherently risky to give untrusted input the ability to modify executable memory. Also, to be honest I'm not sure how a JIT compiler can be made to run on a system with W^X enforcement
2025-04-01 06:00:14
That's a gap in my knowledge
_wb_ The 'programming language' would be just MA trees, so no loops, no writes, no reads besides to well defined previously decoded pixels, etc. Something very different from something like JavaScript.
2025-04-01 06:05:41
With a little cleverness you can probably make a very basic JIT with guaranteed safety, but the main security risk is the lack of separation of process and privilege namespaces inherent to modern programming.
2025-04-01 06:09:08
None of this would be an issue if it was the norm for libraries to run in separate processes with minimum privileges instead of linking code into one process and running all processes with the same privileges as the "user"
2025-04-01 06:09:24
It's a flaw in modern OS design
2025-04-01 06:12:51
It's possible to make a library that handles untrusted input by creating a separate process that revokes its own privileges, but it's a real PITA
2025-04-01 06:17:24
It's a shame. I wish security was easy and convenient instead of an afterthought today.
intelfx
Tirr
2025-04-01 02:49:10
Yup, this! Did someone say WASM? :D
2025-04-01 02:49:24
Sounds like a no-brainer for any kind of untrusted code thing
2025-04-01 02:50:27
Also, prior art: harfbuzz supports a font hinting/shaping bytecode which _is_ WASM
2025-04-01 02:50:50
If you want a laugh: https://fuglede.github.io/llama.ttf/
Demiurge
2025-04-01 03:53:27
WASM is way slower and way more complicated than having libraries running in a separate process and opening/reading/writing to file descriptors instead of linking binary code into a single process, and having the OS and kernel isolate processes into their own namespace with limited access to the system.
2025-04-01 03:55:15
Basically if the OS was better designed and provided easy to use APIs for writing multiprocess code that revokes privileges and communicates with other processes through file descriptors, similar to Plan9
2025-04-01 03:56:01
But security is not a priority for OS devs
intelfx
2025-04-01 03:56:03
then everything would be slower due to context switches 🙃
Demiurge
2025-04-01 03:56:34
Not as slow as WASM, no.
2025-04-01 03:57:13
Linux already has namespaces and multiple processes running as different users and in different namespaces and containers and it isn't slow at all. It's fast as native.
2025-04-01 03:57:43
There's nothing slow about this at all. Linux already technically has all of these abilities but they are not made convenient and easy to use.
intelfx
Demiurge Not as slow as WASM, no.
2025-04-01 03:58:47
Sure about that?
Demiurge
2025-04-01 03:58:50
And developers also have this mindset that linking code into a single process is taken for granted as the "normal" assumption of how to do things without questioning if it makes sense or if it's actually good or bad design
intelfx
2025-04-01 03:59:20
Besides, what you are asking for already exists and is called seccomp mode 1
2025-04-01 03:59:35
Plan 9 isn't the be-all and end-all of OS design
Demiurge
intelfx Sure about that?
2025-04-01 03:59:43
Try creating a separate namespace with systemd-nspawn and run some benchmarks inside the container, and see if it's any slower. It shouldn't be.
intelfx
Demiurge Try creating a separate namespace with systemd-nspawn and run some benchmarks inside the container, and see if it's any slower. It shouldn't be.
2025-04-01 04:00:25
You were talking about passing data between processes via file descriptors, not about benchmarks in a namespace.
Demiurge
intelfx Plan 9 isn't the be-all and end-all of OS design
2025-04-01 04:00:37
Of course not, but it's a shame that no one learned anything from the mistakes of the past.
intelfx
2025-04-01 04:00:53
Linux namespaces have pretty much no relation to secure computing-only processes
Demiurge
intelfx You were talking about passing data between processes via file descriptors, not about benchmarks in a namespace.
2025-04-01 04:01:27
Well then the main slowness there would probably be serialization and copying of data, since it would be going from 1 process into 2.
intelfx
2025-04-01 04:01:40
Correct. That's what I'm talking about.
Demiurge
2025-04-01 04:01:50
But that is a necessary payment for the security of process separation
2025-04-01 04:01:59
Well worth the cost
intelfx
2025-04-01 04:02:07
Which can also be achieved without process separation by WASM.
Demiurge
2025-04-01 04:02:22
Suddenly, you don't have to worry about malicious input anymore. That's a ridiculously massive win
2025-04-01 04:02:56
WASM is an even bigger compromise, and it's way more architecturally complicated.
intelfx
2025-04-01 04:03:24
"Architecturally complicated" is subjective, and could you share data on why exactly it is a bigger compromise?
Demiurge
2025-04-01 04:03:26
It's a JIT compiler
intelfx
2025-04-01 04:03:31
Yes, and?
2025-04-01 04:03:53
It is also architecture-independent.
Demiurge
2025-04-01 04:03:55
It has the same overhead and complexity as trying to mix Java and C together
intelfx
Demiurge It has the same overhead and complexity as trying to mix Java and C together
2025-04-01 04:04:01
That's just not true.
Demiurge
2025-04-01 04:04:58
with multiple processes it's all plain old boring ass code
2025-04-01 04:05:01
native code
2025-04-01 04:05:47
with wasm you would have multiple processes anyways. Unless everything is all compiled to WASM together
2025-04-01 04:06:02
You don't gain or save anything
intelfx
2025-04-01 04:06:04
Who said that? That's just blatantly wrong.
Demiurge
2025-04-01 04:06:18
maybe I'm misunderstanding something?