I'm trying to get a five column matrix with one row for each pixel in an image. The first two columns are the location (x,y) of the pixel, and the remaining columns are the RGB values of the pixel.
I can use a loop to create this matrix, but I would like to vectorize this operation for efficiency purposes. I can get the RGB values with img(:,:,1), img(:,:,2), and img(:,:,3) respectively, but I don't know how to get the x and y value of the associated pixel while vectorizing.
You can use meshgrid to get all x and y coordinates and then append these to your data resulting in a N x 5 matrix.
[x, y] = meshgrid(1:size(img, 2), 1:size(img, 1));
out = cat(2, x(:), y(:), reshape(img, [], 3));
Find is one way to query the coordinates.
You can separate the RGB channels, find the coordinates and then concatenate the results:
imgR = img(:, :, 1);
imgG = img(:, :, 2);
imgB = img(:, :, 3);
[rows, cols] = find(imgR + 1); % +1 to avoid zeros!
M = [rows, cols, imgR(:), imgG(:), imgB(:)];
Related
Dears,
I would like to kindly ask you for support. My goal is to find the brightest region of the RGB image and highlight it without additional tools. Please see my example below.
rgbImage = imread( 'Zoom1_WhiteImage.png' );
imshow(rgbImage);
[rows, columns, numberOfColorChannels] = size(rgbImage)
[x, y] = meshgrid(1:columns, 1:rows);
% Extract the individual red, green, and blue color channels.
% Need to cast to double or else x and y will be clipped to 255 when we concatenate them.
if numberOfColorChannels == 1
% Leave as gray scale.
% Get array listing [r, g, b, x, y]. Using (:) will turn all the 2-D arrays into column vectors.
output = [rgbImage(:), x(:), y(:)];
else
redChannel = double(rgbImage(:, :, 1));
greenChannel = double(rgbImage(:, :, 2));
blueChannel = double(rgbImage(:, :, 3));
% Get array listing [r, g, b, x, y]. Using (:) will turn all the 2-D arrays into column vectors.
output = [redChannel(:), greenChannel(:), blueChannel(:), x(:), y(:)];
end
[rows, columns] = find(rgbImage == 155);
imshow(rgbImage);
hold on
Unfortunately, I am struggling with how to proceed with the plot of the points that should overlay the grey image.
Would you be so kind and help me to finish the code, please?
I'd recommend you read about logical indexing in MATLAB - it's a very powerful concept, and it allows you to skip most of the things you're trying to do by flattening the arrays, and creating separate arrays of indices with meshgrid. Here is an article that addresses logical indexing down at the bottom, for example.
I've modified and added to your code so it accomplishes the job using logical indexing. I tested this in R2019b.
rgbImage = imread( 'Zoom1_WhiteImage.png' );
imshow(rgbImage);
[rows, columns, numberOfColorChannels] = size(rgbImage)
% This is unnecessary - the 'find' command will generate the indices
% without you preparing a matrix of them:
% [x, y] = meshgrid(1:columns, 1:rows);
if numberOfColorChannels == 1
% No need to flatten the image, or combine it with indices:
%output = [rgbImage(:), x(:), y(:)];
brightness = rgbImage; % For a 1 channel image, brightness is the same as the original pixel value.
else
% redChannel = double(rgbImage(:, :, 1));
% greenChannel = double(rgbImage(:, :, 2));
% blueChannel = double(rgbImage(:, :, 3));
% Get array listing [r, g, b, x, y]. Using (:) will turn all the 2-D arrays into column vectors.
% output = [redChannel(:), greenChannel(:), blueChannel(:), x(:), y(:)];
% For an RGB image, the brightness can be estimated in various ways, here's one standard formula:
brightness = (0.2126*rgbImage(:, :, 1) + 0.7152*rgbImage(:, :, 2) + 0.0722*rgbImage(:, :, 3));
end
% Establish a brightness threshold - pixels brighter than this value will
% be highlighted
threshold = 215;
% Create a zero-filled mask of equal size to the image to hold the
% information of which pixels met the brightness criterion:
mask = zeros(rows, columns, 'logical');
% Assign "1" to any mask pixels whose corresponding image pixel met the
% brightness criterion:
mask(brightness > threshold) = 1;
figure;
% Overlay and display mask. imoverlay was introduced in R2017, but the
% syntax has changed a bit, so check the version you're using. You can also
% use imfuse, or imshowpair, or make your own image blending algorithm,
% depending on how you want it to look.
imshow(imoverlay(rgbImage, mask, 'red'));
hold on
I have a heat map
and want to convert this 2D matrix to a 3D volume/shape/surface data points for further processing. Not simply display it in 3D using surf.
What would be a good way to do this?
With a lot of help from this community I could come closer:
I shrunk the size to 45x45 px for simplicity.
I = (imread("TESTGREYPLASTIC.bmp"))./2+125;
Iinv = 255-(imread("TESTGREYPLASTIC.bmp"))./2-80;%
for i = 1:45
for j = 1:45
A(i, j, I(i,j) ) = 1;
A(i, j, Iinv(i,j) ) = 1;
end
end
volshow(A)
Its not ideal but the matrix is what I wanted now. Maybe the loop can be improved to run faster when dealing with 1200x1200 points.
How do I create a real closed surface now?
Following your conversation with #BoilermakerRV, I guess you are looking for one of the following two results:
A list of 3d points, where x and y are index of pixels in the image, and z is value of corresponding pixels. The result will be an m*n by 3 matrix.
An m by n by 256 volume of zeros and ones, that for (i,j)-th pixel in the image, all voxels of the (i, j)-the pile of the volume are 0, except the one at I(i, j).
Take a look at the following example that generates both results:
close all; clc; clear variables;
I = rgb2gray(imread('data2.png'));
imshow(I), title('Data as image')
% generating mesh grid
[m, n] = size(I);
[X, Y] = meshgrid(1:n, 1:m);
% converting image to list of 3-d points
P = [Y(:), X(:), I(:)];
figure
scatter3(P(:, 1), P(:, 2), P(:, 3), 3, P(:, 3), '.')
colormap jet
title('Same data as a list of points in R^3')
% converting image to 256 layers of voxels
ind = sub2ind([m n 256], Y(:), X(:), I(:));
V = zeros(m, n, 256);
V(ind) = 1.0;
figure
h = slice(V, [250], [250], [71]) ;
[h.EdgeColor] = deal('none');
colormap winter
camlight
title('And finally, as a matrix of 0/1 voxels')
The contour plot that is shown can't be generated with "2D" data. It requires three inputs as follows:
[XGrid,YGrid] = meshgrid(-4:.1:4,-4:.1:4);
C = peaks(XGrid,YGrid);
contourf(XGrid,YGrid,C,'LevelStep',0.1,'LineStyle','none')
colormap('gray')
axis equal
Where XGrid, YGrid and C are all NxN matrices defining the X values, Y values and Z values for every point, respectively.
If you want this to be "3D", simply use surf:
surf(XGrid,YGrid,C)
I'm using the set of n = 40 faces from AT&T (http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html) to try and generate eigenfaces via the SVD.
First I calculate the average vector:
Then I subtract it from every vector in the training set, reshape the new vector into a 1 by (p*q) column vector of a n by (p*q) matrix x, and calculate a matrix X such that X = (1/sqrt(n))*x. (here's where the issue is: all my results in X are rounded to 0, resulting in a black image result for eigenface as seen below)
Then I calculate the SVD of this matrix X and try to get the first eigenface of the first column of the unitary matrix by reshaping it back into a p by q matrix
However, this is my result:
Can anyone spot my error in the code below? Any answer is much appreciated
n = 40;
%read images
A = double(imread('faces_training/1.pgm'));
f(:, :, 1) = A;
for j = 2:n
f(:, :, j) = double(imread(['faces_training/',num2str(j),'.pgm']));
A = A + f(:, :, j);
end
%calculate average
a = (1/n)*A;
%imshow(uint8(a))
for i = 1:n
%subtract from images
x_vector(:, i) = reshape(f(:, :, i) - a, [], 1);
end
X = (1/sqrt(n))*x_vector;
%svd
[U S V] = svd(X);
B = reshape(U(:, 1), [size(a, 1) size(a, 2)]);
imshow(uint8(B))
Doing the same thing and had the same problem. The short answer is you have to normalize your eigenvector to get a good image. Before normalizing, you’ll notice your vector values are very close to 0 (probably because of how svd was done) which probably means they’re close to black.
Anyway, use this equation on the eigenvectors you wanna transform:
newpixel[i,j]=(oldpixel[i,j]-min(oldpixel[:,j]))/(max(oldpixel[:,j])--min(oldpixel[:,j]))
Let A be an n by 3 matrix, such that the first two columns are all ordered pairs of the form (5*i,5*i) for i from 1 to 200. The third column contains values from 0 to 1, which I will call intensities. I want to make a 1000 by 1000 plot so that the rectangle at (5*i,5*i) is shaded with intensity described by the third column entry.
I'm familiar with the heatmap function and imshow, but I don't see a way to include this "scaling by 5" to make a nice plot. And of course in general the x and y coordinates may not be scaled by the same amount.
Is there a nice way to do this in Matlab?
With imagesc it's actually pretty simple:
First some example data:
%// generate example data
ii = 1:200;
[xx,yy] = meshgrid(ii);
A(:,1) = 5*xx(:);
A(:,2) = 5*yy(:);
A(:,3) = randi([0,1],1,40000);
Actual answer
n = 200;
%// reshape data
D = reshape( A(:,3),n,n );
%// heatmap
imagesc(A(:,1),A(:,2),D)
colormap(gray)
caxis([0,1])
gives:
Important notice
If your coordinates are not sorted as required for imagesc you can sort them with:
A = sortrows(A,[2,1]);
Clown Example
%// original image
load clown
I = reshape(1:numel(X),size(X));
[R,C] = ind2sub(size(X),I);
A(:,1) = R(:);
A(:,2) = C(:);
A(:,3) = X(:);
D = reshape( A(:,3),200,320 );
figure(1)
subplot(1,3,1)
imagesc(A(:,1),A(:,2),D)
%// shuffled image -> shuffled data
shuffle = randperm(320*200);
A = A(shuffle,:);
D = reshape( A(:,3),200,320 );
subplot(1,3,2)
imagesc(A(:,1),A(:,2),D)
%// sorted image
A = sortrows(A,[2,1]);
D = reshape( A(:,3),200,320 );
subplot(1,3,3)
imagesc(A(:,1),A(:,2),D)
You see, even if your coordinates are sorted like a mess, you can rebuild the image with sortrows.
See this
function DrawHeatmap(X,Y,Z)
%DRAWHEATMAP Draw a 2D heatmap for (X,Y) coordinates whose values are in Z
% X, Y , Z must be columns
% By: Eng. Osama Talaat Abdel-Hafiz - PhD Student
% Egypt - Sept 2017
if size(X,2)==1 && size(Y,2)==1 && size(Z,2)==1
F = scatteredInterpolant(X,Y,Z); % create a function from interpolation
[X,Y] = meshgrid(min(X):0.1:max(X),min(Y):0.1:max(Y));
Z = F(X,Y);
contourf(X, Y, Z, linspace(floor(min(min(Z))),ceil(max(max(Z))),400), 'LineColor','none')
colorbar;
else
error('X, Y , Z must be columns')
end
end
I have a set of 3d data points for each of which the value V of a certain quantity is associated. The data are organized in ordered columns X Y Z V, where the spatial coordinates are distributed on a grid. I have 21 points in every direction, so that the lengths of the several columns is 21x21x21=9261. How can I transform the data into a meshgrid that can be used by isosurface in Octave/Matlab? My code is the following
a=load("data.txt");
X=reshape(a(:,1), 21,21,21);
Y=reshape(a(:,2), 21,21,21);
Z=reshape(a(:,3), 21,21,21);
V=reshape(a(:,2), 21,21,21);
fv=isosurface (X,Y,Z,V,0.9);
patch(fv)
But the result is not meaningful (i get two flat surfaces located at x=0.9 and 1.). The data can be downloaded here.
Here's a way to create a proper meshgrid of your data:
a = load('data.txt');
[X, Y, Z] = meshgrid(unique(a(:, 1)), unique(a(:, 2)), unique(a(:, 3)));
V = zeros(21, 21, 21);
for i = 1:numel(V)
idx = find(a(:, 1) == X(i) & a(:, 2) == Y(i) & a(:, 3) == Z(i));
V(i) = a(idx, 4);
end
fv = isosurface (X,Y,Z,V,0.9);
p = patch(fv);
set(p,'FaceColor','red','EdgeColor','none');
camlight;
lighting gouraud;
xlabel('x');
ylabel('y');
zlabel('z');
If you have much larger data and this is too slow, I can probably come up with a way of reshaping the original data to avoid the for-loop I've used above.