Apply JPEG 2000 channel definitions when decoding - #10049
sfarestam-iproov wants to merge 4 commits into
Conversation
A JP2 channel definition (cdef) box can declare that the codestream components are stored in a different order from the color space, such as blue, green, red, or with the opacity channel first. Pillow decodes tile by tile, which bypasses OpenJPEG's own handling of the box, so the box was ignored and the channels came out swapped. Parse the box, and when it describes a reordering of the components, unpack them in that order. This is done before the conversion from YCbCr, so sYCC images are also handled, such as file2.jp2 from the JPEG 2000 conformance suite. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Hi. You can see in our CI that one of your new tests, |
Before OpenJPEG 2.5.1, opj_read_header() does not set the image color space, so the tile-by-tile decoder cannot identify sYCC images and returns the YCbCr values as RGB, whether or not there is a channel definition box. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Thanks. That test fails wherever OpenJPEG is older than 2.5.1: 2.5.0 on the Ubuntu jobs, and 2.4.0 on CentOS Stream 9. Before uclouvain/openjpeg@0f528e9, I've skipped the test for OpenJPEG < 2.5.1, the same way as |
|
With the skip, the Ubuntu and Docker jobs now pass. The remaining failures look unrelated to this change:
These jobs should pass when re-run once code.videolan.org is reachable again. |
If we delay the YCbCr conversion until we return to Python, it is actually possible to reorder the channels in Python. I think it makes the code simpler. See what you think - sfarestam-iproov#1 |
Cover two YCbCr cases that the channel definition changes must keep working: an sYCC image with an opacity channel, stored in either order, and a J2K codestream with horizontally subsampled chroma but no color space, which is decoded as YCbCr. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Thanks, it is much simpler, and it also removes the need to skip the sYCC test for OpenJPEG < 2.5.1. However, I found two things that it changes when I compared both versions against 12.3.0 and
It also slows down every sYCC image, not just those with a
The existing tests don't cover either case, so I've pushed tests for them to this PR: an sYCC image with alpha stored in either order, and a small 4:2:2 codestream, The OpenJPEG < 2.5.1 problem can be handled separately. It affects every sYCC JP2 there, not just those with a I have that working locally as a separate change, about 65 lines. On OpenJPEG 2.5.0 and 2.4.0, sYCC decodes correctly and If you'd still prefer the reordering in Python, I'm happy to look at a version that only defers the YCbCr conversion for sYCC images that are actually reordered. |
# Conflicts: # docs/releasenotes/13.0.0.rst
Changes proposed in this pull request:
cdef) box, and when it describes a reordering of the codestream components, unpack them in that orderA
cdefbox can declare that the components are stored in a different order from the color space: for example blue, green, red, or with the opacity channel first. OpenJPEG applies the box inopj_decode(), but Pillow decodes tile by tile withopj_read_tile_header()andopj_decode_tile_data(), which skip that step, so the box was ignored and the channels came out swapped.This is the cause of uclouvain/openjpeg#1382, where a portrait from a Belgian ePassport decodes with red and blue swapped. That file stores its components as blue, green, red and declares this in a
cdefbox. With this change, Pillow's output for it matchesopj_decompressbyte for byte.This also fixes
file2.jp2from the JPEG 2000 conformance suite in openjpeg-data, an sYCC image stored as Cr, Cb, Y. This needs OpenJPEG 2.5.1 or later, which is the first version to identify sYCC images when decoding by tile. Compared withopj_decompress, its largest difference drops from 255 to within YCbCr rounding. In the rest of the conformance and non-regression JP2 files, no other decoded pixel changes. Those files already had other differences fromopj_decompress, from ICC profiles and subsampled YCbCr, and they are unchanged.The tests build their images by adding a
cdefbox to a file that Pillow has saved, so no new test images are needed. They cover RGB, RGBA and LA, tiling,reduce, sYCC, and boxes that should be ignored. The new tests and acdeffuzzer were also run under AddressSanitizer.🤖 Generated with Claude Code