How to change MATLAB Surf plot Color Map? - matlab-figure

A surf plot has the default color scheme:
I would like to change the color of 'face of the cliff' to:
facecolor = green for faces for 10 < Z < 20
facecolor = yellow for faces for 20 < Z < 30
facecolor = red for faces for 30 < Z < 40
How is this done? (with a color map?) A reference (link) to the MATLAB documentation \ example would be highly appreciated.

Related

Plotting times of day to a fixed color gradient?

I'm making an appointments app.
I have this gradient structure (created in Pixelmator), with which I want to mark the times of day:
In the intended scheme, 8am would be solid green, 12 noon would be solid yellow, and 8pm would be solid blue.
I need an algorithm to take the times of day and turn them into those colors, but I can't figure it out, particularly from noon to evening.
These colors are composed using the HSB value system: all colors have S and B at 100%, and from left to right the hue values are 121 (green), 60 (yellow), and 229 (blue).
The progression from the green to yellow is (morning to noon) is straightforward, because it's just a linear scaling from 121 to 60, but from yellow to blue (noon to evening), is not; this is clear if you think about the fact that going from 60 to 229 in a linear fashion would first duplicate the green-to-yellow gradient, just in reverse order, and then would go to from green to blue. In other words, a strictly linear progression would make the gradient look more like this:
Can anyone point me in the right direction to understanding how to make the algorithm I need here? Do I have to use a different color value system, like RGB?
Thanks in advance for any and all help!
Pablo-No gives a reasonable answer if it's OK for the yellow->blue transition to go through red. But the OP's original picture doesn't go through red, it goes through some kind of grey. Perhaps the saturation S should be used to try to achieve this:
// Assume time is a real value between 8 (8am) and 20 (8pm)
// H is between 0 and 360
// S and B are between 0 and 100
B = 255;
if (time < 12)
{
// Before noon, linearly go from H=121 (green) to H=60 (yellow)
H = (time - 8) * (60-121)/4.0 + 121;
S = 100;
}
else
{
// After noon, linearly go from H=60 (green) to H=229 (blue)
// But in the middle, where the color goes green, linearly scale
// Saturation down to zero and back to 100.
H = (time - 12) * (229-60)/8.0 + 60;
auto secondGreenTime = (121-60)*8.0/(229-60) + 12;
if (time < secondGreenTime)
S = (time - 12) * (-100.0)/(secondGreenTime-12) + 100;
else
S = (time - secondGreenTime) * 100.0/(20-secondGreenTime);
}
Pixelmator looks like it's using RGB gradients. Demo:
const canvas = document.getElementById("gradient");
const ctx = canvas.getContext("2d");
for (let i = 0; i < canvas.width; i++) {
const alpha = (i + 0.5) / canvas.width;
const r = 2 * Math.min(alpha, 1 - alpha);
const g = Math.min(1, 2 * (1 - alpha));
const b = Math.max(0, 2 * alpha - 1);
ctx.fillStyle = `rgba(${255*r},${255*g},${255*b})`
ctx.fillRect(i, 0, 1, canvas.height);
}
<canvas id="gradient" width="240" height="40">
Here is an algorithm for that:
Convert the hour to 24 hour and pass minutes and seconds to a fraction or a decimal number (i.e 8:30 -> 8.5, 8:20 -> 25/3)
Substract 8 to the hour (now we have a number from 0 to 12)
If the hour, h, is between 0 and 4 we will do ((-h+4)*(61/4))+60
else we will do ((-h+12)*(191/8))-131
If the value is negative we'll add 360
The value we obtain will be the hue value of the color

3D reconstruction from a 2D image in MATLAB

I have some difficulties to reconstruct a 3D scene from a 2D image under Matlab.
I would like to create a top view of scene removing the perspective, in other words, realize an inverse perspective mapping.
Let's assume we know the camera position, orientation and its parameters. Moreover we consider all the captured points lie on the same plane XY.
Then, it is easy to prove that a pixel at a (u,v) location in the image will move to the coordinate (X,Y,0) in the 3D space with:
X=-((u*P(3,4)-P(1,4))*(v*P(3,1)-P(2,1)) + (v*P(3,4)-P(2,4))*(P(1,1)-u*P(3,1)))/((u*P(3,2)-P(1,2))*(v*P(3,1)-P(2,1)) + (v*P(3,2)-P(2,2))*(P(1,1)-u*P(3,1)));
Y=(X*(u*P(3,2)-P(1,2)) + (u*P(3,4)-P(1,4)))/(P(1,1)-u*P(3,1));
P is the projection matrix such that: P=[KR KT] with K,R and T respectively the intrinsic, rotation and translation matrices.
Once all the 3D locations of each pixel are computed, I would like to display the XY plane with the color information of the original pixel as if it was a 2D image.
However, a pixel (u,v) can mapped in 3D space to a non integer location meaning that I get a non-regular scatter plot were each (X,Y) point contain a color information.
I tried to divide the XY plane into small windows and then compute the average color of all points into each squares but it is very slow.
Please find my code below.
Some help would be appreciated.
Thank you in advance,
Pm
% This program aims to convert a 2D image into a 3D scenario. Let's assume that
% all the points in the image lie on the same plan in the 3D space.
% Program:
% 1-Generate the 3D scenario with 4 squares from diferrent colors
% 2-Take a picture of these squares by projecting the scene into an image 2D space
% 3-Reconstruct the 3D scene from the 2D picture
% Author: Pierre-Marie Damon
clear all; close all; clc;
%% 4 SQUARES DEFINITION
c=10;
sq1_3D = [ 0 0 0;
0 c 0;
c c 0;
c 0 0];
sq2_3D = [ 0 0 0;
c 0 0;
c -c 0;
0 -c 0];
sq3_3D = [ 0 0 0;
0 -c 0;
-c -c 0;
-c 0 0];
sq4_3D = [ 0 0 0;
-c 0 0;
-c c 0;
0 c 0];
SQ_3D = [sq1_3D;
sq2_3D;
sq3_3D;
sq4_3D];
%% CAMERA DEFINITION
% Image resolution:
image_size = [640,480];
% Intrinsic matrix:
fx=150; fy=150; % fx, fy: focal lengths in pixel
x0=image_size(1)/2; y0=image_size(2)/2; % x0, y0: optical center projection coordinates in the 2D cxamera space
K = [fx 0 x0 ;
0 fy y0 ;
0 0 1 ];
% 3D camera orientation:
Rot_cam = [ 0 0 1;
-1 0 0;
0 -1 0]';
% 3D camera rotations:
yaw = -20*pi/180;
roll = -0*pi/180;
pitch = -20*pi/180;
% 3D camera position:
O_Rcam = [-20;0;10];
h_cam = O_Rcam(3); % camera height
% Projection & transformation matrices
R = rotationVectorToMatrix([pitch,yaw,roll])*Rot_cam; % Rotation matrix
T = -R*O_Rcam; % Translation matrix
P = [K*R K*T; zeros(1,3) 1]; %Projection Matrix
%% PROJECTION FROM 3D TO THE 2D IMAGE
SQ_2D = (P*[SQ_3D ones(size(SQ_3D,1),1)]')'; % homogeneous coordinates
SQ_2D = SQ_2D(:,1:2)./repmat(SQ_2D(:,3),1,2); % Normalization
% Square splits:
sq1_2D = SQ_2D(1:4,:);
sq2_2D = SQ_2D(5:8,:);
sq3_2D = SQ_2D(9:12,:);
sq4_2D = SQ_2D(13:16,:);
%% PLOT THE 3D SCENARIO
figure('units','normalized','outerposition',[0 0 1 1]);
f1=subplot(1,2,1);hold on; grid on; view(50, 30); axis equal;
xlabel('X');ylabel('Y');zlabel('Z'); title('3D Scene');
% Plot the camera
cam = plotCamera('Location',O_Rcam,'Orientation',R,'Size',1,'AxesVisible',0);
% Plot the squares
patch([sq1_3D(:,1)],[sq1_3D(:,2)],'r','EdgeColor','k');
patch([sq2_3D(:,1)],[sq2_3D(:,2)],'g','EdgeColor','k');
patch([sq3_3D(:,1)],[sq3_3D(:,2)],'b','EdgeColor','k');
patch([sq4_3D(:,1)],[sq4_3D(:,2)],'m','EdgeColor','k');
%% PLOT THE 2D IMAGE
f2=subplot(1,2,2); hold on; grid on; axis equal; set(gca,'YDir','Reverse'); title('2D Image'); axis off;
xlim([0 image_size(1)]); ylim([0 image_size(2)]);
% plot the projected squares
patch ([sq1_2D(:,1)],[sq1_2D(:,2)],'r','EdgeColor','none');
patch ([sq2_2D(:,1)],[sq2_2D(:,2)],'g','EdgeColor','none');
patch ([sq3_2D(:,1)],[sq3_2D(:,2)],'b','EdgeColor','none');
patch ([sq4_2D(:,1)],[sq4_2D(:,2)],'m','EdgeColor','none');
% Plot the image borders
plot([0 image_size(1)],[0 0],'k','linewidth',3);
plot([image_size(1) image_size(1)],[0 image_size(2)],'k','linewidth',3);
plot([0 image_size(1)],[image_size(2) image_size(2)],'k','linewidth',3);
plot([0 0],[0 image_size(2)],'k','linewidth',3);
%% GENERATE A JPG IMAGE
figure; hold on; grid on; set(gca,'YDir','Reverse'); axis off;
hFig = gcf; hAx = gca; % get the figure and axes handles
set(hFig,'units','normalized','outerposition',[0 0 1 1]); % set the figure to full screen
set(hAx,'Unit','normalized','Position',[0 0 1 1]); % set the axes to full screen
set(hFig,'menubar','none'); % hide the toolbar
set(hFig,'NumberTitle','off'); % to hide the title
xlim([0 image_size(1)]); ylim([0 image_size(2)]);
% Plot the squares
patch ([sq1_2D(:,1)],[sq1_2D(:,2)],'r','EdgeColor','none');
patch ([sq2_2D(:,1)],[sq2_2D(:,2)],'g','EdgeColor','none');
patch ([sq3_2D(:,1)],[sq3_2D(:,2)],'b','EdgeColor','none');
patch ([sq4_2D(:,1)],[sq4_2D(:,2)],'m','EdgeColor','none');
% Create the image
set(gcf,'PaperUnits','inches','PaperPosition',[0 0 image_size(1)/100 image_size(2)/100])
print -djpeg Image.jpg -r100
save('ImageParam.mat', 'K','R','T','P','O_Rcam' )
%% 3D RECONSTRUCTION FROM 2D IMAGE
clear all;
close all;
clc;
load('ImageParam');
I=imread('Image.jpg');
I1 = rgb2gray(I);
figure;imshow(I1);impixelinfo;
I2 = zeros(size(I1));
k=1; i=1; j=1;
tic
for y=size(I2,1):-1:1
for x=1:size(I2,2)
% The formula below comes from the projection equations of
% the camera and the additional constraint that all the points lie on the XY
% plane
X(k)=-((x*P(3,4)-P(1,4))*(y*P(3,1)-P(2,1)) + (y*P(3,4)-P(2,4))*(P(1,1)-x*P(3,1)))/((x*P(3,2)-P(1,2))*(y*P(3,1)-P(2,1)) + (y*P(3,2)-P(2,2))*(P(1,1)-x*P(3,1)));
Y(k)=(X(k)*(x*P(3,2)-P(1,2)) + (x*P(3,4)-P(1,4)))/(P(1,1)-x*P(3,1));
Z(k)=0;
C(k)=I1(y,x); % Color (gray intensity) information
k=k+1;
end
end
toc
figure;hold on;axis equal;
plot(X,Y,'.');
grid on;

changing size and position of color bar

I want to change the size and position of my map colorbar. When I try to change the size and location of the colorbar, it is distorted in shape, I don't know why. Can anybody kindly help me. Please guide me why the set command distorts the clorbar? The location of the color bar should be 'southoutside'.The code is as under:
clear all,close all,clc
ax = worldmap('world');
load geoid
R = georasterref('RasterSize',[180 360],'Latlim',[-90 90],'Lonlim', [0 360]);
levels = [-70 -40 -20 -10 0 10 20 40 70];
geoshow(geoid, R, 'DisplayType', 'contour','LevelList',levels,'Fill','on','LineColor','black')
coast = load('coast.mat');
geoshow(coast.lat, coast.long, 'Color', 'white', 'LineWidth', 1.5)
cb = contourcbar('peer',ax,'Location','southoutside');
caxis([-110 90])
colormap(hsv)
set(get(cb,'XLabel'),'String','Geoid Undulation in Meters')
a=get(cb); %gets properties of colorbar
a.Position %gets the positon and size of the color bar
set(cb,'Position',[0.10 0.20 0.80 0.08])% To change size and position

Overlaying MATLAB Scaled Image to Grayscale Image for selected pixels

I am new to MATLAB Image Processing and currently I have two images - one is a grayscale image of my object and the second is the scaled image generated from MATLAB using imagesc function. I am trying to overlay this scaled image on top of my grayscale image to get a spatial resolution for easier observation. Attached are the two images:
A) Grayscale Image:
B) Scaled Image:
There were a few difficulties that I encountered. Firstly, the scaled image is not saved in the same pixel dimensions, but I can get around that using the imwrite function:
im = imagesc(ScaledDiff);
imwrite(get(im,'cdata'),'scaleddiff.tif')
However, doing so will result in a loss of colorbar and the colormap. Secondly, even if I manage to shrink the scaled image to the size of the grayscale image, overlaying it is still a challenge. Ideally, I would like to set the transparency (or 'alpha') to 0 for those pixels with < 0.02 in scaled image value.
Any idea on how to do this will be greatly appreciated! Sorry if I was unclear!
UPDATE:
Thanks to Rotem, I have managed to overlay the grayscale image and a particular region of my heatmap:
However, I need to display the colorbar corresponding to the heatmap values, because otherwise the information is lost and the overlay will be useless. How should I do this? Below is a snippet of my code, where ScaledIntDiff contains the values from 0 to 0.25 that is displayed on the heatmap:
Brightfield = imread('gray.jpg'); % read background image
I1 = ind2rgb(gray2ind(Brightfield), gray); % convert indices into RGB scale
scale = 1000;
ScaledIntDiff2 = round(ScaledIntDiff.*scale);
I2 = ind2rgb(ScaledIntDiff2, jet(256)); % this is for the heatmap
threshold = 0.02;
I2R = I2(:,:,1); I2G = I2(:,:,2); I2B = I2(:,:,3);
I1R = I1(:,:,1); I1G = I1(:,:,2); I1B = I1(:,:,3);
% Replace pixels in I2 with pixels in I1 if the value of ScaledIntDiff of those pixels is below the threshold
I2R(ScaledIntDiff<threshold) = I1R([ScaledIntDiff<threshold]);
I2G(ScaledIntDiff<threshold) = I1G([ScaledIntDiff<threshold]);
I2B(ScaledIntDiff<threshold) = I1B([ScaledIntDiff<threshold]);
I2(:,:,1) = I2R; I2(:,:,2) = I2G; I2(:,:,3) = I2B;
figure
imshow(I2)
I know that the code above is highly inefficient, so suggestions on how to improve it will be very welcomed. Thank you!
Check the following:
I = imread('CKbi2Ll.jpg'); %Load input.
%Convert I to true color RGB image (all pixels R=G=B are gray color).
I1 = ind2rgb(I, gray(256));
%Convert I to true color RGB image with color map parula (instead of using imagesc)
I2 = ind2rgb(I, parula(256));
%Set the transparency (or 'alpha') to 0 for those pixels with < 0.02 in scaled image value.
%Instead of setting transparency, replace pixels with values from I1.
threshold = 0.02; %Set threshold to 0.02
I2(I1 < threshold) = I1(I1 < threshold);
%Blend I1 and I2 into J.
alpha = 0.3; %Take 0.3 from I2 and 0.7 from I1.
J = I2*alpha + I1*(1-alpha);
%Display output
imshow(J);
Adding a colorbar with ticks labels from 0 to 0.25:
Set color map to parula - it doesn't affect the displayed image, because image format is true color RGB.
Crate array of add 6 ticks from 0 to 250.
Create cell array of 6 TickLabels from 0 to 0.25.
Add colorbar with Ticks and TickLabels properties created earlier.
Add the following code after imshow(J);:
colormap(parula(256));
TLabels = cellstr(num2str((linspace(0, 0.25, 6))'));
T = linspace(1, 250, 6);
colorbar('Ticks', T', 'TickLabels', TLabels);
imshow('ClmypzU.jpg');
colormap(jet(256));
TLabels = cellstr(num2str((linspace(0, 0.25, 6))'));
T = linspace(1, 250, 6);
colorbar('Ticks', T', 'TickLabels', TLabels);

Separating Background and Foreground

I am new to Matlab and to Image Processing as well. I am working on separating background and foreground in images like this
I have hundreds of images like this, found here. By trial and error I found out a threshold (in RGB space): the red layer is always less than 150 and the green and blue layers are greater than 150 where the background is.
so if my RGB image is I and my r,g and b layers are
redMatrix = I(:,:,1);
greenMatrix = I(:,:,2);
blueMatrix = I(:,:,3);
by finding coordinates where in red, green and blue the values are greater or less than 150 I can get the coordinates of the background like
[r1 c1] = find(redMatrix < 150);
[r2 c2] = find(greenMatrix > 150);
[r3 c3] = find(blueMatrix > 150);
now I get coordinates of thousands of pixels in r1,c1,r2,c2,r3 and c3.
My questions:
How to find common values, like the coordinates of the pixels where red is less than 150 and green and blue are greater than 150?
I have to iterate every coordinate of r1 and c1 and check if they occur in r2 c2 and r3 c3 to check it is a common point. but that would be very expensive.
Can this be achieved without a loop ?
If somehow I came up with common points like [commonR commonC] and commonR and commonC are both of order 5000 X 1, so to access this background pixel of Image I, I have to access first commonR then commonC and then access image I like
I(commonR(i,1),commonC(i,1))
that is expensive too. So again my question is can this be done without loop.
Any help would be appreciated.
I got solution with #Science_Fiction answer's
Just elaborating his/her answer
I used
mask = I(:,:,1) < 150 & I(:,:,2) > 150 & I(:,:,3) > 150;
No loop is needed. You could do it like this:
I = imread('image.jpg');
redMatrix = I(:,:,1);
greenMatrix = I(:,:,2);
blueMatrix = I(:,:,3);
J(:,:,1) = redMatrix < 150;
J(:,:,2) = greenMatrix > 150;
J(:,:,3) = blueMatrix > 150;
J = 255 * uint8(J);
imshow(J);
A greyscale image would also suffice to separate the background.
K = ((redMatrix < 150) + (greenMatrix > 150) + (blueMatrix > 150))/3;
imshow(K);
EDIT
I had another look, also using the other images you linked to.
Given the variance in background colors, I thought you would get better results deriving a threshold value from the image histogram instead of hardcoding it.
Occasionally, this algorithm is a little to rigorous, e.g. erasing part of the clothes together with the background. But I think over 90% of the images are separated pretty well, which is more robust than what you could hope to achieve with a fixed threshold.
close all;
path = 'C:\path\to\CUHK_training_cropped_photos\photos';
files = dir(path);
bins = 16;
for f = 3:numel(files)
fprintf('%i/%i\n', f, numel(files));
file = files(f);
if isempty(strfind(file.name, 'jpg'))
continue
end
I = imread([path filesep file.name]);
% Take the histogram of the blue channel
B = I(:,:,3);
h = imhist(B, bins);
h2 = h(bins/2:end);
% Find the most common bin in the *upper half*
% of the histogram
m = bins/2 + find(h2 == max(h2));
% Set the threshold value somewhat below
% the value corresponding to that bin
thr = m/bins - .25;
BW = im2bw(B, thr);
% Pad with ones to ensure background connectivity
BW = padarray(BW, [1 1], 1);
% Find connected regions in BW image
CC = bwconncomp(BW);
L = labelmatrix(CC);
% Crop back again
L = L(2:end-1,2:end-1);
% Set the largest region in the orignal image to white
for c = 1:3
channel = I(:,:,c);
channel(L==1) = 255;
I(:,:,c) = channel;
end
% Show the results with a pause every 16 images
subplot(4,4,mod(f-3,16)+1);
imshow(I);
title(sprintf('Img %i, thr %.3f', f, thr));
if mod(f-3,16)+1 == 16
pause
clf
end
end
pause
close all;
Results:
Your approach seems basic but decent. Since for this particular image the background is composed of mainly blue so you be crude and do:
mask = img(:,:,3) > 150;
This will set those pixels which evaluate to true for > 150 to 0 and false to 1. You will have a black and white image though.
imshow(mask);
To add colour back
mask3d(:,:,1) = mask;
mask3d(:,:,2) = mask;
mask3d(:,:,3) = mask;
img(mask3d) = 255;
imshow(img);
Should give you the colour image of face hopefully, with a pure white background. All this requires some trial and error.

Resources