Changing the transparency of patches based on their value - matlab

I have a matrix containing integers ranging from 0 to 20. The regions with the same integer are formed in a quite random fashion. I want to color code each integer with a different degree of grayscale. So for example, for regions with 0, I want them to be white, then for regions with 1, 5% transparency of black, for regions with 2, 10% transparency of black,..., for regions with 20, totally black.
I've tried to get the coordinates of each region, but that does not seem to be efficient for my matrix.
How can I change the transparency of my regions based on their value?

You can use surface() with its FaceAlpha name-value pair. It only accepts scalars though, so you'll have to plot each of your patches separately, something along the lines of:
% Create a random matrix with values 1 - 20 for plotting
my_matrix = ceil(20* rand(30));
% Build its x and y grids
[x, y] = meshgrid(1:30, 1:30);
% Open and hold the figure
figure;
hold on
for ii = unique(my_matrix)
% Create a mask for the current value
temp_colour = nan(size(my_matrix));
temp_colour(my_matrix == ii) = 1;
surface(x, y, ones(size(my_matrix)), ...
temp_colour, ...
'EdgeColor', 'none', ...
'FaceAlpha', 1 - ii/max(my_matrix, 'all'))
colormap(gray)
end
Results in, on my R2007b,
If you're good with just a gray-scale image, rather than true transparency, you can simply use imagesc(my_matrix); colormap(gray).

Related

How can I convert an RGB histogram into a color spectrum?

How can I convert an RGB histogram of an image to create a histogram showing the combined colors along with correct color wavelength range?
Example code:
pkg load image
f=imread('/tmp/marbles.jpg');
f=uint8(f); %need to convert back to uint8 to show picture
%Split into RGB Channels
f_red = f(:,:,1);
f_green = f(:,:,2);
f_blue = f(:,:,3);
%Get histValues for each channel
[y_f_red, x] = imhist(f_red);
[y_f_green, x] = imhist(f_green);
[y_f_blue, x] = imhist(f_blue);
subplot (2,1,1); imshow(f);
subplot (2,1,2); plot(x, y_f_red, 'r', x, y_f_green, 'g', x, y_f_blue, 'b');
Example image along with separate RGB histogram the code produces:
I'm trying to get the histogram to look like the image below but have the colors go from red to blue:
Another image example:
PS: I'm using Octave 4.0 which is very similar to MATLAB.
There's a huge hurdle to converting between standard color representations (like RGB or HSV) and spectral wavelength: many colors can't be represented by a single wavelength of light. Colors such as magenta, pink, brown, or any grayscale color represent mixtures of different wavelengths. Generating an equivalent spectral wavelength is therefore a much more complicated endeavor (you may find some useful ideas and links here and here).
Creating histograms of the colors themselves may be a better way to go (illustrated in one of my other answers), but if you really want to relate color to wavelength in a simple fashion you can try the following...
A first step will be to convert RGB values to HSV values, then create a histogram of the hue channel. I'll adapt part of my answer from here to do that. The next step will be to map hues to wavelengths of light, using some rather gross approximations adapted from this answer:
rgbImage = imread('test_image.png'); % Load image
hsvImage = rgb2hsv(rgbImage); % Convert the image to HSV space
hPlane = 360.*hsvImage(:, :, 1); % Get the hue plane scaled from 0 to 360
binEdges = 0:270; % Edges of histogram bins
N = histc(hPlane(:), binEdges); % Bin the pixel hues from above
wavelength = 620-(170/270).*(0:269); % Approximate wavelength
hBar = bar(wavelength, N(1:end-1), 'histc'); % Plot the histogram
set(hBar, 'CData', 270:-1:1, ... % Change the color of the bars using
'CDataMapping', 'direct', ... % indexed color mapping (360 colors)
'EdgeColor', 'none'); % and remove edge coloring
colormap(hsv(360)); % Change to an HSV color map with 360 points
axis([450 620 0 max(N)]); % Change the axes limits
set(gca, 'Color', 'k'); % Change the axes background color
set(gcf, 'Pos', [50 400 560 200]); % Change the figure size
xlabel('Wavelength (nm)'); % Add an x label
ylabel('Bin counts'); % Add a y label
NOTE: For the above to work properly in Octave, it may be necessary to change the set(hBar, ... line to the following:
set(hBar, 'FaceColor', 'flat', 'EdgeColor', 'none');
set(get(hBar, 'Children'), 'CData', 270:-1:1, 'CDataMapping', 'direct');
And here's the histogram:
There is, however, one issue with this. If we instead use the code exactly as it is in my other answer to plot the histogram of all the hue values, we would get this:
Note that there is a big cluster of magenta, pink, and reddish pixels that gets excluded when we toss out part of the hue range to convert to wavelengths (they don't correspond to a single wavelength in the light spectrum). Incorporating these into the results would require a more complicated conversion from hue to wavelength.
you can not convert RGB to wavelength unless some physical properties of the image and light is met. Anyway you can fake this by inversing:
RGB values of visible spectrum
if you do not know how look at:
Reverse complex 2D lookup table
But the result will not be the same as physical wavelengths histogram ... For that you would need multi-band image acquisition either by rotating prism optics or by set of bandpass filters ...
PS. HSV is far from accurate ...
Btw. the easiest way to do this is create palette from the spectral colors and convert your input image to it (indexed colors) and then just create histogram sorted by wavelength (and or color index)...
Based on gnovices answer but with an image instead of bar (take 0.12s on my system):
rgbImage = imread ("17S9PUK.jpg");
hsvImage = rgb2hsv(rgbImage);
hPlane = 360 .* hsvImage(:, :, 1);
binEdges = 1:360;
N = histc (hPlane(:), binEdges);
cm = permute (hsv (360), [3 1 2]);
img = repmat (cm, max(N), 1);
row_index = max(N) - N';
sp = sparse (row_index(row_index>0), (1:360)(row_index>0), true);
mask = flipud (cumsum (sp));
img(repmat (logical(1 - full(mask)), [1 1 3])) = 0;
image (img)
set (gca, "ydir", "normal");
xlabel('hue');
ylabel('Bin counts');

Matlab: 1D array to RGB triplets with colormap

I'm trying to draw a set of rectangles, each with a fill color representing some value between 0 and 1. Ideally, I would like to use any standard colormap.
Note that the rectangles are not placed in a nice grid, so using imagesc, surf, or similar seems unpractical. Also, the scatter function does not seem to allow me to assign a custom marker shape. Hence, I'm stuck to plotting a bunch of Rectangles in a for-loop and assigning a FillColor by hand.
What's the most efficient way to compute RGB triplets from the scalar values? I've been unable to find a function along the lines of [r,g,b] = val2rgb(value,colormap). Right now, I've built a function which computes 'jet' values, after inspecting rgbplot(jet). This seems a bit silly. I could, of course, obtain values from an arbitrary colormap by interpolation, but this would be slow for large datasets.
So, what would an efficient [r,g,b] = val2rgb(value,colormap) look like?
You have another way to handle it: Draw your rectangles using patch or fill specifying the color scale value, C, as the third parameter. Then you can add and adjust the colorbar:
x = [1,3,3,1,1];
y = [1,1,2,2,1];
figure
for ii = 1:10
patch(x + 4 * rand(1), y + 2 * rand(1), rand(1), 'EdgeColor', 'none')
end
colorbar
With this output:
I think erfan's patch solution is much more elegant and flexible than my rectangle approach.
Anyway, for those who seek to convert scalars to RGB triplets, I'll add my final thoughts on the issue. My approach to the problem was wrong: colors should be drawn from the closest match in the colormap without interpolation. The solution becomes trivial; I've added some code for those who stumble upon this issue in the future.
% generate some data
x = randn(1,1000);
% pick a range of values that should map to full color scale
c_range = [-1 1];
% pick a colormap
colormap('jet');
% get colormap data
cmap = colormap;
% get the number of rows in the colormap
cmap_size = size(cmap,1);
% translate x values to colormap indices
x_index = ceil( (x - c_range(1)) .* cmap_size ./ (c_range(2) - c_range(1)) );
% limit indices to array bounds
x_index = max(x_index,1);
x_index = min(x_index,cmap_size);
% read rgb values from colormap
x_rgb = cmap(x_index,:);
% plot rgb breakdown of x values; this should fall onto rgbplot(colormap)
hold on;
plot(x,x_rgb(:,1),'ro');
plot(x,x_rgb(:,2),'go');
plot(x,x_rgb(:,3),'bo');
axis([c_range 0 1]);
xlabel('Value');
ylabel('RGB component');
With the following result:

Produce a 3D stem plot with a custom colormap in MATLAB

I have a matrix (200 x 4) where first 3 values are X, Y and Z data. I want use the fourth column to display each (X,Y,Z) triplet so that it maps to a color.
The fourth column contains values from 0.0 to 1.0 (200 values). I want to map these values with colormap manually and linearly. The smallest value should have blue color and the largest value may have red color.
I know that it is possible with scatter3. However, I want to do using stem3 where I can specify the color manually from colormap.
Is there a way to do this in MATLAB?
That's pretty simple to do. kkuilla posted a very insightful link. To get something started, if you want to have a colour map that varies from blue to red, you know that an image is decomposed into three colours: Red, green and blue.
Therefore, all you would have to do is vary the red and blue channels. Start with a pure blue colour, which is RGB = (0,0,255) where this is mapped to the initial weight of w = 0 and vary this to the end where RGB = (255,0,0) with w = 1. You can very easily do that by linspace. However, colours in a colour map for plotting in MATLAB are normalized so that they're between [0,1], not [0,255]. Also, because a colour map in MATLAB is a matrix of N x 3, where N is the total number of colours you want, all you have to do is:
num_colours = 10;
colourmap = [linspace(0,1,num_colours).' zeros(num_colours,1) linspace(1,0,num_colours).'];
weights = linspace(0,1,num_colours);
num_colours is the total number of colours you would like displayed. I set it to 10 to get you started. weights is something we will need for later, so don't worry about that righ tnow. Essentially, colormap would be the colour map you apply to your data.
However, what is going to be difficult now is that the data that you're plotting has no correlation to the weight of the data itself (or the fourth column of your data). This means that you can't simply use the (X,Y,Z) data to determine what the colour of each plot in your stem is going to look like. Usually for colour maps in MATLAB, the height of the stem is proportional to the colour that is displayed. For the largest Z value in your data, this would naturally be assigned to the colour at the end of your colour map, or red. The smallestZ value in your data would naturally get assigned at the beginning of your colour map, or blue.
If this was the case, you would only need to make one stem call and specify the colour map as the attribute for Color. Because there is no correlation between the height of the Z value and the weight that you're assigning for each data point, you have no choice but to loop through each of your points and determine the closest value between the weight for a point with every weight and ultimately every colour in your colour map, then apply this closest colour to each point in your stem respectively.
We determine the closest point by using the weights vector that was generated above. We can consider each colour as having a mapping from [0,1], and each weight corresponds to the colour in colourmap. Therefore, a weight of 0 is the first colour in the colour map, and that's in the first row. The next weight after this is the second colour, and that's in the second row and so on.... so we simply need to determine where each weight that's in the fourth column of your matrix is closest to for the above weights vector. This will determine which colour we need to select from the colour map to plot the point.
Given that your matrix of 200 x 4 is stored in data, you must specifically do this:
%// Spawn a new figure
figure;
%// Determine the number of points in the dataset
num_points = size(data,1);
%// For each point in the data set
for idx = 1 : num_points
%// Get 4th column element and determine closest colour
w = data(idx,4);
[~,ind] = min(abs(weights-w));
color = colourmap(ind,:);
%// Plot a stem at this point and change the colour of the stem
%// as well as the marker edge colour and face colour
stem3(data(idx,1), data(idx,2), data(idx,3), 'Color', color, ...
'MarkerEdgeColor', color, 'MarkerFaceColor', color);
%// Make sure multiple calls to stem don't clear the plot
hold on;
end
%// Display colour bar to show colours
colormap(colourmap(1:end-1,:));
colorbar('YTickLabel', colourmap);
The last two lines are a bit hackish, but we basically show a colour bar to the right of the plot that tells you how each weight maps to each colour.
Let's test this on some data. I'm going to generate a random 200 x 4 matrix of points and we will use the above code and plot it using stem3:
rng(123123); %// Set seed for reproducibility
data = rand(200,4);
num_colours = 10;
I set the total number of unique colours to 10. Once I have this above data, when I run through the code above, this is the plot I get:
You can use HSV as well. The Z values would correspond to your fourth column. Low Z values are blue and high Z values are red.
I used the site http://colorizer.org/ to work out that blue is H=0.65 and red is H=1. S and V stay the same.
From http://colorizer.org/, I got that a blue colour is H=236, S=100, V=100. Then the H value for blue is H = 235/360 = 0.65 and H=1, S=1, V=1 for red.
num_elem = 200;
c = linspace(0,1,num_elem)'; % // Replace this with the values from your fourth column
% // The equation gives blue (H=0.65) for c=0 and red (H=1) for c = 1
H = 0.65 + ((1-0.65).* c);
S = ones(size(c,1),1);
V = ones(size(c,1),1);
% // You have to convert it to RGB to be compatible with stem3
colourmap = hsv2rgb([H,S,V]);
% // Generate some sample data
theta = linspace(0,2*pi,num_elem)';
X = cos(theta);
Y = sin(theta);
Z = theta;
% // Plot the sample data with the colourmap
figure;
hold on;
for idx=1:num_elem
stem3(X(idx),Y(idx),Z(idx),':*','Color',colourmap(idx,:) ...
,'MarkerEdgeColor',colourmap(idx,:) ...
,'MarkerFaceColor',colourmap(idx,:) ...
,'LineWidth',4 ...
);
end
hold off;
set(gca,'FontSize',36');

Absolute scale for color map?

I am trying to make a plot with an intensity that varies over time:
[X,Y] = meshgrid(-30:.1:30);
figure;
colormap(bone);
for t = 0:0.1:2*pi
R = sqrt(X.^2 + Y.^2);
Z = cos(t)*abs(besselj(2,R));
surf(Z,'EdgeColor','None');
view(90,90);
axis([0 600 0 600 -0.5 0.5])
pause(0.1);
end
I want to look at this from the top, such that as the Z value changes, the color changes. The problem is that rather than having an absolute scale (black = -0.5, white = 0.5), the color scale is relative to the maximum and minimum values, such that the colors only change when the sign flips change. How can I set an absolute scale for the color map?
Thank you.
You have to use scaled colour mapping mode and set the limits of the scaling by using the caxis command.
Now the problem with your current code is that you call surf at each iteration of the loop, essentially destroying the current plot and generating a new plot each time. This will reset a lot of properties, including the caxis limits to auto. To overcome that, simply create your plot only once before the loop, then in the loop you only change the properties which are modified (the Z values in this case). This way everything else stays the same in the figure.
So you code becomes:
%% // Prepare and initialize the surface plot
[X,Y] = meshgrid(-30:.1:30);
R = sqrt(X.^2 + Y.^2) ; %// this doesn't need to be in the loop
Z = cos(0)*abs(besselj(2,R)) ; %// calculate initial value to create the surface
surfHandle = surf( Z , 'EdgeColor','None' ) ; %// create a first surface, and save the handle to the surface object
colormap(bone);
colorbar %// this is optional, just to make sure the colorbar does not vary
caxis([-5 5 ] ) ; %// this is what sets the color scaling to what you want
view(90,90);
axis([0 600 0 600 -0.5 0.5]) ; %// this doesn't need to be in the loop anymore
%% // Modify and update the surface plot
for t = 0:pi/100:2*pi
Z = cos(t)*abs(besselj(2,R));
set( surfHandle , 'ZData' , Z )
drawnow
pause(0.01);
end
Read coloring-mesh-and-surface-plots for more info on how surfaces can be colored.
If you just want white for values less than 0 and black for values greater than 0, you ca simply do:
surf(Z,sign(Z),'EdgeColor','None');
which uses the optional C argument to surf, telling Matlab to colour the plot depending on the values of C, not Z. sign(Z) is a matrix that has 1's where Z>0, 0's where Z=0, and -1's where Z<0.

Using different intensities of a specific color for contour plots

This question is in reference to visualization of EM clustering(or K-means) of 2D gaussian data. Say, I have displayed 3 clusters obtained from EM in a scatter plot with 3 different colors(say r,g,b) for the data samples of 3 clusters. Now I want to plot elliptical contours on top of this. I don't want the color of each of the three countours vary over entire colorspectrum from r to b. For contours of cluster 1, I want varying intensities of red, for cluster 2, I want varying intensities of blue and same of cluster 3. I have set the number of concentric contours to be 5 and tried passing a Color array as follows, but it did not work.
ColorVec = ['r' ; 'g' ; 'b' ; 'm' ; 'c' ; 'y'; 'b'];
String2RBG = #(C)rem(floor((strfind('kbgcrmyw', C) - 1) * [0.25 0.5 1]), 2);
x = -3:0.1:4;
y = -3:0.1:4;
[X,Y] = meshgrid(x,y);
for k=1:numberOfClusters
Z = mvnpdf([X(:) Y(:)],estimatedMu(k,:),estimatedSigma{k});
Z = reshape(Z,length(y),length(x));
ColorVecConcentricCountours = [String2RBG(ColorVec(k));String2RBG(ColorVec(k))/1.2;String2RBG(ColorVec(k))/1.4;String2RBG(ColorVec(k))/1.6;String2RBG(ColorVec(k))/1.8];
contour(X,Y,Z,5,'color',ColorVecConcentricCountours);hold on;
end
Use of ColorVecConcentricCountours throws an error, but if I give ColorVec(k), it gives a single shade of r, g or b for all 5 contours which is not what I want.
Contour plots are groups of patch objects. Patch objects have an 'EdgeColor' property, which is what sets what we'd call the line color of the contours. You can set the edge colors of all contour objects in the figure with the contourcmap function, but since you want separate control over each of the groups of lines that won't work. What you can do, however, is to address the patch objects themselves and change the edge color of each one separately.
To start, I have written functions called green, cyan, and the rest of the RGB + CYM colors that allow me to use those as colormaps in addition to the built-in ones. The green function looks like this:
function G = green(m)
%GREEN Green Colormap
% GREEN(M) is an M-by-3 matrix colormap for increasing red intensity.
% GREEN, by itself, is the same length as the current figure's
% colormap. If no figure exists, MATLAB creates one.
%
% See also RED, JET, HSV, HOT, PINK, FLAG, COLORMAP, RGBPLOT.
if nargin < 1
m = size(get(gcf,'colormap'),1);
end
G = zeros(m,3);
G(:,2) = (0:(1/(m-1)):1);
You can take a look at the built-in functions to see how this one is similar. To run the code below you'll need a cyan function as well (or change that function call to whatever colormap function you'd like).
Armed with a couple of contour plots put into the same axes and their handles (H1 and H2 below) we can pull out the levels the contours are drawn at with the contour group's LevelList property, yielding a vector of each contour level. Each of the contour patch objects (children of the group) has the level that line was drawn at saved in the patch object's UserData property. We can use the group's LevelList property to make a matrix of colors we want to use, using the colormap function call, then the position of the contour line's level in the LevelList vector is the row of the colormap that we want to use to color that line. Change the EdgeColor of the patch object to that color for each line and we're good to go. Two groups are drawn here to show how to get two contour groups with different colors.
figure()
[C1, H1] = contour(linspace(-50, 0, 50), linspace(-50, 0, 50), peaks(50), 50);
hold on
[C2, H2] = contour(linspace(0, 50, 50), linspace(0, 50, 50), peaks(50), 20);
hold off
axis([-50 50 -50 50]);
levels1 = get(H1, 'LevelList');
cmap1 = green(numel(levels1));
child1 = get(H1, 'Children');
for m = 1:numel(child1)
cmapHere = cmap1(levels1 == get(child1(m), 'UserData'), :);
set(child1(m), 'EdgeColor', cmapHere);
end
levels2 = get(H2, 'LevelList');
cmap2 = cyan(numel(levels2));
child2 = get(H2, 'Children');
for m = 1:numel(child2)
cmapHere = cmap2(levels2 == get(child2(m), 'UserData'), :);
set(child2(m), 'EdgeColor', cmapHere);
end