Version 1, December 2020. Caution: this is work in progress! → will take a few weeks at least before it stabilizes...

MacOS Color Management

Color Management is already complex in theory, but can be even more complex in practice, in particular wrt where is it done (apps, OS, GPU). I found it difficult to find answers to my questions, to wrap my head around all this. What follows are my notes, which I'm sharing as it could be useful to someone else. If you have any comments, corrections, don't hesitate:  (français, English, Deutsch).

The situation is probably quite different on MacOS, Windows, and Linux. I know next to nothing about Windows and Linux color management. This blog applies only to the MacOS environment, with a bias towards photography.

Introduction

[gamut.jpg] This section is a quick summary of what I understand of color management.

Let's start with an example. When a camera takes a JPEG image, it stores the image as pixels. Each pixel has three numbers, in the range 0-255, labelled "red", "green", "blue". However, these numbers, just by themselves, are not enough. If I say that some sculpture is 27 high, you'll want to know whether it's 27 centimeters, inches, feet or meters (to stick to plausible cases). Same thing with pixels, we need to know the "unit" to give them meaning. For RGB pixels, this will be a color profile, such as "sRGB" (web default), "Adobe RGB" or "ProPhoto RGB" (frequently seen in photography), or one from among hundreds of others. It shouldn't be possible to have pixels values with no tags, but it is. The usual convention is to assume sRGB in such cases.

A color profile describes the color characteristics of some real or imaginary device expressed in terms of a standardized reference (known as the profile connection space (PCS)), typically XYZ or L*a*b*. Both of these are defined by the CIE (Comission Internationale de l'Éclairage).

When converting from one color profile to another, there are options on how to deal with the out-of-gamut colors. If the target gamut entirely includes the source gamut (eg. converting from sRGB to Adobe RGB), then there is no problem. Otherwise, there are four possible rendering intents (the first two are relevant to photography) Back to the example, assume that I configured my camera to generate images using the "Adobe RGB" color profile (this conversion is done by the in-camera RAW to JPG processing). Once my images are on the MacBook, I can open them with Preview. The images get displayed on my MacBook. However we cannot just send the pixel numbers of the image to the GPU for display. It is very unlikely that the result will match the colors intended in my images and the colors will look wrong. Color management is the solution: somehow the pixel numbers of my images need to be converted to numbers that will cause the correct colors to be displayed.

To be able to do this, we measure (using a colorimeter such as SpyderX) the display to determine what colors it generates and how it reacts to input numbers. From this we can generate a color profile that represents the characteristics of the display.

With this color profile, we can now convert the numbers from the original image, thru the profile connection space (eg. XYZ), to the display's color space by doing two steps:
Adobe RGB XYZ display profile.
Of course, in practice, this would probably be done by a single 3D lookup table, in the GPU.

Further reading:

MacOS

[gimp.jpg] In 2020, MacOS is (as far as I'm concerned) a fully color-managed environment, and Preview is a typical app. When it opens an image, it finds out what profile is attached to it. Then, conceptually, two steps are needed to display the image:
image profile (eg. Adobe RGB) XYZ display profile.

Essentially, as I understand it, when an application sends pixels to the MacOS API, it provides both the RGB values and the associated color profile. MacOS then handles the conversion necessary for the display device.

A good overview of color management on MacOS can be found on this Apple page.

GIMP

In the 1990s, color management was often not handled by the OS, but by the application. This probably explains why GIMP's Preferences still has (including on MacOS) an item called "Color Management" (as counterexample, Preview has no such item). I still find this quite confusing.

I am currently running GIMP 2.10. As installed, it does what it should. If one explicitly provides a monitor profile (including via the checkbox for system monitor profile), it will cause two display profiles to be used, resulting in false colors, something like:
Image profile XYZ GIMP monitor profile XYZ MacOS display profile!!!

Also, for a given image, Image Color Management Enable Color Management should be checked. Otherwise, the color profile information carried with the image is ignored.

Display calibration

Much of the head scratching that led to this blog comes from an attempt to calibrate my MacBook's display. I have a SpyderX puck and the SpyderXPro software. But the resulting profile, altho it probably results in better colors, introduces rather significant artefacts. I think this is because the gamut of my display is rather small, about 65% of sRGB (see image at top of this blog), so there is a lot of clipping. Here's an experiment to illustrate this. [triangles] [triangles] [triangles]

There is also clipping at 255. But in comparison, it's very little. Because there is clipping, this means that the conversion was done with a colorometric intent (presumably relative). Perhaps I would prefer a perceptual intent for conversion to my display's color profile. I think the artefacts are more disturbing than color shifts that come with latter. If you're curious, here's my display's color profile.

At first, I thought there might be a bug either with the display profile or with MacOS' handling of it. But probably there is no bug, it's just a not-so-great display, and any other calibration tool would have similar trouble with it. On my main system, with a decent monitor, I see no issue.

Color wxPython/wxWidgets

I've been working on a picture viewer in python 3, based on wxPython 3.8 phoenix (wxWidgets 3.1.4). As far as I can tell, wxPython is not aware of the color profile of images it handles, and (probably implicitly) tells MacOS that the pixel values it sends are sRGB. MacOS then does the conversion for the display profile based on this assumption.

So for now, my app reads the image's profile itself and does a conversion to sRGB for the few color profiles I deal with. I wouldn't be surprised that this will eventually be addressed in this framework.

Tools and techniques

Here are some useful tools and techniques to explore color management on MacOS.