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

jxl

Anything JPEG XL related

Razor54672
2023-03-13 03:56:45
this is the first lossless image I've stumbled upon which doesn't get compressed using lossy methods (probably well encoded to begin with)
_wb_
2023-03-13 03:56:51
lossy compression doesn't work well on hard lines and dots. It's a worst-case behavior for DCT.
Razor54672
2023-03-13 03:56:57
ooh
2023-03-13 03:59:07
AVIF made it 119KiB at q=0 and e=9 so yay?
_wb_
2023-03-13 04:00:05
is that lossy or lossless?
2023-03-13 04:00:53
(is it the lowest quality setting or the highest? I don't know what "q" means here)
Razor54672
2023-03-13 04:01:30
lowest setting; lossy
2023-03-13 04:01:46
major artifacting
afed
2023-03-13 04:03:36
maybe it would be useful to have an extra option something like `patches identifying strength`, for lossy and lossless, because it can make encoding slower or doesn't always look good for progressive lossy
_wb_
2023-03-13 04:14:01
part of what makes the png compress well here is that it's using exactly 16 shades of gray, which means it fits 2 pixels per byte, which is quite effective. We can do something similar in jxl but it's a bit complicated and libjxl currently doesn't do that
jonnyawsom3
2023-03-13 04:15:20
Perhaps something for higher efforts down the road
yoochan
Razor54672 dang
2023-03-13 04:30:06
I'm very interested in the conversion of mangas too ! I'll try on your image ! (when I'm back home)
Traneptora
2023-03-13 05:51:19
I'm struggling to figure out how to set up a Hybrid Integer Config so I could, say, have 8bits of an ANS token and 6bits raw
2023-03-13 05:51:46
for 14-bit uint32_t values
2023-03-13 05:53:45
I figure I want `lsb_in_token` to be zero, but I also want `msb_in_token` to be 8
2023-03-13 05:55:41
but that means I always want `n = 6`, so `((token - split) >> (config.msbInToken + config.lsbInToken))` must always equal 14
2023-03-13 05:56:20
so what I'm gathering is that `config.lsbInToken` tells you how many bits in the token contribute to the lower portion
2023-03-13 05:56:34
`config.msbInToken` tells you how many bits in the token contribute to the upper portion
2023-03-13 05:57:09
and `config.splitExponent` tells you where to split the token into mantissa and exponent
2023-03-13 05:58:43
is this correct?
_wb_
2023-03-13 06:39:58
No, not completely. Splitexponent k means that tokens 0 to 2^k -1 encode themselves as a symbol, without any raw bits. Starting with token 2^k, the token signals the exponent and possibly also some msb and lsb from the mantissa
2023-03-13 06:43:24
So e.g. in the config 4-0-0 (splitexp-msb_in_token-lsb_in_token), you would have the following: Tokens 0..15 represent symbols 0..15 Token 16 means the symbol is 1xxxx where xxxx are 4 raw bits. Token 17 means the symbol is 1xxxxx (with 5 raw bits). Token 18 means the symbol is 1xxxxxx (6 raw bits). And so on.
2023-03-13 06:50:02
Hybriduintconfig 3-2-1 would work something like this: Tokens 0..7 are symbols 0..7 Token 8 means 100x0 Token 9 means 100x1 Token 10 means 101x0 Token 11 means 101x1 Token 12 means 110x0 Token 15 means 111x1 Token 16 means 100xx0 Token 23 means 111xx1 Token 24 means 100xxx0 etc
2023-03-13 06:50:26
(maybe I got the details wrong, didn't check spec or code, just going by memory)
afed
Razor54672
2023-03-13 06:51:40
slightly lossy, but visually almost the same, especially bitdepth 3 `--override_bitdepth 2` (bd2) `--override_bitdepth 3` (bd3)
_wb_
2023-03-13 08:15:17
https://www.reddit.com/r/jpegxl/comments/11p0edq/comment/jc3mdot/
2023-03-13 08:15:56
I came up with another way to do bitpacking in jxl, not by using squeeze+palette but by using palette+MA tree
2023-03-13 08:19:25
Could be easier to detect as a special case in the decoder and do without actually explicitly going through the transforms and MA tree stuff, just directly reading only one symbol per set of packed pixels, and expanding palette indices directly into n pixel values
yoochan
afed
2023-03-13 08:27:09
what were the options used exactly ? I can't reproduce the result... with `-e 9 -d 0.0 -I 1 -E 3` I get 674257 bytes... worse than 589559 bytes for a simple `-e 9 -d 0.0`
jonnyawsom3
yoochan what were the options used exactly ? I can't reproduce the result... with `-e 9 -d 0.0 -I 1 -E 3` I get 674257 bytes... worse than 589559 bytes for a simple `-e 9 -d 0.0`
2023-03-13 08:29:15
Try with -g 3 too
yoochan
2023-03-13 08:30:36
`-e 9 -d 0.0 -I 1 -E 3 -g 3` -> 680280 bytes
jonnyawsom3
_wb_ Could be easier to detect as a special case in the decoder and do without actually explicitly going through the transforms and MA tree stuff, just directly reading only one symbol per set of packed pixels, and expanding palette indices directly into n pixel values
2023-03-13 08:30:47
From the sounds of it, different packing could automatically be applied depending on the bit depth if it's either manually set or low enough in the source
yoochan
2023-03-13 08:31:01
I couldn't stript the container though... even with `--container=0`
2023-03-13 08:31:21
Time to go to bed ! I'll try again tomorrow !
jonnyawsom3
2023-03-13 08:31:33
Huh... I'm surprised the larger group actually made it worse
afed
2023-03-13 08:35:15
I used benchmark_xl for some bruteforcing for various options, it's like `--codec jxl:d0:E3:I1:9` + `--override_bitdepth 3`/`--override_bitdepth 2` (for second encoding)
_wb_
_wb_ Could be easier to detect as a special case in the decoder and do without actually explicitly going through the transforms and MA tree stuff, just directly reading only one symbol per set of packed pixels, and expanding palette indices directly into n pixel values
2023-03-13 08:35:57
WDYT, <@179701849576833024> ? So basically the scheme is like this: To pack n pixels with k possible colors each, make a palette of \sum_{i=0..n-1} k^(n-i) colors. The first pixel of a pack uses one of the first k^n indices, and completely determines the remaining pixels of the pack. The second pixel uses one of the next k^(n-1) indices, which also completely determines itself and the remaining pixels. The palette is just mapping index i to value i%k (so it should compress well even if it gets large), and the MA tree is just explicitly mapping how values for W fully determine the current pixel, except for the first column and the case where W is one of the last k indices (last pixel of a pack). Could be a largish tree but hopefully still compresses quite well since it's very structured — and maybe something clever can be done to make it shorter than the naive thing with one leaf per palette index.
veluca
2023-03-13 08:43:14
I am not sure how good we are at encoding the tree
2023-03-13 08:43:19
Otherwise this could make sense
_wb_
2023-03-13 08:43:57
Maybe using nk^n colors so every pixel of the pack 'knows' the full contents of the pack is easier. Then the tree can be just something like this: ``` if x > 0 if W > (n-1) k^n -1 - [actual tree for the entropy coded bitpacked pixels goes here, can look at bitpacks around it] - W + k^n /* zero entropy ctx */ - [subtree for first column] ```
veluca
2023-03-13 08:47:15
Maybe with a - but yeah that could work out
2023-03-13 08:49:13
well, what do you do if w > k?
2023-03-13 08:50:27
ah no nvm that works out too
_wb_
2023-03-13 08:50:41
The first column is special because there you cannot use W. In all other cases, this scheme would work
2023-03-13 08:50:55
Cycling through buckets of indices
veluca
2023-03-13 08:51:23
yep
2023-03-13 08:51:28
you don't even need k^n
2023-03-13 08:52:01
for things like qr codes, where you have x \* x squares of constant color, you just need something like 2n
2023-03-13 08:52:40
I wonder if we can do something like group4 (basically RLE of 0/1s but with the run lengths delta coded wrt runs in the above row)
_wb_ The first column is special because there you cannot use W. In all other cases, this scheme would work
2023-03-13 08:52:58
also encoding the palette is easy, it's something like W in the tree + 0 / -k^n (k-1 1s followed by -k^n) repeated enough many times
Traneptora
_wb_ (maybe I got the details wrong, didn't check spec or code, just going by memory)
2023-03-13 09:21:59
if this is the case, in general how would one generate the right hybrid uint config?
veluca
Traneptora if this is the case, in general how would one generate the right hybrid uint config?
2023-03-13 09:25:40
what does "right" mean?
Traneptora
2023-03-13 09:26:34
well the particular issue I have is I'm encoding LF coefficients and the particular LF quant values make them range between 0 and 2^14 - 1
2023-03-13 09:27:01
since these are LF Quant values (after prediction) then there will be lots of entropy in the lower order bits
2023-03-13 09:27:08
but much less entropy in the higher order bits
2023-03-13 09:27:43
so what I'd like to do, ideally is have the token represent the higher order bits and the lower order bits, which are mostly random, be signalled raw
2023-03-13 09:27:55
but I'm not sure which hybrid uint config accomplishes this the best way
veluca
2023-03-13 09:30:02
eh, pretty much any
2023-03-13 09:30:15
go with 400, it's kinda a safe bet
2023-03-13 09:31:08
can also do 440 but that almost never helps with DC IME
Traneptora
2023-03-13 09:31:22
wouldn't msb in token = lsb in token = 0 make any value greater than `1 << split` encoded almost raw
veluca
2023-03-13 09:35:44
the way I choose these things usually: * last value is 0 unless you know all the values are even/odd or similar * pick the second value based on how many high bits you'd like to entropy code * pick the first value such that it's >= sum of the others, = by default and > if you have reason to think small values are particularly likely
Traneptora
2023-03-13 09:36:11
Makes sense, thanks
gb82
2023-03-13 10:11:07
is there any simple way to convert xyb jpegs encoded with jpegli to sRGB PNGs that ssimu2 understands?
jonnyawsom3
2023-03-13 10:58:39
Wonder if the benchmark would let you do that as input and output
gb82
2023-03-13 11:23:33
if I'm comparing to a standard PNG that's RGB encoded though
Traneptora
gb82 is there any simple way to convert xyb jpegs encoded with jpegli to sRGB PNGs that ssimu2 understands?
2023-03-14 04:17:30
``` convert input-xyb.jpg -depth 16 xyb.tiff tificc -w16 xyb.tiff srgb.tiff convert temp2.tiff -depth 16 input-sRGB.png ```
2023-03-14 04:17:57
if you want a CLI option
2023-03-14 04:18:06
if you want a GUI option, open it with chrome, right click the image and click "copy"
2023-03-14 04:18:15
and then paste it in an image editor like GIMP and export
2023-03-14 04:18:38
the sRGB PNG won't be tagged as sRGB but it will be sRGB
2023-03-14 04:20:13
unfortunately libplacebo ignores the xyb-jpeg ICC
Demiurge
2023-03-14 10:20:57
I just realized that "mux" is short for "multiplex"
DZgas Ж
2023-03-14 01:02:26
<@238552565619359744> I have plans to compress about q50 in quality and in fact the only problem is what to do with it. For some reason I can't find any algorithms or neural networks that could smooth out the manga grid and make a color guardian out of it
2023-03-14 01:03:50
<@794205442175402004> it is realistic to change several values of the CJXL code so that it stops creating a DCT grid at all?
yoochan
2023-03-14 01:05:26
it's not always easy, to smooth out these pattern as sometime they shouldn't be smoothed, like the sweat shirt of this invisible man : https://i0.wp.com/www.gohanblog.fr/www/wp-content/uploads/2019/04/My-hero-scan-1.png?ssl=1
2023-03-14 01:05:57
and in this picture, the motion lines are also patterns by the way
DZgas Ж
yoochan it's not always easy, to smooth out these pattern as sometime they shouldn't be smoothed, like the sweat shirt of this invisible man : https://i0.wp.com/www.gohanblog.fr/www/wp-content/uploads/2019/04/My-hero-scan-1.png?ssl=1
2023-03-14 01:07:16
in this image there is no grid at all that I am talking about
yoochan
2023-03-14 01:07:48
what ?
DZgas Ж
2023-03-14 01:08:06
don't you see?
yoochan
2023-03-14 01:08:47
I see "grids" everywhere
2023-03-14 01:09:39
the floor, the hairs, and the sweatshirt
DZgas Ж
2023-03-14 01:09:47
e7 e5
_wb_
2023-03-14 01:10:13
Halftoning patterns are tricky for the DCT, it's basically a worst-case kind of image. Changing them into continuous-tone grayscale gradients would help a lot for compression, but of course then it becomes a different image.
yoochan
2023-03-14 01:10:48
nice transform ! how did you ?
2023-03-14 01:11:05
indeed, mangas without the pattern... is it still manga 😄 ?
DZgas Ж
_wb_ Halftoning patterns are tricky for the DCT, it's basically a worst-case kind of image. Changing them into continuous-tone grayscale gradients would help a lot for compression, but of course then it becomes a different image.
2023-03-14 01:12:01
I would agree to just smooth them out if there are some parameters in the code that are responsible for the probability with which they should be used
yoochan
2023-03-14 01:12:07
even a bilateral filter choke on this kind of smmothing
_wb_
2023-03-14 01:12:08
If the halftoning is basically done because the actual image is grayscale but to get it printed, halftoning needed to be done, then you could argue that it's only a printing artifact, but then you should be encoding the grayscale originals, not scans of the halftoned prints.
yoochan
2023-03-14 01:13:04
for mangas, the half toning is not done to print from the greyscale image, but is made by the author for artistic purposes
_wb_
2023-03-14 01:13:08
But I would assume that the halftone patterns are part of the actual art and turning them into grayscale gradients is ruining things.
DZgas Ж
2023-03-14 01:14:35
interestingly, WEBP just stops using the DCT at all, when compressing q 0
yoochan
2023-03-14 01:15:01
I think it discard too much HF coefficients, hence the result
_wb_
2023-03-14 01:15:37
And in that case, basically you just shouldn't be trying to smooth them out or using DCT. Lossless is the only good approach (possibly with some bit depth reduction since presumably the actual content is 1-bit, so maybe 2-4 bit encoding is good so you can keep some antialiasing, but no need to go 8-bit or higher)
DZgas Ж
_wb_ But I would assume that the halftone patterns are part of the actual art and turning them into grayscale gradients is ruining things.
2023-03-14 01:15:56
Well that. this is exactly the goal I have.
yoochan
2023-03-14 01:17:05
I was looking for the exact word : screentones. Before the computers, you bought them in sheets and you cut them to put the pattern on your drawing
DZgas Ж
_wb_ And in that case, basically you just shouldn't be trying to smooth them out or using DCT. Lossless is the only good approach (possibly with some bit depth reduction since presumably the actual content is 1-bit, so maybe 2-4 bit encoding is good so you can keep some antialiasing, but no need to go 8-bit or higher)
2023-03-14 01:17:07
Well. no, this solution does not suit me
_wb_
2023-03-14 01:17:54
I don't know what the best way is to try to go back from halftoning to grayscale, but I would imagine there are tools for that. Something like blurring enough to kill the patterns and then sharpening again might work.
2023-03-14 01:18:51
I don't think image codecs are the best tools for ~~ruining~~'enhancing' images
DZgas Ж
_wb_ I don't know what the best way is to try to go back from halftoning to grayscale, but I would imagine there are tools for that. Something like blurring enough to kill the patterns and then sharpening again might work.
2023-03-14 01:19:42
I have already done multi-day experiments in this area. and the results are just disgusting. this is only suitable if you plan to compress very much. I did it for AVIF qp 45-50
2023-03-14 01:21:22
median filter, Gaussian smoothing, 5x5 squared average, and everything like that is not a solution, it ruined the contours
_wb_
2023-03-14 01:21:40
It does sound like trying to convert the Mona Lisa into a photograph, trying to find ways to hide the brush strokes that make it obvious that it's a painting
DZgas Ж
2023-03-14 01:22:34
this is even assuming that the scans of the manga, or even its originals, are already recompressed images with not the clearest contours, which also, as a result of interpolation and image reduction, acquire this entire grid
yoochan
2023-03-14 01:23:11
yep, the filter wouldn't be trivial... couldn't the predictors of jpegXL do a better job here ?
2023-03-14 01:23:50
(I don't understand exactly what MA trees should be able to do 😄 )
afed
yoochan it's not always easy, to smooth out these pattern as sometime they shouldn't be smoothed, like the sweat shirt of this invisible man : https://i0.wp.com/www.gohanblog.fr/www/wp-content/uploads/2019/04/My-hero-scan-1.png?ssl=1
2023-03-14 01:27:19
for good scans or digital manga I don't see the point of using lossy at all (source in lossless webp btw)
2023-03-14 01:27:41
yoochan
2023-03-14 01:29:45
That's indeed what I do most of the time (and the reason why I'm such a fan of jpegXL :D)
2023-03-14 01:30:24
I think the scan/translation people could be very very intersted in jxl
2023-03-14 01:31:08
and for the kind of galleries they publish, downloading once a 300kb wasm shouldn't be an issue
2023-03-14 01:31:40
and they wouldn't need thumbnail neither... 😄 a win-win-win situation
afed
2023-03-14 01:33:35
except that the decoding speed in lossless is quite slow, especially if using a wasm decoder
yoochan
2023-03-14 01:36:06
we can still that the situation improve 🙂 I was just think it could be a good way to increase the user base
DZgas Ж
2023-03-14 01:44:04
slow neural can
2023-03-14 01:44:20
(very very slow)
yoochan
2023-03-14 01:50:05
what is this code ?
DZgas Ж
2023-03-14 01:51:29
upresnet10
2023-03-14 01:51:51
works bad
yoochan
2023-03-14 01:53:32
well... the grey areas are now grey 😄
Traneptora
2023-03-14 01:55:35
I'd post a close-up cropped image of the invisible girl but Clyde apparently thinks it's explicit
2023-03-14 01:56:29
2023-03-14 01:56:38
either way, you can see that Hagakure isn't actual gray
yoochan
2023-03-14 01:57:50
should it be ? the dot are too spaced
DZgas Ж
2023-03-14 02:02:27
<:Thonk:805904896879493180> https://github.com/natethegreate/Screentone-Remover
2023-03-14 02:03:29
The main thing I learned is that - no one on the Internet calls it either Manga grid or Manga dotted
2023-03-14 02:08:40
well. its work very fast
yoochan
yoochan I was looking for the exact word : screentones. Before the computers, you bought them in sheets and you cut them to put the pattern on your drawing
2023-03-14 02:10:05
neat ! yes screentones 🙂
DZgas Ж
2023-03-14 02:45:22
how use jpeg xl 🙂
2023-03-14 02:45:36
Okay I'll try in 1 thread
2023-03-14 02:51:43
Work 👍
Traneptora
2023-03-14 02:58:48
It appears that libavcodec can't handle RGB jpegs. If I wanted to add support to libavcodec for that, I'd want to be able to read up on where that's tagged in a JPEG file. ideas?
_wb_
Traneptora It appears that libavcodec can't handle RGB jpegs. If I wanted to add support to libavcodec for that, I'd want to be able to read up on where that's tagged in a JPEG file. ideas?
2023-03-14 03:12:56
This is done using an Adobe marker: https://exiftool.org/TagNames/JPEG.html#Adobe
2023-03-14 03:13:23
at least I think that's how it's done
2023-03-14 03:14:13
the JPEG spec surprisingly doesn't say anything about how to interpret the components, they intended to put that in a later part of the spec
Traneptora
2023-03-14 03:14:22
is there a way to get jpegli to output an RGB jpeg that doesn't have an XYB profile attached?
2023-03-14 03:14:32
for test purposes
_wb_
2023-03-14 03:14:59
then JFIF came as an ad-hoc file format for JPEG to fill the gap, and it just specifies that 3 components are to be interpreted as YCbCr
2023-03-14 03:15:28
then Adobe came and added their own marker to do RGB, CMYK, and YCCK
2023-03-14 03:15:48
it's all pretty messy
2023-03-14 03:16:51
one thing I never understood is why nobody just added a marker to do RGBA or YCbCrA in JPEG, it wouldn't be hard to do that and it would be pretty useful
Traneptora
2023-03-14 03:26:56
looks like somehow the lavc decoder supports the adobe transform
2023-03-14 03:27:03
but it's not working for pixel format id 0x22221100
_wb_ This is done using an Adobe marker: https://exiftool.org/TagNames/JPEG.html#Adobe
2023-03-14 03:35:51
it is? I don't see the 0xEE marker anywhere in the file
spider-mario
Traneptora is there a way to get jpegli to output an RGB jpeg that doesn't have an XYB profile attached?
2023-03-14 03:36:10
I believe `cjpeg_hdr` should come close to that
_wb_
Traneptora it is? I don't see the 0xEE marker anywhere in the file
2023-03-14 03:36:57
if I produce an rgb jpeg with `cjpeg -rgb`, I do see that marker. Maybe there are other ways to do it, I dunno...
Traneptora
2023-03-14 03:37:12
I'm referring to `lenna-xyb.jpg` produced by jpegli
2023-03-14 03:39:58
2023-03-14 03:40:07
this image doesn't have 0xEE marker
2023-03-14 03:41:21
it goes D8, E2, DB, C2 followed by various alternating C4/DAs
_wb_
2023-03-14 03:42:10
ah right, it uses the Exif file format, not the JFIF one
2023-03-14 03:47:56
then I suppose this elegant thing is what is used to signal the info: https://github.com/libjxl/libjxl/blob/627bad401cc205243314abad7d651467042d08aa/lib/jxl/jpeg/jpeg_data.cc#L146
Traneptora
2023-03-14 03:47:57
I don't think so either
_wb_
2023-03-14 03:48:28
basically if the component identifiers are 1,2,3 (as in, bytes with that value), then it's YCbCr
2023-03-14 03:48:51
if they are 'R', 'G', 'B' (as in, bytes with those ascii values), then it's RGB
Traneptora
2023-03-14 03:50:35
it looks like it's only subsampled in the B channel?
2023-03-14 03:50:59
that's a little strange ngl
_wb_
2023-03-14 03:51:27
yes, a choice I think we should undo because it causes these jpegs to not be recompressible to jxl 😱
veluca and, well, then we decided to do that in jpegli
2023-03-14 03:53:21
(link to relevant earlier discussion about this)
DZgas Ж
2023-03-14 07:59:14
d4 for manga works very well, so well that I even decided to use d5
2023-03-14 08:02:06
The quality is less than d4 transparent in normal viewing (pixel on pixel) - but must understand that this is an opinion based on images that have been smoothed and processed by the neural network.
Eugene Vert
DZgas Ж The quality is less than d4 transparent in normal viewing (pixel on pixel) - but must understand that this is an opinion based on images that have been smoothed and processed by the neural network.
2023-03-14 09:36:42
Color quantization may be a better option than trying to smooth out the screentone of manga
2023-03-14 09:40:31
E.g. using pngquant: ```bash pngquant -o $out -f --nofs $ncolors -- $in ``` with $ncolors in [4, 8,12,...] maybe add the `--posterize 4` for lower-precision pngs
2023-03-14 09:44:12
Or using gmic with fixed palette: ```bash # ncolor == 4 gmic i $in "to_gray" i "(0,64,192,255)" "index[-2]" "[-1],0,1" "o" -.png \> $out # ncolor == 8 gmic i $in "to_gray" i "(0,32,64,96,128,192,224,255)" "index[-2]" "[-1],0,1" "o" -.png \> $out ```
2023-03-14 09:46:38
And then encode resulting png with modular jxl
DZgas Ж
Eugene Vert Color quantization may be a better option than trying to smooth out the screentone of manga
2023-03-14 09:48:50
no... you missed everything talk, I have already found the best and fastest (of the existing) methods https://github.com/natethegreate/Screentone-Remover
Eugene Vert
DZgas Ж no... you missed everything talk, I have already found the best and fastest (of the existing) methods https://github.com/natethegreate/Screentone-Remover
2023-03-14 09:52:24
I was using bilateral blur, like your example do, for some time. It's a good hack for images with destroyed low-res screentone, but not so for good-quality manga scans
DZgas Ж
2023-03-14 09:53:35
it suits me.
OkyDooky
2023-03-15 01:17:44
Could you post a few examples, if it's reasonable? Being able to use this in manga-like settings would definitely help with adoption. (<@736666062879195236>)
w
2023-03-15 02:31:16
updated stats on my manga (lossless jxl) 38847 jxl files Filesize: 20 GB Original filesize: 31.4 GB Total compression ratio: 157.03% Per chapter Mean: 167.82%, Median: 151.05%, Min: 109.44%, Max: 1775.88%, 10th %ile: 121.23%, 90th %ile: 232.41%, sd: 68.75% Per page Mean: 200.36%, Median: 151.53%, Min: 69.3%, Max: 57858.86%, 10th %ile: 119.84%, 90th %ile: 227.94%, sd: 797.98%
Eugene Vert
Could you post a few examples, if it's reasonable? Being able to use this in manga-like settings would definitely help with adoption. (<@736666062879195236>)
2023-03-15 02:47:11
378K image.png 356K image.cwebp_z9.webp 299K image.cjxl_lI1E3e9.jxl 194K image.pngq_16.png 145K image.pngq_16.cjxl_lI1E3e9.jxl 127K image.cjxl_d4.jxl Previews: original, quantized, cjxl_d4
2023-03-15 02:48:45
Where pngq_16 in filename -- `pngquant -o $out -f --nofs 16 -- $in`
w
2023-03-15 02:51:27
i tried both -I 1 and -I 100 and they are both different from your 299K <:thenk:349311010584395788>
Eugene Vert
w i tried both -I 1 and -I 100 and they are both different from your 299K <:thenk:349311010584395788>
2023-03-15 02:57:27
`cjxl image.png --num_threads=0 -j 0 -m 1 -e 9 -I 1 -E 3 -d 0 out.jxl` on latest main (1af035f) 🤷‍♂️
w
2023-03-15 02:58:59
the -I1 turns out to be 299K (counting). -I100 is 273K
OkyDooky
2023-03-15 03:19:46
Thanks. So, the last one is the one using bilateral blur? (<@736666062879195236>)
Eugene Vert
Thanks. So, the last one is the one using bilateral blur? (<@736666062879195236>)
2023-03-15 03:20:54
Nope, just `cjxl -d 4`
Demiurge
Eugene Vert 378K image.png 356K image.cwebp_z9.webp 299K image.cjxl_lI1E3e9.jxl 194K image.pngq_16.png 145K image.pngq_16.cjxl_lI1E3e9.jxl 127K image.cjxl_d4.jxl Previews: original, quantized, cjxl_d4
2023-03-15 08:20:19
jxl dct is doing surprisingly well for this kind of image. Of course it still has a lot of wavy low-freq ringing artifacts and blurring.
2023-03-15 08:22:18
I think if there was a way to "trade" LF blurring artifacts with higher-frequency noise/grain, it could improve the appearance and the visual fidelity of lossy JXL
2023-03-15 08:24:55
Since large low-freq wavy artifacts and loss of large details is worse than grainy noise that is easier for the brain to filter out and see through
2023-03-15 08:28:05
currently libjxl is awfully blurry, and I feel like it would be possible to make the DCT coefficients more compressible with noise rather than blurring/zeroing
2023-03-15 08:29:01
When I say "noise," think easily-compressible pseudorandomness
2023-03-15 08:29:23
I think x264 probably does something like that.
2023-03-15 08:30:36
It has very good psy tuning that's very good at "preserving" noise and texture and fine detail
2023-03-15 10:25:24
By the way, I hope you will enjoy this comparison. avifenc aom default settings vs libjxl d=2
2023-03-15 10:25:57
JXL is smaller and very slightly more detailed.
OkyDooky
2023-03-15 11:57:20
Would anyone like to see a .png that's larger when .jxl lossless? Or is that a well known thing already?
2023-03-15 11:57:47
The original png is 1.3 mb and -d 0 -e 9 .jxl is 1.4 mb
2023-03-15 11:58:02
[Edit](https://discord.com/channels/794206087879852103/794206170445119489/1085532183432790118): Would anyone like to see a .png that's larger when .jxl lossless? Or is that a well known non-curiosity already?
jonnyawsom3
2023-03-15 11:59:53
I'd imagine it might be because of the bit packing we've discussed before
Demiurge
2023-03-15 12:10:38
in my past old experience, pngquant is able to generate files that are hard for other tools to crush further...
_wb_
2023-03-15 12:10:46
It's always nice to have examples where png is beating libjxl, so we can improve libjxl. Don't forget that even e10 is far from exhaustive, there is still plenty of room for encoder improvements, enough to keep 100 PhD students busy for at least 10 years.
Demiurge
2023-03-15 12:15:51
I just hope that the interest and passion in improving image compression and lossy coding techniques stays aflame long enough to try implementing some really unique techniques to increase coding efficiency.
2023-03-15 12:17:26
Like borrowing ideas from pngquant, djvu, and maybe other new and creative noise-shaping techniques to help preserve detail and fidelity in ways that have the greatest impact.
2023-03-15 12:18:36
Maybe even some foreground/background recognition and separation, spending more bits on areas of the image that the eye is most likely to focus on
Jyrki Alakuijala
Demiurge in my past old experience, pngquant is able to generate files that are hard for other tools to crush further...
2023-03-15 12:31:32
the palette selection (the static internal palette that includes delta coding) in jpeg xl should be pretty good for palette encoding photographs (if that is an interest)
Would anyone like to see a .png that's larger when .jxl lossless? Or is that a well known thing already?
2023-03-15 12:34:35
I'd be interested about the process that was used to generate that PNG rather than the fact that it exists
jonnyawsom3
Demiurge Maybe even some foreground/background recognition and separation, spending more bits on areas of the image that the eye is most likely to focus on
2023-03-15 01:08:39
Google already made a program to arrange JXL's progressive blocks in order of what draws attention first, I'd imagine it could be adapted especially since mixing modular and VarDCT blocks is already an idea
4ravind
2023-03-15 01:46:53
[uploading from discord because my matrix client doesn't let me upload pictures for some reason]
2023-03-15 01:47:23
2023-03-15 01:48:39
Almost all of the difference is very close to 0, so I thought you'd be able to compress is very well. LZMA compresses them with a 2.55 ratio.
Jyrki Alakuijala I'd be interested about the process that was used to generate that PNG rather than the fact that it exists
2023-03-15 01:51:07
I converted an image to OKLab colour space, did DCT+Quantisation on it, converted back to RGB, and subtracted the result from the original image, and made the difference into a png But you'd get a very similar image if you subtract a .jxl image from an original .png
fab
Demiurge By the way, I hope you will enjoy this comparison. avifenc aom default settings vs libjxl d=2
2023-03-15 02:00:01
Try e8 d1
jonnyawsom3
Almost all of the difference is very close to 0, so I thought you'd be able to compress is very well. LZMA compresses them with a 2.55 ratio.
2023-03-15 02:14:50
Here's the best I could get by lowering the bit depth, hard to tell if it kept all the colors properly or not though -d 0 -g 3 -I 100 -E 3 -e 9 --override_bitdepth=6
Jyrki Alakuijala
_wb_ yes, a choice I think we should undo because it causes these jpegs to not be recompressible to jxl 😱
2023-03-15 02:42:33
B-2x-subsampling gives about 1 % compression density improvement -- it is ok to undo
I converted an image to OKLab colour space, did DCT+Quantisation on it, converted back to RGB, and subtracted the result from the original image, and made the difference into a png But you'd get a very similar image if you subtract a .jxl image from an original .png
2023-03-15 02:47:18
wow, you are exploring cool things
2023-03-15 03:24:12
0.02 db better PSNR https://github.com/libjxl/libjxl/pull/2298 😄
_wb_
Jyrki Alakuijala B-2x-subsampling gives about 1 % compression density improvement -- it is ok to undo
2023-03-15 04:21:33
Perhaps most of it can be recovered by quantizing high freq B more aggressively, and possibly by cheating the DC for B to be more predictable? (either w.r.t. jpeg's very basic predictor, so it helps in jpeg syntax, or w.r.t. a more useful predictor like ClampedGradient, with gains only when recompressing as jxl)
Jyrki Alakuijala
2023-03-15 04:25:18
that's where we started
2023-03-15 04:25:49
compression within jxl is of course something we didn't optimize much for -- just the jpeg1 compression
_wb_
2023-03-15 04:26:37
In general I think it would be cool to do DC in jpegli with a higher precision than usual, but requantizing the coefficients w.r.t. a modular predictor (keeping only residuals near zero intact, and e.g. keeping only 5 msb of the residual intact).
Jyrki Alakuijala
2023-03-15 04:27:17
I don't understand
_wb_
2023-03-15 04:27:24
That way you can get great slow gradients, while also getting magically great jxl recompression
Jyrki Alakuijala
2023-03-15 04:27:53
human vision is super adaptive for Y but not very adaptive for X
2023-03-15 04:28:17
so decoding could rework the Y to be off in low frequency as long as it is not surprisingly discontinuous
2023-03-15 04:28:28
then again low frequency is not a lot of info
2023-03-15 04:29:54
decoding could rework the dc values, too -- within their quantization buckets
2023-03-15 04:30:21
Eugene had something like that in brunsli at a stage but it is probably not currently in
_wb_
2023-03-15 04:36:16
Say you use quant factor 1 for DC, so you have 12-bit precision for the DC. That's quite costly if you just keep it like that, both in jpeg and in jxl. But then you could make it near-lossless by computing the residual w.r.t. a predictor (e.g. Gradient) and adjusting the values so the residuals in [-3,3] are preserved but residuals with higher amplitude get requantized. In JPEG itself, that doesn't buy you anything (since it doesn't have any predictor besides Left, which is not good enough for this kind of thing), but when recompressing in JXL, you can use the predictor and get histograms with lots of holes in them, so effectively it becomes much cheaper to encode the values...
Jyrki Alakuijala
2023-03-15 05:30:43
predictor is always backward looking -- you need ideally here other approaches that use larger support
2023-03-15 05:31:06
both forward and backward, like Eugene did for brunsli
_wb_
2023-03-15 06:03:39
Just backward looking already works quite well, at least for 1:1 images. I suspect for 1:8 images it also works.
Traneptora
2023-03-15 08:40:07
how does alias mapping work with regard to JXL ANS on the encode side?
2023-03-15 08:40:41
and the general writing of symbols? I figure the setup code is just the same thing, but I'm wondering how you actually go about writing the symbols and dealing with the alias mapping of them
2023-03-15 08:48:59
Annex O says `k is chosen so AliasMapping(D, k) = (symbol, state Umod D[symbol])` but doesn't provide any ideas on how to invert AliasMapping
_wb_
2023-03-15 08:49:40
I must confess I still don't quite fully understand alias mapping. <@179701849576833024> perhaps it would be useful to document it somewhere, because it's a rather funky thing and there isn't really any clear description of it (in particular of the encoder side of it, but even the decode side in the spec is just some rather obscure code without much hints at why things are done the way they are)
2023-03-15 08:52:17
We need more informal descriptions with some design rationale and examples. At some point I still want to co-write a book on jxl with all of that, before we all forget why we did things. Just hard to find time for such a thing...
veluca
2023-03-15 08:58:31
Ehhh It is a bit tricky
Traneptora
2023-03-15 09:00:23
I'd like to learn how to do it without just taking code from libjxl
HLBG007
2023-03-15 10:15:46
AliasMapping is a function that takes two parameters, D and k. D is a dictionary where keys are the symbols (e.g., items, events, or categories) and values are their corresponding probabilities. k is an integer that is chosen so that the function returns a pair (symbol, state), where the state is calculated as U % D[symbol]. Here's a step-by-step explanation of this function: First, we find the total probability mass M by summing all the probabilities in D. Next, we normalize the probabilities by multiplying each probability by k and dividing it by M. This normalization step is important because it ensures that the sum of the probabilities is equal to k. We store the normalized probabilities in a new dictionary called nD. Now, we need to find the pair (symbol, state) such that AliasMapping(D, k) = (symbol, state U % D[symbol]). To do this, we iterate over the normalized probabilities dictionary nD and check if U % nD[symbol] is less than or equal to the original probability D[symbol]. If it is, we return (symbol, state), where the state is U % D[symbol]. This function is designed to find the appropriate k value, which ensures that the sampling can be done efficiently. In the context of the alias method, this function would be used during the setup phase to preprocess the probabilities and create the alias mapping. ```python def AliasMapping(D, k): M = sum(D.values()) nD = {key: (value * k) / M for key, value in D.items()} U = random.uniform(0, k) for symbol, prob in nD.items(): if U % prob <= D[symbol]: state = U % prob return symbol, state return None ```
2023-03-15 10:15:49
If you are looking to invert the AliasMapping function as described in Annex O, then the goal is to find k such that AliasMapping(D, k) = (symbol, state Umod D[symbol]). To achieve this, you would need to modify the AliasMapping function as follows: ```python def AliasMapping(D, k): M = sum(D.values()) nD = {key: (value * k) / M for key, value in D.items()} U = random.uniform(0, k) for symbol, prob in nD.items(): if U % prob <= D[symbol]: state = U % D[symbol] return symbol, state return None ``` To invert this function, you would need to find a way to calculate k and U given a (symbol, state) pair. Since U is generated from a uniform distribution, it would be challenging to determine the exact value of U from the output. Inverting the function might not be feasible or practical due to the nature of random sampling.
Traneptora
HLBG007 AliasMapping is a function that takes two parameters, D and k. D is a dictionary where keys are the symbols (e.g., items, events, or categories) and values are their corresponding probabilities. k is an integer that is chosen so that the function returns a pair (symbol, state), where the state is calculated as U % D[symbol]. Here's a step-by-step explanation of this function: First, we find the total probability mass M by summing all the probabilities in D. Next, we normalize the probabilities by multiplying each probability by k and dividing it by M. This normalization step is important because it ensures that the sum of the probabilities is equal to k. We store the normalized probabilities in a new dictionary called nD. Now, we need to find the pair (symbol, state) such that AliasMapping(D, k) = (symbol, state U % D[symbol]). To do this, we iterate over the normalized probabilities dictionary nD and check if U % nD[symbol] is less than or equal to the original probability D[symbol]. If it is, we return (symbol, state), where the state is U % D[symbol]. This function is designed to find the appropriate k value, which ensures that the sampling can be done efficiently. In the context of the alias method, this function would be used during the setup phase to preprocess the probabilities and create the alias mapping. ```python def AliasMapping(D, k): M = sum(D.values()) nD = {key: (value * k) / M for key, value in D.items()} U = random.uniform(0, k) for symbol, prob in nD.items(): if U % prob <= D[symbol]: state = U % prob return symbol, state return None ```
2023-03-15 10:34:06
why is there a random U here? alias mapping as defined in the JXL decoder is deterministic, entirely from the frequencies
veluca
2023-03-15 10:37:01
Where did you get this from?
HLBG007
Traneptora why is there a random U here? alias mapping as defined in the JXL decoder is deterministic, entirely from the frequencies
2023-03-15 10:38:14
question: is aliasmapping aliastable in libjxl ? https://github.com/libjxl/libjxl/search?q=alias+mapping
Traneptora
HLBG007 question: is aliasmapping aliastable in libjxl ? https://github.com/libjxl/libjxl/search?q=alias+mapping
2023-03-15 10:39:27
I don't know. I know it from section C.2.6 in the spec
HLBG007
Traneptora I don't know. I know it from section C.2.6 in the spec
2023-03-15 10:50:40
the specification is to broken. I saw this when i wanted develop a port from scratch. I watched then your jxlatte code to understand the source code. xD it is a better documentation. 🙂 The random.uniform(0, k) part was a misunderstanding on my part, and it should not be included in the context of the JPEG XL decoder.
Traneptora Annex O says `k is chosen so AliasMapping(D, k) = (symbol, state Umod D[symbol])` but doesn't provide any ideas on how to invert AliasMapping
2023-03-15 10:59:57
is your aliasmapping in jxlatte not true too? https://github.com/thebombzen/jxlatte/blob/main/java/com/thebombzen/jxlatte/entropy/ANSSymbolDistribution.java#L148
Traneptora
HLBG007 is your aliasmapping in jxlatte not true too? https://github.com/thebombzen/jxlatte/blob/main/java/com/thebombzen/jxlatte/entropy/ANSSymbolDistribution.java#L148
2023-03-15 11:01:55
it's based on the spec
HLBG007
Traneptora it's based on the spec
2023-03-15 11:33:37
in the past it was so that the webm team put the vp8 source code written in c into a document and the source code was then the specification 😄 https://datatracker.ietf.org/doc/rfc6386/ maybe is this solution for jxl a better way to make specification readable
Traneptora
HLBG007 in the past it was so that the webm team put the vp8 source code written in c into a document and the source code was then the specification 😄 https://datatracker.ietf.org/doc/rfc6386/ maybe is this solution for jxl a better way to make specification readable
2023-03-16 12:20:36
what
2023-03-16 12:20:43
no
veluca Ehhh It is a bit tricky
2023-03-16 12:38:32
another question I'm wondering is how do you write the hybrid integer residues in all of this?
2023-03-16 12:39:05
if it was *just* an ANS stream then I could see how you just write the tokens in reverse order and then write the 4-byte state at the end
2023-03-16 12:39:18
but now you have the hybrid integers interleaved
2023-03-16 12:39:43
how do you make that work?
_wb_
2023-03-16 05:39:42
The full stream is basically a mix of u(16) for ANS state updates and lots of small u(n) for raw bits
veluca
Traneptora how do you make that work?
2023-03-16 06:55:21
That part is easy (ish), you just need to go through symbols in reverse, figure out where state flushes happen, and then go through symbols forwards and insert the bits between state updates
Jyrki Alakuijala
_wb_ Just backward looking already works quite well, at least for 1:1 images. I suspect for 1:8 images it also works.
2023-03-16 12:38:08
unfortunately banding often happens very slowly -- think 10 8x8 blocks having the same value, then switching to the next value -- local filtering is not going to be effective, you need to analyze it more globally
_wb_
2023-03-16 12:56:56
Slowness of gradients is not an issue in what I am proposing, on slow gradients the residuals will have low amplitude (if they're really slow, only -1/0/+1), so you get full 12-bit precision.
Traneptora
veluca That part is easy (ish), you just need to go through symbols in reverse, figure out where state flushes happen, and then go through symbols forwards and insert the bits between state updates
2023-03-16 03:35:43
ugh, this is actually a huge pain
2023-03-16 03:36:08
it's so memory greedy
veluca
2023-03-16 03:36:36
you can do some creative memory reusing
Traneptora
2023-03-16 03:38:07
I meant more like, I need yet another buffer to hold the state flushes
veluca
2023-03-16 03:39:05
well, maybe
2023-03-16 03:39:45
a suffix of N encoded symbols can only use I believe (12 bits + raw bits) * number of symbols
2023-03-16 03:40:01
presumably the struct you use to hold the symbols is bigger than that
2023-03-16 03:40:02
😛
Traneptora
2023-03-16 03:42:26
I wonder if I could do it in the same for loop
2023-03-16 03:43:09
like, every time I hit a state flush, I write the hybrid integer residues first
2023-03-16 03:43:22
and then flush the state
Jyrki Alakuijala
2023-03-16 03:52:39
<@226977230121598977>, possibly time to check out your images again after https://github.com/libjxl/libjxl/pull/2298
DZgas Ж
2023-03-16 06:50:58
👀
Traneptora
veluca That part is easy (ish), you just need to go through symbols in reverse, figure out where state flushes happen, and then go through symbols forwards and insert the bits between state updates
2023-03-16 07:40:23
would the state flushes on decode happen in same places as they would on encode? or in reverse order?
_wb_
2023-03-16 08:18:10
The encoder should write the bitstream from end to start, just like it encodes the symbols from last to first. And then the decoder can read bitstream start to end and get symbols from first to last.
Traneptora
_wb_ The encoder should write the bitstream from end to start, just like it encodes the symbols from last to first. And then the decoder can read bitstream start to end and get symbols from first to last.
2023-03-16 09:05:21
I meant with regard to the hybrid uints. for example the decoder flushes twice at the beginning and the encoder flushes twice at the end
2023-03-16 09:05:55
but does the third to last flush on encode correspond to the third flush on decode?
2023-03-16 09:06:14
ie on the same token?
2023-03-17 02:30:08
ah, I figured it out
2023-03-17 02:30:33
you have to write the symbols in the opposite order, but then you have to reverse all the state flushes
2023-03-17 02:30:39
so those are in forwards order
DZgas Ж
Jyrki Alakuijala <@226977230121598977>, possibly time to check out your images again after https://github.com/libjxl/libjxl/pull/2298
2023-03-17 04:55:40
no. the new latest build looks just as disgusting on the newly images. much worse than the old version. But the very first 4 test images look good
2023-03-17 05:04:21
Can you run tests with this image? it really work on any old JPEG XL version, even by 0.6.0, on any quality q90 q80 q70 q60 q50 - it looks as it should. but on the last build it starts to break even starting with the quality of q95
Traneptora
2023-03-18 08:42:03
I'm trying to figure out how to cluster all 7425 or however many HF coefficient clusters there are
2023-03-18 08:42:20
is there a simple way to do that?
veluca
2023-03-18 08:42:49
define simple
Traneptora
2023-03-18 08:42:56
"sane default"
2023-03-18 08:43:12
like how 4-0-0 and 4-4-0 are "sane default" options for hybrid uint configs
veluca
2023-03-18 08:43:31
I mean, if you just want some clustering that works, map all #nonzeros to 0-1-2, and all coefficient contexts to 3-4-5
Traneptora
2023-03-18 08:43:31
for my initial implementation I want to pick something that usually will work well for photographic images
2023-03-18 08:43:49
oh, does that work well?
veluca
2023-03-18 08:43:54
it works OK
2023-03-18 08:44:09
0-1-2 being according to channel
Traneptora
2023-03-18 08:44:15
YXB order?
veluca
2023-03-18 08:44:33
does it matter? 😉
Traneptora
2023-03-18 08:44:43
I suppose not
2023-03-18 08:44:48
cause I can cluster them however I want
2023-03-18 08:44:58
if the clusters are reordered it doesn't change anything
veluca
2023-03-18 08:45:03
yup
2023-03-18 08:45:55
if you want something fancier, you can do kmeans++ with "how much does it cost more to merge these two clusters" as a distance metric (that's what libjxl does)
Traneptora
2023-03-18 08:46:10
in the future I'd like to improve the clustering so I can have better ratios with minimal extra computation overhead, but for now I want something that Just Works OK™️ to get this library to produce compliant codestreams
2023-03-18 08:46:47
so far it produces complaint LF Groups but libjxl won't decode them even with --decode_partial_files
2023-03-18 08:47:10
which is somewhat annoying
2023-03-18 08:49:28
I'd like to make sure the LF Coefficients are working correctly and so if the HF Pass and HF Coefficient metadata is missing, ideally then libjxl will still produce the LF downsampled version
2023-03-18 08:49:38
but libjxl rejects this file as not-partial
2023-03-18 08:51:00
it just goes "there is no preview available yet"
2023-03-18 08:51:38
veluca
2023-03-18 08:51:43
that's slightly odd
2023-03-18 08:51:49
maybe you also need AC global
Traneptora
2023-03-18 08:52:11
I actually have AC Global, but its' one bit
2023-03-18 08:52:35
even weirder: djxl --allow_partial_files fails
2023-03-18 08:52:42
but libjxl actually says ok
veluca
2023-03-18 08:59:18
ugh why
Traneptora
2023-03-18 08:59:30
but libjxl also reports all black, idk if that's because I'm writing garbo or if it's because it won't fill teh buffer
veluca
2023-03-18 09:04:21
fwiw, getting an all-0-ac is not *too* hard
Traneptora
2023-03-18 09:05:12
so just for testing purposes I could write an all-0 AC?
2023-03-18 09:06:05
not a bad idea
2023-03-18 09:06:29
I just need to write non_zeroes = 0 for every varblock and I'm basically done
veluca
2023-03-18 09:09:52
yep
2023-03-18 09:10:06
that's how I went for fmjxl
Traneptora
2023-03-18 09:14:56
well, looks like this wasn't supposed to happen
2023-03-18 09:15:03
but it's compliant!
2023-03-18 09:15:14
probably an issue with quant
veluca
2023-03-18 09:19:21
probably
2023-03-18 09:19:33
well, at least an issue with DC 😛
2023-03-18 09:19:48
actually, probably not quantization but prediction, if I had to guess
Traneptora
2023-03-18 09:21:06
supposed to be locked to gradiant predictor
2023-03-18 09:21:11
so what really matters is the upper left value
2023-03-18 09:21:28
for quant
2023-03-18 11:22:41
well that certainly looks a lot better than a random white square
2023-03-18 11:22:54
original
2023-03-18 11:23:18
still a little bit confused about how much I should quant B by, but hey it's working
veluca
Traneptora still a little bit confused about how much I should quant B by, but hey it's working
2023-03-19 08:04:16
~~just use the default tables~~
Tirr
2023-03-19 08:14:31
Progress in jxl-oxide: binaries are being built at GitHub, so you can try jxl-oxide decoder using those <https://github.com/tirr-c/jxl-oxide/actions/workflows/build.yml>
2023-03-19 08:14:47
it should be able to decode and render "simple" images (it's slow though)
Traneptora
2023-03-19 10:41:19
progress!
2023-03-19 10:41:33
original:
2023-03-19 10:41:47
clearly I'm doing something wrong with quantizing but I can't quite figure out what
2023-03-19 10:42:07
(decoded with libjxl for discord)
yoochan
2023-03-19 10:57:37
You progress quickly!
fab
2023-03-19 10:58:13
https://github.com/cssobral2013/Argentum-Sans
Traneptora
2023-03-19 10:58:58
probably makes sense to debug LF coefficients before HF coefficients
veluca ~~just use the default tables~~
2023-03-19 10:59:40
I'm doing that, but it's a little confusing as I'm internally representing my values as int16
veluca
2023-03-19 10:59:54
ohhh
2023-03-19 11:00:10
yeah that's rough
Traneptora
2023-03-19 11:00:22
according to the default quant tables, B is divided by 256 and Y by 512
2023-03-19 11:00:52
but if I quantize B by half as much as Y, I get way too much B
2023-03-19 11:01:12
this is assuming B is actually B-Y, because Y is added later using CFL.
veluca
2023-03-19 11:02:08
wdym way too much B?
Traneptora
veluca wdym way too much B?
2023-03-19 11:02:57
2023-03-19 11:03:07
look at the color of the shirt
2023-03-19 11:03:29
it's supposed to be purplish blue but it's yellowish
veluca
2023-03-19 11:03:38
ah yeah that could be a problem indeed
Traneptora
2023-03-19 11:03:56
weirdly enough, the hair and face are the right color
2023-03-19 11:04:02
but maybe that's cause B samples are close to zero there
2023-03-19 11:04:54
it's possible that I have the wrong sign for B
veluca
2023-03-19 11:05:00
very possible
2023-03-19 11:05:10
also possible that you sent the wrong CfL values
Traneptora
2023-03-19 11:05:16
I sent the default ones
2023-03-19 11:05:36
which iirc just adds Y to B on decode
veluca
2023-03-19 11:06:02
mhhh yeah that should be right
2023-03-19 11:06:27
and how do you encode? quantize+dequantize Y, subtract that from B, quantize residual?
Traneptora
2023-03-19 11:06:43
no, I subtract B from Y when I do the XYB transform
2023-03-19 11:08:17
er, I subtract Y from B
veluca
2023-03-19 11:53:39
that's not *as* accurate, but close enough 🙂
2023-03-19 11:53:57
I guess you just have the b quant factor wrong somehow
Traneptora
2023-03-19 11:56:31
that's what I thought, but tweaking it didn't fix it
2023-03-19 11:57:26
I'm considering writing a 0 value for CFL at this point so I can test other parts of the process
2023-03-19 12:02:51
alright, disabling CFL didn't fix the issue
2023-03-19 12:03:01
it's exactly the same problem as before
2023-03-19 01:38:35
I wonder if my XYB transform is not going well
2023-03-19 02:14:52
indeed, I tried it using floats
2023-03-19 02:14:57
my integer approximation was messing up somehow
diskorduser
fab https://github.com/cssobral2013/Argentum-Sans
2023-03-19 02:53:24
<#806898911091753051>
Traneptora
2023-03-19 04:36:27
and we've got a working image!
2023-03-19 04:36:36
quality needs improvement, and it's decoded with libjxl
2023-03-19 04:36:38
but it looks like the original
veluca
2023-03-19 05:15:17
what was the issue with the B channel?
2023-03-19 05:15:30
is it just 8x8s?
Traneptora
veluca what was the issue with the B channel?
2023-03-19 05:34:24
a typo of `g * 0.551` instead of `b * 0.551` 😅
veluca
2023-03-19 05:34:58
Ah that would do it 😛
Traneptora
2023-03-19 05:36:28
so far hydrium doesn't beat libjxl at anything atm because highway
2023-03-19 05:36:32
but it works!
2023-03-19 05:36:40
second working jpeg xl encoder ever
2023-03-19 05:37:59
I suppose it *does* beat libjxl at binary size and memory usage
2023-03-19 05:39:04
libhydrium is 101k, which is smaller than lodepng
2023-03-19 05:39:14
and that's before I optimized anything
afed
2023-03-19 05:55:23
second encoder if not counting the official ones <:FeelsReadingMan:808827102278451241> there is also <https://github.com/libjxl/libjxl-tiny> also with less memory consumption and binary size than libjxl **cjxl.exe -d 1 -e 4 --num_threads 0** `Cpu 44 mb/s (0.202 sec), real 44 mb/s (0.203 sec) = 99%. ram 165724 KB, vmem 229988 KB` **cjxl_tiny -d 1** `Cpu 210 mb/s (0.171 sec), real 210 mb/s (0.171 sec) = 100%. ram 77624 KB, vmem 75280 KB`
veluca
afed second encoder if not counting the official ones <:FeelsReadingMan:808827102278451241> there is also <https://github.com/libjxl/libjxl-tiny> also with less memory consumption and binary size than libjxl **cjxl.exe -d 1 -e 4 --num_threads 0** `Cpu 44 mb/s (0.202 sec), real 44 mb/s (0.203 sec) = 99%. ram 165724 KB, vmem 229988 KB` **cjxl_tiny -d 1** `Cpu 210 mb/s (0.171 sec), real 210 mb/s (0.171 sec) = 100%. ram 77624 KB, vmem 75280 KB`
2023-03-19 06:10:42
you mean fmjxl and fjxl? 😛
afed
2023-03-19 06:17:09
I mean among supporting all basic features in some future, but simplified in some way <:YEP:808828808127971399> though, fmjxl + fjxl together is also lossy (with animation) and lossless 🤔
veluca
2023-03-19 06:18:14
actually fjxl also does animation now (although the main interface doesn't)
fab
2023-03-19 08:29:44
an exe please
2023-03-19 08:29:48
https://github.com/thebombzen/hydrium
2023-03-19 08:29:54
no avx2
Traneptora
afed second encoder if not counting the official ones <:FeelsReadingMan:808827102278451241> there is also <https://github.com/libjxl/libjxl-tiny> also with less memory consumption and binary size than libjxl **cjxl.exe -d 1 -e 4 --num_threads 0** `Cpu 44 mb/s (0.202 sec), real 44 mb/s (0.203 sec) = 99%. ram 165724 KB, vmem 229988 KB` **cjxl_tiny -d 1** `Cpu 210 mb/s (0.171 sec), real 210 mb/s (0.171 sec) = 100%. ram 77624 KB, vmem 75280 KB`
2023-03-19 10:58:31
77M?
2023-03-19 10:59:50
libhydrium uses <2 MB ram
fab an exe please
2023-03-19 11:04:36
2023-03-19 11:04:50
it's not optimized yet ofc
fab no avx2
2023-03-19 11:06:23
I'm not planning on using intrinsic extensions or multithreading: the goal is for it to be portable easily to small systems like digital cameras
afed
2023-03-19 11:08:09
yeah, but still less than libjxl
Traneptora
2023-03-19 11:08:24
yea, but 77 MB is still a lot of memory
2023-03-19 11:08:40
libhydrium has a *very* low memory footprint by encoding one Frame at a time as a 256x256 tile
2023-03-19 11:08:48
so it only needs to buffer that, and nothing else
2023-03-19 11:08:59
naturally, this sacrifices ratio
2023-03-19 11:09:10
but coding efficiency is not the primary concern
afed
2023-03-19 11:09:15
probably because it hasn't been the main goal yet
Traneptora
2023-03-19 11:09:39
yea. I could improve the efficiency a lot if I buffered one LF Group at a time rather than one Group
2023-03-19 11:09:49
but I don't want to sacrifice fidelity, yet
afed
afed second encoder if not counting the official ones <:FeelsReadingMan:808827102278451241> there is also <https://github.com/libjxl/libjxl-tiny> also with less memory consumption and binary size than libjxl **cjxl.exe -d 1 -e 4 --num_threads 0** `Cpu 44 mb/s (0.202 sec), real 44 mb/s (0.203 sec) = 99%. ram 165724 KB, vmem 229988 KB` **cjxl_tiny -d 1** `Cpu 210 mb/s (0.171 sec), real 210 mb/s (0.171 sec) = 100%. ram 77624 KB, vmem 75280 KB`
2023-03-19 11:20:05
hydrium `Cpu 1034 kb/s (5.514 sec), real 1028 kb/s (5.548 sec) = 99%. ram 32828 KB, vmem 35096 KB` also jxl is broken?
2023-03-19 11:20:23
Traneptora
2023-03-19 11:20:34
most of that RAM is consumed by lodepng
afed
2023-03-19 11:21:01
ppm is not supported yet?
Traneptora
2023-03-19 11:21:12
not atm
2023-03-19 11:21:20
the library just takes a buffer
2023-03-19 11:21:23
the frontend takes png
afed hydrium `Cpu 1034 kb/s (5.514 sec), real 1028 kb/s (5.548 sec) = 99%. ram 32828 KB, vmem 35096 KB` also jxl is broken?
2023-03-19 11:22:21
2023-03-19 11:22:40
mighta fixed a bug, try recompiling
2023-03-19 11:22:43
as this one builds fine
2023-03-19 11:24:03
I could possibly quant the HF coeffs more, tbh
2023-03-19 11:24:13
for photographic images they work fine
afed
Traneptora
2023-03-19 11:24:32
It's from this build
Traneptora
2023-03-19 11:24:44
interesting, wonder if something happens on windows
afed It's from this build
2023-03-19 11:26:30
interesting, something weird happens on windows that isn't happening on linux
afed
2023-03-19 11:27:29
<:Thonk:805904896879493180>
Traneptora
2023-03-19 11:28:32
oh, that's what it is
2023-03-19 11:28:56
on windows when I write `0xFF 0x0A` it replaces each instance of `0x0A` with a `0x0D 0x0A`
2023-03-19 11:29:01
it's inserting a carriage return
2023-03-19 11:29:15
I have an `fopen("w")` not `"wb"` cause I forget that they're actually different on windows
afed
2023-03-19 11:29:26
btw fjxl has a small pam-input.h for pam/ppm support <https://github.com/libjxl/libjxl/tree/main/experimental/fast_lossless>
Traneptora
2023-03-19 11:29:39
yea, though fjxl is a different type of tool
afed <:Thonk:805904896879493180>
2023-03-19 11:30:23
try this
afed
2023-03-19 11:32:00
yeah, it works, but weirdly slow decoding compared to regular libjxl
2023-03-19 11:35:28
hydrium jxl djxl: `1534 x 2048, 1.90 MP/s [1.90, 1.90]` tiny jxl djxl: `1534 x 2048, 114.13 MP/s [114.13, 114.13]`
2023-03-19 11:43:31
and it seems like the colors are a slightly different `-d 0.01` for jibjxl(-tiny) because I thought size might affect decoding speed
Traneptora
afed and it seems like the colors are a slightly different `-d 0.01` for jibjxl(-tiny) because I thought size might affect decoding speed
2023-03-20 12:01:22
I think the colors being slightly different is because of my sRGB to Linear approximation
afed yeah, it works, but weirdly slow decoding compared to regular libjxl
2023-03-20 12:02:47
that's probably due to how it tiles, there's a lot of ANS overhead
2023-03-20 12:04:58
if I enabled gaborish then I might be able to get away with more quant
Tirr
2023-03-20 04:50:10
oh so hydrium encodes tiles into separate frames
2023-03-20 04:50:30
maybe frame composition is slow then
_wb_
2023-03-20 07:31:02
I wouldn't be surprised if frame composition is not exactly optimized for this
Traneptora if I enabled gaborish then I might be able to get away with more quant
2023-03-20 07:38:29
gaborish crosses group boundaries but not frame boundaries, so you might get some seams if you do gaborish while encoding one frame per group
2023-03-20 07:40:15
same with epf
2023-03-20 07:47:42
Maybe we could let libjxl decode files where the `jxlp` boxes are out of order, with a warning or something, or only if a certain option is enabled (it does mean it needs to buffer all of the bitstream until it finds the first `jxlp` box). It's technically not conforming according to 18181-2 to do that, but then again, we do have an index counter field in `jxlp` so we may as well use it. Then you can write all the data sections in a single frame, with the TOC at the end. It would be easy to make a tool to put the `jxlp` boxes in the right order.
fab
Traneptora 77M?
2023-03-20 09:33:23
13MB
Traneptora
_wb_ gaborish crosses group boundaries but not frame boundaries, so you might get some seams if you do gaborish while encoding one frame per group
2023-03-20 11:06:36
ah, that's right. I'm still trying to figure out how to avoid seams on varblock boundaries without setting HFMul to be too high though
veluca
2023-03-20 12:19:26
ah you're encoding one frame per group? yeah djxl won't like that
2023-03-20 12:21:58
as in, I am 99% sure the current code will just pay O(wh) in memory copies for each group
2023-03-20 12:22:16
probably not too hard to fix though
2023-03-20 12:22:47
~~but for sure I will not do it~~
fab
2023-03-20 01:02:42
2023-03-20 01:03:13
Could you compress this photo with latest hydrium
2023-03-20 02:31:13
Send me latest hybrid I'll tell you how it should perform
Traneptora
fab Send me latest hybrid I'll tell you how it should perform
2023-03-20 02:54:00
you *can* compile it yourself, you know
fab
2023-03-20 02:54:23
Try
Traneptora
2023-03-20 02:55:08
it's als still very WIP
fab
2023-03-20 02:55:56
2023-03-20 02:56:06
Also try with this photo
2023-03-20 02:56:11
Difficult one
2023-03-20 02:56:47
Send jxl and I will review
diskorduser
2023-03-20 03:15:00
Fab, sus
fab
2023-03-20 04:29:43
in theory you should do
2023-03-20 04:29:44
cjxl C:\Users\Use\Pictures\vu\vu.jpeg -e 8 -q 52.2 --photon_noise_iso=21 --lossless_jpeg=0 C:\Users\Use\Pictures\vu\vu.jxl
2023-03-20 04:29:56
but with better quality than original libjxl
2023-03-20 04:30:15
are you able to achieve that?
2023-03-20 04:30:19
<@853026420792360980>
2023-03-20 04:30:34
i know is a png
2023-03-20 04:30:59
jon will be angry about this
2023-03-20 04:31:02
he hates jpeg
2023-03-20 04:31:15
especially re encoding of them
2023-03-20 05:03:57
2023-03-20 05:04:10
Another photo you can test
2023-03-20 05:04:23
Xiaomi android 13 denoising
2023-03-20 05:11:37
Ok I'll make a review of the current encoder
2023-03-20 05:19:40
It don't decode with xnview
2023-03-20 05:20:01
More than one minute already passsed
2023-03-20 05:20:17
1365
2023-03-20 05:20:24
Done
Traneptora
2023-03-20 05:20:27
you need to build it again
2023-03-20 05:20:36
previous version had a bug
fab
2023-03-20 05:21:49
2023-03-20 05:24:23
2023-03-20 05:24:28
A zoom
2023-03-20 05:24:39
Great job/
2023-03-20 05:29:01
2023-03-20 05:29:16
I'm curious this hydrium vs libjxl
Traneptora
2023-03-20 05:42:56
it looks like higher HF Multipliers are needed where there's smooth gradients
2023-03-20 05:43:02
since those are more noticeable
2023-03-20 05:44:27
I'm currently operating on a constant hfmultof 8
2023-03-20 05:46:23
or rather, when I have a smooth gradient, the varblock boundaries are more noticeable
2023-03-20 05:52:26
<@794205442175402004> idea w/r/t jpegli subsampling B channel: why not create a new XYB icc profile, whose matrix is the standard Opsin matrix times the YCbCr inverse matrix
fab
2023-03-20 05:52:37
2023-03-20 05:52:50
New difficult image
Traneptora
2023-03-20 05:52:51
so the opsin inverse matrix is the YCbCr forward matrix, times the standard opsin inverse matrix
2023-03-20 05:52:58
then you tag the image as YCbCr
fab
2023-03-20 05:52:58
Very difficult
Traneptora
2023-03-20 05:53:44
perhaps the other way around
2023-03-20 05:54:05
but either way you conjugate the encode-decode process with the YCbCr conversion matrix
2023-03-20 05:54:22
that way, decoders will decode the JPEG, then apply YCbCr -> RGB, then apply the ICC Profile
2023-03-20 05:55:23
nothing changes on decode except decoders apply inverse YCbCr, which is then inverted by the ICC Profile application
2023-03-20 05:55:34
so it still decodes to what you want
2023-03-20 05:56:11
the primary purpose of doing this would be that you can mark the image as YCbCr instead of RGB, which allows you to subsample the third channel, and have it be JPEG-Recompressable using cjxl
2023-03-20 05:56:42
you'd just fake the YCbCr as RGB with the ICC profile, essentially
_wb_
Traneptora <@794205442175402004> idea w/r/t jpegli subsampling B channel: why not create a new XYB icc profile, whose matrix is the standard Opsin matrix times the YCbCr inverse matrix
2023-03-20 06:26:06
Because the point is to quantize the XYB components differently. Doing YCbCr on XYB will give something weird where luma has been mixed with chroma again
Traneptora
_wb_ Because the point is to quantize the XYB components differently. Doing YCbCr on XYB will give something weird where luma has been mixed with chroma again
2023-03-20 06:48:38
Well on encode youd still just encode XYB
2023-03-20 06:49:52
but the ICC profile youd attach would have YCbCr matrix multiplied on the right of the opsin inverse matrix
_wb_
2023-03-20 06:51:36
Ah, I see. That could work, yes.
2023-03-20 06:52:36
Not sure if existing decoders will work with 'YCbCr' where only Cr is subsampled, not Cb. I suspect they won't expect that...
2023-03-20 06:52:51
Jxl can represent that though.
Traneptora
2023-03-20 06:53:16
If they can handle RGB with only B subsampled then probably they can handle only Cr subsampled
2023-03-20 06:54:42
atm avcodec cant handle jpegli files but ive got a patch to fix that
DZgas Ж
Traneptora
2023-03-20 08:10:55
and now take this file and place it in the Releases On github tab
2023-03-20 08:14:30
https://github.com/tirr-c/jxl-oxide <:Thonk:805904896879493180> <:Thonk:805904896879493180> <:Thonk:805904896879493180> <:Thonk:805904896879493180> <:Thonk:805904896879493180> <:Thonk:805904896879493180>