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

No multi-component transform for JPEG2000 lossless #88

Closed erikogabrielsson closed 7 months ago

erikogabrielsson commented 7 months ago

Hi,

From my understanding, multi-component transform (MCT) must be set for the openjpeg encoder to do RGB to YBR conversion, which produces better compression. If the encoding is lossy, the irreversible color transform (ICT) is used, and if lossless the reversible color transform (RCT).

With imagecodecs, setting mct=True does not seem to produce YBR-converted compressed data when used together with lossless settings. I used the Parse J2K codestream code-snippet from this issue to parse out the MCT and DWT:

jpeg2k_encoded_lossless = jpeg2k_encode(data, level=0, reversible=True, mct=True,  codecformat=JPEG2K.CODEC.J2K)
parse_jpeg2k(jpeg2k_encoded_lossless)
MCT: 0
DWT: 1

When setting level=80 and reversible=False, MCT is applied:

jpeg2k_encoded_lossy = jpeg2k_encode(data, level=80, reversible=False, mct=True, codecformat=JPEG2K.CODEC.J2K)
parse_jpeg2k(jpeg2k_encoded_lossy)
MCT: 1
DWT: 0

Comparing with Pillow (lossless):

with io.BytesIO() as buffer:
    image.save(buffer, format='JPEG2000', irreversible=False, mct=1, no_jp2=True)
    pillow_encoded_lossless = buffer.getvalue()
parse_jpeg2k(pillow_encoded_lossless)
MCT: 1
DWT: 1

and lossy:

with io.BytesIO() as buffer:
    image.save(buffer, format='JPEG2000', irreversible=True, mct=1, no_jp2=True)
    pillow_encoded_lossy = buffer.getvalue()
parse_jpeg2k(pillow_encoded_lossy)
MCT: 1
DWT: 0

I.e. Pillow will apply MCT for both lossless and lossy compression when mct=1 is specified.

Looking at the code, MCT is not set when quality != 0. Is there a reason for this? Am I missing something?

cgohlke commented 7 months ago

I can't remember if there was a reason to disable MCT for lossless. Anyway, tests are passing here with MCT so it'll be enabled in the next version.

I'll also test if MCT can be enabled for > 3 components...

cgohlke commented 7 months ago

multi-component transform (MCT) must be set for the openjpeg encoder to do RGB to YBR conversion, which produces better compression.

It's not that easy. In my limited testing, lossless compression without MCT sometimes produced smaller images compared to with MCT. One synthetic image was a factor 3.2 smaller...

cgohlke commented 6 months ago

Fixed in the v2024.1.1 release.