Illuminant correction
Illuminant correction is often performed by calculating a 3x3 matrix that maps the XYZ values for surfaces under one light into the XYZ values under another light.
Here, we create a scene that consists of patches illuminated by light A these patches can be created by randomly sampling surfaces reflectances the selection of surfaces will matter
We then create another scene that consists of the same patches illuminated by light B
We calculate a general 3x3 matrix that maps the XYZ values for surfaces under light A into the XYZ values of surfaces under light B. We then find a 3x3 diagonal matrix that maps the XYZ values for surfaces under light A into the XYZ values of surfaces under light B.
The general 3x3 matrix does a better job than the diagonal (as expected).
Some authors refer to the diagonal 3x3 as von Kries adaptation Note that a matrix that is diagonal in one coordinate frame (XYZ) will not generally be diagonal in another coordinate frame (e.g., LMS or RGB). Von Kries meant diagonal in LMS.
See also: RGB2XWFormat
Copyright ImagEval Consultants, LLC, 2012.
Contents
ieInit
Create a test scene (Natural 100)
% Set the illumination to D65 scene = sceneCreate('reflectance chart'); sceneD65 = sceneAdjustIlluminant(scene,'D65.mat'); sceneD65 = sceneSet(sceneD65,'name','Reflectance Chart D65'); ieAddObject(sceneD65); sceneWindow;

Solve for matrix relating the chart under two different lights
% This are the surfaces under a D65 light xyz1 = sceneGet(sceneD65,'xyz'); % This is a nSample x 3 representation of the surfaces under D65 xyz1 = RGB2XWFormat(xyz1);
This are the surfaces under a Tungsten light
sceneT = sceneAdjustIlluminant(scene,'Tungsten.mat'); xyz2 = sceneGet(sceneT,'xyz'); % This is a nSample x 3 representation of the surfaces under Tungsten xyz2 = RGB2XWFormat(xyz2); % We are looking for a 3x3 matrix, L, that maps % % xyz1 = xyz2 * L % L = inv(xyz2'*xyz2)*xyz2'*xyz1 = pinv(xyz2)*xyz1 % % Or, we just use the \ operator from Matlab for which inv(A)*B is A\B L = xyz2 \ xyz1; % To solve with just a diagonal, do it one column at a time D = zeros(3,3); for ii=1:3 D(ii,ii) = xyz2(:,ii) \ xyz1(:,ii); end
Plot predicted versus actual
vcNewGraphWin; pred2 = xyz2*L; plot(xyz1(:),pred2(:),'o'); grid on title('Full inear'); xlabel('Observed XYZ'); ylabel('Predicted XYZ') vcNewGraphWin; pred2 = xyz2*D; plot(xyz1(:),pred2(:),'o'); grid on title('Diagonal'); xlabel('Observed XYZ'); ylabel('Predicted XYZ')

