# v_colormap

## PURPOSE

V_COLORMAP set and plot color map

## SYNOPSIS

function [rgb,y,l]=v_colormap(map,m,n,p)

## DESCRIPTION

    V_COLORMAP set and plot color map

       Usage: (1) v_colormap([],'g');          % plot the current color map

              (2) v_colormap([],'',256);       % intepolate the current color map to 256 entries

              (3) v_colormap('copper','y');    % make copper color map linear in luminance

              (4) v_colormap('copper','L');    % make copper color map linear in lightness^2

              (5) imagesc(x,max(abs(x(:)))*[-1 31/32]);        % plot an image containing signed data and set the color range
                  v_colormap('v_bipveey');                     % select a bipolar colormap
                  colorbar;                                    % show the color bar

              (6) rev=360;                                                     % 360 for degrees or 2*pi for radians
                  nmap=64;                                                     % number of map entries
                  phase=repmat(linspace(0,2*rev,200),200,1);                   % create 2 cycles of linear phase
                  imagesc(mod(phase+rev*0.5/nmap,rev)-rev*0.5/nmap,rev*[-0.5 nmap-0.5]/nmap);    % ensure data range is rev*[-1 127]/128
                  v_colormap('v_circrby','y',nmap);                                     % select a circular colormap
                  colorbar;

      Inputs:
               map  Either an (r,3) array specifying the RGB colourmap entries
                    or else a string specifying a bult-in colour map. Use []
                    to leave the colour map unchanged.
                    Standard maps are:
                       'Jet','HSV','Hot','Cool','Spring','Summer','Autumn',
                       'Winter','Gray','Bone','Copper','Pink','Lines','Parula'
                    Additional maps, all with 64 entries, are:
                       'v_thermliny'  thermal scale that is linear in luminance
                                      Varies from black through blue, red, yellow to white.
                                      Luminance varies linearly from black to white.
                       'v_bipliny'    bipolar scale that is linear in luminance
                                      Negative values are blue/turquoise and postive value are orange/yellow.
                                      Luminance varies linearly from black to white with zero at 50.8% gray.
                       'v_bipveey'    bipolar scale that is V-shaped in luminance
                                      Negative values are blue/turqoise and positive values are red/yellow.
                                      Luminance is proportional to absolute value with zero=black.
                       'v_circrby'    Circular scale that is V-shaped in luminance and is red for
                                      entries 2-32 and blue for entries 34-64. Intended for plotting phase.
                    For 'v_bipliny' and 'v_circrby', zero corresponds to entry 33 and so the range of values
                    is -32:31 or, equivalently, either [-1, +0.96875=31/32] or [-1.0323=-32/31,+1]. See usage
                    example (5) above.

                 m  Mode string:
                       'g' to plot information about the color map
                       'y' to force luminance^p to be linear or V-shaped (two linear segments)
                       'l' to force lightness^p to be linear or V-shaped (two linear segments)
                       'Y' like 'y' but with default p=0.667
                       'L' like 'l' but with default p=2
                       'f' flips the map to reverse its order
                       'b' force minimum luminance >=0.05 (or 0.1 for 'B')
                       'w' force maximum luminance <=0.95 (or 0.9 for 'W')
                       'k' to keep the current color map (i.e. don't update it to a new one]

                 n  the number of entries in the colourmap or the number in
                    each linearly-interpolated segment excluding the entry shared
                    with the previous segment. The total number of entries is n=sum(n).
                    For modes 'y','Y','l','L' the number of segments must be 1
                    or 2; otherwise the number of segments must be 1 or r-1.

                 p  power law to use for linearized luminance or lightness [default p=1]
                    see the description of 'y' and 'l' for its effect

     Outputs:       Note that the colormap will be updated regardless of whether outputs are
                    specified. Use the 'k' option to supress updating.

               rgb  RGB color map entries; one per row.
                    All values will be in the range 0 to 1

                 y  column vector of luminance values (from CIEXYZ colour space)

                 l  column vector of lightness values (lightness is the perceived brightness from CIELUV colour space)

## CROSS-REFERENCE INFORMATION

This function calls:

- v_colormap V_COLORMAP set and plot color map

This function is called by:

- v_colormap V_COLORMAP set and plot color map
- v_correlogram V_CORRELOGRAM calculate correlogram [y,ty]=(x,inc,nw,nlag,m,fs)

## SOURCE CODE

    0001 function [rgb,y,l]=v_colormap(map,m,n,p)
    0002 %V_COLORMAP set and plot color map
    0003 %
    0004 %   Usage: (1) v_colormap([],'g');          % plot the current color map
    0005 %
    0006 %          (2) v_colormap([],'',256);       % intepolate the current color map to 256 entries
    0007 %
    0008 %          (3) v_colormap('copper','y');    % make copper color map linear in luminance
    0009 %
    0010 %          (4) v_colormap('copper','L');    % make copper color map linear in lightness^2
    0011 %
    0012 %          (5) imagesc(x,max(abs(x(:)))*[-1 31/32]);        % plot an image containing signed data and set the color range
    0013 %              v_colormap('v_bipveey');                     % select a bipolar colormap
    0014 %              colorbar;                                    % show the color bar
    0015 %
    0016 %          (6) rev=360;                                                     % 360 for degrees or 2*pi for radians
    0017 %              nmap=64;                                                     % number of map entries
    0018 %              phase=repmat(linspace(0,2*rev,200),200,1);                   % create 2 cycles of linear phase
    0019 %              imagesc(mod(phase+rev*0.5/nmap,rev)-rev*0.5/nmap,rev*[-0.5 nmap-0.5]/nmap);    % ensure data range is rev*[-1 127]/128
    0020 %              v_colormap('v_circrby','y',nmap);                                     % select a circular colormap
    0021 %              colorbar;
    0022 %
    0023 %  Inputs:
    0024 %           map  Either an (r,3) array specifying the RGB colourmap entries
    0025 %                or else a string specifying a bult-in colour map. Use []
    0026 %                to leave the colour map unchanged.
    0027 %                Standard maps are:
    0028 %                   'Jet','HSV','Hot','Cool','Spring','Summer','Autumn',
    0029 %                   'Winter','Gray','Bone','Copper','Pink','Lines','Parula'
    0030 %                Additional maps, all with 64 entries, are:
    0031 %                   'v_thermliny'  thermal scale that is linear in luminance
    0032 %                                  Varies from black through blue, red, yellow to white.
    0033 %                                  Luminance varies linearly from black to white.
    0034 %                   'v_bipliny'    bipolar scale that is linear in luminance
    0035 %                                  Negative values are blue/turquoise and postive value are orange/yellow.
    0036 %                                  Luminance varies linearly from black to white with zero at 50.8% gray.
    0037 %                   'v_bipveey'    bipolar scale that is V-shaped in luminance
    0038 %                                  Negative values are blue/turqoise and positive values are red/yellow.
    0039 %                                  Luminance is proportional to absolute value with zero=black.
    0040 %                   'v_circrby'    Circular scale that is V-shaped in luminance and is red for
    0041 %                                  entries 2-32 and blue for entries 34-64. Intended for plotting phase.
    0042 %                For 'v_bipliny' and 'v_circrby', zero corresponds to entry 33 and so the range of values
    0043 %                is -32:31 or, equivalently, either [-1, +0.96875=31/32] or [-1.0323=-32/31,+1]. See usage
    0044 %                example (5) above.
    0045 %
    0046 %             m  Mode string:
    0047 %                   'g' to plot information about the color map
    0048 %                   'y' to force luminance^p to be linear or V-shaped (two linear segments)
    0049 %                   'l' to force lightness^p to be linear or V-shaped (two linear segments)
    0050 %                   'Y' like 'y' but with default p=0.667
    0051 %                   'L' like 'l' but with default p=2
    0052 %                   'f' flips the map to reverse its order
    0053 %                   'b' force minimum luminance >=0.05 (or 0.1 for 'B')
    0054 %                   'w' force maximum luminance <=0.95 (or 0.9 for 'W')
    0055 %                   'k' to keep the current color map (i.e. don't update it to a new one]
    0056 %
    0057 %             n  the number of entries in the colourmap or the number in
    0058 %                each linearly-interpolated segment excluding the entry shared
    0059 %                with the previous segment. The total number of entries is n=sum(n).
    0060 %                For modes 'y','Y','l','L' the number of segments must be 1
    0061 %                or 2; otherwise the number of segments must be 1 or r-1.
    0062 %
    0063 %             p  power law to use for linearized luminance or lightness [default p=1]
    0064 %                see the description of 'y' and 'l' for its effect
    0065 %
    0066 % Outputs:       Note that the colormap will be updated regardless of whether outputs are
    0067 %                specified. Use the 'k' option to supress updating.
    0068 %
    0069 %           rgb  RGB color map entries; one per row.
    0070 %                All values will be in the range 0 to 1
    0071 %
    0072 %             y  column vector of luminance values (from CIEXYZ colour space)
    0073 %
    0074 %             l  column vector of lightness values (lightness is the perceived brightness from CIELUV colour space)
    0075
    0076 % Bugs/Suggestions:
    0077 %  (1) Should allow an optional "target" input (like colormap(0) does).
    0078
    0079 %      Copyright (C) Mike Brookes 2012-2018
    0080 %      Version: $Id: v_colormap.m 10866 2018-09-21 17:32:44Z dmb $
    0081 %
    0082 %   VOICEBOX is a MATLAB toolbox for speech processing.
    0083 %   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
    0084 %
    0085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0086 %   This program is free software; you can redistribute it and/or modify
    0087 %   it under the terms of the GNU Lesser General Public License as published by
    0088 %   the Free Software Foundation; either version 3 of the License, or
    0089 %   (at your option) any later version.
    0090 %
    0091 %   This program is distributed in the hope that it will be useful,
    0092 %   but WITHOUT ANY WARRANTY; without even the implied warranty of
    0093 %   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    0094 %   GNU Lesser General Public License for more details.
    0095 %
    0096 %   You can obtain a copy of the GNU Lesser General Public License from
    0097 %   https://www.gnu.org/licenses/ .
    0098 %
    0099 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    0100 persistent maps nams mcal modes nmap nszs pows la lb lc lci laci lk lq yv
    0101 if isempty(maps)
    0102     lk=(6/29)^3;
    0103     la=841/108;
    0104     lb=4/29;
    0105     lc=1.16;
    0106     lq=la*lc*lk;
    0107     lci=1/lc;
    0108     laci=lci/la;
    0109     %     yv=inv([1.0 0.956 0.621; 1.0 -0.272 -0.647; 1.0 -1.106 1.703]);
    0110     %     yv=yv(:,1);
    0111     yv=[0.2126; 0.7152; 0.0722];                    % Mapping from RGB to luminance
    0112     % specifications for local color maps
    0113     nams={'v_thermliny','v_bipliny','v_bipveey','v_circrby'};
    0114     mcal=[1 1 1 1];                                 % +1 if need to calculate maps entry
    0115     modes={'y' 'y' 'y' 'y'};                        % modes for map calculation
    0116     nszs={64 64 [33 31]  [33 32]};                  % linear interpolation regions
    0117     nmap=[64 64 64 64];                             % final map size
    0118     pows=[1 1 1 1];                                 % powers for maps
    0119     % 'v_thermliny'
    0120     maps{1}=[0 0 0; 72 0 167; 252 83 16; 255 249 0; 255 255 255]/255;
    0121     % 'v_bipliny': bipolar map with grayscale linearity
    0122     maps{2}=[0 0 0; 0 2 203; 1 101 226; 128 128 128; 252 153 12; 252 245 0; 252 249 18; 252 252 252]/252;
    0123     % 'v_bipveey'
    0124     maps{3}=[0 0.95 1; 0 0 0.9; 0 0 0; 0.5 0 0; 0.80 0.78 0];
    0125     % 'v_circrby'
    0126     maps{4}=[0 0 0; 1 0.183 0; 1 0.9 0; 1 1 1; 0 1 0.8; 0 0.379 1; 0 0 0];
    0127 end
    0128 if nargin<2
    0129     m='';
    0130 end
    0131 if nargin<4
    0132     if any(m=='Y')
    0133         p=2/3;
    0134     elseif any(m=='L')
    0135         p=2;
    0136     else
    0137         p=1;                            % power to raise lightness/luminance to
    0138     end
    0139 end
    0140 pr=1/p;
    0141 um=m;
    0142 m=lower(m);                                     % convert mode letters to lower case
    0143 oldmap=colormap;                                % get existing map
    0144 rest=0;                                         % do not restore old map by default
    0145 if nargin==0 || numel(map)==0                   % use existing map
    0146     rgb=oldmap;
    0147 elseif ischar(map)                              % if map given as a string
    0148     ix=find(strcmpi(map,nams),1);               % check if it is one of ours
    0149     if numel(ix)                                % if it is one of ours
    0150         if mcal(ix)                             % need to calculate the map the first time around
    0151             maps{ix}=v_colormap(maps{ix},modes{ix},nszs{ix},pows(ix));
    0152             mcal(ix)=0;                         % don't calculate it again
    0153             maps{ix}=maps{ix}(1:nmap(ix),:);    % only keep the first nmap(ix) entries
    0154         end
    0155         rgb=maps{ix};
    0156     else
    0157         rgb=colormap(map);              % not one of ours - just pass it on to standard colormap function
    0158         rest=any(m=='k');               % need to restore the old map if 'k' option is set
    0159     end
    0160 else
    0161     rgb=map; % numeric map specified
    0162 end
    0163 if any(m=='y') ||  any(m=='l') || (nargin>2 && numel(n)>0) % need to do linear interpolation
    0164     nm=size(rgb,1);
    0165     if any(m=='y') ||  any(m=='l')
    0166         y=rgb*yv;  % convert map to luminance
    0167         up=y(2:nm)>y(1:nm-1); % find increasing
    0168         ex=up(1:nm-2)~=up(2:nm-1); % +1 for a peak or valley
    0169         yd=2*up(1)-1; % +1 if y initially increasing
    0170         switch sum(ex)
    0171             case 0 % monotonic
    0172                 if nargin<3
    0173                     r=nm;
    0174                 else
    0175                     r=n(1);
    0176                 end
    0177                 if any(m=='y')
    0178                     l=y([1 nm]).^p;
    0179                     tt=(l(1)+(0:r-1)'*(l(2)-l(1))/(r-1)).^pr; % target luminances
    0180                 else
    0181                     tt=y([1 nm]');
    0182                     l=(lc*(la*tt+(tt>lk).*(tt.^(1/3)-la*tt-lb))).^p;
    0183                     tt=(l(1)+(0:r-1)'*(l(2)-l(1))/(r-1)).^pr; % target lightnesses
    0184                     tt=laci*tt+(tt>lq).*((lci*tt+lb).^3-laci*tt); % target luminances
    0185                 end
    0186                 [ss,ix]=sort([tt;y]*yd);
    0187             case 1 % V-shaped
    0188                 ipk=find(ex,1)+1;  % position of peak/valley in y
    0189                 if nargin<3
    0190                     n=[ipk nm-ipk];  % size of linear segments
    0191                 end
    0192                 r=n(1)+n(2);  % total size of map
    0193                 if any(m=='y')
    0194                     l=y([1 ipk nm]).^p;
    0195                     tt=(l(2)+[(1-n(1):0)*(l(2)-l(1))/(n(1)-1) (1:n(2))*(l(3)-l(2))/(n(2))]').^pr; % target luminances
    0196                 else
    0197                     tt=y([1 ipk nm]');
    0198                     l=(lc*(la*tt+(tt>lk).*(tt.^(1/3)-la*tt-lb))).^p;
    0199                     tt=(l(2)+[(1-n(1):0)*(l(2)-l(1))/(n(1)-1) (1:n(2))*(l(3)-l(2))/(n(2))]').^pr; % target lightnesses
    0200                     tt=laci*tt+(tt>lq).*((lci*tt+lb).^3-laci*tt); % target luminances
    0201                 end
    0202                 [ss,ix]=sort([tt(1:n(1))-y(ipk); y(ipk)-tt(n(1)+1:r);y(1:ipk)-y(ipk); y(ipk)-y(ipk+1:nm)]*yd);
    0203             otherwise
    0204                 error('luminance has more than two monotonic segments');
    0205         end
    0206     else                % just linearly interpolate the given values
    0207         if numel(n)==nm-1
    0208             r=sum(n);
    0209             y=[1;cumsum(n(:))];
    0210         else
    0211             r=n(1);
    0212             y=1+(0:nm-1)'*(r-1)/(nm-1);
    0213         end
    0214         tt=(1:r)';
    0215         [ss,ix]=sort([tt;y]);
    0216     end
    0217     jx=zeros(size(ix));
    0218     jx(ix)=1:numel(jx);
    0219     jx=min(max(jx(1:r)-(1:r)',1),nm-1);
    0220     al=(tt-y(jx))./(y(jx+1)-y(jx)); % fraction of upper sample to include
    0221     rgb=rgb(jx,:)+(rgb(jx+1,:)-rgb(jx,:)).*al(:,ones(1,3)); % update the map
    0222 end
    0223 if any(m=='f')
    0224     rgb=rgb(end:-1:1,:);
    0225 end
    0226 y=rgb*yv;  % convert RGB to luminance
    0227 if any(m=='b') || any(m=='w') % need to constrain luminance
    0228     minyt=0.05*(any(m=='b')+any(um=='B')); % target minimum luminance
    0229     maxyt=1-0.05*(any(m=='w')+any(um=='W')); % target maximum luminance
    0230     maxy=max(y);
    0231     miny=min(y);
    0232     if maxy>maxyt || miny<minyt
    0233         maxy=max(maxy,maxyt);
    0234         miny=min(miny,minyt);
    0235         rgb=(rgb-miny)*(maxyt-minyt)/(maxy-miny)+minyt;
    0236         y=rgb*yv;  % convert RGB to luminance
    0237     end
    0238 end
    0239 l=lc*(la*y+(y>lk).*(y.^(1/3)-la*y-lb)); % convert luminance to lightness
    0240 if rest
    0241     colormap(oldmap);  % restore the old map
    0242 elseif ~isequal(rgb,oldmap)
    0243     colormap(rgb); % update the system map
    0244 end
    0245 if any(m=='g')
    0246     sp=[1 2 2];
    0247     ssp=sum(sp);
    0248     axw=0.05;
    0249     nc=size(rgb,1);  % size of color map
    0250     hsv=rgb2hsv(rgb);
    0251     subplot(ssp,1,sp(1)+(1:sp(2)));
    0252     plot(1:nc,y,'--k');
    0253     hold on
    0254     plot(1:nc,rgb(:,1),'-r');
    0255     plot(1:nc,rgb(:,2),'-g');
    0256     plot(1:nc,rgb(:,3),'-b');
    0257     hold off
    0258     axis([0.5 nc+0.5 -axw 1+axw]);
    0259     legend('Y','R','G','B','location','best');
    0260     ylabel('RGB + Y');
    0261     subplot(ssp,1,sp(1)+sp(2)+1:ssp);
    0262     plot(1:nc,l,'--k');
    0263     hold on
    0264     plot(1:nc,hsv(:,1),'-r');
    0265     plot(1:nc,hsv(:,2),'-g');
    0266     plot(1:nc,hsv(:,3),'-b');
    0267     hold off
    0268     axis([0.5 nc+0.5 -axw 1+axw]);
    0269     legend('L*','H','S','V','location','best');
    0270     ylabel('HSV + L*');
    0271     subplot(ssp,1,1:sp(1));
    0272     image(permute(reshape([rgb y(:,[1 1 1])],[nc,3,2]),[3 1 2]));
    0273     set(gca,'YTick',[]);
    0274 end