loris-imageserver / loris

Loris IIIF Image Server
Other
209 stars 87 forks source link

Investigation into colour profiles with Kakadu/OpenJPEG #414

Open alexwlchan opened 6 years ago

alexwlchan commented 6 years ago

These are some notes on colour profiles issues with JP2.


Our test suite has a JP2 image with an embedded ColorSpin profile. This is a screenshot taken in Preview.app on macOS 10.12, which correctly identifies and renders the profile:

screen shot 2018-03-22 at 23 25 27

We have tests for both the Kakadu and OpenJPEG transformers that check if you have the config:

map_profile_to_srgb = True
srgb_profile_fp = icc/sRGB2014.icc

they can convert the image to a JPEG, and the colours are correct.

Previously this just checked that the images had different histograms, which is a fairly feeble test – making every pixel black would also pass! I’ve made the test a bit more specific, looking for the red stripe at the top – see #413.

This is the JPEG produced by Kakadu. It has no embedded colour profile, and the colours are correct. (Downsized for readability.)

kakadu_output

This is the JPEG produced by OpenJPEG. It has no embedded colour profile, and the colours are mangled. (Downsized for readability.)

openjpg_output

Curious!


Digging into the transformer code in transformers.py, the process for both these transformers goes something like this:

  1. Make a shell call to kdu_expand or opj_decompress, which converts the JP2 into a BMP image
  2. Attempt to convert the embedded colour profile to sRGB if required
  3. Pass the new BMP into _derive_with_pil, which calls the Pillow transformer, and does any transformations defined by the user’s request – e.g. cropping, rotation

For Kakadu, here’s the image before and after step 2:

kdu_intermediate1 kdu_intermediate2

And likewise for the OpenJPEG transformer:

opj_intermediate opj_intermediate2

These were obtained by adding im.save(filename) inside transforms.py, then running the Loris test suite. We see that:

So I think our current Kakadu implementation is fine, but we need to think about changing the OpenJPEG implementation, possibly skipping the step where we fix the colour profile – it seems like OpenJPEG is already doing that for us?


I’d like to test this theory with more JP2 examples, and ideally not one where the colours cycle in quite such a neat way. I have at least one other test case (https://github.com/wellcometrust/platform/issues/734) – I’ll try it when I’m back at work on Monday.

alexwlchan commented 6 years ago

Worth noting: I couldn’t find anything in the OpenJPEG docs explaining this behaviour, but I only did a cursory Google.