Understanding RawTherapee color output

Hey all,

RawTherapee is outputting colors that are different than what I expect, so I’m trying to understand why.

As a test case I’m giving RT an image with these properties:

  • 16 bit image, 1 sample per pixel

  • 256px * 256px

  • all pixels set to 0x7FFF (the midpoint for a 16-bit pixel)

  • exiftool command to set DNG metadata:

      exiftool                                            \
      -DNGVersion="1.4.0.0"                               \
      -DNGBackwardVersion="1.4.0.0"                       \
      -ColorMatrix1="1 0 0 0 1 0 0 0 1"                   \
      -CalibrationIlluminant1=D65                         \
      -IFD0:BlackLevel=0                                  \
      -IFD0:WhiteLevel=65535                              \
      -PhotometricInterpretation="Color Filter Array"     \
      -SamplesPerPixel=1                                  \
      -IFD0:CFARepeatPatternDim="2 2"                     \
      -IFD0:CFAPattern2="1 0 2 1"                         \
      -overwrite_original                                 \
      img.dng
    

Since ColorMatrix1=the identity matrix, and CalibrationIlluminant1=D65, I expected that RT would interpret the color as XYZ.D65 = [0x7FFF 0x7FFF 0x7FFF] / 0xFFFF = [.5 .5 .5]. Converted to sRGB.D65, that’s [0.799 0.718 0.704], and therefore that’s what I expected RT’s output image to be filled with. I arrived at that value using:

But instead of my expected value, RT outputs sRGB=[0.737 0.737 0.737] (with all image processing disabled, except de-bayering).

Would anyone happen to know how RT arrives at that value, and what exiftool/RT settings I need to get RT to output the expected sRGB color? (I’ve tried many exiftool and RT settings, but RT’s output never matches my manual calculation.)

My test DNG file is here in case it helps:

Thanks,
David

I am no expert but since your DNG does not appear to have directories my guess is that RT interprets your image as a thumbnail in its final color space. I believe raw data is supposed to be in the first SubIFD or however specified in the DNG Spec.

Jack

1 Like

Welcome @daviejones

Very partial answer. II let proficient people give complete answer.

  • color model

There is no XYZ color model (Color model - Wikipedia) at the input of RT. Input can be either RGB color model for demosaiced photo or camera space for raw.
Your input is thus interpreted as RGB values (0.5, 0.5,0.5).
XYZ can be used inside some processing (for instance as connection space)

If you use as input profile “no profile” and as output profile “no profile”, with no processing, the pipeline is a pass-through.
The output file will be RGB (0.5,0.5,0.5)

The values shown are 0.5 corrected with a 2.8 gamma. Note: I think this gamma correction made by design on the WS values is a bad choice.

  • Furthermore, I am not sure that your DNG metadata can be interpreted by Rawtherapee. A test case should be compliant with what the software is made for (here process raw files and demosaiced files).

Output colors depend upon the processing you did. It’s either aimed at being realistic or being artistic. If you look at the playraw here, you will see lots of different processing.
Even the camera jpeg as they are processed inside the camera, have a specific manufacturer look.
see Editor - RawPedia

The best should be to upload an example of raw photo with pp3 sidecar were output colors are not what you expect.

2 Likes

Thanks for the help @JackH and @gaaned92.

I was trying for the latter: give RT values in camera raw space, and since ColorMatrix1=identity, they’d be interpreted as XYZ.D65 values (since the DNG spec defines ColorMatrix1 as translating between XYZ and camera space).

Anyway, since I couldn’t get the DNG fields to work as I expected, I created a custom ICC profile containing my color matrix, and fortunately that works.

In case it helps someone else, I’ve outlined my steps below, which allow you to:

  • control the color matrix that RawTherapee uses for interpreting raw CFA data, and
  • independently verify RawTherapee’s color math.

Controlling the color matrix that RawTherapee uses to interpret color filter array data

  • Create a custom ICC profile

    • Compile the iccFromXml tool from the RefIccMAX project:
      https://github.com/InternationalColorConsortium/RefIccMAX

    • Create the ICC profile in XML. Here’s a template:

      <?xml version="1.0" encoding="UTF-8"?>
      <IccProfile>
          <Header>
              <PreferredCMMType></PreferredCMMType>
              <CreationDateTime>2020-01-01T00:00:00</CreationDateTime>
              <ProfileVersion>4.3.0.0</ProfileVersion>
              <ProfileDeviceClass>scnr</ProfileDeviceClass>
              <DataColourSpace>RGB </DataColourSpace>
              <PCS>XYZ </PCS>
              <DeviceAttributes ReflectiveOrTransparency="reflective" GlossyOrMatte="glossy" MediaPolarity="positive" MediaColour="colour"/>
              <ProfileFlags EmbeddedInFile="false" UseWithEmbeddedDataOnly="false"/>
              <RenderingIntent>Perceptual</RenderingIntent>
              <PCSIlluminant> <XYZNumber X="0.96422" Y="1" Z="0.8249"/> </PCSIlluminant>
          </Header>
      
          <Tags>
              <profileDescriptionTag> <multiLocalizedUnicodeType>
                <LocalizedText LanguageCountry="enUS">Description</LocalizedText>
              </multiLocalizedUnicodeType> </profileDescriptionTag>
      
              <copyrightTag> <multiLocalizedUnicodeType>
                <LocalizedText LanguageCountry="enUS">Copyright</LocalizedText>
              </multiLocalizedUnicodeType> </copyrightTag>
      
              <mediaWhitePointTag> <XYZArrayType> <XYZNumber X="1" Y="1" Z="1"/> </XYZArrayType> </mediaWhitePointTag>
      
              <redColorantTag> <XYZArrayType> <XYZNumber X="1" Y="0" Z="0"/> </XYZArrayType> </redColorantTag>
              <greenColorantTag> <XYZArrayType> <XYZNumber X="0" Y="1" Z="0"/> </XYZArrayType> </greenColorantTag>
              <blueColorantTag> <XYZArrayType> <XYZNumber X="0" Y="0" Z="1"/> </XYZArrayType> </blueColorantTag>
      
              <redTRCTag> <curveType> <Curve> </Curve> </curveType> </redTRCTag>
              <greenTRCTag> <curveType> <Curve> </Curve> </curveType> </greenTRCTag>
              <blueTRCTag> <curveType> <Curve> </Curve> </curveType> </blueTRCTag>
          </Tags>
      </IccProfile>
      
    • Insert the color matrix into the redColorantTag / greenColorantTag / blueColorantTag fields. (Each field is a column of the matrix.)

    • Convert the XML ICC profile into its binary represenation:

      iccFromXml profile.xml profile.icc
      
  • Convert CFA data to a DNG image

    • Save the CFA data as a monochrome TIFF, but name it img.dng

    • Strip metadata from the image:

      exiftool -overwrite_original -all= img.dng

    • Add DNG fields:

      exiftool                                            \
      -DNGVersion="1.4.0.0"                               \
      -DNGBackwardVersion="1.4.0.0"                       \
      -IFD0:BlackLevel=0                                  \
      -IFD0:WhiteLevel=65535                              \
      -PhotometricInterpretation="Color Filter Array"     \
      -SamplesPerPixel=1                                  \
      -IFD0:CFARepeatPatternDim="2 2"                     \
      -IFD0:CFAPattern2="1 0 2 1"                         \
      -overwrite_original                                 \
      img.dng
      
  • Load img.dng in RawTherapee

    • Disable all image processing (except demosaicing)
    • Input Profile: select the custom ICC profile
    • Output Profile: RTv4_sRGB

Verifying RawTherapee’s color output

Given a 16-bit image filled with [0x7FFF 0x7FFF 0x7FFF]/65535 = [.5 .5 .5], RawTherapee should output sRGB = [0.744 0.726 0.812] = [190 185 207], assuming the ICC profile contains the identity matrix. Other color values (using the identity matrix) can be verified using this color calculator:

The generalized formula to determine the sRGB output color for any rawColor and colorMatrix is:

srgb = SRGBFromLSRGB(LSRGB_From_XYZ * XYZ_D65_From_XYZ_D50_Bradford * colorMatrix * rawColor)

where:

// From brucelindbloom.com
LSRGB_From_XYZ = [ 3.2404542 -1.5371385 -0.4985314 ; -0.9692660 1.8760108 0.0415560 ; 0.0556434 -0.2040259 1.0572252 ]
XYZ_D65_From_XYZ_D50_Bradford = [ 0.9555766 -0.0230393 0.0631636 ; -0.0282895 1.0099416 0.0210077 ; 0.0122982 -0.0204830 1.3299098 ]

float SRGBFromLSRGB(float x) {
    if (x <= 0.0031308) return 12.92*x;
    return 1.055*pow(x, 1/2.4) - .055;
}

Note that SRGBFromLSRGB() is applied to each component separately.


Anyway hopefully that helps someone in the future.

David