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
iccFromXmltool from theRefIccMAXproject:
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/blueColorantTagfields. (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.dngin 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:
- Welcome to Bruce Lindbloom's Web Site
SetRef. White=D50, since ICC profiles use D50 as the illuminant
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