Transformation of sensor's colors into sRGB

in Matlab

according to DNG specification


Transformation of camera sensor's output into sRGB values can be done by algorithm from specification of DNG ("digital negative") format. Needed parameters of the image and the camera are taken from metadata of DNG files, created from camera's raw images with Adobe DNG Converter. This works for a large number of camera models.

The following script implements the algorithm from Adobe Digital Negative (DNG) Specification version 1.4.0.0, 2012 (the latest one as for 2018), pages 79-82. The only simplification is absence of supporting of cameras, whose color space has dimensionality >3 (whose G1 and G2 are different colors).

You need one parameter of the photo ("As Shot Neutral" or "As Shot White xy") and several parameters of the camera (in the general case including parameters of individual camera specimen). Majority of the parameters is taken from Exif of the DNG files (you can read it with RawDigger). Matrix variables are given there as a string of numbers, which must fill the matrix row-wise. "As Shot Neutral" can also be obtained without DNG conversion: you can calculate it from "Camera multipliers" given by dcraw (command dcraw-9.27-ms-32-bit.exe -i -v *.ARW). The "Camera multipliers" must be divided by 1024 and inversed. For example, if "Camera multipliers" are [2088 1024 1964 1024] (they are given for R, G1, B, G2), "As Shot Neutral" will be [0.490421 1 0.521385] (given for R, G, B without separation of G1 and G2).

Values of variables corr_col_temp, xy_illum_1 and xy_illum_2 must be googled. Two elements of the corr_col_temp are correlated color temperatures of illuminants from EXIF tags "Calibration Illuminant 1" and "Calibration Illuminant 2" (e.g., if "Calibration Illuminant 1" is "Standard Light A", corr_col_temp(1) is 2856). Variables xy_illum_1 and xy_illum_2 are xy (chromaticity) values of these illuminants.

The script needs two functions:

    • xy2cct.m from OPTPROP toolbox by Jerker Wagberg (folder of the toolbox should be added to Matlab's search path);

    • the script bradford.m by Marcel Patek, converted into a function. To convert it, you should:

        • add at the beginning the string

function [Mbfd_nl] = bradford(DXX)

        • add at the end the string

end

        • delete the string

DXX = [95.105 100.000 108.548];

The following script contains matrices of conversion between sRGB and XYZ using reference white D50, taken from Bruce Lindbloom's site.

Given values of parameters of the camera relate to Sony NEX-5. In the case of my camera, "Camera Calibration" matrices (which must describe the difference between individual camera specimen and reference camera) are actually harmful: they cause turning white objects into pinkish and must be ignored.

% Transformation of camera sensor's brightness values into sRGB values

% according to Adobe Digital Negative (DNG) Specification (version 1.4.0.0, 2012)

% (https://www.adobe.com/content/dam/acom/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf)

% both possible methods from the specification are implemented:

% using "Forward Matrices" and using "Color Matrices".

%

% Input variables:

% one parameter of the photo ("As Shot Neutral" or "As Shot White xy"),

% several parameters of the camera (including parameters of individual camera specimen),

% several settings of the script,

% test image and its darkness and saturation levels.

%

% This script needs the function xy2cct.m from OPTPROP toolbox by Jerker Wagberg

% (https://www.mathworks.com/matlabcentral/fileexchange/13788-optprop-a-color-properties-toolbox)

% and the script bradford.m by Marcel Patek (http://www.marcelpatek.com/color.html),

% converted into a function (with DXX as input and Mbfd_nl as output).

%

% This script contains matrices of conversion between sRGB and XYZ using reference white D50

% from Bruce Lindbloom's site: http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html.

%

% Shortcomings: the script works only with cameras, whose color space has dimensionality 3

% (i.e., camera has no differences between G1 and G2). Generalization is welcomed.

%

% Indices in the names of variables:

% "_m1" (method 1): using "Forward Matrices"; "_m2" (method 2): using "Color Matrices",

% "_clean": matrices for images, already corrected for

% white balance, color sensitivity of individual camera and "analog balance";

% "_norm": matrices, normalized to convert corrected sensor values [1;1;1] into sRGB [1;1;1].

%

% Source: https://sites.google.com/site/crossstereo/raw-converting/dng

clear all

close all

clc


% parameters of the photo:

% (only one of these parameters is given for each camera;

% if you have "As Shot Neutral", assign as_shot_switch = 1, if "As Shot White xy" - 2):

as_shot_switch = 1;

switch as_shot_switch

case 1

% "As Shot Neutral" (selected white balance at time of capture;

% coordinates of a perfectly neutral color in linear reference space values):

as_shot_neutral = [0.493256 1 0.535565];

case 2

% "As Shot White xy" (selected white balance at time of capture;

% x-y chromaticity coordinates):

as_shot_white_xy = [0.384296 0.396109];

end


% settings of the script:

% ignore "Camera Calibration" matrices (turned out to be necessary for Sony NEX-5):

ignore_cc = 1;

% allow extrapolation of matrices, if

% color temperature of the illumination is outside corr_col_temp range:

% (DNG specification prescribes not extrapolation, but taking of closest variant)

allow_extr = 0;

% for testing: replace white-balance-expressing and related variables

% with values, corresponding to the lighting by calibration illuminant 1 or 2:

% 0 - no, 1 - to the illuminant 1, 2 - to the illuminant 2:

test_illum = 0;

% for testing: normalize brightnesses of white-balance-expressing variables to 1:

% (this option doesn't affect matrices for images with already applied corrections

% for white balance, color sensitivity of individual camera and similar things.

% For other images (with original colors), it is harmful, expressing in

% increase of discrepancy between matrices, obtained with and without ForwardMatrix.)

normalize_wb_vars = 0;


% test image (mosaiced or RGB) with original raw colors:

im = imread('D:\Tech\преобразование RAW\тестовые картинки\2018-02-09 white\DSC09242.tiff');

% [dark_brightness saturation_brightness]:

dark_sat = [128 4090]; % values for Sony NEX-5

% cropping:

im = im(701:1000, 1701:2000, :);


% parameters of the camera:

% "Analog Balance":

ab = [1 0 0; 0 1 0; 0 0 1];

% temperatures of illuminants 1 and 2:

corr_col_temp = [2856 6504];

% xy of illuminants 1 and 2:

xy_illum_1 = [0.44758 0.40745];

xy_illum_2 = [0.31271 0.32902];

% "Color Matrix" 1 and 2:

% (XYZ values -> reference camera native space values)

% (under illuminants 1 and 2)

cm_1 = [0.671 -0.1934 -0.0172; -0.4303 1.1507 0.3202; -0.0527 0.1307 0.7431];

cm_2 = [0.6549 -0.155 -0.0436; -0.488 1.2435 0.2753; -0.0854 0.1868 0.6976];

% "Camera Calibration" 1 and 2:

% (reference camera native space values -> individual camera native space values)

% (under illuminants 1 and 2)

cc_1 = [0.9415 0 0; 0 1 0; 0 0 1.0057];

cc_2 = [0.9415 0 0; 0 1 0; 0 0 1.0057];

% "Forward Matrix" 1 and 2 (if absent, assign forward_matrix_exists = 0):

% (white balanced reference camera colors -> XYZ D50 colors)

% (under illuminants 1 and 2)

forward_matrix_exists = 1;

if forward_matrix_exists

fm_1 = [0.7587 0.0956 0.11; 0.2653 0.8476 -0.113; 0.0286 -0.3588 1.1554];

fm_2 = [0.7467 0.2686 -0.051; 0.2927 1.0103 -0.303; 0.0074 -0.1697 0.9874];

end

% "Camera Calibration Signature":

cc_sig = 'com.adobe';

% "Profile Calibration Signature":

pc_sig = 'com.adobe';


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% end of settings


% preprocessing of the test image:

im = double(im);

% converting of brightness to [0 - 1] interval:

im = (im - dark_sat(1)) / (dark_sat(2) - dark_sat(1));

im(im<0) = 0;

im(im>1) = 1;

if size(im, 3) == 1

% demosaicing:

im = uint16(65535*im);

im = demosaic(im, 'rggb');

im = double(im)/65535;

end


% function of image transformation according to a 3x3 matrix:

img_trans_mat = @(img, mat) [cat(3, ...

mat(1,1) * img(:,:,1) + mat(1,2) * img(:,:,2) + mat(1,3) * img(:,:,3), ...

mat(2,1) * img(:,:,1) + mat(2,2) * img(:,:,2) + mat(2,3) * img(:,:,3), ...

mat(3,1) * img(:,:,1) + mat(3,2) * img(:,:,2) + mat(3,3) * img(:,:,3))];


% translation of sRGB (based on D65) and XYZ with "Reference white" D50, as DNG algorithm needs:

% (from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html)

xyz2rgb = [...

[ 3.1338561 -1.6168667 -0.4906146];

[-0.9787684 1.9161415 0.0334540];

[ 0.0719453 -0.2289914 1.4052427]];

rgb2xyz = [...

[ 0.4360747 0.3850649 0.1430804];

[ 0.2225045 0.7168786 0.0606169];

[ 0.0139322 0.0971045 0.7141733]];


if ignore_cc

disp(['camera calibration matrices are ignored']);

disp([' ']);

% replacing camera calibration matrices with identity matrices:

cc_1 = [1 0 0; 0 1 0; 0 0 1];

cc_2 = [1 0 0; 0 1 0; 0 0 1];

end


% comparison of camera calibration signature and profile calibration signature:

% (according to p.79 of DNG specification)

if ~isequal(cc_sig, pc_sig) && ~ignore_cc

disp(['camera calibration signature differs from profile calibration signature;']);

disp([' camera calibration matrices are not applied']);

disp([' ']);

% replacing camera calibration matrices with identity matrices:

cc_1 = [1 0 0; 0 1 0; 0 0 1];

cc_2 = [1 0 0; 0 1 0; 0 0 1];

end


switch as_shot_switch

case 1 % if "As Shot Neutral" exists and "As Shot White xy" - no:

as_shot_neutral = as_shot_neutral';

% iterative "translating camera neutral coordinates to white balance xy coordinates"

% with determination of correlated color temperature and interpolating of matrices

% (search of correlated color temperature, at which

% interpolated matrices convert "As Shot Neutral" to xy values,

% which correspond to this corr. col. temperature)

disp('search of correlated color temperature of the illumination and other parameters...');

corr_col_temp_curr = mean(corr_col_temp); % initial value for iterations

% (100 iterations is more than enough)

for s = 1 : 100

coeff_for_interp = (...

(1/corr_col_temp_curr - 1/corr_col_temp(1)) /...

(1/corr_col_temp(2) - 1/corr_col_temp(1)));

% current interpolated matrices:

cc_curr = cc_1 + (cc_2 - cc_1) * coeff_for_interp;

cm_curr = cm_1 + (cm_2 - cm_1) * coeff_for_interp;

if forward_matrix_exists

fm_curr = fm_1 + (fm_2 - fm_1) * coeff_for_interp;

end

% matrix of translation of XYZ values to color space of the given specimen of camera

% at the current trial corr. color temperature:

XYZ_to_camera_curr = ab * cc_curr * cm_curr;

% response of the given specimen of camera to neutral color:

camera_neutral = cc_curr * as_shot_neutral;

if normalize_wb_vars

camera_neutral = camera_neutral / camera_neutral(2); % normalizing to 1 for G

end

% in XYZ system:

XYZ_curr = (XYZ_to_camera_curr^(-1)) * camera_neutral;

if normalize_wb_vars

XYZ_curr = XYZ_curr / XYZ_curr(2); % normalizing to 1 for Y

end

% white balance xy coordinates:

xy = [XYZ_curr(1) XYZ_curr(2)] / sum(XYZ_curr);

disp([' iteration ' num2str(s) blanks(3-length(num2str(s)))...

': coeff. for interp.: ' num2str(coeff_for_interp)...

', corr. color temperature: ' num2str(corr_col_temp_curr, '%.2f')]);

% new value of the correlated color temperature:

corr_col_temp_curr = xy2cct(xy);

end

if ~allow_extr % if extrapolation of matrices is disallowed (following DNG specification):

% if the color temperature falls outside the interval "corr_col_temp(1)...corr_col_temp(2)",

% extrapolated value is replaced by closest base value:

if corr_col_temp_curr < corr_col_temp(1) || corr_col_temp_curr > corr_col_temp(2)

disp(['found corr.col.temp (' num2str(corr_col_temp_curr, '%.2f')...

') is outside corr_col_temp range '...

num2str(corr_col_temp(1)) ' - ' num2str(corr_col_temp(2)) '; closest value is taken']);

if corr_col_temp_curr < corr_col_temp(1)

corr_col_temp_curr = corr_col_temp(1);

end

if corr_col_temp_curr > corr_col_temp(2)

corr_col_temp_curr = corr_col_temp(2);

end

end

% recalculating of interpolation-dependent variables:

coeff_for_interp = (...

(1/corr_col_temp_curr - 1/corr_col_temp(1)) /...

(1/corr_col_temp(2) - 1/corr_col_temp(1)));

cc_curr = cc_1 + (cc_2 - cc_1) * coeff_for_interp;

cm_curr = cm_1 + (cm_2 - cm_1) * coeff_for_interp;

if forward_matrix_exists

fm_curr = fm_1 + (fm_2 - fm_1) * coeff_for_interp;

end

XYZ_to_camera_curr = ab * cc_curr * cm_curr;

camera_neutral = cc_curr * as_shot_neutral;

XYZ_curr = (XYZ_to_camera_curr^(-1)) * camera_neutral;

if normalize_wb_vars

XYZ_curr = XYZ_curr / XYZ_curr(2); % normalizing to 1 for Y

end

xy = [XYZ_curr(1) XYZ_curr(2)] / sum(XYZ_curr);

end

disp(' ');

case 2 % if "As Shot White xy" exists and "As Shot Neutral" - no:

% correlated color temperature:

xy = as_shot_white_xy;

corr_col_temp_curr = xy2cct(xy);

if ~allow_extr % if extrapolation of matrices is disallowed (following DNG specification):

% if the color temperature falls outside the interval "corr_col_temp(1)...corr_col_temp(2)",

% extrapolated value is replaced by closest base value:

if corr_col_temp_curr < corr_col_temp(1) || corr_col_temp_curr > corr_col_temp(2)

disp(['found corr.col.temp (' num2str(corr_col_temp_curr, '%.2f')...

') is outside corr_col_temp range '...

num2str(corr_col_temp(1)) ' - ' num2str(corr_col_temp(2)) '; closest value is taken']);

if corr_col_temp_curr < corr_col_temp(1)

corr_col_temp_curr = corr_col_temp(1);

end

if corr_col_temp_curr > corr_col_temp(2)

corr_col_temp_curr = corr_col_temp(2);

end

end

end

coeff_for_interp = (...

(1/corr_col_temp_curr - 1/corr_col_temp(1)) /...

(1/corr_col_temp(2) - 1/corr_col_temp(1)));

% current interpolated matrices:

cc_curr = cc_1 + (cc_2 - cc_1) * coeff_for_interp;

cm_curr = cm_1 + (cm_2 - cm_1) * coeff_for_interp;

if forward_matrix_exists

fm_curr = fm_1 + (fm_2 - fm_1) * coeff_for_interp;

end

% matrix of translation of XYZ values to the color space of given specimen of the camera:

XYZ_to_camera_curr = ab * cc_curr * cm_curr;

% XYZ of neutral color:

XYZ_curr = [xy(1)/xy(2); 1; (1 - xy(1) - xy(2)) / xy(2)];

% response of the given specimen of camera to neutral color:

camera_neutral = XYZ_to_camera_curr * XYZ_curr;

if normalize_wb_vars

camera_neutral = camera_neutral / camera_neutral(2); % normalizing to 1 for G

end

end

disp(['white balance correlated color temperature: ' num2str(corr_col_temp_curr, '%.2f')]);

disp(['white balance xy coordinates: ' num2str(xy, ' %.6f')]);

disp(['white balance XYZ: ' num2str(XYZ_curr', ' %.6f')]);

disp(['white balance: response of the given camera: ' num2str(camera_neutral', ' %.6f')]);



% for testing: make "as_shot_neutral" and related variables

% to correspond to the lighting by the illuminant number test_illum:

% (Sony NEX-5 has illum.1 = "Standard Light A" and illum.2 = "D65")

if test_illum

disp(' ');

switch test_illum

case 1

cc_curr = cc_1;

cm_curr = cm_1;

if forward_matrix_exists

fm_curr = fm_1;

end

xy = xy_illum_1;

corr_col_temp_curr = corr_col_temp(1);

case 2

cc_curr = cc_2;

cm_curr = cm_2;

if forward_matrix_exists

fm_curr = fm_2;

end

xy = xy_illum_2;

corr_col_temp_curr = corr_col_temp(2);

end

disp([' test work: as in the case of illuminant ' num2str(test_illum) ':']);

XYZ_to_camera_curr = ab * cc_curr * cm_curr;

XYZ_curr = [xy(1)/xy(2); 1; (1 - xy(1) - xy(2)) / xy(2)];

% response of the given specimen of camera to neutral color:

camera_neutral = XYZ_to_camera_curr * XYZ_curr;

if normalize_wb_vars

camera_neutral = camera_neutral / camera_neutral(2); % normalizing to 1 for G

end

% response of the reference camera to neutral color:

as_shot_neutral = (cc_curr^(-1)) * camera_neutral;

disp([' new values of parameters:']);

disp(['white balance correlated color temperature: ' num2str(corr_col_temp_curr, '%.2f')]);

disp(['white balance xy coordinates: ' num2str(xy, ' %.6f')]);

disp(['white balance XYZ: ' num2str(XYZ_curr', ' %.6f')]);

disp(['white balance: response of the given camera: ' num2str(camera_neutral', ' %.6f')]);

clear x t cc_1 cc_2 cm_1 cm_2 fm_1 fm_2 corr_col_temp

end

disp(' ');


% reference_neutral: response of the reference camera to neutral color,

% camera_neutral: response of the given camera specimen to neutral color

reference_neutral = ((ab * cc_curr)^(-1)) * camera_neutral;

reference_neutral = [reference_neutral(1) 0 0; 0 reference_neutral(2) 0; 0 0 reference_neutral(3)];


if forward_matrix_exists

% matrix "xyz2rgb * fm_curr" translates white balanced camera colors to sRGB;

% so, it must translate [1 1 1] into [1 1 1], hence sum of each its row must be 1:

disp(['quality test of intepolated forward matrix: following numbers must be close to 1:']);

disp([' ' num2str(sum(xyz2rgb * fm_curr, 2)', ' %.6f')]);

disp(' ');

disp('===================== using ForwardMatrix ("ForwardMatrix included"), "_m1"')

disp([' (basing on camera_neutral = [' num2str(camera_neutral', ' %.6f') '])']);

% (1st method)

% recommended method, applicable "if the ForwardMatrix tags are included in the camera profile"

%

d_matrix = reference_neutral^(-1);

% matrix for translation of individual camera's sensor values into XYZ_D50:

camera_to_XYZ_D50_m1 = fm_curr * d_matrix * ((ab * cc_curr)^(-1));

% the matrix "camera_to_XYZ_D50_m1", determined as above,

% translates response of a given camera specimen to neutral color

% into XYZ of illuminant D50: [0.964220; 1.000000; 0.825210]

disp(['quality test of "camera_to_XYZ_D50_m1" matrix: following numbers must be close to 0:']);

disp([' ' num2str([(camera_to_XYZ_D50_m1*(camera_neutral))' - [0.964220 1 0.825210]], ' %.6f')]);

disp(' ');

% matrix for translating individual camera's colors into sRGB (first method):

cam2rgb_m1 = xyz2rgb * camera_to_XYZ_D50_m1;

% ("camera_to_XYZ_D50_m1" differs from "fm_curr"

% by presence of corrections for white balance and for color sensitivity of individual camera specimen.

% If these corrections were applied to the image beforehand,

% matrix for such image will use fm_curr):

cam2rgb_m1_clean = xyz2rgb * fm_curr;

% normalizing sum of each row to 1:

cam2rgb_m1_clean_norm = cam2rgb_m1_clean ./ repmat(sum(cam2rgb_m1_clean, 2), [1 3]);

% displaying matrices:

disp(' camera_to_XYZ_D50_m1: cam2rgb_m1:');

for z = 1 : size(camera_to_XYZ_D50_m1, 1)

disp([...

sprintf(' %9.6f', camera_to_XYZ_D50_m1(z, :)) ' '...

sprintf(' %9.6f', cam2rgb_m1(z, :))]);

end

disp(' ');

disp(' camera_to_XYZ_D50_m1_clean: cam2rgb_m1_clean: cam2rgb_m1_clean_norm:');

disp(' ("Forward Matrix")');

for z = 1 : size(camera_to_XYZ_D50_m1, 1)

disp([...

sprintf(' %9.6f', fm_curr(z, :)) ' '...

sprintf(' %9.6f', cam2rgb_m1_clean(z, :)) ' '...

sprintf(' %9.6f', cam2rgb_m1_clean_norm(z, :))]);

end

disp([' sum of rows of cam2rgb_m1_clean (must be [1 1 1]): '...

num2str(sum(cam2rgb_m1_clean, 2)', ' %.6f')]);

disp([' sum of rows of cam2rgb_m1_clean_norm (must be [1 1 1]): '...

num2str(sum(cam2rgb_m1_clean_norm, 2)', ' %.6f')]);

disp(' ');

% transformation of the test image with matrix from the first method:

im_m1 = im;

im_m1 = img_trans_mat(im_m1, cam2rgb_m1);

im_m1(im_m1<0) = 0;

% gamma correction and turning into 3*8-bit image:

im_m1 = im_m1.^(1/2.2);

im_m1 = uint8(255*im_m1);

end


disp('===================== not using ForwardMatrix ("ForwardMatrix not included"), "_m2"');

disp([' (basing on white balance XYZ = [' num2str(XYZ_curr', ' %.6f') '])']);

% (2nd method)

% "If the ForwardMatrix tags are not included in the camera profile:"

% matrix for translation of individual camera's sensor values into XYZ:

camera_to_XYZ_m2 = XYZ_to_camera_curr^(-1);

% matrix of chromatic adaptation:

% (translates "white balance xy value" into XYZ with illuminant D50: [0.964220; 1.000000; 0.825210]):

chrom_adapt_matrix_m2 = bradford(100*XYZ_curr');

disp(['quality test of "chrom_adapt_matrix_m2": following numbers must be close to 0:']);

disp([' ' num2str([(chrom_adapt_matrix_m2*XYZ_curr)' - [0.964220 1 0.825210]], ' %.6f')]);

disp(' ');

% matrix for translation of individual camera's sensor values into XYZ_D50:

camera_to_XYZ_D50_m2 = chrom_adapt_matrix_m2 * camera_to_XYZ_m2;

% matrix for translating individual camera's colors into sRGB:

cam2rgb_m2 = xyz2rgb * camera_to_XYZ_D50_m2;


% if corrections for white balance and for color sensitivity of individual camera specimen

% were applied beforehand,

% cam2rgb_m2_clean = cam2rgb_m2 * reference_neutral

% = xyz2rgb * bradford(100*XYZ_curr') * (cm_curr^(-1)) * reference_neutral

cam2rgb_m2_clean = xyz2rgb * bradford(100*XYZ_curr') * (cm_curr^(-1)) * reference_neutral;

% unlike cam2rgb_m2, this matrix can be and should be normalized for converting [1;1;1] into [1;1;1]:

% (sum of each row is normalized to 1):

cam2rgb_m2_clean_norm = cam2rgb_m2_clean ./ repmat(sum(cam2rgb_m2_clean, 2), [1 3]);

cam2rgb_m2_norm = cam2rgb_m2_clean_norm * reference_neutral^(-1);


% displaying matrices:

disp(' camera_to_XYZ_D50_m2: cam2rgb_m2: cam2rgb_m2_norm:');

for z = 1 : size(camera_to_XYZ_D50_m2, 1)

disp([...

sprintf(' %9.6f', camera_to_XYZ_D50_m2(z, :)) ' '...

sprintf(' %9.6f', cam2rgb_m2(z, :)) ' '...

sprintf(' %9.6f', cam2rgb_m2_norm(z, :)) ' '...

]);

end

disp(' ');

disp(' cam2rgb_m2_clean: cam2rgb_m2_clean_norm:');

for z = 1 : size(camera_to_XYZ_D50_m1, 1)

disp([...

blanks(30) ' '...

sprintf(' %9.6f', cam2rgb_m2_clean(z, :)) ' '...

sprintf(' %9.6f', cam2rgb_m2_clean_norm(z, :))]);

end

disp([' sum of rows of cam2rgb_m2_clean (must be [1 1 1]): '...

num2str(sum(cam2rgb_m2_clean, 2)', ' %.6f')]);

disp([' sum of rows of cam2rgb_m2_clean_norm (must be [1 1 1]): '...

num2str(sum(cam2rgb_m2_clean_norm, 2)', ' %.6f')]);

disp(' ');


% transformation of the test image with matrices from the second method:

im_m2 = [img_trans_mat(im, cam2rgb_m2_norm) img_trans_mat(im, cam2rgb_m2)];

im_m2(im_m2<0) = 0;

im_m2 = im_m2.^(1/2.2);

im_m2 = uint8(255*im_m2);


% image with non-converted colors: gamma correction and turning into 3*8-bit:

im = uint8(255 * im.^(1/2.2));


% displaying images:

% upper row:

% 1: original colors

% 2: processed using ForwardMatrix ( recommended) - if ForwardMatrix exists,

% 3: processed not using ForwardMatrix (not recommended) with matrix normalizing,

% 4: processed not using ForwardMatrix (not recommended) without matrix normalizing

% lower row:

% maps of overexposed places

if forward_matrix_exists

imshow([im im_m1 im_m2; im2uint8([im im_m1 im_m2]>=255)]);

else

imshow([im im_m2; im2uint8([im im_m2]>=255)]);

end