4

I'm trying to generate a colormap in MATLAB given three colors, a high extreme, zero, and low extreme. My thought process has been to loop from the high extreme to the middle and store each step to a a 3xN (first column is R, second is G, and third is B)matrix. So I'm using:

%fade from high to zero
oldRed=high(1);
oldGreen=high(2);
oldBlue=high(3);
newRed=mid(1);
newGreen=mid(2);
newBlue=mid(3);

currentRed=oldRed; currentGreen=oldGreen; currentBlue=oldBlue;
for x=1:steps
    currentRed=oldRed+((x*(newRed-oldRed))/(steps-1));
    currentGreen=oldGreen+((x*(newRed-oldRed))/(steps-1));
    currentBlue=oldBlue+((x*(newRed-oldRed))/(steps-1));
    cmap=[cmap;[currentRed currentGreen currentBlue]];
end

Then I would do the same thing going from the zero value to the low extreme. However my code is not giving me any kind of useful matrix. Would someone be able to help me with how I should approach this?

Shai
  • 111,146
  • 38
  • 238
  • 371
Sam
  • 405
  • 2
  • 5
  • 13
  • 1
    First of all, why use loops when you can use element-wise arithmetic? – Eitan T Jun 24 '13 at 15:36
  • I'm not sure what you mean. What arithmetic expression could I use to fade between colors? – Sam Jun 24 '13 at 15:37
  • Using `linspace` or the `colon` operator (`:`). Everything you did in the loop could be rewritten without a loop using vectorized expressions. – Eitan T Jun 24 '13 at 15:46

3 Answers3

5

You can use linear interpolation to expand the color

 nCol = 256; % number of colors for the resulting map
 cmap = zeros( nCol, 3 ); % pre-allocate
 xi = linspace( 0, 1, nCols );
 for ci=1:3 % for each channel
     cmap(:,ci) = interp1( [0 .5 1], [low(ci) mid(ci) high(ci)], xi )';
 end
Floris
  • 45,857
  • 6
  • 70
  • 122
Shai
  • 111,146
  • 38
  • 238
  • 371
  • 1
    Very timely question and answer, as I read this question in the morning, and then I had to generate some new colormaps this afternoon. One change that I made to this solution was to extend it a little to handle an arbitrary number of control points by `ControlPoints = [r1 b1 g1;r2 b2 g2; <...> ;rn bn gn]/255` for n points, with the rn/gn/bn replaced with actual numbers. Then `[m,~] = size(ControlPoints);` and `zi = (0:m-1)/(m-1);`. Then `zi` and `ControlPonts(:,ci)` can be inserted into the next-to-last line. That way, I could put a `switch...case` block beforehand to define multiple maps. – craigim Jun 24 '13 at 21:19
  • Sorry for taking so long to respond. Thank you! This is exactly the solution I was looking for! – Sam Jun 27 '13 at 13:13
3

Inspired by @Shai's answer, here is a small twist on his solution (which I prefer - it is more flexible, and avoids use of a for loop).

The form of the cmap that you want is an nx3 array. Further you say that you have three colors that you want to represent three "breakpoints" on your curve. This screams "interpolation"!

% set the "breakpoints" for the color curve:
lowValue = 0;
midValue = 128;
highValue = 255;

% pick "any" three colors to correspond to the breakpoints:
lowColor = [255 0 0];
midColor = [40 40 40];
highColor = [0 255 255];

% create the colormap:
myMap = interp1( [lowValue midValue highValue], ...
  [lowColor; midColor; highColor]/255, ...
  linspace(lowValue, highValue, 256));

This ensures a map with 256 colors that go smoothly from lowColor at the lowest value (index 1 into the colormap) to highColor at the highest value (index 255 into the colormap).

I believe this is exactly what you are looking for. And "look ma, no loops!".

Floris
  • 45,857
  • 6
  • 70
  • 122
1

I would use linspace:

cmap=[linspace(oldRed,newRed,steps)' ...
linspace(oldGreen,newGreen,steps)' ...
linspace(oldBlue,newBlue,steps)'];

And then for do the same for your next step, and concatenate them:

cmap_full = [cmap;cmap2];
Hugh Nolan
  • 2,508
  • 13
  • 15