dip::ColorSpaceManager class

An object of this class is used to convert images between color spaces.

By default, the object will known a set of color spaces, and be able to convert between them. It is possible to define new color spaces, see below.

To convert an image into a different color space, simply call the dip::ColorSpaceManager::Convert function. In this code snippet, we first set the image’s color space to sRGB. This causes no change to the pixel values, it simply tags the image object with the color space name.

dip::ColorSpaceManager csm;
dip::Image img = ...
img.SetColorSpace( "sRGB" );                     // img is sRGB
dip::Image img_lab = csm.Convert( img, "Lab" );  // img_lab will be Lab

These are the color spaces known by default. Color space names are case-sensitive, but aliases are registered for all these names using all-lowercase.

Name Aliases Description
"grey" "gray" An empty string is also interpreted as grey. Defined to be in the range [0,255].
"RGB" Linear RGB, defined in the range [0,255].
"sRGB" "R'G'B'" Industry-standard non-linear, gamma-corrected RGB (average gamma is approximately 2.2, with a linear segment near 0). Values are in the range [0,255].
"CMY" Cyan-Magenta-Yellow. Subtractive colors, defined simply as 255-RGB. Values are in the range [0,255].
"CMYK" Cyan-Magenta-Yellow-blacK. Subtractive colors with black added. Note that printers need a more complex mapping to CMYK to work correctly.
"HSI" Hue-Saturation-Intensity. L1 norm polar decomposition of the RGB cube, more suited to image analysis than HSV or HCV. S and I are in the range [0,255], H is an angle in degrees. Defined by Hanbury and Serra (2003).
"ICH" Intensity-Chroma-Hue. Rotation of the RGB cube, where I is along the black-white diagonal of the cube, and the CH-plane is perpendicular. I is in the range [0,255], H is an angle in degrees.
"ISH" Intensity-Saturation-Hue. Based in ICH, where S is the C channel normalized so that the maximum saturation for each H is 1. For each H, the largest value of C is attained for a different value of I.
"HCV" Hue-Chroma-Value. V is the max of R, G and B, and C is the difference between largest and smallest RGB intensities. C and V are in range [0,255], H is an angle in degrees.
"HSV" Hue-Saturation-Value. Based on HCV, where S is equal to C normalized by V. S is in range [0,1] and V in range [0,255], H is an angle in degrees.
"Y'PbPr" "YPbPr", "YPP" Luma and two chroma components. Computed from R’G’B’ (non-linear, gamma-corrected RGB). Y’ is in the range [0,1], and Pb and Pr are in the range [-0.5,0.5].
"Y'CbCr" "YCbCr", "YCC" Luma and two chroma components. Scaled Y’PbPr such that all three components are in the range [0,255]. Sometimes incorrectly referred to as YUV.
"XYZ" CIE 1931 XYZ, standard observer tristimulus values. A rotation of the (linear) RGB cube that aligns Y with the luminance axis. X, Y and Z are in the range [0,1].
"Yxy" CIE Yxy, where x and y are normalized X and Y. They are the chromaticity coordinates.
"Lab" "L*a*b*", "CIELAB" Lightness and two chromaticity coordinates. One of two color spaces proposed by CIE in 1976 in an attempt for perceptual uniformity. L is in the range [0,100], and a and b are approximately in the range [-100,100].
"Luv" "L*u*v*", "CIELUV" Lightness and two chromaticity coordinates. One of two color spaces proposed by CIE in 1976 in an attempt for perceptual uniformity. L is in the range [0,100], and u and v are in a range significantly wider than [-100,100].
"LCH" "L*C*H*" Lightness-Chroma-Hue. Computed from CIE Lab, where C and H are the polar coordinates to a and b. H is an angle in degrees.
"Oklab" An “OK Lab colorspace”, a better approximation to perceptual uniformity than CIE Lab. Oklab was designed to better predict CAM16-UCS results than other existing color spaces, while being numerically stable. Assumes a D65 white point. L is in the range [0,1], a and b are approximately in the range [-1,1]. Defined by Ottosson (2020).
"Oklch" Lightness-Chroma-Hue derived from Oklab, where C and H are the polar coordinates to a and b. H is an angle in degrees.
"wavelength" Can only be converted from, not to. Yields an approximate color representation for the given wavelength in nanometers, in the range 380 through 780 nanometers. For values outside the range, produces black. The conversion to XYZ is according to CIE rec. 709, but most of these colors lie outside of the RGB gamut. The conversion to RGB produces colors within the gamut, computed according to Young (2012).

Note that most color images are stored to file as (nonlinear) sRGB. After loading a color image, it is therefore often advantageous to convert the image to (linear) RGB for computation (or some other desired color space).

When converting to/from gray, it is assumed that gray is linear (i.e. a weighted addition of the linear R, G and B values, the weights depending on the white point). But in the case of HSI and ISH color spaces, the I channel is the gray channel; I is a non-weighted mean of linear RGB, the conversion does not take the white point into accout.

Defining a new color space

It is possible to define new color spaces, and register conversion functions that translate from one color space to another. The ColorSpaceManager object is capable of finding optimal paths trough the graph defined by these conversion functions, to convert between color spaces. Thus, it is not necessary to create functions that translate from your new color space to all known color spaces, it is sufficient to register two function that translate to and from your new color space from/to any existing color space.

In this example, we define a conversion from a new color space (Frank) to XYZ, and from Yxy to Frank. The dip::ColorSpaceManager::Convert function then is able to convert an image from any color space to Frank, and from Frank to any color space.

dip::ColorSpaceManager csm;
csm.Define( "Frank", 4 );                        // A new color space with 4 channels
csm.DefineAlias( "f", "Frank" );                 // "f" is an alias for "Frank"
csm.Register( std::make_shared< frank2xyz >() ); // an object that converts from Frank to XYZ
csm.Register( std::make_shared< yxy2frank >() ); // an object that converts from Yxy to Frank

dip::Image img = ...                             // assume img is sRGB
csm.Convert( img, img, "f" );                    // img will be converted from sRGB to Frank

In the example above, frank2xyz and yxy2frank are objects derived from dip::ColorSpaceConverter.

Note that one could add conversion functions to and from more color spaces as deemed appropriate, for example to save computational time. And it is not necessary for a new color space to have a conversion path to or from it. For example, by default the “wavelength” color space has only a conversion function from it to XYZ and to RGB, there are no functions that can convert to the “wavelength” color space.

Constructors, destructors, assignment and conversion operators

ColorSpaceManager()
Constructor, registers the default color spaces.

Aliases

using ColorSpaceConverterPointer = std::shared_ptr<ColorSpaceConverter>
A shared pointer to a dip::ColorSpaceConverter object.

Functions

void Define(dip::String colorSpaceName, dip::uint nChannels)
Defines a new color space, that requires nChannels channels.
void DefineAlias(dip::String const& alias, dip::String const& colorSpaceName)
Defines an alias for a defined color space name.
void Register(dip::ColorSpaceManager::ColorSpaceConverterPointer converter)
Registers a function object to translate from one color space to another. The dip::ColorSpaceManager object takes ownership of the converter.
void Register(dip::ColorSpaceConverter* converter)
Overload for the previous function, for backwards compatibility. Not recommended.
auto IsDefined(dip::String const& colorSpaceName) const -> bool
Check to see if a color space name is defined.
auto GetColorSpaceConverter(dip::String const& inputColorSpaceName, dip::String const& outputColorSpaceName) const -> dip::ColorSpaceConverter*
Gets a pointer to a color space converter object registered with this ColorSpaceManager. Use this to access the object to modify it, for example configure a parameter.
auto NumberOfChannels(dip::String const& colorSpaceName) const -> dip::uint
Returns the number of channels used by the given color space.
auto CanonicalName(dip::String const& colorSpaceName) const -> dip::String const&
Returns the canonical name for the given color space (i.e. looks up name aliases).
void Convert(dip::Image const& in, dip::Image& out, dip::String const& colorSpaceName = "") const
Converts an image to a different color space.
void SetWhitePoint(dip::XYZ whitePoint)
Configure the conversion functions to use the given white point.
void SetWhitePoint(dip::xy const& whitePoint)
Overload of the function above that takes a (x,y) chromaticity coordinate.

Variables

static dip::XYZ const IlluminantA
The CIE Standard Illuminant A (typical, domestic, tungsten-filament lighting).
static dip::XYZ const IlluminantD50
The CIE Standard Illuminant D50 (mid-morning or mid-afternoon daylight, color temperature is about 5000 K).
static dip::XYZ const IlluminantD55
The CIE Standard Illuminant D55 (morning or evening daylight, color temperature is about 5500 K).
static dip::XYZ const IlluminantD65
The CIE Standard Illuminant D65 (noon daylight, color temperature is about 6500 K). This is also used in the sRGB standard.
static dip::XYZ const IlluminantE
The CIE Standard Illuminant E (synthetic, equal energy illuminant).

Function documentation

void Convert(dip::Image const& in, dip::Image& out, dip::String const& colorSpaceName = "") const

Converts an image to a different color space.

Both the source (in.ColorSpace()) and destination (colorSpaceName) color spaces must be known, and a path of registered conversion functions must exist between the two.

Note that it is possible to assign an arbitrary string as a color space name in an image. Setting an image’s color space property is always possible, and gives no guarantee that the image has the right number of tensor elements (color channels).

When converting from one color channel to another, the input image is checked for number of color channels. If it doesn’t match the number expected for its color space, an exception will be thrown.

If in.ColorSpace() is an empty string:

  • If the image has the same number of color channels as expected for colorSpaceName, it will be assumed that the image already is in the colorSpaceName color space, and no conversion is done.
  • Else, if the image is scalar, it will be assumed that its color space is “grey”.
  • Otherwise, an exception will be thrown.

If colorSpaceName is an empty string, “grey” is assumed.

The output image is of a floating-point type. If you want the output to be written in, for example, an 8-bit unsigned integer, you can use the “protect” flag on the output image:

dip::Image in = ...; // read in a color image
dip::Image out;
out.SetDataType( dip::DT::UINT8 );
out.Protect();
dip::ColorSpaceManager csm;
cms.Convert( in, out, "HSV" );

In this case, all computations are still performed as double-precision floating-point computations, but the result is cast to 8-bit unsigned integers when written to the output image. Some color spaces, such as RGB and CMYK are defined to use the [0,255] range of 8-bit unsigned integers. Other color spaces such as Lab and XYZ are not. For those color spaces, casting to an integer will destroy the data.

void SetWhitePoint(dip::XYZ whitePoint)

Configure the conversion functions to use the given white point.

This will configure each of the converter functions that use the white point information (grey ↔ RGB ↔ XYZ ↔ Lab/Luv). The default white point is the Standard Illuminant D65 (dip::ColorSpaceManager::IlluminantD65).

The white point is given as an XYZ triplet or (x,y) chromaticity coordinates.