Matlab: Create 3D cube RGB and show it - matlab

I'll try to be precise and short.
I have a volume (128x128x128) and a mask (same size with [0|1|2] values)
I want to make the 3D volume matrix a 3D image with RGB, and store in each channel (red,green,blue) the points marked in the mask.
This is to use a 2D representation by taking a slice of that 3D cube, and not compute it over and over to make things way more faster (very important in my project), so actually, the 3D volume + rgb would be like a store for 128 2D images.
The question is, what steps and how do I have to make all this:
- Create a volume 128x128x128x3 ?
- Define a new colormap (original is gray) ?
- Join each channel ?
- How do I use imagesc/whatever to show one slice of that cube with the points in the color as marked in the mask (ex: imageRGB(:,:,64)) ?
That's just my guess, but I don't even know how to do it properly...I'm a bit lost, I hope you can help me, this is a piece of code that may be wrong but may help you out
% Create the matrix 4D
ovImg = zeros(size(volImg,1),size(volImg,2),size(volImg,3),3); % 128x128x128x3
% Store in each channel the points marked as groups
ovImg(:,:,:,1) = volImg .* (mask==1);
ovImg(:,:,:,2) = volImg .* (mask==2);
ovImg(:,:,:,3) = volImg .* (mask==3);
many many thanks!!
UPDATE:
I'm having some trouble with transparency and the colormap, this is what I did.
% Create the matrix 4D
ovImg = zeros(size(volImg,1),size(volImg,2),size(volImg,3),3);
% Store in each channel the points marked as groups
ovImg(:,:,:,1) = imaNorm.*(mask==1);
ovImg(:,:,:,2) = imaNorm.*(mask==2);
ovImg(:,:,:,3) = imaNorm.*(mask==3);
[X,Y,Z] = meshgrid(1:128,1:128,1:128);
imaNorm = volImg - min(volImg(:));
maxval = max(imaNorm(:));
ovImg = imaNorm + mask * maxval;
N= ceil(maxval);
c = [linspace(0,1,N)' zeros(N,2)];
my_colormap = [c(:,[1 2 3]) ; c(:,[3 1 2]) ; c(:,[2 3 1])];
figure;
imshow(squeeze(ovImg(:,:,64)),my_colormap);
figure;
imagesc(squeeze(mask(:,:,64)));
Result (Overlayed image / mask)
Any ideas? Thanks again, everybody
FINAL UPDATE:
With the other approach that Gunther Struyf suggested, I had exactly what I wanted.
Thanks mate, I really appreciate it, hope this helps other people too.

You can use imshow with a colormap to 'fake' an RGB image from a grayscale image (which you have). For the scale I'd not multiply it, but add an offset to the value, so each mask is a different range in the colormap.
For plotting a slice of the 3d matrix, you can just index it and then squeeze it to remove the resulting singleton dimension:
Example:
[X,Y,Z]=meshgrid(1:128,1:128,1:128);
volImg =5*sin(X/3)+13*cos(Y/5)+8*sin(Z/10);
volImg=volImg-min(volImg(:));
mask = repmat(floor(linspace(0,3-2*eps,128))',[1 128 128]);
maxval=max(volImg(:));
ovImg=volImg+mask*maxval;
imshow(squeeze(ovImg(:,:,1)),jet(ceil(max(ovImg(:)))));
Unmasked, original image (imshow(squeeze(volImg(:,:,1)),jet(ceil(maxval))))
Resulting with mask (code block above):
For different colormaps, see here, or create your own colormap. Eg you're mask has three values, so let's match those with R,G and B:
N = ceil(maxval);
c = [linspace(0,1,N)' zeros(N,2)];
my_colormap = [c(:,[1 2 3]) ; c(:,[3 1 2]) ; c(:,[2 3 1])];
figure
imshow(squeeze(ovImg(:,:,1)),my_colormap);
which gives:
Other approach:
Now I understand your question, I see you got it quite right from the beginning, you only need rescale the variable to a value between 0 and 1, since from imshow:
Color intensity can be specified on the interval 0.0 to 1.0.
which you can do using:
minval=min(volImg(:));
maxval=max(volImg(:));
volImg=(volImg-minval)/(maxval-minval);
next up is your code:
ovImg = zeros([size(volImg),3]);
ovImg(:,:,:,1) = volImg .* (mask==1);
ovImg(:,:,:,2) = volImg .* (mask==2);
ovImg(:,:,:,3) = volImg .* (mask==3);
You just have to plot it now:
imshow(squeeze(ovImg(:,:,64,:)))

Related

How can I traverse through pixels?

Suppose, I have the following image in my hand.
I have marked some pixels of the image as follows,
Now, I have obtained the pixel mask,
How can I traverse through only those pixels that are in that mask?
Given a binary mask, mask, where you want to iterate over all the true pixels in mask, you have at least two options that are both better than the double for loop example.
1) Logical indexing.
I(mask) = 255;
2) Use find.
linearIdx = find(mask);
I(linearIdx) = 255;
The original question:
How can I save only those pixels which I am interested in?
...
Question: Now, in the Step#2, I want to save those pixels in a data-structure (or, whatever) d so that I can apply another function f2(I, d, p,q,r) which does something on that image on the basis of those pixels d.
Create a binary mask
Try using a logical mask of the image to keep track of the pixels of interest.
I'll make up a random image for example here:
randImg = rand(64,64,3);
imgMask = false(size(randImg(:,:,1)));
imgMask(:,[1:4:end]) = true; % take every four columns This would be your d.
% Show what we are talking about
maskImg = zeros(size(randImg));
imgMaskForRGB = repmat(imgMask,1,1,3);
maskImg(imgMaskForRGB) = randImg(imgMaskForRGB);
figure('name','Psychadelic');
subplot(2,1,1);
imagesc(randImg);
title('Random image');
subplot(2,1,2);
imagesc(maskImg);
title('Masked pixels of interest');
Here's what it looks like:
It will be up to you to determine how to store and use the image mask (d in your case) as I am not sure how your functions are written. Hopefully this example will give you an understanding of how it can be done though.
EDIT
You added a second question since I posted:
But, now the problem is, how am I going to traverse through those pixels in K?
Vectrorization
To set all pixels to white:
randImg(imgMaskForRGB) = 255;
In my example, I accessed all of the pixels of interest at the same time with my mask in a vectorized fashion.
I translated my 2D mask into a 3D mask, in order to grab the RGB values of each pixel. That was this code:
maskImg = zeros(size(randImg));
imgMaskForRGB = repmat(imgMask,1,1,3);
Then to access all of these pixels in the image of interest, I used this call:
randImg(imgMaskForRGB)
These are your pixels of interest. If you want to divide these values in 1/2 you could do something like this:
randImg(imgMaskForRGB) = randImg(imgMaskForRGB)/2;
Loops
If you really want to traverse, one pixel at a time, you can always use a double for loop:
for r=1:size(randImg,1)
for c=1:size(randImg,2)
if(imgMask(r,c)) % traverse all the pixels
curPixel = randImg(r,c,:); % grab the ones that are flagged
end
end
end
Okay. I have solved this using the answer of #informaton,
I = imread('gray_bear.png');
J = rgb2gray(imread('marked_bear.png'));
mask = I-J;
for r=1:size(I,1)
for c=1:size(I,2)
if(mask(r,c))
I(r,c) = 255;
end
end
end
imshow(I);

What's the difference between HSV and grayscale intensities?

I'm doing some research on image processing using MATLAB and I've created grayscale intensity images in two different ways using rgb2gray and rgb2hsv like so:
read_image = imread(handles.myImage);
bc_gambar2 = imresize(read_image,[280 540]);
g = rgb2gray(bc_gambar2); % First intensity image
g2 = rgb2hsv(bc_gambar2);
g = g2(:,:,3); % Second intensity image
The result seems better using rgb2hsv and indexing than using rgb2gray. Can anybody tell me what the difference is and why it's happening?
Here's a sample image I'm using (if needed):
The calculation used by rgb2hsv to compute the value (i.e. lightness) channel is different than that used by rgb2gray to compute the grayscale intensity. They are described by the second and fourth bullet points here, respectively. Briefly:
The computation for the value channel (rgb2hsv) is:
g = max(bc_gambar2, [], 3);
The computation for the grayscale intensity (rgb2gray) is:
g = 0.299.*bc_gambar2(:, :, 1) + ...
0.587.*bc_gambar2(:, :, 2) + ...
0.114.*bc_gambar2(:, :, 3);
More information about different color spaces can be found here.

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:

Matlab - Trying to use vectors with grid coordinates and value at each point for a color plot

I'm trying to make a color plot in matlab using output data from another program. What I have are 3 vectors indicating the x-position, y-yposition (both in milliarcseconds, since this represents an image of the surroundings of a black hole), and value (which will be assigned a color) of every point in the desired image. I apparently can't use pcolor, because the values which indicate the color of each "pixel" are not in a matrix, and I don't know a way other than meshgrid to create a matrix out of the vectors, which didn't work due to the size of the vectors.
Thanks in advance for any help, I may not be able to reply immediately.
If we make no assumptions about the arrangement of the x,y coordinates (i.e. non-monotonic) and the sparsity of the data samples, the best way to get a nice image out of your vectors is to use TriScatteredInterp. Here is an example:
% samplesToGrid.m
function [vi,xi,yi] = samplesToGrid(x,y,v)
F = TriScatteredInterp(x,y,v);
[yi,xi] = ndgrid(min(y(:)):max(y(:)), min(x(:)):max(x(:)));
vi = F(xi,yi);
Here's an example of taking 500 "pixel" samples on a 100x100 grid and building a full image:
% exampleSparsePeakSamples.m
x = randi(100,[500 1]); y = randi(100,[500 1]);
v = exp(-(x-50).^2/50) .* exp(-(y-50).^2/50) + 1e-2*randn(size(x));
vi = samplesToGrid(x,y,v);
imagesc(vi); axis image
Gordon's answer will work if the coordinates are integer-valued, but the image will be spare.
You can assign your values to a matrix based on the x and y coordinates and then use imagesc (or a similar function).
% Assuming the X and Y coords start at 1
max_x = max(Xcoords);
max_y = max(Ycoords);
data = nan(max_y, max_x); % Note the order of y and x
indexes = sub2ind(size(data), max_y, max_x);
data(indexes) = Values;
imagesc(data); % note that NaN values will be colored with the minimum colormap value

Getting stuck on Matlab's subplot mechanism for matching images' points for vlfeat

I am doing vlfeat in Matlab and I am following this question here.
These below are my simple testing images:
Left Image:
Right Image:
I did a simple test with 2 simple images here (the right image is just rotated version of the left), and I got the result accordingly:
It works, but I have one more requirement, which is to match the SIFT points of the two images and show them, like this:
I do understand that vl_ubcmatch returns 2 arrays of matched indices, and it is not a problem to map them for which point goes to which point on two images. However, I am currently stuck in matlab's procedure. I found this. But that only works if the subplot stays that way. When you add an image into the subplot, the size changes and the normalization failed.
Here is my code: (im and im2 are images. f, d and f2, d2 are frames and descriptors from vl_sift function from 2 images respectively)
[matches score] = vl_ubcmatch(d,d2,threshold);%threshold originally is 1.5
if (mode >= 2)%verbose 2
subplot(211);
imshow(uint8(im));
hold on;
plot(f(1,matches(1,:)),f(2,matches(1,:)),'b*');
subplot(212);
imshow(uint8(im2));
hold on;
plot(f2(1,matches(2,:)),f2(2,matches(2,:)),'g*');
end
if (mode >= 3)%verbose 3
[xa1 ya1] = ds2nfu( f(1,matches(1,:)), f(2,matches(1,:)));
[xa2 ya2] = ds2nfu( f2(1,matches(2,:)), f2(2,matches(2,:)));
for k=1:numel(matches(1,:))
xxa1 = xa1(1, k);
yya1 = ya1(1, k);
xxa2 = xa2(1, k);
yya2 = ya2(1, k);
annotation('line',[xxa1 xxa2],[yya1 yya2],'color','r');
end
end
The code above yields this:
I think subplot isn't a good way to go for something like this. Is there a better method for this in Matlab? If possible, I want something like an empty panel that I can draw my image, draw lines freely and zoom freely, just like drawing 2D games in OpenGL style.
From zplesivcak's suggestion, yes, it is possible, and not that problematic after all. Here is the code:
% After we have applied vl_sift with 2 images, we will get frames f,f2,
% and descriptor d,d2 of the images. After that, we can apply it into
% vl_ubcmatch to perform feature matching:
[matches score] = vl_ubcmatch(d,d2,threshold); %threshold originally is 1.5
% check for sizes and take longest width and longest height into
% account
if (size(im,1) > size(im2,1))
longestWidth = size(im,1);
else
longestWidth = size(im2,1);
end
if (size(im,2) > size(im2,2))
longestHeight = size(im,2);
else
longestHeight = size(im2,2);
end
% create new matrices with longest width and longest height
newim = uint8(zeros(longestWidth, longestHeight, 3)); %3 cuz image is RGB
newim2 = uint8(zeros(longestWidth, longestHeight, 3));
% transfer both images to the new matrices respectively.
newim(1:size(im,1), 1:size(im,2), 1:3) = im;
newim2(1:size(im2,1), 1:size(im2,2), 1:3) = im2;
% with the same proportion and dimension, we can now show both
% images. Parts that are not used in the matrices will be black.
imshow([newim newim2]);
hold on;
X = zeros(2,1);
Y = zeros(2,1);
% draw line from the matched point in one image to the respective matched point in another image.
for k=1:numel(matches(1,:))
X(1) = f(1, matches(1, k));
Y(1) = f(2, matches(1, k));
X(2) = f2(1, matches(2, k)) + longestHeight; % for placing matched point of 2nd image correctly.
Y(2) = f2(2, matches(2, k));
line(X,Y);
end
Here is the test case:
By modifying the canvas width and height of one of the images from the question, we see that the algorithm above will take care of that and display the image accordingly. Unused area will be black. Furthermore, we see that the algorithm can match the features of two images respectively.
EDIT:
Alternatively, suggested by Maurits, for cleaner and better implementation, check out Lowe SIFT matlab wrappers.
If you have Matlab Computer Vision Library installed on your disc already, you can simply use
M1 = [f(1, match(1, :)); f(2, match(1, :)); ones(1, length(match))];
M2 = [f2(1, match(2, :)); f2(2, match(2, :)); ones(1, length(match))];
showMatchedFeatures(im,im2,[M1(1:2, :)]',[M2(1:2, :)]','montage','PlotOptions',{'ro','g+','b-'} );