My overriding emotion, working in R, has been incomprehension: incomprehension at the gallery of ugly gnomes that populate the namespace and worried puzzlement over the strange incantations required to get them to dance in a statistically harmonious way. But all that aside, I resolved, joining the group, to put aside my misgivings and give the gnomes another try.
Soon, I found myself engaged in a reassessment of my life choices. I realized that life’s too short to spend it tickling gnomes – especially when only one of them knows how to do linear regression, but he won’t tell you your p value unless you give him the right kinds of treats. I fired up MATLAB and I haven’t looked back.
However, there was issue of continued perplexity, and I’m not referring to why MATLAB insists on shouting itself at you. I need to make a lot of 2-D plots of protein distance matrices. The trouble is that I like to highlight parts of them, and that’s not straightforward in MATLAB. Let’s have a look at an example:
>> dists=dlmread('1hel.distances');
>> colormap gray;
>> imagesc(dists>8);
>> axis square;
Now, let’s load up a set of residues and try to overlay them on top of the first image:
>> resn=dlmread('1hel.resn');
>> mask = zeros(size(dists));
>> mask(resn,resn)=1;
>> hold on
>> imagesc(1-mask, 'AlphaData',mask*.5);
So far, so easy. To review the main points:
mask
is a matrix which has a one at all the pixels that we want to highlight. But we use imagesc(1-mask)
because the gray colormap displays black at 0 and white at 1. If we did imagesc(mask)
, we would end up with grey everywhere and white only where we hoped to highlight – the opposite effect from the one that we sought.
AlphaData
is a property which sets the transparency of the image. We want the image to be fully transparent where mask
is 0 – so as not to fog out the underlying image – and partially transparent where mask
is 1. 0.5*mask
is a matrix which is 0.5 everywhere that mask
is 1 and 0 everywhere else. If we set 0.5*mask
as the AlphaData
property, then the colour we add will be at half transparency and the white areas will be fully transparent.
But this isn’t a very pleasant image. We want to be able to highlight the regions in some colour other than grey. Let’s try.
>> close all
>> imagesc(dists>8)
>> colormap gray
>> axis square
>> imagesc(1-mask, 'AlphaData',mask*.3,'ColorMap','jet');
Error using image
There is no ColorMap property on the Image class.
Error in imagesc (line 39)
hh = image(varargin{:},'CDataMapping','scaled');
No luck! What’s more, setting the colormap between calls to image()
and imagesc()
also doesn’t work. Here’s the problem: the colormap
is a property of the figure, not the data. (More precisely, it is not a property of the MATLAB axes.) When you change the colormap
, you change the colors of every datapoint in the image.
The fix
MATLAB’s colormap
mechanism is just simple enough to be confusing. MATLAB stores colours as 1×3 vectors, where each element in the vector is the proportion of red, green, or blue, respectively. [1 1 1] is white, [0 0 0] is black, and [1 0 0] is a frightfully iridescent red. A colormap is just a list of colors – 64 will normally do – which change smoothly from from one colour to another. To have a look at the built-in MATLAB colormaps, see here.
image
rounds every value in the matrix to the nearest whole number (call that number i
) and plots that pixel with the color given by colormap(i,:)
. Zero or below gets the first entry in the colormap
and any index higher than the maximum is displayed with the last color in the colormap
. So: if we construct a new colormap
by concatenating two colormaps – the first running from rows 1 to 64 and the second running from 65 to 128 – if we scale our data so that the minimum is 65 and the maximum is 128, the data will never use the first set of colors. And, likewise, if we scale so that the lowest value is 1 and the highest is 64, we will use the first colormap. This seems like the sort of thing that we could manage automatically – and should, in fact. So I set myself to replace image
and imagesc
so that they would accept a ColorMap
parameter.
How would it work?
>> colormap bone
>> imagesc(dists>8)
>> hold on
>> imagesc(mask,'ColorMap',[0.5 0 0.5],'AlphaData',0.5*(mask>0))
>> axis square
Beautiful!
Implementation notes
image
is implemented in the MATLAB Java source code, but imagesc
is a wrapper to image
, written directly in MATLAB code. Therefore, overloading image
requires the new function to be placed in a special directory called @double
, while imagesc
can be placed anywhere (except it cannot be placed in @double
). If you then want to call the original version of image()
, you can use builtin(‘image’,arg1,arg2,…)
, whereas if you want to call the original imagesc
, it is a right pain. Instead, I used type imagesc
to extract the source of imagesc
and I modified that source directly – obviating any need to call the original imagesc
. For reference, though, the most efficient way works out to be to find the function with which('imagesc')
, cd
into the containing directory, create a function handle to imagesc
, and then cd
out. As I said, it’s a mess.
- These edits break colorbars. I added a spacer entry in each colormap which stores the maximum and minimum ‘real’ values of the data – in case that is useful for when I get around to extending
colorbar
. colormap entries must be inside [0,1] so these data are stored in the first twelve decimal places of the colormap entries: a strange burlesque on floating points. It’s a hack, but for my purposes it works.
- In addition to the standard colormaps, I often require a mask in a particular color. For this purpose it helps to have a
colormap
that smoothly varies from white to the color in question. It actually doesn’t matter if it varies from white or any other color – ultimately, I only use the full colour value, since I set the transparency of all other pixels to maximum – but either way, passing the colour on [0,1] scale or [0,255] scale sets a colormap
which varies from white to that color.
The code is available on MATLAB File Exchange at this link and is installable by copying imagesc.m
, bootleg_fp.m
, and the directory @double
into your working directory. The idea to concatenate colormaps is widely available online – for example, here.