I converted a DCP camera profile of my Nikon D5200 to a readable format with dcpTool
to learn about forward and color matrixes (as I first came across in Android’s camera2 API here and here )
As the Android documentation explains, the forward matrix is for transforming from sensor to XYZ space, and the color matrix for transforming from XYZ to sensor space.
What is the use of a re-transformation to sensor colorspace? I see that this color matrix even come first in the XML produced from DCP files in dcpTool
:
<dcpData>
<ProfileName>Adobe Standard</ProfileName>
<CalibrationIlluminant1>17</CalibrationIlluminant1>
<CalibrationIlluminant2>21</CalibrationIlluminant2>
<ColorMatrix1 Rows="3" Cols="3">
<Element Row="2" Col="2">0.704100</Element>
<Element Row="2" Col="1">0.075200</Element>
<Element Row="2" Col="0">-0.036700</Element>
<Element Row="1" Col="2">0.309800</Element>
<Element Row="1" Col="1">1.260100</Element>
<Element Row="1" Col="0">-0.534900</Element>
<Element Row="0" Col="2">-0.046700</Element>
<Element Row="0" Col="1">-0.374500</Element>
<Element Row="0" Col="0">0.869300</Element> 111x... = 0.2977 0.9613 0.9672
</ColorMatrix1>
<ColorMatrix2 Rows="3" Cols="3">
<Element Row="2" Col="2">0.639400</Element>
<Element Row="2" Col="1">0.163800</Element>
<Element Row="2" Col="0">-0.098800</Element>
<Element Row="1" Col="2">0.217900</Element>
<Element Row="1" Col="1">1.434200</Element>
<Element Row="1" Col="0">-0.636700</Element>
<Element Row="0" Col="2">-0.104700</Element>
<Element Row="0" Col="1">-0.311200</Element>
<Element Row="0" Col="0">0.832200</Element>
</ColorMatrix2>
<ForwardMatrix1 Rows="3" Cols="3">
<Element Row="2" Col="2">1.015300</Element>
<Element Row="2" Col="1">-0.216100</Element>
<Element Row="2" Col="0">0.025900</Element>
<Element Row="1" Col="2">-0.081200</Element>
<Element Row="1" Col="1">0.804300</Element>
<Element Row="1" Col="0">0.276800</Element>
<Element Row="0" Col="2">0.096800</Element>
<Element Row="0" Col="1">0.203800</Element>
<Element Row="0" Col="0">0.663700</Element>
</ForwardMatrix1>
<ForwardMatrix2 Rows="3" Cols="3">
<Element Row="2" Col="2">0.936800</Element>
<Element Row="2" Col="1">-0.126600</Element>
<Element Row="2" Col="0">0.014900</Element>
<Element Row="1" Col="2">-0.163900</Element>
<Element Row="1" Col="1">0.921500</Element>
<Element Row="1" Col="0">0.242400</Element>
<Element Row="0" Col="2">0.039400</Element>
<Element Row="0" Col="1">0.371300</Element>
<Element Row="0" Col="0">0.553600</Element>
</ForwardMatrix2>
...
I wrote a test using mathjs to at least proof that the color matrix will recover the original sensor RGB:
const math = require('mathjs')
const fwd = math.matrix([
[0.6637,0.2038,0.0968],
[0.2768,0.8043,-0.0812],
[0.0259,-0.2161,1.0153]]),
invFwd = math.inv(fwd),
color = math.matrix([
[0.8693,0.3745,-0.0467],
[-0.5349,1.2601,0.3098],
[-0.0367,0.0752,0.7041]])
console.log("Inverted fwd", invFwd.valueOf())
var sens = math.matrix([1, 1, 1]),
xyz = math.multiply(fwd, sens)
console.log("xyz", xyz.valueOf())
sens = math.multiply(invFwd, xyz)
console.log("sens2 with inverted fwd", sens.valueOf())
sens = math.multiply(color, xyz)
console.log("sens2 with color", sens.valueOf())
Result:
Inverted fwd [
[ 1.7190528372458245, -0.4901558556027806, -0.20309757718934465 ],
[ -0.6091286267429826, 1.444302076495557, 0.17358512723348757 ],
[ -0.17350159039084548, 0.319914030720774, 1.0270579860576783 ]
]
xyz [ 0.9642999999999999, 0.9999, 0.8251000000000002 ]
sens2 with inverted fwd [ 0.9999999999999998, 0.9999999999999999, 1.0000000000000002 ]
sens2 with color [ 1.1741963699999998, 0.9997859, 0.6207555800000001 ]
According to the definition, I’d expect that the inverted forward matrix is the same as the color matrix (with the same illuminant), but this is not the case; and the back transformation to sensor space results in a different value.
Either Android’s definition or the DCP profile is wrong or I have just misunderstood sth. important.