Syntax sugar for adding singleton dimensions and/or broadcasting - matlab

I wrote some code that calculates each of the cartesian distances from M N-dimensional points to K centroids, so I have points (M,N), centroids (K,N) and I would like an (M,K) result. It works, but I find the reshaping and summing along a hardcoded axis really clunky, is there an elegant way to write this?
[M Nx] = size(X);
[K Nk] = size(centroids);
assert(Nx == Nk, "Dimensions do not match.")
X_reshape = reshape(X, [M 1 Nx]);
centroids_reshape = reshape(centroids, [1 K Nk]);
distance = sqrt(sum((X_reshape - centroids_reshape).^2, 3));

Related

Volumetric 3D data plotting from 2D map in MATLAB?

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)

Best way to perform a convolution with a new vector for each image?

I try to figure out the best way to perform a kind of convolution.
I have a 3D matrix I = [N x M x P] and a 2D matrix S = [1 x 1 x K x P]. For each pth frame (third dimension) of my 3D matrix I want to return the valid convolution between I(:, :, p-K/2:p+K/2) and S(1, 1, :, p). Do you see a way to do this ?
In fact, in terms of computation the numbers of operation in very close to a standard convolution, the difference is that I need to change the second matrix for each frame...
This is the method I currently use:
% I = 3D matrix [N x M x P]
% S = Filter [1 x 1 x K x P] (K is an odd number)
% OUT = Result
[N, M, P] = size(I); % Data size
K = size(S, 3); % Filter length
win = (K-1)/2 ; % Window
OUT = zeros(size(I)); % Pre-allocation
for p = win+1:P-win
OUT(:, :, p) = convn(I(:, :, p-win:p+win), S(1, 1, :, p), 'valid'); % Perform convolution
end
At the end we have the same number of operations than the standard convolution, the only difference is that the filter is different for each frame...
Any idea ?
Thanks ;)
So you want to convolve a NxMxK sub-image with a 1x1xKx1 kernel, and then only take the valid part, which is an NxM image.
Let's look at this operation for a single (x,y) location. This 1D convolution, of which you only keep 1 value, is equivalent to the dot product of the sub-image and your kernel:
OUT(x,y,p) = squeeze(I(x,y,p-win:p+win))' * squeeze(S(1,1,:,p))
This you can vectorize across all (x,y) by reshaping the sub-image of I to a (N*M)xK matrix (the K is horizontal, S is a column vector).
Repeating this across all p is easiest to implement with a loop, as you do now. The alternative is to create a larger S where each column is shifted by one, so you can do a single dot product between tge two matrices. But that S is also espensive to create, presumably requires a loop too. I don't think that avoiding loops is that pressing any more in MATLAB (it's gotten a lot faster over the years) and the product itself is probably the most expensive part of the algorithm anyway.

Using the SVD rather than covariance matrix to calculate eigenfaces

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]))

Vectorizing a weighted sum of matrices in MATLAB

I'm trying to vectorize the following operation in MATLAB, but it's got me stumped. I've learned from experience that there usually is a way, so I'm not giving up just yet. Any help would be appreciated.
I have a collection of m row-vectors each of size n, arranged in an m x n matrix; call it X.
I also have an m-sized vector of weights, w.
I want to compute a weighted sum of the matrices formed by the self outer products of the vectors in X.
Here is a MWE using a for loop:
m = 100;
n = 5;
X = rand(m, n);
w = rand(1, m);
S = zeros(n, n);
for i = 1 : m
S = S + (w(i) * X(i, :)' * X(i, :));
end
S
This is probably the fastest approach:
S = X' * bsxfun(#times, X, w(:));
You could also do
S = squeeze(sum(bsxfun(#times, ...
bsxfun(#times, conj(X), permute(X, [1 3 2])), w(:)), 1));
(or remove the complex conjugate if not needed).
You can employ two approaches here that use one bsxfun call and few permutes and reshapes. The reshaping trick basically allows us to use the efficient matrix multiplication and thus avoid any extra bsxfun call we might have required otherwise.
Approach #1
[m1,n1] = size(X);
XXmult = bsxfun(#times,X,permute(X,[1 3 2])); %// For X(i, :)' * X(i, :) step
S = reshape(reshape(permute(XXmult,[2 3 1]),[],m1)*w(:),n1,[]) %// multiply weights w
Approach #2
[m1,n1] = size(X);
XXmult = bsxfun(#times,permute(X,[2 3 1]),permute(X,[3 2 1]));
S = reshape(reshape(XXmult,[],m1)*w(:),n1,[])
Shortest answer, and probably fastest:
S = X'*diag(W)*X
Been using it for an unscented Kalman filter, works great.

Computing an ODE in Matlab

Given a system of the form y' = A*y(t) with solution y(t) = e^(tA)*y(0), where e^A is the matrix exponential (i.e. sum from n=0 to infinity of A^n/n!), how would I use matlab to compute the solution given the values of matrix A and the initial values for y?
That is, given A = [-2.1, 1.6; -3.1, 2.6], y(0) = [1;2], how would I solve for y(t) = [y1; y2] on t = [0:5] in matlab?
I try to use something like
t = 0:5
[y1; y2] = expm(A.*t).*[1;2]
and I'm finding errors in computing the multiplication due to dimensions not agreeing.
Please note that matrix exponential is defined for square matrices. Your attempt to multiply the attenuation coefs with the time vector doesn't give you what you'd want (which should be a 3D matrix that should be exponentiated slice by slice).
One of the simple ways would be this:
A = [-2.1, 1.6; -3.1, 2.6];
t = 0:5;
n = numel(t); %'number of samples'
y = NaN(2, n);
y(:,1) = [1;2];
for k =2:n
y(:,k) = expm(t(k)*A) * y(:,1);
end;
figure();
plot(t, y(1,:), t, y(2,:));
Please note that in MATLAB array are indexed from 1.