cgohlke / imagecodecs

Image transformation, compression, and decompression codecs
https://pypi.org/project/imagecodecs
BSD 3-Clause "New" or "Revised" License
111 stars 21 forks source link

Lossless JPEG to JXL transcoding #78

Closed JJGO closed 1 year ago

JJGO commented 1 year ago

I was wondering whether there is support for lossless transcoding of JPEG images into JPEG XL, since JPEG XL natively supports this.

Thanks!

cgohlke commented 1 year ago

At the time the module was implemented, libjxl did not support transcoding. Time to revisit...

https://github.com/cgohlke/imagecodecs/blob/a74e4f305a9c8c317da17beb7a0698543c27b4ae/imagecodecs/_jpegxl.pyx#L885-L901

cgohlke commented 1 year ago

The next version of imagecodecs will have two functions to transcode between JPEGXL and JPEG:

 data = image_data('rgb', 'uint8')
 jpeg1 = jpeg8_encode(data)
 jpegxl = jpegxl_encode_jpeg(jpeg1)
 jpeg2 = jpegxl_decode_jpeg(jpegxl)
 assert_array_equal(jpeg8_decode(jpeg1), jpeg8_decode(jpeg2))

Let me know if that is enough or if you have any other specific use cases in mind.

cgohlke commented 1 year ago

Fixed in v2023.7.4.

JJGO commented 1 year ago

Great! Just confirmed I could perform the lossless transcoding with version 2023.7.4

import imagecodecs
from imagecodecs import jpeg_encode, jpegxl_decode_jpeg, jpegxl_encode_jpeg, jpeg_decode

data = np.random.randint(0,255,size=(128,128,3),dtype=np.uint8)
jpeg1 = jpeg_encode(data)
jpegxl = jpegxl_encode_jpeg(jpeg1)
jpeg2 = jpegxl_decode_jpeg(jpegxl)
np.testing.assert_allclose(jpeg_decode(jpeg1), jpeg_decode(jpeg2))

Thank you for implementing this!

william-silversmith commented 3 weeks ago

Hi! Thanks so much for this excellent package. I seem to have created a jxl file that is unable to be transcoded. I think it is because I tried storing a 3D grayscale image as a series of animation frames in the hopes there was some 3D compression (there wasn't). Would it be possible to detect this situation and return a more informative error?

The current error I'm seeing is:

E   RuntimeError: JxlDecoderProcessInput unknown status=5

Thanks so much!

EDIT: I found that I ran into this issue even with a non-animated image (as determined by the CLI tool jxlinfo). I'm attaching the three files that are not working.

bad.jxl — intentionally created animation bad2.jxl - accidentally created animation bad3.jxl - not an animation, but still doesn't work

For bad3.jxl, here's the output of jxlinfo:

JPEG XL image, 64x4096, lossy, 8-bit Grayscale
Color space: Grayscale, D65, sRGB transfer function, rendering intent: Relative

bad_images.zip

cgohlke commented 3 weeks ago

Would it be possible to detect this situation and return a more informative error?

The next version will raise ValueError("can't transcode grayscale or animated JPEG XL to JPEG").

william-silversmith commented 3 weeks ago

Thank you!

Do you know if the inability to transcode grayscale is intrinsic to the format or just a missing implementation detail in the reference implementation? It seems like JPEG does have a grayscale format.

https://stackoverflow.com/questions/51008883/is-there-a-grayscale-jpg-format

On Thu, Jun 13, 2024, 10:17 AM Christoph Gohlke @.***> wrote:

Would it be possible to detect this situation and return a more informative error?

The next version will raise ValueError("can't transcode grayscale or animated JPEG XL to JPEG").

— Reply to this email directly, view it on GitHub https://github.com/cgohlke/imagecodecs/issues/78#issuecomment-2165809491, or unsubscribe https://github.com/notifications/unsubscribe-auth/AATGQSLE3N7PP4XVPVBXEPTZHGSZFAVCNFSM6AAAAABJHVFV7GVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNRVHAYDSNBZGE . You are receiving this because you commented.Message ID: @.***>

cgohlke commented 3 weeks ago

According to this comment it should be possible to transcode grayscale JPEG.

But when trying to convert your files with djxl, it emits Warning: could not decode losslessly to JPEG

william-silversmith commented 3 weeks ago

Thank you very much for your help!

william-silversmith commented 2 weeks ago

FYI, I figured out what the problem was. The transfer to grayscale does in fact work, but (duh) transcoding from JPEG-XL to JPEG doesn't work if the image wasn't originally a JPEG.

Of note, I've found that the JPEG and JPEG-XL decoders can produce slightly different images even when a round trip transcoding produces an identical JPEG image.