├── 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 | ![overview_figure](./figures/overview_figure.jpg) 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 | ![result](./figures/CombinedResults.jpg) 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 --------------------------------------------------------------------------------