cgohlke / imagecodecs

Image transformation, compression, and decompression codecs

Home Page:https://pypi.org/project/imagecodecs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

JxlDecoderProcessInput returned JXL_DEC_ERROR on certain images

cheng-chi opened this issue · comments

Hi Christoph,
Thank you so much for maintaining this package! It's super handy for my current research project.
I'm currently countering a bug with JpegXL on Ubuntu 20.04 with this image (attached as png).
Minimal reproducible example:

import imagecodecs
img = imagecodecs.png_decode(open('test.png', 'rb').read())
imagecodecs.jpegxl_decode(imagecodecs.jpegxl_encode(img, level=4, lossless=False))

test

Looks like the jxl library encoder produces an invalid JPEG XL stream with level > 1 in lossy mode.

The level parameter maps to the "decoding speed tier", which is probably not what you want. I better change level to something like the "quality" parameter of the cjxl tool, although that is not part of the public jxl API...

For now, consider the distance or effort parameters:

/**
 * Sets the distance level for lossy compression: target max butteraugli
 * distance, lower = higher quality. Range: 0 .. 15.
 * 0.0 = mathematically lossless (however, use JxlEncoderSetFrameLossless
 * instead to use true lossless, as setting distance to 0 alone is not the only
 * requirement). 1.0 = visually lossless. Recommended range: 0.5 .. 3.0. Default
 * value: 1.0.
  /** Sets encoder effort/speed level without affecting decoding speed. Valid
   * values are, from faster to slower speed: 1:lightning 2:thunder 3:falcon
   * 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise.

Going to use the formula from the GIMP plugin to map level/quality to distance in the next version of imagecodecs:
https://github.com/libjxl/libjxl/blob/f95da131cf7c7ccd4da256356fde2fec1fa23bb5/plugins/gimp/file-jxl-save.cc#L541-L555

Unfortunately that formula is going to change in the next version of libjxl...

Thanks for the fast response! I can confirm that the following code works. It seems like libjxl is still not stable enough for general usage. I will use Jpeg2k for now!

import imagecodecs
img = imagecodecs.png_decode(open('test.png', 'rb').read())
imagecodecs.jpegxl_decode(imagecodecs.jpegxl_encode(img, effort=3, distance=1.0, lossless=False, decodingspeed=1))

Just to confirm, is "jxl library encoder produces an invalid JPEG XL stream with level > 1 in lossy mode" a bug or expected behavior? Is it documented in libjxl? Or should I submit an issue to libjxl as well?

Looks like a bug. To reproduce the issue, effort has to be below 5 too. I don't see this documented and would expect an error during writing if this combination of parameters is not valid.

In order to report the issue to libjxl, this needs to be reproduced outside of imagecodecs with the current libjxl development code. Unfortunately the decoding speed parameter cannot be set with the cjxl tool. Let's take that as a hint not to change the default decoding speed...

In v2022.12.22, the level parameter maps to distance in a way that it resembles the libjpeg quality parameter.

Thanks for making the change!