Code covered by the BSD License  

Highlights from
Colormaps compatible with red-green color perception deficiencies

image thumbnail

Colormaps compatible with red-green color perception deficiencies

by

 

12 Jun 2011 (Updated )

Colormaps for displaying data compatible with red-green color perception deficiencies

Editor's Notes:

This file was selected as MATLAB Central Pick of the Week

cmap=morgenstemning(n,varargin)
function cmap=morgenstemning(n,varargin)
%MORGENSTEMNING Colormap that increases linearly in lightness (with colors)
%
%	Written by Matthias Geissbuehler - matthias.geissbuehler@a3.epfl.ch
%	January 2013
%
%   Colormap that increases linearly in lightness (such as a pure black to white
%   map) but incorporates additional colors that help to emphasize the
%   transitions and hence enhance the perception of the data.
%   This colormap is designed to be printer-friendly both for color printers as
%   as well as B&W printers.
%
%   Credit: The idea of the passages over blue&red stems from ImageJ's LUT 'Fire'
%   Our colormap corrects the color-printout-problems as well as the
%   non-linearity in the fire-colormap which would make it incompatible
%   with a B&W printing.
%
%
%   See also: isolum, ametrine
%
%
%   Please feel free to use this colormap at your own convenience.
%   A citation to the original article is of course appreciated, however not "mandatory" :-)
%   
%   M. Geissbuehler and T. Lasser "How to display data by color schemes compatible
%   with red-green color perception deficiencies" Opt. Express 21, 9862-9874 (2013)
%   http://www.opticsinfobase.org/oe/abstract.cfm?URI=oe-21-8-9862
%
%
%   For more detailed information, please see:
%   http://lob.epfl.ch -> Research -> Color maps
%
%
%   Usage:
%   cmap = morgenstemning(n)
%
%   All arguments are optional:
%
%   n           The number of elements (256)
%
%   Further on, the following options can be applied
%     'minColor' The absolute minimum value can have a different color
%                ('none'), 'white','black','lightgray', 'darkgray'
%                or any RGB value ex: [0 1 0]
%     'maxColor' The absolute maximum value can have a different color
%     'invert'   (0), 1=invert the whole colormap
%     'gamma'    The gamma of the monitor to be used (1.8)
%
%
%   Examples:
%     figure; imagesc(peaks(200));
%     colormap(morgenstemning)
%     colorbar
%
%     figure; imagesc(peaks(200));
%     colormap(morgenstemning(256,'minColor','black','maxColor',[0 1 0]))
%     colorbar
%
%     figure; imagesc(peaks(200));
%     colormap(morgenstemning(256,'invert',1,'minColor','darkgray'))
%     colorbar
%
%
%
%
% 
%     Copyright (c) 2013, Matthias Geissbuehler
%     All rights reserved.
% 
%     Redistribution and use in source and binary forms, with or without
%     modification, are permitted provided that the following conditions are
%     met:
% 
%         * Redistributions of source code must retain the above copyright
%           notice, this list of conditions and the following disclaimer.
%         * Redistributions in binary form must reproduce the above copyright
%           notice, this list of conditions and the following disclaimer in
%           the documentation and/or other materials provided with the distribution
% 
%     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
%     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
%     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
%     ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
%     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
%     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
%     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
%     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
%     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
%     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%     POSSIBILITY OF SUCH DAMAGE.

%   Copyright 2013 Matthias Geissbuehler - matthias.geissbuehler@a3.epfl.ch
%   $Revision: 3.0 $  $Date: 2013/01/29 12:00:00 $
p=inputParser;
p.addParamValue('minColor','none');
p.addParamValue('maxColor','none');
p.addParamValue('invert',0, @(x)x==0 || x==1);
p.addParamValue('gamma',1.8, @(x)x>0);

if nargin==1
    p.addRequired('n', @(x)x>0 && mod(x,1)==0);
    p.parse(n);
elseif nargin>1
    p.addRequired('n', @(x)x>0 && mod(x,1)==0);
    p.parse(n, varargin{:});
else
    p.addParamValue('n',256, @(x)x>0 && mod(x,1)==0);
    p.parse();
end
config = p.Results;
n=config.n;

%the ControlPoints
cP(:,1) = [0 0 0]./255;
cP(:,2) = [25 53 95]./255;         %cyan
cP(:,3) = [192 27 111]./255;       %redish-magenta
cP(:,4) = [252 229 0]./255;        %yellow
cP(:,5) = [255 255 255]./255;

number_of_elements_reached = false;
last_n = size(cP,2);
curr_n = last_n .* 2 - 1;
last_cmap = double(cP');

% Normalization and smooth interpolation while keeping
% strictly monotonically increasing gray-values:
%
% 1. interpolate 2x the number of points of the previous cmap (controlpoints)
% 2. normalize all of them
% 3. Loop from 1. until number of points is >n
% 4. Interpolate to the correct number of points (n)

while ~number_of_elements_reached;
    cmap = abs(interp1((1:last_n),last_cmap,linspace(1,last_n,curr_n),'pchip'));  % Interpolation between the control-Points
    
    checkIfAnyAbove1 = 1;
    while checkIfAnyAbove1
        % Normalization by calculation of the gray-value
        % using the average RGB-value (gamma-corrected)
        tempgraymap = mean(cmap.^config.gamma,2);
        tempgraymap = tempgraymap .^(1/config.gamma);
        cmap(:,1)=cmap(:,1)./tempgraymap.*linspace(0,1,curr_n)';
        cmap(:,2)=cmap(:,2)./tempgraymap.*linspace(0,1,curr_n)';
        cmap(:,3)=cmap(:,3)./tempgraymap.*linspace(0,1,curr_n)';
        cmap(isnan(cmap))=0;
        cmap = round(10000*cmap)./10000; % staying within reasonable required precision
        
        % check if during normalization any value is now bigger than 1
        above1 = cmap>1;
        if sum(above1(:))
            mydiff = 0.025;
            if sum(above1(:,1))  % any R>1 ?
                myIndexes = find(above1(:,1));
                cmap(myIndexes,1) = (1-mydiff) .* cmap(myIndexes,1);                          % remove a little bit
                cmap(myIndexes,2) = (mydiff/2) .* (1-cmap(myIndexes,2)) + cmap(myIndexes,2);  % add a little bit to other values
                cmap(myIndexes,3) = (mydiff/2) .* (1-cmap(myIndexes,3)) + cmap(myIndexes,3);  % add a little bit to other values
            end
            if sum(above1(:,2))  % any G>1 ?
                myIndexes = find(above1(:,2));
                cmap(myIndexes,2) = (1-mydiff) .* cmap(myIndexes,2);                          % remove a little bit
                cmap(myIndexes,1) = (mydiff/2) .* (1-cmap(myIndexes,1)) + cmap(myIndexes,1);  % add a little bit to other values
                cmap(myIndexes,3) = (mydiff/2) .* (1-cmap(myIndexes,3)) + cmap(myIndexes,3);  % add a little bit to other values
            end
            if sum(above1(:,3))  % any B>1 ?
                myIndexes = find(above1(:,3));
                cmap(myIndexes,3) = (1-mydiff) .* cmap(myIndexes,3);                          % remove a little bit
                cmap(myIndexes,1) = (mydiff/2) .* (1-cmap(myIndexes,1)) + cmap(myIndexes,1);  % add a little bit to other values
                cmap(myIndexes,2) = (mydiff/2) .* (1-cmap(myIndexes,2)) + cmap(myIndexes,2);  % add a little bit to other values
            end
            checkIfAnyAbove1 = 1;
        else
            checkIfAnyAbove1 = 0;
        end
    end
    last_n = curr_n;
    curr_n = last_n .* 2 - 1;
    last_cmap = cmap;
    if last_n > n
        number_of_elements_reached = true;
    end
end
cmap = abs(interp1((1:last_n),last_cmap,linspace(1,last_n,n)));


% Additional modifications of the colormap
if config.invert
    cmap = flipud(cmap);
end

if ischar(config.minColor)
    if ~strcmp(config.minColor,'none')
        switch config.minColor
            case 'white'
                cmap(1,:) = [1 1 1];
            case 'black'
                cmap(1,:) = [0 0 0];
            case 'lightgray'
                cmap(1,:) = [0.8 0.8 0.8];
            case 'darkgray'
                cmap(1,:) = [0.2 0.2 0.2];
        end
    end
else
    cmap(1,:) = config.minColor;
end
if ischar(config.maxColor)
    if ~strcmp(config.maxColor,'none')
        switch config.maxColor
            case 'white'
                cmap(end,:) = [1 1 1];
            case 'black'
                cmap(end,:) = [0 0 0];
            case 'lightgray'
                cmap(end,:) = [0.8 0.8 0.8];
            case 'darkgray'
                cmap(end,:) = [0.2 0.2 0.2];
        end
    end
else
    cmap(end,:) = config.maxColor;
end

Contact us