Image matrix operation in Matlab - matlab

I have save a image in a matrix like this:
image1=imread('abcn.tif');
nfilas= tamanio(1);
ncols= tamanio(2);
nbandas= tamanio(3);
imagenn = zeros(nfilas, ncols, nbandas);
And my result is that:
Name Size Bytes Class Attributes
imagenn 4x4x3 96 uint16
And now, I want to graph the value of the same pĂ­xel on the three bands. I want to get the value of the first position (1,1), for example, and graph it. How can I indicate the position with a matrix?
Thanks in advance,

The matrix imagenn is square (4x4) and has 3 "layers" (R, G and B?). So, to get a pixel P on each "layer" you have to write P(1,1,1), P(1,1,2) and P(1,1,3). Note that Matlab's indexes start from 1.
You will have to plot vector P(1,1,:)

Related

MATLAB 3D matrix, max in different directions + rotation using MIP

I have a 3D image called img, let's say it is a 291x287x801 int16 array. I am using the MIP (Maximum intensity projection) to find the image with the maximum intensity in different directions. I know that I could use max to get the MIP:
MIPimg=max(img,[],3);
imagesc(MIPimg);
However, this is not giving me the right direction. I think it is along the z-direction, but what should I do if I want to find the MIP along the y or x direction?
I did try to change that 3 which indicates dimension to 1 or 2, but MATLAB tells me
Error using image
Color data must be an m-by-n-by-3 or m-by-n matrix.
when calling imagesc(MIPimg).
I also tried MIPimg=max(img,[ ],[2 3]); but that didn't help.
Your problem is that imagesc expects either a 2D array as input, or a 3D array where the 3rd dimension has exactly 3 values (this is the way MATLAB represents an RGB image). When you do max(img,[],1), you get an 1x287x801 array back, which has 801 elements along the 3rd dimension, not 3 as MATLAB expects.
What you need to do for display is to convert this 1x287x801 array into an 287x801 array. The function squeeze does this (it removes all dimensions with size 1):
MIPimg = squeeze(max(img,[],1));
I cannot reproduce your problem:
% create random 3D-unit8-matrix (to mimic an image)
img = uint8(randi(255,10,12,3)); % 10x12x3 matrix
% maximum over all rows (direction 1) of each of the 3 10x12 matrices => returns 3 1x12 arrays
[val,idx] = max(img,[],1); % default direction
% maximum over all columns (direction 2) of each of the 3 10x12 matrices => returns 3 10x1 vectors
[val,idx] = max(img,[],2);
% maximum over all slices (direction 3) of each of the 10x12 1x3 "depth" arrays => returns a 10x12 matrix
[val,idx] = max(img,[],3);
overall maximum
max(max(max(img))) % no useful information about the position
position of maximum:
[val_slc,idx_slc] = max(img,[],3); % I can better think in 2D
[val_row,idx_row] = max(val_slc);
[val_col,idx_col] = max(val_row);
idx_max = [idx_row(idx_col),idx_col,idx_slc(idx_row(idx_col),idx_col)];
check
assert( max(max(max(img))) == img(idx_max(1),idx_max(2),idx_max(3)) )

Subtract vector from 3d array

Say if I have a 3d array:
lat = 45:49;
lon = -116:-110;
b = rand(5,7,12);
where the first dimension represents the latitude, the second dimension represents the longitude and the third row represents the data. I aim to plot this 3d data on a map using the mapping toolbox. But, before doing this, I would like to find the difference between the data in 'b' and the following vector:
vals = [2.3,5,6.8,5.4,3.3,12,1.5,4.6,9.8,82,3.3,1];
Specifically, for each spatial data point that I have in my mapped data I owuld like to calculate the mean absolute error between that vector and the data at each point in b. If these were two normal vectors I would use:
mae = mean(abs(bv - vals))
but I'm not sure how this can be done with the 3d array. Eventually, I aim to map this mean absolute error to see how it varies spatially. Can anyone suggest how this can be done in matlab?
Use bsxfun for this (it's more efficient than repmat):
V = permute(vals, [1,3,2]) %// Make sure that the dimesions 'align' correctly. i.e. 12 elements must go in the thrid dimension to match b
mae = mean(abs(bsxfun(#minus, b, V)),3)
As MATLAB does not support broadcasting, you need to create a matrix the same size as b with the repeated values of vals. To be able to do that, first you need to change vals to a shape of 1x1x12 and then repeat it 5x7 times. You can do that with
values=repmat(permute(vals,[1 3 2]),[5 7 1]);
now you can
mae = mean(abs(bv - values))

Using SVD to compress an image in MATLAB

I am brand new to MATLAB but am trying to do some image compression code for grayscale images.
Questions
How can I use SVD to trim off low-valued eigenvalues to reconstruct a compressed image?
Work/Attempts so far
My code so far is:
B=imread('images1.jpeg');
B=rgb2gray(B);
doubleB=double(B);
%read the image and store it as matrix B, convert the image to a grayscale
photo and convert the matrix to a class 'double' for values 0-255
[U,S,V]=svd(doubleB);
This allows me to successfully decompose the image matrix with eigenvalues stored in variable S.
How do I truncate S (which is 167x301, class double)? Let's say of the 167 eigenvalues I want to take only the top 100 (or any n really), how do I do that and reconstruct the compressed image?
Updated code/thoughts
Instead of putting a bunch of code in the comments section, this is the current draft I have. I have been able to successfully create the compressed image by manually changing N, but I would like to do 2 additional things:
1- Show a pannel of images for various compressions (i/e, run a loop for N = 5,10,25, etc.)
2- Somehow calculate the difference (error) between each image and the original and graph it.
I am horrible with understanding loops and output, but this is what I have tried:
B=imread('images1.jpeg');
B=rgb2gray(B);
doubleB=im2double(B);%
%read the image and store it as matrix B, convert the image to a grayscale
%photo and convert the image to a class 'double'
[U,S,V]=svd(doubleB);
C=S;
for N=[5,10,25,50,100]
C(N+1:end,:)=0;
C(:,N+1:end)=0;
D=U*C*V';
%Use singular value decomposition on the image doubleB, create a new matrix
%C (for Compression diagonal) and zero out all entries above N, (which in
%this case is 100). Then construct a new image, D, by using the new
%diagonal matrix C.
imshow(D);
error=C-D;
end
Obviously there are some errors because I don't get multiple pictures or know how to "graph" the error matrix
Although this question is old, it has helped me a lot to understand SVD. I have modified the code you have written in your question to make it work.
I believe you might have solved the problem, however just for the future reference for anyone visiting this page, I am including the complete code here with the output images and graph.
Below is the code:
close all
clear all
clc
%reading and converting the image
inImage=imread('fruits.jpg');
inImage=rgb2gray(inImage);
inImageD=double(inImage);
% decomposing the image using singular value decomposition
[U,S,V]=svd(inImageD);
% Using different number of singular values (diagonal of S) to compress and
% reconstruct the image
dispEr = [];
numSVals = [];
for N=5:25:300
% store the singular values in a temporary var
C = S;
% discard the diagonal values not required for compression
C(N+1:end,:)=0;
C(:,N+1:end)=0;
% Construct an Image using the selected singular values
D=U*C*V';
% display and compute error
figure;
buffer = sprintf('Image output using %d singular values', N)
imshow(uint8(D));
title(buffer);
error=sum(sum((inImageD-D).^2));
% store vals for display
dispEr = [dispEr; error];
numSVals = [numSVals; N];
end
% dislay the error graph
figure;
title('Error in compression');
plot(numSVals, dispEr);
grid on
xlabel('Number of Singular Values used');
ylabel('Error between compress and original image');
Applying this to the following image:
Gives the following result with only first 5 Singular Values,
with first 30 Singular Values,
and the first 55 Singular Values,
The change in error with increasing number of singular values can be seen in the graph below.
Here you can notice the graph is showing that using approximately 200 first singular values yields to approximately zero error.
Just to start, I assume you're aware that the SVD is really not the best tool to decorrelate the pixels in a single image. But it is good practice.
OK, so we know that B = U*S*V'. And we know S is diagonal, and sorted by magnitude. So by using only the top few values of S, you'll get an approximation of your image. Let's say C=U*S2*V', where S2 is your modified S. The sizes of U and V haven't changed, so the easiest thing to do for now is to zero the elements of S that you don't want to use, and run the reconstruction. (Easiest way to do this: S2=S; S2(N+1:end, :) = 0; S2(:, N+1:end) = 0;).
Now for the compression part. U is full, and so is V, so no matter what happens to S2, your data volume doesn't change. But look at what happens to U*S2. (Plot the image). If you kept N singular values in S2, then only the first N rows of S2 are nonzero. Compression! Except you still have to deal with V. You can't use the same trick after you've already done (U*S2), since more of U*S2 is nonzero than S2 was by itself. How can we use S2 on both sides? Well, it's diagonal, so use D=sqrt(S2), and now C=U*D*D*V'. So now U*D has only N nonzero rows, and D*V' has only N nonzero columns. Transmit only those quantities, and you can reconstruct C, which is approximately like B.
For example, here's a 512 x 512 B&W image of Lena:
We compute the SVD of Lena. Choosing the singular values above 1% of the maximum singular value, we are left with just 53 singular values. Reconstructing Lena with these singular values and the corresponding (left and right) singular vectors, we obtain a low-rank approximation of Lena:
Instead of storing 512 * 512 = 262144 values (each taking 8 bits), we can store 2 x (512 x 53) + 53 = 54325 values, which is approximately 20% of the original size. This is one example of how SVD can be used to do lossy image compression.
Here's the MATLAB code:
% open Lena image and convert from uint8 to double
Lena = double(imread('LenaBW.bmp'));
% perform SVD on Lena
[U,S,V] = svd(Lena);
% extract singular values
singvals = diag(S);
% find out where to truncate the U, S, V matrices
indices = find(singvals >= 0.01 * singvals(1));
% reduce SVD matrices
U_red = U(:,indices);
S_red = S(indices,indices);
V_red = V(:,indices);
% construct low-rank approximation of Lena
Lena_red = U_red * S_red * V_red';
% print results to command window
r = num2str(length(indices));
m = num2str(length(singvals));
disp(['Low-rank approximation used ',r,' of ',m,' singular values']);
% save reduced Lena
imwrite(uint8(Lena_red),'Reduced Lena.bmp');
taking the first n max number of eigenvalues and their corresponding eigenvectors may solve your problem.For PCA, the original data multiplied by the first ascending eigenvectors will construct your image by n x d where d represents the number of eigenvectors.

Matlab padarray function

I have an image of size 350X450. I'm trying to pad with zeros the matrix which represents the image in a way that I'll have the original matrix in the center of the new padded matrix with a new dimensions of 700X900. Here is what I want to do:
I'm trying to implement that using padarray function:
(Suppose w is the desired width, h is the desired height and im is the image(matrix))
new_image=paddarray(im, [0.5*w 0.5*h]);
I don't get the desired result. What am I missing? Is there a better way to do this?
Your syntax is right, you should set w = ceil((700-350)/2) and h = ceil((900-450)/2).
As the HELP entry says:
B = padarray(A,PADSIZE)
pads array A with PADSIZE(k) number of zeros along the k-th dimension of A.
padarray([1 2; 3 4],[1 1]) %makes a 4x4 matrix
You don't want to pad with w and h, you want to pad with
(wDesired - wCurrent)/2 %floor or ceil, depending on your mood.

Generate Color Plot

There is an object on an area with dimension M*M unit cells. The coverage rate C=1/M * Sum(i=1 to M J(i)) where J(i)=1 when the cell i is covered and 0 otherwise. This is a color scale map representing the visit of the cells vs the times of visiting by the object. So, the legend shows that there are cells which have been visited from 0 to 8 times in N number of iterations. Can anyone tell me how this color representation can be coded? What and how this can be generated?
Use image (or imagesc). You need a matrix of X values and Y values, and the corresponding matrix of Z values.
For example:
% generate some x,y
[x,y]=meshgrid(1:10,1:10);
% generate some z values: random numbers from 0 to 8
z = randi([0 8],size(x));
% plot
imagesc(x,y,z)
How you determine your x, y, z ... well, you'd have to provide more information.
You could use a two dimensional array and read the information in from a file.