├── README.md
├── figures
├── .DS_Store
├── CombinedResults.jpg
└── overview_figure.jpg
└── gamut-mapping
├── .DS_Store
├── Global_P3.mat
├── Global_ProPhoto.mat
├── PT_mapping_evaluation.m
├── applyMapping.m
├── calc_deltaE.m
├── calc_deltaE2000.m
├── calc_mae.m
├── calc_mse.m
├── computeMapping.m
├── convertLab2RGB.m
├── convertRGB2Lab.m
├── deltaE2000.m
├── drawDeltaE.m
├── drawHeatmap.m
├── evaluation.m
├── extract_xy.m
├── global_mapping.m
├── global_mapping_RGB.m
├── global_mapping_evaluation.m
├── gma_global_mapping.m
├── gma_global_mapping_16b.m
├── gma_local_mapping.m
├── gma_local_mapping_16b.m
├── kernel.m
├── local_mapping.m
├── local_mapping_single_image.m
├── startpooling.m
├── subsampling.m
└── visualize_gamut_of_image.m
/README.md:
--------------------------------------------------------------------------------
1 | # Improving-Color-Space-Conversion-for-Camera-Captured-Images-via-Wide-Gamut-Metadata
2 |
3 | _[Hoang M. Le](https://www.linkedin.com/in/hminle/)_1, _[Mahmoud Afifi](https://sites.google.com/view/mafifi)_1 and _[Michael S. Brown](http://www.cse.yorku.ca/~mbrown/)_1,2
4 |
5 | 1York University
6 | 2Samsung AI Center (SAIC) - Toronto
7 |
8 | ### Overview
9 | Link to [PDF](https://drive.google.com/file/d/1vjXx5UQazxWY2h6RTcKRp2XzGvJt-Gcz/view?usp=share_link)
10 |
11 | 
12 |
13 | ### Dataset
14 |
15 | - Input sRGB: [NUS_sRGB.zip 237MB](https://ln2.sync.com/dl/e90536850/8fucetye-2c2re4d5-5nuc8hjd-txr33r77)
16 | - Target Display P3: [NUS_DisplayP3.zip 11.1GB](https://ln2.sync.com/dl/69c792a80/7pknnv9g-cf77nyft-sdqgtzej-3t48cphk)
17 | - Target AdobeRGB: [NUS_AdobeRGB.zip 11GB](https://ln2.sync.com/dl/32e5001d0/8xx75cyq-f8g7gs8y-qg7ffdk5-gsqi4hsj)
18 |
19 | ### Results
20 |
21 | 
22 |
23 | This software is provided for research purposes only and CANNOT be used for commercial purposes.
24 |
--------------------------------------------------------------------------------
/figures/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hminle/improving-color-space-conversion-via-metadata/9297ce913bf9e0a3dcbaef4b64f11e9c1e45260d/figures/.DS_Store
--------------------------------------------------------------------------------
/figures/CombinedResults.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hminle/improving-color-space-conversion-via-metadata/9297ce913bf9e0a3dcbaef4b64f11e9c1e45260d/figures/CombinedResults.jpg
--------------------------------------------------------------------------------
/figures/overview_figure.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hminle/improving-color-space-conversion-via-metadata/9297ce913bf9e0a3dcbaef4b64f11e9c1e45260d/figures/overview_figure.jpg
--------------------------------------------------------------------------------
/gamut-mapping/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hminle/improving-color-space-conversion-via-metadata/9297ce913bf9e0a3dcbaef4b64f11e9c1e45260d/gamut-mapping/.DS_Store
--------------------------------------------------------------------------------
/gamut-mapping/Global_P3.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hminle/improving-color-space-conversion-via-metadata/9297ce913bf9e0a3dcbaef4b64f11e9c1e45260d/gamut-mapping/Global_P3.mat
--------------------------------------------------------------------------------
/gamut-mapping/Global_ProPhoto.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hminle/improving-color-space-conversion-via-metadata/9297ce913bf9e0a3dcbaef4b64f11e9c1e45260d/gamut-mapping/Global_ProPhoto.mat
--------------------------------------------------------------------------------
/gamut-mapping/PT_mapping_evaluation.m:
--------------------------------------------------------------------------------
1 | clc
2 | clear
3 | warning off
4 |
5 | experiment_name = 'PS_ProPhoto';
6 | in_dir = 'C:\Users\Afifi\OneDrive - York University\ProPhoto_from_sRGB_photoshop';
7 | target_dir = 'C:\Users\Afifi\OneDrive - York University\ProPhoto_from_DNG';
8 | in_ext = '.tif';
9 | target_ext = '.tif';
10 | if strcmpi(target_ext,'.tif')
11 | target_colorSpace = 'ProPhoto RGB';
12 | elseif strcmpi (target_ext, '.jpg')
13 | target_colorSpace = 'sRGB';
14 | end
15 | in_images = dir(fullfile(in_dir,['*' in_ext]));
16 | in_images = fullfile(in_dir,{in_images(:).name});
17 | target_images = dir(fullfile(target_dir,['*' target_ext]));
18 | target_images = fullfile(target_dir,{target_images(:).name});
19 |
20 | deltaE2000 = [];
21 | deltaE = [];
22 | PSNR = [];
23 | MAE = [];
24 |
25 | for i = 1 : length(in_images)
26 | fprintf('Processing (%d/%d) ... \n',i,length(in_images));
27 |
28 | in_img = im2double(imread(in_images{i}));
29 | target_img = im2double(imread(target_images{i}));
30 |
31 | %% Evaluation
32 | % deltaE
33 | deltaE2000 = [deltaE2000; ...
34 | calc_deltaE2000(in_img, target_img, target_colorSpace)];
35 | deltaE = [deltaE;...
36 | calc_deltaE(in_img, target_img, target_colorSpace)];
37 | %% Calculate MSE MAE
38 | PSNR = [PSNR; psnr(in_img, target_img)];
39 | MAE = [MAE; calc_mae(in_img, target_img)];
40 | end
41 |
42 |
43 | fprintf('DeltaE 2000:\n mean: %0.3f, median: %0.3f, Q1: %0.3f, Q3: %0.3f\n',...
44 | mean(deltaE2000), median(deltaE2000), quantile(deltaE2000,0.25), ...
45 | quantile(deltaE2000,0.75));
46 |
47 |
48 | fprintf('DeltaE 76:\n mean: %0.3f, median: %0.3f, Q1: %0.3f, Q3: %0.3f\n',...
49 | mean(deltaE), median(deltaE), quantile(deltaE,0.25), ...
50 | quantile(deltaE,0.75));
51 |
52 |
53 | fprintf('PSNR:\n mean: %0.3f, median: %0.3f, Q1: %0.3f, Q3: %0.3f\n',...
54 | mean(PSNR), median(PSNR), quantile(PSNR,0.25), quantile(PSNR,0.75));
55 |
56 | fprintf('MAE:\n mean: %0.3f, median: %0.3f, Q1: %0.3f, Q3: %0.3f\n',...
57 | mean(MAE), median(MAE), quantile(MAE,0.25), quantile(MAE,0.75));
58 |
59 |
60 | results.deltaE2000 = deltaE2000;
61 | results.deltaE = deltaE;
62 | results.PSNR = PSNR;
63 | results.MAE = MAE;
64 |
65 | save(experiment_name,'results','-v7.3');
66 |
--------------------------------------------------------------------------------
/gamut-mapping/applyMapping.m:
--------------------------------------------------------------------------------
1 | function output = applyMapping(I, M, mask)
2 | if nargin == 2
3 | mask = [];
4 | end
5 | sz = size(I);
6 | I = reshape(I,[],3);
7 | if isempty(mask) == 0
8 | mask = reshape(mask,[],1);
9 | output = I;
10 | output(mask,:) = kernel(I(mask,:)) * M;
11 | else
12 | output = kernel(I) * M;
13 | output = reshape(output,[sz(1) sz(2) sz(3)]);
14 | end
15 |
16 |
--------------------------------------------------------------------------------
/gamut-mapping/calc_deltaE.m:
--------------------------------------------------------------------------------
1 | %% Calculate Delta E76 between source and target images.
2 | %
3 | % Copyright (c) 2018-present, Mahmoud Afifi
4 | % York University, Canada
5 | % mafifi@eecs.yorku.ca | m.3afifi@gmail.com
6 | %
7 | % This source code is licensed under the license found in the
8 | % LICENSE file in the root directory of this source tree.
9 | % All rights reserved.
10 | %
11 | % Please cite the following work if this program is used:
12 | % Mahmoud Afifi, Brian Price, Scott Cohen, and Michael S. Brown,
13 | % "When color constancy goes wrong: Correcting improperly white-balanced
14 | % images", CVPR 2019.
15 | %
16 | % Input:
17 | % -source: image A
18 | % -target: image B
19 | % -color_chart_area: If there is a color chart in the image (that is
20 | % masked out from both images, this variable represents the number of
21 | % pixels of the color chart.
22 | %
23 | % Output:
24 | % -deltaE: the value of Delta E76 between image A and image B.
25 | %
26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27 | %%
28 |
29 | function deltaE=calc_deltaE(source,target, color_space)
30 |
31 | if ~isa(source, 'double')
32 | source = im2double(source);
33 | end
34 |
35 | if ~isa(target, 'double')
36 | target = im2double(target);
37 | end
38 |
39 | if (strcmp(color_space, 'sRGB'))
40 | source = rgb2lab(source);
41 | target = rgb2lab(target);
42 | elseif (strcmp(color_space, 'Adobe RGB'))
43 | source = rgb2lab(source, 'ColorSpace','adobe-rgb-1998');
44 | target = rgb2lab(target, 'ColorSpace','adobe-rgb-1998');
45 | elseif (strcmp(color_space, 'Display P3'))
46 | profile = iccread('D:\colorprofiles\Display P3.icc');
47 | cform = makecform('mattrc',profile.MatTRC, ...
48 | 'direction','forward');
49 | source = applycform(source, cform);
50 | target = applycform(target, cform);
51 | source = xyz2lab(source);
52 | target = xyz2lab(target);
53 | elseif (strcmp(color_space, 'ProPhoto RGB'))
54 | icc_folder = iccroot;
55 | profiles = iccfind(icc_folder, color_space);
56 | pro_profile = profiles{1};
57 | cform = makecform('mattrc',pro_profile.MatTRC, ...
58 | 'direction','forward');
59 | source = applycform(source, cform);
60 | target = applycform(target, cform);
61 | source = xyz2lab(source);
62 | target = xyz2lab(target);
63 |
64 |
65 | else
66 | disp('Unknow Color Space'); return;
67 | end
68 | source = reshape(source,[],3); %l,a,b
69 | target = reshape(target,[],3); %l,a,b
70 | deltaE = sqrt(sum((source - target).^2,2));
71 | deltaE=sum(deltaE)/(size(deltaE,1));
72 | end
73 |
74 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
75 | % References:
76 | % [1] http://zschuessler.github.io/DeltaE/learn/
--------------------------------------------------------------------------------
/gamut-mapping/calc_deltaE2000.m:
--------------------------------------------------------------------------------
1 | %% Calculate Delta E2000 between source and target images.
2 | %
3 | % Copyright (c) 2018-present, Mahmoud Afifi
4 | % York University, Canada
5 | % mafifi@eecs.yorku.ca | m.3afifi@gmail.com
6 | %
7 | % This source code is licensed under the license found in the
8 | % LICENSE file in the root directory of this source tree.
9 | % All rights reserved.
10 | %
11 | % Please cite the following work if this program is used:
12 | % Mahmoud Afifi, Brian Price, Scott Cohen, and Michael S. Brown,
13 | % "When color constancy goes wrong: Correcting improperly white-balanced
14 | % images", CVPR 2019.
15 | %
16 | % Input:
17 | % -source: image A
18 | % -target: image B
19 | % -color_chart_area: If there is a color chart in the image (that is
20 | % masked out from both images, this variable represents the number of
21 | % pixels of the color chart.
22 | %
23 | % Output:
24 | % -deltaE: the value of Delta E2000 between image A and image B.
25 | %
26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27 | %%
28 |
29 | function deltaE00=calc_deltaE2000(source,target, color_space)
30 |
31 | if ~isa(source, 'double')
32 | source = im2double(source);
33 | end
34 |
35 | if ~isa(target, 'double')
36 | target = im2double(target);
37 | end
38 |
39 | if (strcmp(color_space, 'sRGB'))
40 | source = rgb2lab(source);
41 | target = rgb2lab(target);
42 | elseif (strcmp(color_space, 'Adobe RGB'))
43 | source = rgb2lab(source, 'ColorSpace','adobe-rgb-1998');
44 | target = rgb2lab(target, 'ColorSpace','adobe-rgb-1998');
45 | elseif (strcmp(color_space, 'Display P3'))
46 | profile = iccread('D:\colorprofiles\Display P3.icc');
47 | cform = makecform('mattrc',profile.MatTRC, ...
48 | 'direction','forward');
49 | source = applycform(source, cform);
50 | target = applycform(target, cform);
51 | source = xyz2lab(source);
52 | target = xyz2lab(target);
53 | elseif (strcmp(color_space, 'ProPhoto RGB'))
54 | icc_folder = iccroot;
55 | profiles = iccfind(icc_folder, color_space);
56 | pro_profile = profiles{1};
57 | cform = makecform('mattrc',pro_profile.MatTRC, ...
58 | 'direction','forward');
59 | source = applycform(source, cform);
60 | target = applycform(target, cform);
61 | source = xyz2lab(source);
62 | target = xyz2lab(target);
63 |
64 |
65 | else
66 | disp('Unknow Color Space'); return;
67 | end
68 |
69 | source = reshape(source,[],3); %l,a,b
70 | target = reshape(target,[],3); %l,a,b
71 | deltaE00 = deltaE2000(source , target)';
72 | deltaE00=sum(deltaE00)/(size(deltaE00,1));
73 | end
74 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
75 | %References:
76 | % [1] The CIEDE2000 Color-Difference Formula: Implementation Notes,
77 | % Supplementary Test Data, and Mathematical Observations,",
78 | % G. Sharma, W. Wu, E. N. Dalal, Color Research and Application, 2005.
--------------------------------------------------------------------------------
/gamut-mapping/calc_mae.m:
--------------------------------------------------------------------------------
1 | %% Calculate mean angular error between source and target images
2 | %
3 | % Copyright (c) 2018-present, Mahmoud Afifi
4 | % York University, Canada
5 | % mafifi@eecs.yorku.ca | m.3afifi@gmail.com
6 | %
7 | % This source code is licensed under the license found in the
8 | % LICENSE file in the root directory of this source tree.
9 | % All rights reserved.
10 | %
11 | % Please cite the following work if this program is used:
12 | % Mahmoud Afifi, Brian Price, Scott Cohen, and Michael S. Brown,
13 | % "When color constancy goes wrong: Correcting improperly white-balanced
14 | % images", CVPR 2019.
15 | %
16 | % Input:
17 | % -source: image A
18 | % -target: image B
19 | % -color_chart_area: If there is a color chart in the image (that is
20 | % masked out from both images, this variable represents the number of
21 | % pixels of the color chart.
22 | %
23 | % Output:
24 | % -f: the mean angular error between image A and image B.
25 | %
26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27 | %%
28 | function f = calc_mae(source, target)
29 | source = reshape(source, [], 3);
30 | target = reshape(target, [], 3);
31 |
32 | if ~isa(source, 'double')
33 | source = im2double(source);
34 | end
35 |
36 | if ~isa(target, 'double')
37 | target = im2double(target);
38 | end
39 | target_norm = sqrt(sum(target.^2,2));
40 | source_mapped_norm = sqrt(sum(source.^2,2));
41 | angles=dot(source,target,2)./(source_mapped_norm.*target_norm);
42 | angles(angles>1)=1;
43 | f=acosd(angles);
44 | f(isnan(f))=0;
45 | f=sum(f)/(size(source,1));
46 | end
--------------------------------------------------------------------------------
/gamut-mapping/calc_mse.m:
--------------------------------------------------------------------------------
1 | %% Calculate mean squared error between source and target images.
2 | %
3 | % Copyright (c) 2018-present, Mahmoud Afifi
4 | % York University, Canada
5 | % mafifi@eecs.yorku.ca | m.3afifi@gmail.com
6 | %
7 | % This source code is licensed under the license found in the
8 | % LICENSE file in the root directory of this source tree.
9 | % All rights reserved.
10 | %
11 | % Please cite the following work if this program is used:
12 | % Mahmoud Afifi, Brian Price, Scott Cohen, and Michael S. Brown,
13 | % "When color constancy goes wrong: Correcting improperly white-balanced
14 | % images", CVPR 2019.
15 | %
16 | % Input:
17 | % -source: image A
18 | % -target: image B
19 | % -color_chart_area: If there is a color chart in the image (that is
20 | % masked out from both images, this variable represents the number of
21 | % pixels of the color chart.
22 | %
23 | % Output:
24 | % -f: the mean squared error between image A and image B.
25 | %
26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27 | %%
28 |
29 | function mse=calc_mse(source,target)
30 |
31 | if ~isa(source, 'double')
32 | source = im2double(source);
33 | end
34 |
35 | if ~isa(target, 'double')
36 | target = im2double(target);
37 | end
38 | diff=(source(:)-target(:)).^2;
39 | mse=sum(diff)/(length(diff));
40 | end
--------------------------------------------------------------------------------
/gamut-mapping/computeMapping.m:
--------------------------------------------------------------------------------
1 | function M = computeMapping(source, target)
2 | %source: txtx3 image
3 | %target: txtx3 image
4 |
5 | source_reshaped = reshape(source,[],3);
6 | target_reshaped = reshape(target,[],3);
7 |
8 | M = kernel(source_reshaped)\target_reshaped;
9 |
10 | end
11 |
12 |
--------------------------------------------------------------------------------
/gamut-mapping/convertLab2RGB.m:
--------------------------------------------------------------------------------
1 | function [output_RGB] = convertLab2RGB(source,color_space)
2 | %UNTITLED4 Summary of this function goes here
3 | % Detailed explanation goes here
4 | if ~isa(source, 'double')
5 | source = im2double(source);
6 | end
7 | icc_folder = iccroot;
8 |
9 | % Find color profile
10 | profiles = iccfind(icc_folder, color_space);
11 | profile = profiles{1};
12 |
13 | cform = makecform('lab2xyz');
14 | output_XYZ = applycform(source, cform);
15 | cform = makecform('mattrc',profile.MatTRC, ...
16 | 'direction','inverse');
17 | output_RGB = applycform(output_XYZ, cform);
18 | end
19 |
20 |
--------------------------------------------------------------------------------
/gamut-mapping/convertRGB2Lab.m:
--------------------------------------------------------------------------------
1 | function [output_Lab] = convertRGB2Lab(source, color_space)
2 | %UNTITLED3 Summary of this function goes here
3 | % Detailed explanation goes here
4 |
5 | if ~isa(source, 'double')
6 | source = im2double(source);
7 | end
8 | icc_folder = iccroot;
9 |
10 | % Find color profile
11 | profiles = iccfind(icc_folder, color_space);
12 | profile = profiles{1};
13 | % Convert to XYZ
14 | cform = makecform('mattrc',profile.MatTRC, ...
15 | 'direction','forward');
16 | output_XYZ = applycform(source, cform);
17 | cform = makecform('xyz2lab');
18 | output_Lab = applycform(output_XYZ, cform);
19 | end
20 |
21 |
--------------------------------------------------------------------------------
/gamut-mapping/deltaE2000.m:
--------------------------------------------------------------------------------
1 | function de00 = deltaE2000(Labstd,Labsample)
2 | % Based on the article:
3 | % "The CIEDE2000 Color-Difference Formula: Implementation Notes,
4 | % Supplementary Test Data, and Mathematical Observations,", G. Sharma,
5 | % W. Wu, E. N. Dalal, Color Research and Application, vol. 30. No. 1, pp.
6 | % 21-30, February 2005.
7 | % available at http://www.ece.rochester.edu/~/gsharma/ciede2000/
8 | kl = 1; kc=1; kh =1;
9 | Lstd = Labstd(:,1)';
10 | astd = Labstd(:,2)';
11 | bstd = Labstd(:,3)';
12 | Cabstd = sqrt(astd.^2+bstd.^2);
13 | Lsample = Labsample(:,1)';
14 | asample = Labsample(:,2)';
15 | bsample = Labsample(:,3)';
16 | Cabsample = sqrt(asample.^2+bsample.^2);
17 | Cabarithmean = (Cabstd + Cabsample)/2;
18 | G = 0.5* (1 - sqrt( (Cabarithmean.^7)./(Cabarithmean.^7 + 25^7)));
19 | apstd = (1+G).*astd;
20 | apsample = (1+G).*asample;
21 | Cpsample = sqrt(apsample.^2+bsample.^2);
22 | Cpstd = sqrt(apstd.^2+bstd.^2);
23 | Cpprod = (Cpsample.*Cpstd);
24 | zcidx = find(Cpprod == 0);
25 | hpstd = atan2(bstd,apstd);
26 | hpstd = hpstd+2*pi*(hpstd < 0);
27 | hpstd((abs(apstd)+abs(bstd))== 0) = 0;
28 | hpsample = atan2(bsample,apsample);
29 | hpsample = hpsample+2*pi*(hpsample < 0);
30 | hpsample((abs(apsample)+abs(bsample))==0) = 0;
31 | dL = (Lsample-Lstd);
32 | dC = (Cpsample-Cpstd);
33 | dhp = (hpsample-hpstd);
34 | dhp = dhp - 2*pi* (dhp > pi );
35 | dhp = dhp + 2*pi* (dhp < (-pi) );
36 | dhp(zcidx ) = 0;
37 | dH = 2*sqrt(Cpprod).*sin(dhp/2);
38 | Lp = (Lsample+Lstd)/2;
39 | Cp = (Cpstd+Cpsample)/2;
40 | hp = (hpstd+hpsample)/2;
41 | hp = hp - ( abs(hpstd-hpsample) > pi ) *pi;
42 | hp = hp+ (hp < 0) *2*pi;
43 | hp(zcidx) = hpsample(zcidx)+hpstd(zcidx);
44 | Lpm502 = (Lp-50).^2;
45 | Sl = 1 + 0.015*Lpm502./sqrt(20+Lpm502);
46 | Sc = 1+0.045*Cp;
47 | T = 1 - 0.17*cos(hp - pi/6 ) + 0.24*cos(2*hp) + 0.32*cos(3*hp+pi/30) ...
48 | -0.20*cos(4*hp-63*pi/180);
49 | Sh = 1 + 0.015*Cp.*T;
50 | delthetarad = (30*pi/180)*exp(- ( (180/pi*hp-275)/25).^2);
51 | Rc = 2*sqrt((Cp.^7)./(Cp.^7 + 25^7));
52 | RT = - sin(2*delthetarad).*Rc;
53 | klSl = kl*Sl;
54 | kcSc = kc*Sc;
55 | khSh = kh*Sh;
56 | de00 = sqrt( (dL./klSl).^2 + (dC./kcSc).^2 + (dH./khSh).^2 + ...
57 | RT.*(dC./kcSc).*(dH./khSh) );
58 | return
59 |
60 |
61 |
--------------------------------------------------------------------------------
/gamut-mapping/drawDeltaE.m:
--------------------------------------------------------------------------------
1 | function drawHeatmap(deltaE, heatmap_title)
2 | %UNTITLED2 Summary of this function goes here
3 | % Detailed explanation goes here
4 | % source, target are RGB or double
5 |
6 |
7 | figure;imagesc(deltaE);
8 | if strlength(heatmap_title) ~= 0
9 | title(heatmap_title);
10 | end
11 | caxis([0 15]);
12 |
13 | c = colorbar;
14 | c.Location = 'southoutside';
15 | ax = gca;
16 | axpos = ax.Position;
17 | c.Position(4) = 0.5*c.Position(4);
18 | ax.Position = axpos;
19 |
20 | axis off;
21 | set(gca,'LooseInset',get(gca,'TightInset'));
22 | end
23 |
24 |
--------------------------------------------------------------------------------
/gamut-mapping/drawHeatmap.m:
--------------------------------------------------------------------------------
1 | function drawHeatmap(source, target, heatmap_title)
2 | %UNTITLED2 Summary of this function goes here
3 | % Detailed explanation goes here
4 | % source, target are RGB or double
5 |
6 | if ~isa(source, 'double')
7 | source = im2double(source);
8 | end
9 |
10 | if ~isa(target, 'double')
11 | target = im2double(target);
12 | end
13 | absdiff = imabsdiff(source, target);
14 | % absdiff: NxNx3
15 | % take mean of three channels R G B
16 | mean3channels = mean(absdiff, 3);
17 | figure;imagesc(mean3channels);
18 | if strlength(heatmap_title) ~= 0
19 | title(heatmap_title);
20 | end
21 | caxis([0 0.08]);
22 |
23 | c = colorbar;
24 | c.Location = 'southoutside';
25 | ax = gca;
26 | axpos = ax.Position;
27 | c.Position(4) = 0.5*c.Position(4);
28 | ax.Position = axpos;
29 |
30 | axis off;
31 | set(gca,'LooseInset',get(gca,'TightInset'));
32 | end
33 |
34 |
--------------------------------------------------------------------------------
/gamut-mapping/evaluation.m:
--------------------------------------------------------------------------------
1 | clc
2 | clear
3 | warning off
4 |
5 | experiment_name = 'sRGB2ProPhoto_Global';
6 | target_colorspace = 'ProPhoto RGB'; %ProPhoto RGB | P3 | sRGB
7 | in_dir = '../From_sRGB_to_ProPhoto_Global';
8 | target_dir = '../ProPhoto';
9 | in_ext = '.tif';
10 | target_ext = '.tif';
11 |
12 | in_images = dir(fullfile(in_dir,['*' in_ext]));
13 | in_images = fullfile(in_dir,{in_images(:).name});
14 | target_images = dir(fullfile(target_dir,['*' target_ext]));
15 | target_images = fullfile(target_dir,{target_images(:).name});
16 |
17 | if strcmpi(target_colorspace,'prophoto rgb')
18 | deltaE2000 = [];
19 | end
20 | PSNR = [];
21 | MAE = [];
22 |
23 | for i = 1 : length(in_images)
24 | fprintf('Processing (%d/%d) ... \n',i,length(in_images));
25 |
26 | in_img = im2double(imread(in_images{i}));
27 | target_img = im2double(imread(target_images{i}));
28 |
29 | %% Evaluation
30 | if strcmpi(target_colorspace,'prophoto rgb')
31 | % deltaE
32 | deltaE2000 = [deltaE2000; ...
33 | calc_deltaE2000(in_img, target_img, target_colorspace)];
34 | end
35 | %% Calculate MSE MAE
36 | PSNR = [PSNR; psnr(in_img, target_img)];
37 | MAE = [MAE; calc_mae(in_img, target_img)];
38 | end
39 |
40 | if strcmpi(target_colorspace,'prophoto rgb')
41 | fprintf(...
42 | 'DeltaE 2000:\n mean: %0.2f, median: %0.2f, Q1: %0.2f, Q3: %0.2f\n',...
43 | mean(deltaE2000), median(deltaE2000), quantile(deltaE2000,0.25), ...
44 | quantile(deltaE2000,0.75));
45 | end
46 |
47 | fprintf('PSNR:\n mean: %0.2f, median: %0.2f, Q1: %0.2f, Q3: %0.2f\n',...
48 | mean(PSNR), median(PSNR), quantile(PSNR,0.25), quantile(PSNR,0.75));
49 |
50 | fprintf('MAE:\n mean: %0.2f, median: %0.2f, Q1: %0.2f, Q3: %0.2f\n',...
51 | mean(MAE), median(MAE), quantile(MAE,0.25), quantile(MAE,0.75));
52 |
53 | if strcmpi(target_colorspace,'prophoto rgb')
54 | results.deltaE2000 = deltaE2000;
55 | end
56 | results.PSNR = PSNR;
57 | results.MAE = MAE;
58 |
59 | save(experiment_name,'results','-v7.3');
60 |
--------------------------------------------------------------------------------
/gamut-mapping/extract_xy.m:
--------------------------------------------------------------------------------
1 | % ===================================================
2 | % *** FUNCTION extract RGB in xy
3 | % *** color_space can be 'sRGB', 'ProPhoto'
4 |
5 | function [rgb_xy, wp_xy, rgb_xy_lines] = extract_xy(color_space)
6 |
7 | icc_folder = iccroot;
8 | profiles = iccfind(icc_folder, color_space);
9 | profile = profiles{1};
10 |
11 | % Get RGB XYZ values
12 | rXYZ = profile.MatTRC.RedColorant;
13 | gXYZ = profile.MatTRC.GreenColorant;
14 | bXYZ = profile.MatTRC.BlueColorant;
15 | wpXYZ = profile.MediaWhitePoint;
16 | % create a 3x3 matrix of XYZ values of RGB
17 | rgbXYZ = [rXYZ; gXYZ; bXYZ];
18 |
19 | % Convert XYZ 2 xy
20 | cform_xyz2xyl = makecform('xyz2xyl');
21 | xyl = applycform(rgbXYZ, cform_xyz2xyl);
22 | wp_xyl = applycform (wpXYZ, cform_xyz2xyl);
23 |
24 | % size(xyl) = 3x3
25 | % column 3 is Y --> we remove it
26 | rgb_xy = [xyl(:, 1), xyl(:, 2)];
27 |
28 | % WhitePoint xy
29 | wp_xy = [wp_xyl(:,1), wp_xyl(:,2)];
30 |
31 | % rgb_xy_lines for drawing color space gamut lines on chromaticity diagram
32 | rgb_xy_lines = [rgb_xy; rgb_xy(1, :)];
33 |
34 | end
--------------------------------------------------------------------------------
/gamut-mapping/global_mapping.m:
--------------------------------------------------------------------------------
1 | clc
2 | clear
3 | warning off
4 |
5 | in_dir = '../sRGB';
6 | target_dir = '../P3';
7 | out_dir = '../From_sRGB_to_P3_Global';
8 | in_ext = '.jpg';
9 | target_ext = '.tif';
10 | in_images = dir(fullfile(in_dir,['*' in_ext]));
11 | in_images = fullfile(in_dir,{in_images(:).name});
12 | target_images = dir(fullfile(target_dir,['*' target_ext]));
13 | target_images = fullfile(target_dir,{target_images(:).name});
14 |
15 | for i = 1 : length(in_images)
16 | fprintf('Processing (%d/%d) ... \n',i,length(in_images));
17 | [~,name,ext] = fileparts(in_images{i});
18 | in_img = im2double(imread(in_images{i}));
19 | target_img = im2double(imread(target_images{i}));
20 | %% Perform Global Mapping
21 | % Subsmpaling
22 | sub_sample_size = 150;
23 | sub_in_img = subsampling(in_img, sub_sample_size);
24 | sub_target_img = subsampling(target_img, sub_sample_size);
25 | % Compute mapping on subsamples
26 | M = computeMapping(sub_in_img, sub_target_img);
27 | reconstructed = applyMapping(in_img, M);
28 | % Clipping
29 | reconstructed(reconstructed > 1) = 1;
30 | reconstructed(reconstructed < 0) = 0;
31 | if strcmpi(target_ext,'.tif') == 1 || strcmpi(target_ext,'.png') == 1
32 | imwrite(im2uint16(reconstructed),fullfile(out_dir,[name target_ext]));
33 | else
34 | imwrite(reconstructed,fullfile(out_dir,[name target_ext]));
35 | end
36 | end
--------------------------------------------------------------------------------
/gamut-mapping/global_mapping_RGB.m:
--------------------------------------------------------------------------------
1 | image_name = "a0088-_DGW6376";
2 | sRGB_filename = image_name + ".jpg";
3 | proPhoto_filename = image_name + ".tif";
4 | pro_PTS_filename = 'a0088-_DGW6376_converted_back_PTS.tif';
5 |
6 | srgb_img = imread(sRGB_filename);
7 | proPhoto_img = imread(proPhoto_filename);
8 |
9 | sRGB = im2double(srgb_img);
10 | proRGB = im2double(proPhoto_img);
11 |
12 | visualize_gamut_of_image(proRGB, 'ProPhoto RGB', ...
13 | 'Original ProPhoto Image CIE diagram');
14 | visualize_gamut_of_image(sRGB, 'sRGB', ...
15 | 'sRGB Image CIE diagram');
16 |
17 | %% Perform Global Mapping
18 | % Subsmpaling
19 | sub_sample_size = 150;
20 | sub_sRGB = subsampling(sRGB, sub_sample_size);
21 | sub_pro = subsampling(proRGB, sub_sample_size);
22 |
23 | % Compute mapping on subsamples
24 | M = computeMapping(sub_sRGB, sub_pro);
25 | reconstructed_proPhoto_RGB = applyMapping(sRGB, M);
26 | % Clipping
27 | reconstructed_proPhoto_RGB = double(reconstructed_proPhoto_RGB);
28 | reconstructed_proPhoto_RGB(reconstructed_proPhoto_RGB > 1) = 1;
29 | reconstructed_proPhoto_RGB(reconstructed_proPhoto_RGB < 0) = 0;
30 |
31 | visualize_gamut_of_image(reconstructed_proPhoto_RGB, 'ProPhoto RGB', ...
32 | 'Reconstructed ProPhoto Image using Global Mapping CIE Diagram');
33 |
34 |
35 | %% Visualize Photoshop
36 |
37 | pro_PTS = imread(pro_PTS_filename);
38 | visualize_gamut_of_image(pro_PTS, 'ProPhoto RGB', ...
39 | 'Reconstructed ProPhoto Using PTS CIE Diagram');
40 |
41 |
42 | %% Evaluation
43 | % deltaE
44 |
45 | deltaE2000_our = calc_deltaE2000(reconstructed_proPhoto_RGB, ...
46 | proPhoto_img, 'ProPhoto RGB');
47 | deltaE_our = calc_deltaE(reconstructed_proPhoto_RGB, proPhoto_img, ...
48 | 'ProPhoto RGB');
49 |
50 | deltaE2000_PTS = calc_deltaE2000(pro_PTS, proPhoto_img, 'ProPhoto RGB');
51 | deltaE_PTS = calc_deltaE(pro_PTS, proPhoto_img, 'ProPhoto RGB');
52 |
53 | disp(sprintf('DeltaE2000 - DeltaE Global Mapping: %0.3d - %0.3d', deltaE2000_our, deltaE_our));
54 | disp(sprintf('DeltaE2000 - DeltaE PTS: %0.3d - %0.3d', deltaE2000_PTS, deltaE_PTS));
55 |
56 |
57 | %% Calculate MSE MAE
58 | mse_our = calc_mse(reconstructed_proPhoto_RGB, proPhoto_img);
59 | mae_our = calc_mae(reconstructed_proPhoto_RGB, proPhoto_img);
60 |
61 | mse_PTS = calc_mse(pro_PTS, proPhoto_img);
62 | mae_PTS = calc_mae(pro_PTS, proPhoto_img);
63 |
64 |
65 | disp(sprintf('MSE - MAE Global Mapping: %0.3d - %0.3d', mse_our, mae_our));
66 | disp(sprintf('MSE - MAE PTS: %0.3d - %0.3d', mse_PTS, mae_PTS));
67 |
68 |
69 | %% Draw Heatmap
70 | drawHeatmap(reconstructed_proPhoto_RGB, ...
71 | proPhoto_img, 'Heatmap between Global Mapping and the original');
72 | drawHeatmap(pro_PTS, ...
73 | proPhoto_img, 'Heatmap between PTS and the original');
74 |
75 | %% Saving reconstructed ProPhoto RGB
76 |
77 | out_filename = image_name + "_reconstructed_GlobalMappingRGB.tif";
78 |
79 | RGB16 = uint16(round(reconstructed_proPhoto_RGB*65535));
80 | imwrite(RGB16, out_filename);
81 |
82 |
--------------------------------------------------------------------------------
/gamut-mapping/global_mapping_evaluation.m:
--------------------------------------------------------------------------------
1 | clc
2 | clear
3 | warning off
4 |
5 | experiment_name = 'Global_ProPhoto';
6 | in_dir = 'C:\Users\Afifi\OneDrive - York University\sRGB_from_DNG';
7 | target_dir = 'C:\Users\Afifi\OneDrive - York University\ProPhoto_from_DNG';
8 | in_ext = '.jpg';
9 | target_ext = '.tif';
10 | if strcmpi(target_ext,'.tif')
11 | target_colorSpace = 'ProPhoto RGB';
12 | elseif strcmpi (target_ext, '.jpg')
13 | target_colorSpace = 'sRGB';
14 | end
15 | in_images = dir(fullfile(in_dir,['*' in_ext]));
16 | in_images = fullfile(in_dir,{in_images(:).name});
17 | target_images = dir(fullfile(target_dir,['*' target_ext]));
18 | target_images = fullfile(target_dir,{target_images(:).name});
19 |
20 | deltaE2000 = [];
21 | deltaE = [];
22 | PSNR = [];
23 | MAE = [];
24 |
25 | for i = 1 : length(in_images)
26 | fprintf('Processing (%d/%d) ... \n',i,length(in_images));
27 |
28 | in_img = im2double(imread(in_images{i}));
29 | target_img = im2double(imread(target_images{i}));
30 | %% Perform Global Mapping
31 | % Subsmpaling
32 | sub_sample_size = 150;
33 | sub_in_img = subsampling(in_img, sub_sample_size);
34 | sub_target_img = subsampling(target_img, sub_sample_size);
35 | % Compute mapping on subsamples
36 | M = computeMapping(sub_in_img, sub_target_img);
37 | reconstructed = applyMapping(in_img, M);
38 | % Clipping
39 | reconstructed(reconstructed > 1) = 1;
40 | reconstructed(reconstructed < 0) = 0;
41 | %% Evaluation
42 | % deltaE
43 | deltaE2000 = [deltaE2000; ...
44 | calc_deltaE2000(reconstructed, target_img, target_colorSpace)];
45 | deltaE = [deltaE;...
46 | calc_deltaE(reconstructed, target_img, target_colorSpace)];
47 | %% Calculate MSE MAE
48 | PSNR = [PSNR; psnr(reconstructed, target_img)];
49 | MAE = [MAE; calc_mae(reconstructed, target_img)];
50 | end
51 |
52 |
53 | fprintf('DeltaE 2000:\n mean: %0.3f, median: %0.3f, Q1: %0.3f, Q3: %0.3f\n',...
54 | mean(deltaE2000), median(deltaE2000), quantile(deltaE2000,0.25), ...
55 | quantile(deltaE2000,0.75));
56 |
57 |
58 | fprintf('DeltaE 76:\n mean: %0.3f, median: %0.3f, Q1: %0.3f, Q3: %0.3f\n',...
59 | mean(deltaE), median(deltaE), quantile(deltaE,0.25), ...
60 | quantile(deltaE,0.75));
61 |
62 |
63 | fprintf('PSNR:\n mean: %0.3f, median: %0.3f, Q1: %0.3f, Q3: %0.3f\n',...
64 | mean(PSNR), median(PSNR), quantile(PSNR,0.25), quantile(PSNR,0.75));
65 |
66 | fprintf('MAE:\n mean: %0.3f, median: %0.3f, Q1: %0.3f, Q3: %0.3f\n',...
67 | mean(MAE), median(MAE), quantile(MAE,0.25), quantile(MAE,0.75));
68 |
69 | %
70 | results.deltaE2000 = deltaE2000;
71 | results.deltaE = deltaE;
72 | results.PSNR = PSNR;
73 | results.MAE = MAE;
74 |
75 | save(experiment_name,'results','-v7.3');
76 |
--------------------------------------------------------------------------------
/gamut-mapping/gma_global_mapping.m:
--------------------------------------------------------------------------------
1 | clc
2 | clear
3 | warning off
4 |
5 | in_dir = 'D:\Dataset\gamut_mapping_data\WideGamutDataset\test-srgb-8bpc';
6 | target_dir = 'D:\Dataset\gamut_mapping_data\WideGamutDataset\test-prop-8bpc';
7 | out_dir = 'D:\Dataset\gamut_mapping_data\WideGamutDataset\cic2020_global';
8 |
9 | in_ext = '.png';
10 | target_ext = '.png';
11 | in_images = dir(fullfile(in_dir,['*' in_ext]));
12 | in_images = fullfile(in_dir,{in_images(:).name});
13 | target_images = dir(fullfile(target_dir,['*' target_ext]));
14 | target_images = fullfile(target_dir,{target_images(:).name});
15 |
16 | for i = 1 : length(in_images)
17 | fprintf('Processing (%d/%d) ... \n',i,length(in_images));
18 | [~,name,ext] = fileparts(in_images{i});
19 | in_img = im2double(imread(in_images{i}));
20 | target_img = im2double(imread(target_images{i}));
21 | %% Perform Global Mapping
22 | % Subsmpaling
23 | sub_sample_size = 150;
24 | sub_in_img = subsampling(in_img, sub_sample_size);
25 | sub_target_img = subsampling(target_img, sub_sample_size);
26 | % Compute mapping on subsamples
27 | M = computeMapping(sub_in_img, sub_target_img);
28 | reconstructed = applyMapping(in_img, M);
29 | % Clipping
30 | reconstructed(reconstructed > 1) = 1;
31 | reconstructed(reconstructed < 0) = 0;
32 | if strcmpi(target_ext,'.tif') == 1 || strcmpi(target_ext,'.png') == 1
33 | imwrite(im2uint8(reconstructed),fullfile(out_dir,[name target_ext]));
34 | else
35 | imwrite(reconstructed,fullfile(out_dir,[name target_ext]));
36 | end
37 | end
--------------------------------------------------------------------------------
/gamut-mapping/gma_global_mapping_16b.m:
--------------------------------------------------------------------------------
1 | clc
2 | clear
3 | warning off
4 |
5 | in_dir = 'D:\Dataset\gamut_mapping_data\WideGamutDataset\test-srgb-8bpc';
6 | target_dir = 'D:\Dataset\gamut_mapping_data\WideGamutDataset\test-prop-8bpc';
7 | out_dir = 'D:\Dataset\gamut_mapping_data\WideGamutDataset\cic2020_local';
8 |
9 | in_ext = '.png';
10 | target_ext = '.png';
11 | in_images = dir(fullfile(in_dir,['*' in_ext]));
12 | in_images = fullfile(in_dir,{in_images(:).name});
13 | target_images = dir(fullfile(target_dir,['*' target_ext]));
14 | target_images = fullfile(target_dir,{target_images(:).name});
15 |
16 | for i = 1 : length(in_images)
17 | fprintf('Processing (%d/%d) ... \n',i,length(in_images));
18 | [~,name,ext] = fileparts(in_images{i});
19 | in_img = im2double(imread(in_images{i}));
20 | target_img = im2double(imread(target_images{i}));
21 | %% Perform Global Mapping
22 | % Subsmpaling
23 | sub_sample_size = 150;
24 | sub_in_img = subsampling(in_img, sub_sample_size);
25 | sub_target_img = subsampling(target_img, sub_sample_size);
26 | % Compute mapping on subsamples
27 | M = computeMapping(sub_in_img, sub_target_img);
28 | reconstructed = applyMapping(in_img, M);
29 | % Clipping
30 | reconstructed(reconstructed > 1) = 1;
31 | reconstructed(reconstructed < 0) = 0;
32 | if strcmpi(target_ext,'.tif') == 1 || strcmpi(target_ext,'.png') == 1
33 | imwrite(im2uint16(reconstructed),fullfile(out_dir,[name target_ext]));
34 | else
35 | imwrite(reconstructed,fullfile(out_dir,[name target_ext]));
36 | end
37 | end
--------------------------------------------------------------------------------
/gamut-mapping/gma_local_mapping.m:
--------------------------------------------------------------------------------
1 | clear
2 | clc
3 | close all;
4 | warning off;
5 |
6 | %% Read input
7 | in_dir = 'D:\Dataset\gamut_mapping_data\WideGamutDataset\test-srgb-8bpc';
8 | target_dir = 'D:\Dataset\gamut_mapping_data\WideGamutDataset\test-prop-8bpc';
9 | out_dir = 'D:\Dataset\gamut_mapping_data\WideGamutDataset\cic2020_local';
10 | in_ext = '.png';
11 | target_ext = '.png';
12 | in_images = dir(fullfile(in_dir,['*' in_ext]));
13 | in_images = fullfile(in_dir,{in_images(:).name});
14 | target_images = dir(fullfile(target_dir,['*' target_ext]));
15 | target_images = fullfile(target_dir,{target_images(:).name});
16 |
17 | for i = 1 : length(in_images)
18 | fprintf('Processing (%d/%d) ... \n',i,length(in_images));
19 | [~,name,ext] = fileparts(in_images{i});
20 | source_filename = in_images{i};
21 | target_filename = target_images{i};
22 | source = im2double(imread(source_filename));
23 | target = im2double(imread(target_filename));
24 |
25 | % Subsampling
26 | sub_sample_size = 150;
27 | sub_source = subsampling(source, sub_sample_size);
28 | sub_target = subsampling(target, sub_sample_size);
29 |
30 |
31 | %% global
32 | % Compute mapping on subsamples
33 | M = computeMapping(sub_source, sub_target);
34 | corrected_global = applyMapping(source, M);
35 | % Clipping
36 | reconstructed_global = corrected_global;
37 | reconstructed_global(reconstructed_global > 1) = 1;
38 | reconstructed_global(reconstructed_global < 0) = 0;
39 | sub_corrected_global = applyMapping(sub_source, M);
40 |
41 | %% super-pixels
42 | N = 100;
43 | [super_target,N] = superpixels(sub_target,N, 'IsInputLab', true);
44 | sub_target = reshape(sub_target,[],3);
45 | sub_corrected_global = reshape(sub_corrected_global,[],3);
46 | se = strel('disk', 15);
47 | Mf = zeros(N,11,3);
48 | %% local post-correction
49 | for s = 1 : N
50 | mask = super_target == s;
51 | inds = find(reshape(mask,[],1));
52 | mask = double(imdilate(mask,se));
53 | mask = imgaussfilt(mask,8);
54 | w = reshape(mask,[],1);
55 | w = w./sum(w);
56 | m = lscov(kernel(reshape(sub_corrected_global,[],3)),...
57 | reshape(sub_target,[],3),w);
58 | Mf(s,:,:) = reshape(m, [11,3]);
59 | end
60 |
61 |
62 | %% correction
63 | reconstructed = corrected_global;
64 | for s = 1 : N
65 | mask = super_target == s;
66 | blend_mask = imresize(mask,[size(corrected_global,1), size(corrected_global,2)]);
67 | temp = applyMapping(corrected_global, squeeze(Mf(s,:,:)), blend_mask);
68 | reconstructed(repmat(blend_mask,1,1,3)) = temp(repmat(blend_mask,1,1,3));
69 | end
70 |
71 | %% Clipping out of gamut
72 | reconstructed(reconstructed > 1) = 1;
73 | reconstructed(reconstructed < 0) = 0;
74 |
75 | if strcmpi(target_ext,'.tif') == 1 || strcmpi(target_ext,'.png') == 1
76 | imwrite(im2uint8(reconstructed),fullfile(out_dir,[name target_ext]));
77 | else
78 | imwrite(reconstructed,fullfile(out_dir,[name target_ext]));
79 | end
80 | end
81 |
82 |
--------------------------------------------------------------------------------
/gamut-mapping/gma_local_mapping_16b.m:
--------------------------------------------------------------------------------
1 | clear
2 | clc
3 | close all;
4 | warning off;
5 |
6 | %% Read input
7 | in_dir = 'D:\Dataset\2021_gma_dataset\refined_dataset\srgb_full';
8 | target_dir = 'D:\Dataset\2021_gma_dataset\refined_dataset\prophoto_full_16b';
9 | out_dir = 'D:\Dataset\2021_gma_dataset\refined_dataset\cic2020_prophoto_full_16b';
10 | in_ext = '.png';
11 | target_ext = '.png';
12 | in_images = dir(fullfile(in_dir,['*' in_ext]));
13 | in_images = fullfile(in_dir,{in_images(:).name});
14 | target_images = dir(fullfile(target_dir,['*' target_ext]));
15 | target_images = fullfile(target_dir,{target_images(:).name});
16 |
17 | tic;
18 | for i = 1 : length(in_images)
19 | fprintf('Processing (%d/%d) ... \n',i,length(in_images));
20 | [~,name,ext] = fileparts(in_images{i});
21 | source_filename = in_images{i};
22 | target_filename = target_images{i};
23 | source = im2double(imread(source_filename));
24 | target = im2double(imread(target_filename));
25 |
26 | % Subsampling
27 | sub_sample_size = 150;
28 | sub_source = subsampling(source, sub_sample_size);
29 | sub_target = subsampling(target, sub_sample_size);
30 |
31 |
32 | %% global
33 | % Compute mapping on subsamples
34 | M = computeMapping(sub_source, sub_target);
35 | corrected_global = applyMapping(source, M);
36 | % Clipping
37 | reconstructed_global = corrected_global;
38 | reconstructed_global(reconstructed_global > 1) = 1;
39 | reconstructed_global(reconstructed_global < 0) = 0;
40 | sub_corrected_global = applyMapping(sub_source, M);
41 |
42 | %% super-pixels
43 | N = 100;
44 | [super_target,N] = superpixels(sub_target,N, 'IsInputLab', true);
45 | sub_target = reshape(sub_target,[],3);
46 | sub_corrected_global = reshape(sub_corrected_global,[],3);
47 | se = strel('disk', 15);
48 | Mf = zeros(N,11,3);
49 | %% local post-correction
50 | for s = 1 : N
51 | mask = super_target == s;
52 | inds = find(reshape(mask,[],1));
53 | mask = double(imdilate(mask,se));
54 | mask = imgaussfilt(mask,8);
55 | w = reshape(mask,[],1);
56 | w = w./sum(w);
57 | m = lscov(kernel(reshape(sub_corrected_global,[],3)),...
58 | reshape(sub_target,[],3),w);
59 | Mf(s,:,:) = reshape(m, [11,3]);
60 | end
61 |
62 |
63 | %% correction
64 | reconstructed = corrected_global;
65 | for s = 1 : N
66 | mask = super_target == s;
67 | blend_mask = imresize(mask,[size(corrected_global,1), size(corrected_global,2)]);
68 | temp = applyMapping(corrected_global, squeeze(Mf(s,:,:)), blend_mask);
69 | reconstructed(repmat(blend_mask,1,1,3)) = temp(repmat(blend_mask,1,1,3));
70 | end
71 |
72 | %% Clipping out of gamut
73 | reconstructed(reconstructed > 1) = 1;
74 | reconstructed(reconstructed < 0) = 0;
75 |
76 | if strcmpi(target_ext,'.tif') == 1 || strcmpi(target_ext,'.png') == 1
77 | imwrite(im2uint16(reconstructed),fullfile(out_dir,[name target_ext]));
78 | else
79 | imwrite(reconstructed,fullfile(out_dir,[name target_ext]));
80 | end
81 | end
82 |
83 | timeElapsed = toc;
84 |
--------------------------------------------------------------------------------
/gamut-mapping/kernel.m:
--------------------------------------------------------------------------------
1 | function O=kernel(I)
2 | % kernel(R,G,B)=[R,G,B,RG,RB,GB,R2,G2,B2,RGB,1];
3 | % Kernel func reference:
4 | % Hong, et al., "A study of digital camera colorimetric
5 | % characterization based on polynomial modeling." Color
6 | % Research & Application, 2001.
7 | O=[I,... %r,g,b
8 | I(:,1).*I(:,2),I(:,1).*I(:,3),I(:,2).*I(:,3),... %rg,rb,gb
9 | I.*I,... %r2,g2,b2
10 | I(:,1).*I(:,2).*I(:,3),... %rgb
11 | ones(size(I,1),1)]; %1
12 | end
--------------------------------------------------------------------------------
/gamut-mapping/local_mapping.m:
--------------------------------------------------------------------------------
1 | clear
2 | clc
3 | close all;
4 | warning off;
5 |
6 | %% Read input
7 | in_dir = '../sRGB';
8 | target_dir = '../ProPhoto';
9 | out_dir = '../From_sRGB_to_ProPhoto_Local';
10 | in_ext = '.jpg';
11 | target_ext = '.tif';
12 | in_images = dir(fullfile(in_dir,['*' in_ext]));
13 | in_images = fullfile(in_dir,{in_images(:).name});
14 | target_images = dir(fullfile(target_dir,['*' target_ext]));
15 | target_images = fullfile(target_dir,{target_images(:).name});
16 |
17 | for i = 1 : length(in_images)
18 | fprintf('Processing (%d/%d) ... \n',i,length(in_images));
19 | [~,name,ext] = fileparts(in_images{i});
20 | source_filename = in_images{i};
21 | target_filename = target_images{i};
22 | source = im2double(imread(source_filename));
23 | target = im2double(imread(target_filename));
24 |
25 | % Subsampling
26 | sub_sample_size = 150;
27 | sub_source = subsampling(source, sub_sample_size);
28 | sub_target = subsampling(target, sub_sample_size);
29 |
30 |
31 | %% global
32 | % Compute mapping on subsamples
33 | M = computeMapping(sub_source, sub_target);
34 | corrected_global = applyMapping(source, M);
35 | % Clipping
36 | reconstructed_global = corrected_global;
37 | reconstructed_global(reconstructed_global > 1) = 1;
38 | reconstructed_global(reconstructed_global < 0) = 0;
39 | sub_corrected_global = applyMapping(sub_source, M);
40 |
41 | %% super-pixels
42 | N = 100;
43 | [super_target,N] = superpixels(sub_target,N, 'IsInputLab', true);
44 | sub_target = reshape(sub_target,[],3);
45 | sub_corrected_global = reshape(sub_corrected_global,[],3);
46 | se = strel('disk', 15);
47 | Mf = zeros(N,11,3);
48 | %% local post-correction
49 | for s = 1 : N
50 | mask = super_target == s;
51 | inds = find(reshape(mask,[],1));
52 | mask = double(imdilate(mask,se));
53 | mask = imgaussfilt(mask,8);
54 | w = reshape(mask,[],1);
55 | w = w./sum(w);
56 | m = lscov(kernel(reshape(sub_corrected_global,[],3)),...
57 | reshape(sub_target,[],3),w);
58 | Mf(s,:,:) = reshape(m, [11,3]);
59 | end
60 |
61 |
62 | %% correction
63 | reconstructed = corrected_global;
64 | for s = 1 : N
65 | mask = super_target == s;
66 | blend_mask = imresize(mask,[size(corrected_global,1), size(corrected_global,2)]);
67 | temp = applyMapping(corrected_global, squeeze(Mf(s,:,:)), blend_mask);
68 | reconstructed(repmat(blend_mask,1,1,3)) = temp(repmat(blend_mask,1,1,3));
69 | end
70 |
71 | %% Clipping out of gamut
72 | reconstructed(reconstructed > 1) = 1;
73 | reconstructed(reconstructed < 0) = 0;
74 |
75 | if strcmpi(target_ext,'.tif') == 1 || strcmpi(target_ext,'.png') == 1
76 | imwrite(im2uint16(reconstructed),fullfile(out_dir,[name target_ext]));
77 | else
78 | imwrite(reconstructed,fullfile(out_dir,[name target_ext]));
79 | end
80 | end
81 |
82 |
--------------------------------------------------------------------------------
/gamut-mapping/local_mapping_single_image.m:
--------------------------------------------------------------------------------
1 | clear
2 | clc
3 | close all;
4 | warning off;
5 |
6 | %% Read input
7 | tic
8 | image_name = "Canon1DsMkIII_0003";
9 | target_colorspace = 'ProPhoto RGB';
10 | source_filename = image_name + ".jpg";
11 | target_filename = image_name + ".tif";
12 |
13 | source = im2double(imread(source_filename));
14 | target = im2double(imread(target_filename));
15 |
16 | % Subsampling
17 | sub_sample_size = 150;
18 | sub_source = subsampling(source, sub_sample_size);
19 | sub_target = subsampling(target, sub_sample_size);
20 |
21 |
22 | %% global
23 | % Compute mapping on subsamples
24 | M = computeMapping(sub_source, sub_target);
25 | corrected_global = applyMapping(source, M);
26 | % Clipping
27 | reconstructed_global = corrected_global;
28 | reconstructed_global(reconstructed_global > 1) = 1;
29 | reconstructed_global(reconstructed_global < 0) = 0;
30 | sub_corrected_global = applyMapping(sub_source, M);
31 |
32 | %% super-pixels
33 | N = 100;
34 | [super_target,N] = superpixels(sub_target,N,'IsInputLab',true);
35 | sub_target = reshape(sub_target,[],3);
36 | sub_corrected_global = reshape(sub_corrected_global,[],3);
37 | se = strel('disk', 15);
38 | Mf = zeros(N,11,3);
39 | %% local post-correction
40 | for s = 1 : N
41 | mask = super_target == s;
42 | inds = find(reshape(mask,[],1));
43 | mask = double(imdilate(mask,se));
44 | mask = imgaussfilt(mask,8);
45 | w = reshape(mask,[],1);
46 | w = w./sum(w);
47 | m = lscov(kernel(reshape(sub_corrected_global,[],3)),...
48 | reshape(sub_target,[],3),w);
49 | Mf(s,:,:) = reshape(m, [11,3]);
50 | end
51 |
52 |
53 | %% correction
54 | reconstructed = corrected_global;
55 | for s = 1 : N
56 | mask = super_target == s;
57 | blend_mask = imresize(mask,[size(corrected_global,1), size(corrected_global,2)]);
58 | temp = applyMapping(corrected_global, squeeze(Mf(s,:,:)), blend_mask);
59 | reconstructed(repmat(blend_mask,1,1,3)) = temp(repmat(blend_mask,1,1,3));
60 | end
61 | toc
62 |
63 | %% Clipping out of gamut
64 | reconstructed(reconstructed > 1) = 1;
65 | reconstructed(reconstructed < 0) = 0;
66 |
67 |
68 | %% Evaluation
69 | fprintf('Global: PSNR=%0.3f, MAE=%0.3f, deltaE=%0.3f\n',...
70 | psnr(reconstructed_global, target), calc_mae(reconstructed_global, target),...
71 | calc_deltaE2000(reconstructed_global, target, target_colorspace));
72 |
73 | fprintf('Ours: PSNR=%0.3f, MAE=%0.3f, deltaE=%0.3f\n',...
74 | psnr(reconstructed, target), calc_mae(reconstructed, target),...
75 | calc_deltaE2000(reconstructed, target, target_colorspace));
76 |
77 |
--------------------------------------------------------------------------------
/gamut-mapping/startpooling.m:
--------------------------------------------------------------------------------
1 | function startpooling(NUM_WORKERS)
2 | %Start parallel pool using the given number of cores (requires Parallel
3 | %Computing Toolbox).
4 | %
5 | %Input:
6 | % -NUM_WORKERS: number of cores.
7 | %
8 | % Copyright (c) 2018 Mahmoud Afifi
9 | % Lassonde School of Engineering
10 | % York University
11 | % mafifi@eecs.yorku.ca
12 | %
13 | % Permission is hereby granted, free of charge, to any person obtaining
14 | % a copy of this software and associated documentation files (the
15 | % "Software"), to deal in the Software with restriction for its use for
16 | % research purpose only, subject to the following conditions:
17 | %
18 | % The above copyright notice and this permission notice shall be included
19 | % in all copies or substantial portions of the Software.
20 | %
21 | % The Software is provided "as is", without warranty of any kind.
22 | %
23 | % Please cite the following work if this program is used:
24 | % Mahmoud Afifi, Brian Price, Scott Cohen, and Michael S. Brown, "White Balance Correction for sRGB Rendered Images", ECCV 2018.
25 | %
26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27 | po = gcp('nocreate');
28 | if ~isempty(po)
29 | if po.NumWorkers ~= NUM_WORKERS
30 | delete(po);
31 | pc=parcluster('local');
32 | pc.NumWorkers=NUM_WORKERS;
33 | po = parpool(NUM_WORKERS);
34 | end
35 | else
36 | pc=parcluster('local');
37 | pc.NumWorkers=NUM_WORKERS;
38 | po = parpool(NUM_WORKERS);
39 | end
40 | end
--------------------------------------------------------------------------------
/gamut-mapping/subsampling.m:
--------------------------------------------------------------------------------
1 | function output = subsampling (I, t)
2 | inds_1 = round(linspace(1,size(I,1),t));
3 | inds_2 = round(linspace(1,size(I,2),t));
4 | output = I(inds_1,inds_2,:);
5 |
6 |
--------------------------------------------------------------------------------
/gamut-mapping/visualize_gamut_of_image.m:
--------------------------------------------------------------------------------
1 |
2 | function visualize_gamut_of_image(RGB, color_space, diagram_title, sampleStep)
3 |
4 | if nargin == 3
5 | sampleStep = 1; %no sampling
6 | end
7 | RGB = reshape(RGB, [], 3);
8 |
9 | if ~isa(RGB, 'double')
10 | RGB = im2double(RGB);
11 | end
12 |
13 | icc_folder = iccroot;
14 | % if strcmp(color_space, 'sRGB')
15 |
16 | fprintf('Color Space %s', color_space);
17 |
18 | profiles = iccfind(icc_folder, color_space);
19 | profile = profiles{1};
20 | % convert ProPhoto RGB 2 XYZ
21 | cform = makecform('mattrc',profile.MatTRC, ...
22 | 'direction','forward');
23 | XYZ = applycform(RGB, cform);
24 | cform_xyz2xyl = makecform('xyz2xyl');
25 | xyl = applycform(XYZ, cform_xyz2xyl);
26 |
27 |
28 | disp("xyl")
29 | size(xyl)
30 | xv = xyl(1:sampleStep:end,1);
31 | yv = xyl(1:sampleStep:end,2);
32 |
33 |
34 | [rgb_xy, wp_xy, rgb_xy_lines] = extract_xy(color_space);
35 | % plotting the CIE 1931 XY Chromaticity diagram
36 |
37 | figure
38 | plotChromaticity
39 | hold on % hold on figure, there's more to come
40 |
41 | scatter(xv, yv, 1, 'MarkerFaceColor', 'flat');
42 | % Draw gamut border
43 | gamut_border = line(rgb_xy_lines(:,1), rgb_xy_lines(:,2), 'Color', 'red');
44 | legend([gamut_border], {color_space});
45 | title(diagram_title);
46 | hold off;
47 |
48 | end
--------------------------------------------------------------------------------