I am attempting to perform Gaussian smoothing on a 2D object in Matlab. Here is a condensed version of my code
[m,n] = size(object);
area = m*n;
x = [1:m]; y = [1:n]; [x,y] = meshgrid(x,y);
z = zeros(size(x));
for i = 1:m,
for j = 1:n,
z = z + object(i,j)*exp(-((x-i).*(x-i) + (y-j).*(y-j)));
end
end
z = z/area;
This code works quite well, but is very slow for large input matrices. I'm wondering if there is a way to make this faster by avoiding the for loops? I've tried thinking of ways to speed this up but I can't seem to figure out anything useful.
Here you have a vectorized form with no loops. But it uses much more memory and depending on the machine can be slower than the loop. However, most of the computing time is used to compute the variable "kernel" that do not depend on the image. Therefore, if you need to process many images of the same size, you have to compute it only once, and then you might have significant savings in time. I repeat here your code first because I fixed some mixing up of x,y,m,n
[m,n] = size(object);
area = m*n;
x = [1:n]; y = [1:m]; [y,x] = meshgrid(x,y);
z = zeros(size(x));
for i = 1:m,
for j = 1:n,
z = z + object(i,j)*exp(-((x-i).*(x-i) + (y-j).*(y-j)));
end
end
z = z/area;
Here is a first vectorized form
xi=bsxfun(#minus,repmat(x(:),1,area),x(:)');
yj=bsxfun(#minus,repmat(y(:),1,area),y(:)');
kernel=exp(-(xi.^2 + yj.^2));
w=kernel*object(:);
w=reshape(w,[m,n])/area;
And an alternative form not using bsxfun
xx=repmat(x(:),1,area);
yy=repmat(y(:),1,area);
xx=xx-xx';
yy=yy-yy';
kernel=exp(-(xx.^2 + yy.^2));
w=kernel*object(:);
w=reshape(w,[m,n])/area;
Related
I've been trying to evaluate a function in matlab. I want my x vector to go from 0 to 1000 and my y vector to go from 0 to 125. They should both have a length of 101.
The equation to be evaluated is z(x,y) = ay + bx, with a=10 and b=20.
a = 10;
b = 20;
n = 101;
dx = 10; % Interval length
dy = 1.25;
x = zeros(1,n);
y = zeros(1,n);
z = zeros(n,n);
for i = 1:n;
x(i) = dx*(i-1);
y(i) = dy*(i-1);
for j = 1:n;
z(i,j) = a*dy*(j-1) + b*dx*(j-1);
end
end
I get an answer, but I don't know if I did it correctly with the indices in the nested for loop?
See MATLAB's linspace function.
a=10;
b=20;
n=101;
x=linspace(0,1000,n);
y=linspace(0,125,n);
z=a*y+b*x;
This is easier and takes care of the interval spacing for you. From the linspace documentation,
y = linspace(x1,x2,n) generates n points. The spacing between the points is (x2-x1)/(n-1).
Edit:
As others have pointed out, my solution above makes a vector, not a matrix which the OP seems to want. As #obchardon pointed out, you can use meshgrid to make that 2D grid of x and y points to generate a matrix of z. Updated approached would be:
a=10;
b=20;
n=101;
x=linspace(0,1000,n);
y=linspace(0,125,n);
[X,Y] = meshgrid(x,y);
z=a*Y+b*X;
(you may swap the order of x and y depending on if you want each variable along the row or column of z.)
[X,Y] = meshgrid(-8:.5:8);
R = sqrt(X.^2 + Y.^2) + eps;
Z = sin(R)./R;
scatter3(X,Y,Z)
Error using scatter3 (line 64)
X, Y and Z must be vectors of the same length.
Matlab R2018b windows x64
As shown in the documentation, X, Y, Z must be vectors. (When you enter an article on mathworks from Googling, say, "matlab scatter3", you will first see the syntax for the function. Blue text means hyperlink. All the inputs are linked to the bottom of the page where their exact typing is defined.)
The reason is (probably) as follows.
As stated in the documentation, scatter3 puts circles (or other symbols of your choice if you modify the graphic object) on 3D coordinates of your choice. The coordinates are the ith element of X, Y, Z respectively. For example, the x-coordinate of the 10th point you wish to plot in 3D is X(10).
Thus it is not natural to input matrices into scatter3. If you know X(i), Y(i), Z(i) are indeed the coordinates you want to plot for all i, even though your X, Y, Z are not vectors for some reason, you need to reshape X, Y, Z.
In order to reshape, you can simply do scatter3(X(:), Y(:), Z(:)) which tells Matlab to read your arrays as a vectors. (You should look up in what order this is done. But it is in the intuitive way.) Or you can use reshape. Chances are: reshape is faster for large data set. But ofc (:) is more convenient.
The following should work:
[X,Y] = meshgrid(-8:.5:8);
R = sqrt(X.^2 + Y.^2) + eps;
Z = sin(R)./R;
X = X(:);
Y = Y(:);
Z = Z(:);
scatter3(X,Y,Z)
scatter3 needs vectors, not matrices as far as I can see here
this is my result:
If you want to use meshgrid without reshaping the matrices you have to use plot3 and the 'o' symbol. So you can get a similar result with:
plot3(X,Y,Z,'o')
EDIT:
A question that arose in association with this post was, which of the following methods is more efficient in terms of computation speed: The function reshape(X,[],1), suggested by me, or the simpler colon version X(:), suggested by #Argyll.
After timing the reshape function versus the : method, I have to admit that the latter is more efficient.
I added my results and the code I used to time both functions:
sizes = linspace(100,10000,100);
time_reshape = [];
time_col = [];
for i=1:length(sizes)
X = rand(sizes(i)); % Create random squared matrix
r = #() ResFcn(X);
c = #() ColFcn(X);
time_reshape = [time_reshape timeit(r)/1000] % Take average of 1000 measurements
time_col = [time_col timeit(c)/1000] % Take average of 1000 measurements
end
figure()
hold on
grid on
plot(sizes(2:end), time_col(2:end))
plot(sizes(2:end), time_reshape(2:end))
legend("Colon","Reshape","Location","northwest")
title("Comparison: Reshape vs. Colon Method")
xlabel("Length of squared matrix")
ylabel("Average execution time [s]")
hold off
function res = ResFcn(X)
for i = 1:1000 % Repeat 1000 times
res = reshape(X,[],1);
end
end
function res = ColFcn(X)
for i = 1:1000 % Repeat 1000 times
res = X(:);
end
end
A zero mean random vector y consisting of three random variables has a 3x3 covariance matrix given by R. I need to determine the innovation representation of y i.e y= B*E using gram schmidt construction where E is the vector of uncorrelated components and B is a lower triangular matrix.
I have visited numerous pages giving tutorials on how to do this in MATLAB but only when my input is a matrix with independent column vectors. Here, I have been given covariance matrix and I can't seem to make a connection on how to utilize this matrix and implement it on MATLAB.
Following is the code I got from MathWorks site created by Reza Ahmadzadeh:
function v = GramSchmidt(v)
k = size(v,2);
for ii = 1:1:k
v(:,ii) = v(:,ii) / norm(v(:,ii));
for jj = ii+1:1:k
v(:,jj) = v(:,jj) - proj(v(:,ii),v(:,jj));
end
end
function w = proj(u,v)
% This function projects vector v on vector u
w = (dot(v,u) / dot(u,u)) * u;
end
end
I don't have such an input matrix. Or is it just that I am not able to understand this code?
Any help would be deeply appreciated. This is the first time I am working on such a project on MATLAB and a little help would really make me understand this concept much better.
I downloaded the function you refer to here and I started messing around with it. Actually, the v argument it requires is nothing but the random vector to which the Gram-Schmidt algorithm is applied, your y. Proof:
% Create sample data and check it's correlation...
y = randi(10,10,3);
y = y - repmat(mean(y),10,1);
corr(y)
% Compute the Gram-Schmidt using a well known formulation...
[Q,R] = GramSchmidt_Standard(y);
% Compute the Gram-Schmidt using the Reza formulation...
UNK = GramSchmidt_Reza(y);
% Q and UNK are identical...
Q
UNK
function [Q,R] = GramSchmidt_Standard(y)
[m,n] = size(y);
Q = zeros(m,n);
R = zeros(n,n);
for j = 1:n
v = y(:,j);
for i = 1:j-1
R(i,j) = Q(:,i).' * y(:,j);
v = v - R(i,j) * Q(:,i);
end
R(j,j) = norm(v);
Q(:,j) = v / R(j,j);
end
end
function v = GramSchmidt_Reza(v)
function w = proj(u,v)
w = (dot(v,u) / dot(u,u)) * u;
end
k = size(v,2);
for ii = 1:1:k
v(:,ii) = v(:,ii) / norm(v(:,ii));
for jj = ii+1:1:k
v(:,jj) = v(:,jj) - proj(v(:,ii),v(:,jj));
end
end
end
So you can pick the function you prefer and proceed with your computations.
I actually want to use a linear model to fit a set of 'sin' data, but it turns out the loss function goes larger during each iteration. Is there any problem with my code below ? (gradient descent method)
Here is my code in Matlab
m=20;
rate = 0.1;
x = linspace(0,2*pi,20);
x = [ones(1,length(x));x]
y = sin(x);
w = rand(1,2);
for i=1:500
h = w*x;
loss = sum((h-y).^2)/m/2
total_loss = [total_loss loss];
**gradient = (h-y)*x'./m ;**
w = w - rate.*gradient;
end
Here is the data I want to fit
There isn't a problem with your code. With your current framework, if you can define data in the form of y = m*x + b, then this code is more than adequate. I actually ran it through a few tests where I define an equation of the line and add some Gaussian random noise to it (amplitude = 0.1, mean = 0, std. dev = 1).
However, one problem I will mention to you is that if you take a look at your sinusoidal data, you define a domain between [0,2*pi]. As you can see, you have multiple x values that get mapped to the same y value but of different magnitude. For example, at x = pi/2 we get 1 but at x = -3*pi/2 we get -1. This high variability will not bode well with linear regression, and so one suggestion I have is to restrict your domain... so something like [0, pi]. Another reason why it probably doesn't converge is the learning rate you chose is too high. I'd set it to something low like 0.01. As you mentioned in your comments, you already figured that out!
However, if you want to fit non-linear data using linear regression, you're going to have to include higher order terms to account for the variability. As such, try including second order and/or third order terms. This can simply be done by modifying your x matrix like so:
x = [ones(1,length(x)); x; x.^2; x.^3];
If you recall, the hypothesis function can be represented as a summation of linear terms:
h(x) = theta0 + theta1*x1 + theta2*x2 + ... + thetan*xn
In our case, each theta term would build a higher order term of our polynomial. x2 would be x^2 and x3 would be x^3. Therefore, we can still use the definition of gradient descent for linear regression here.
I'm also going to control the random generation seed (via rng) so that you can produce the same results I have gotten:
clear all;
close all;
rng(123123);
total_loss = [];
m = 20;
x = linspace(0,pi,m); %// Change
y = sin(x);
w = rand(1,4); %// Change
rate = 0.01; %// Change
x = [ones(1,length(x)); x; x.^2; x.^3]; %// Change - Second and third order terms
for i=1:500
h = w*x;
loss = sum((h-y).^2)/m/2;
total_loss = [total_loss loss];
% gradient is now in a different expression
gradient = (h-y)*x'./m ; % sum all in each iteration, it's a batch gradient
w = w - rate.*gradient;
end
If we try this, we get for w (your parameters):
>> format long g;
>> w
w =
Columns 1 through 3
0.128369521905694 0.819533906064327 -0.0944622478526915
Column 4
-0.0596638117151464
My final loss after this point is:
loss =
0.00154350916582836
This means that our equation of the line is:
y = 0.12 + 0.819x - 0.094x^2 - 0.059x^3
If we plot this equation of the line with your sinusoidal data, this is what we get:
xval = x(2,:);
plot(xval, y, xval, polyval(fliplr(w), xval))
legend('Original', 'Fitted');
I'd like to compute kernel matrices efficiently for generic kernel functions in
Matlab. This means I need to compute k(x,y) for every row x of X
and every row y of Y. Here is some matlab code that computes what I'd
like, but it is rather slow,
function K=compute_kernel( k_func, X, Y )
m = size(X,1);
n = size(Y,1);
K = zeros(m,n);
for i = 1:m
for j = 1:n
K(i,j) = k_func(X(i,:)', Y(j,:)');
end
end
end
Is there any other approach to this problem, e.g. some bsxfun variant that
calls a function on every row taken from X and Y?
pdist2(X,Y, dist_func) unfortunately computes dist_func(X, Y(i,:)), instead of dist_func(X(i,:), Y(i,:)). So the actual function I need is,
function K=compute_kernel( k_func, X, Y )
% Woohoo! Efficient way to compute kernel
size(X)
size(Y)
m = size(X,1);
n = size(Y,1);
for i = 1:n
K(:,i) = pdist2( Y(i,:), X, k_func);
end
It's not as nice as just using pdist2, but is still much more efficient than the previous case.
Have you tired pdist2 with custom distance function?
P.S.
It is best not to use i and j as variables in Matlab