imwrite() saved colormap incorrectly

17 views (last 30 days)
Waveform Dai
Waveform Dai on 8 Jul 2021
Edited: Walter Roberson on 9 Jul 2021
imwrite() was used to save indexed images along with its colormap. Suppose we use 'givenMap' to denote the colormap provided to imwrite() and 'savedMap' to denote the colormap obtained using imread() on the saved image.
The weird things were that:
1) For some colormaps, exact values in 'givenMap' and 'savedMap' were different. The magnitude of differences was in the order of 10%-20%;
2) But for other colormaps, the content in 'givenMap' and 'savedMap' were the same.
Below are exemplar codes:
% file path for saving image
savedFilePath = 'D:\BMP\imwriteTest';
if ~exist(savedFilePath,'dir')
mkdir(savedFilePath);
end
% load image data
givenData = load('clown.mat');
givenData = givenData.X;
% set range of image to 0-1
minImg = min(givenData(:));
maxImg = max(givenData(:));
givenData = (givenData-minImg)./(maxImg-minImg);
% set image to uint8 type
givenData = givenData*255;
givenData = uint8(givenData);
Exemplar colormap that remained the same after imwrite():
% create a colormap
defaultMap = (0:255)'*[1,1,1]; % 256*3
defaultMap = defaultMap./255;
givenMap = defaultMap;
% use imwrite to save both data and colormap
imwrite(givenData,givenMap,[savedFilePath,'\copperclown.bmp'],'bmp');
% load saved data and colormap
[~,savedMap] = imread([savedFilePath,'\copperclown.bmp']);
% sum of differences
diffMap = sum(abs(givenMap(:)-savedMap(:)));
Exemplar colormap that changed after imwrite():
% create a colormap
defaultMap = (0:255)'*[1,1,1]; % 256*3
defaultMap = defaultMap./255;
givenMap = defaultMap;
% modify centeral part of colormap
centralMapValue = (0:200)/200;
givenMap(2:202,:) = repmat(centralMapValue(:),1,3);
% use imwrite to save both data and colormap
imwrite(givenData,givenMap,[savedFilePath,'\copperclown.bmp'],'bmp');
% load saved data and colormap
[~,savedMap] = imread([savedFilePath,'\copperclown.bmp']);
% sum of differences
diffMap = sum(abs(givenMap(:)-savedMap(:)));
Below are first 10 rows of 'givenMap' and 'savedMap', it's obvious that they were quite different:
Moreever, we can see that differences of adjacent rows was fixed at 0.05 in 'givenMap', but the value oscillate at '0.39' and '0.78' in 'savedMap'.
% show differences in adjacent rows of colormap
modifiedMapIndex = 2:202;
diffGivenMap_adjacentRow = diff(givenMap(modifiedMapIndex,1));
diffSavedMap_adjacentRow = diff(savedMap(modifiedMapIndex,1));
diffGivenMap_adjacentRow = [0;diffGivenMap_adjacentRow(:)];
diffSavedMap_adjacentRow = [0;diffSavedMap_adjacentRow(:)];
maxDiffMap_adjacentRow = max(abs([diffGivenMap_adjacentRow(:);diffSavedMap_adjacentRow(:)]));
maxDiffMap_adjacentRow = maxDiffMap_adjacentRow*(1+0.05*sign(maxDiffMap_adjacentRow));
minDiffMap_adjacentRow = 0;
figure;
fontSize = 15;
subplot(1,2,1)
plot(modifiedMapIndex,diffGivenMap_adjacentRow,'ko')
xlim([2 202])
ylim([minDiffMap_adjacentRow maxDiffMap_adjacentRow])
xlabel('#Row','FontSize',fontSize)
ylabel('mapDiff\_adjacentRow','FontSize',fontSize)
title('Difference in Adjacent Row@givenMap','FontSize',fontSize)
subplot(1,2,2)
plot(modifiedMapIndex,diffSavedMap_adjacentRow,'ro')
xlim([2 202])
ylim([minDiffMap_adjacentRow maxDiffMap_adjacentRow])
xlabel('#Row','FontSize',fontSize)
ylabel('mapDiff\_adjacentRow','FontSize',fontSize)
title('Difference in Adjacent Row@savedMap','FontSize',fontSize)
More detailled comparison can see the attached code.
My question is: are these differences caused by
1) the innate 'imprecision' of imwrite() or imread()?
or
2) my misuse of imwrite() or imread()?
  5 Comments
Walter Roberson
Walter Roberson on 9 Jul 2021
Edited: Walter Roberson on 9 Jul 2021
Representing colors using at most one byte per component (0:255) is inherent in bitmap format. With some options, even fewer bits are used.
I have to wonder about that first map. 0.005 * 255 is about 1 1/4 so if that table 0.005 apart were to continue to 256 entries you would be dealing with relative intensity greater than 1.
Waveform Dai
Waveform Dai on 9 Jul 2021
Thanks for all your patience and detailed answers.
Following Stephen's comment, I think I know how imwrite() deal with provided colormaps: for giivenMap(in range 0-1), it will first be multiplied by 255 to be in range 0-255, and then be rounded to nearest integer(like the function of uint8). In the last step, 255 will be divided to obtain savedMap which is supposed to be in range 0-1.
The logic behind such processing, as is pointed out by both Stephen and Walter, is that: for any color channel of an image, the maximum number of different values is 256 (I don't know if this is also true for display devices/monitor). Thus, values in the colormap must be discretized within [0 255].
Lastly, I really appreciate the help of Stephen and Walter. Thanks for your help.
Below are my code to demonstrate the discretization of imwrite():
% pre-define some parameter
savedFilePath = 'D:\BMP\imwriteTest';
if ~exist(savedFilePath,'dir')
mkdir(savedFilePath);
end
% load image
givenData = load('clown.mat');
givenData = givenData.X;
% set range of image to 0-1
minImg = min(givenData(:));
maxImg = max(givenData(:));
givenData = (givenData-minImg)./(maxImg-minImg);
% set image to uint8 type
givenData = givenData*255;
givenData = uint8(givenData);
% create a colormap in which each row is scaled by values in [0 255]
givenMap = ones(256,3);
givenGain = 1./linspace(0,255,256);
givenGain = repmat(givenGain(:),1,3);
givenMap = givenMap.*givenGain;
givenMap(givenMap > 1) = 1;
% use imwrite to save both data and colormap
imwrite(givenData,givenMap,[savedFilePath,'\copperclown.bmp'],'bmp');
% load saved data and colormap
[savedData,savedMap] = imread([savedFilePath,'\copperclown.bmp']);
% multiply givenMap and savedMap with 255
givenMap_float = givenMap*255;
savedMap_float = savedMap*255;
diffMap_float = abs(givenMap_float-savedMap_float);
% set givenMap and savedMap to type uint8
givenMap_int = uint8(givenMap_float);
savedMap_int = uint8(savedMap_float);
diffMap_int = abs(givenMap_int-savedMap_int);
diffMap_int = double(diffMap_int);
% show results before and after discretization
figure;
plot(1:256,diffMap_float(:,1),'ro')
hold on;
plot(1:256,diffMap_int(:,1),'ko')
plot([0 257],0.5+[0 0],'k-.','linewidth',2)
currLegend = legend('float','integer','location','northeast');
set(currLegend,'fontsize',15);
xlabel('#Row','FontSize',15)
ylabel('absDiff','FontSize',15)
xlim([0 257])
ylim([0 1])
title('abs(givenMap-savedMap)','FontSize',15)
Output of above code is:

Sign in to comment.

Answers (0)

Categories

Find more on Images in Help Center and File Exchange

Products


Release

R2017a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!