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)
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 General Public License as published by 0088 % the Free Software Foundation; either version 2 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 General Public License for more details. 0095 % 0096 % You can obtain a copy of the GNU General Public License from 0097 % http://www.gnu.org/copyleft/gpl.html or by writing to 0098 % Free Software Foundation, Inc.,675 Mass Ave, Cambridge, MA 02139, USA. 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