Averaging properties of a 3D point cloud on a 2D grid with sliding kernel (Matlab) - matlab

I have a 3D point cloud organised in a 3-colums format [x,y,z], with additional properties of each point from column 4 onwards. Note that the distance of all points is random. I am trying to implement a moving filter similar to blockproc in order to create a 2D matrix of size y,x that "flattens" the data along z and averages a given property of the point cloud in a volume. The volume should be a kernel of a fixed size along x and y, let's call them dx and dy within which values of any z will be taken. In addition, the volume should be able to slide, so for instance the moving steps (lets call them xStep and yStep) are not necessarily equal to dx and dy (though dx=dy and xStep=yStep).
So far, Matlab functions I found are:
blockproc: Has the ability to implement a sliding kernel but works on matrices
accumarray: Works on discrete points but no sliding kernel
Here is a cartoon of what I am trying to do conceptually . The function should capture the red points and apply a function (e.g. mean, stdev) to calculate the value of the red cell. Then move by xStep and re-apply the function.
Any idea of how I could achieve that? I've been stuck on that for a while. I wrote a function that indices the points at each iteration, but my datasets are rather large (>10^7 points) so this approach is very time consuming. This thread provides a MWE using accumarray, without the sliding kernel:
table = [ 20*rand(1000,1) 30*rand(1000,1) 40*rand(1000,1)]; % random data
x_partition = 0:2:20; % partition of x axis
y_partition = 0:5:30; % partition of y axis
L = size(table,1);
M = length(x_partition);
N = length(y_partition);
[~, ii] = max(repmat(table(:,1),1,M) <= repmat(x_partition,L,1),[],2);
[~, jj] = max(repmat(table(:,2),1,N) <= repmat(y_partition,L,1),[],2);
% Calculate the sum
result_sum = accumarray([ii jj], table(:,3), [M N], #sum, NaN);
Any input would be greatly appreciated, thank you!
PS: First post here, sorry if there is any bad formatting

Related

Fitting a 2D Gaussian to 2D Data Matlab

I have a vector of x and y coordinates drawn from two separate unknown Gaussian distributions. I would like to fit these points to a three dimensional Gauss function and evaluate this function at any x and y.
So far the only manner I've found of doing this is using a Gaussian Mixture model with a maximum of 1 component (see code below) and going into the handle of ezcontour to take the X, Y, and Z data out.
The problems with this method is firstly that its a very ugly roundabout manner of getting this done and secondly the ezcontour command only gives me a grid of 60x60 but I need a much higher resolution.
Does anyone know a more elegant and useful method that will allow me to find the underlying Gauss function and extract its value at any x and y?
Code:
GaussDistribution = fitgmdist([varX varY],1); %Not exactly the intention of fitgmdist, but it gets the job done.
h = ezcontour(#(x,y)pdf(GaussDistributions,[x y]),[-500 -400], [-40 40]);
Gaussian Distribution in general form is like this:
I am not allowed to upload picture but the Formula of gaussian is:
1/((2*pi)^(D/2)*sqrt(det(Sigma)))*exp(-1/2*(x-Mu)*Sigma^-1*(x-Mu)');
where D is the data dimension (for you is 2);
Sigma is covariance matrix;
and Mu is mean of each data vector.
here is an example. In this example a guassian is fitted into two vectors of randomly generated samples from normal distributions with parameters N1(4,7) and N2(-2,4):
Data = [random('norm',4,7,30,1),random('norm',-2,4,30,1)];
X = -25:.2:25;
Y = -25:.2:25;
D = length(Data(1,:));
Mu = mean(Data);
Sigma = cov(Data);
P_Gaussian = zeros(length(X),length(Y));
for i=1:length(X)
for j=1:length(Y)
x = [X(i),Y(j)];
P_Gaussian(i,j) = 1/((2*pi)^(D/2)*sqrt(det(Sigma)))...
*exp(-1/2*(x-Mu)*Sigma^-1*(x-Mu)');
end
end
mesh(P_Gaussian)
run the code in matlab. For the sake of clarity I wrote the code like this it can be written more more efficient from programming point of view.

Outline your hand with splines (Matlab)

I'm trying to write a script so that one can put his hand on the screen, click a few points with ginput, and have matlab generate an outline of the persons hand using splines. However, I'm quite unsure how you can have splines connect points that result from your clicks, as they of course are described by some sort of parametrization. How can you use the spline command built into matlab when the points aren't supposed to be connected 'from left to right'?
The code I have so far is not much, it just makes a box and lets you click some points
FigHandle = figure('Position', [15,15, 1500, 1500]);
rectangle('Position',[0,0,40,40])
daspect([1,1,1])
[x,y] = ginput;
So I suppose my question is really what to do with x and y so that you can spline them in such a way that they are connected 'chronologically'. (And, in the end, connecting the last one to the first one)
look into function cscvn
curve = cscvn(points)
returns a parametric variational, or natural, cubic spline curve (in ppform) passing through the given sequence points(:j), j = 1:end.
An excellent example here:
http://www.mathworks.com/help/curvefit/examples/constructing-spline-curves-in-2d-and-3d.html
I've found an alternative for using the cscvn function.
Using a semi-arclength parametrisation, I can create the spline from the arrays x and y as follows:
diffx = diff(x);
diffy = diff(y);
t = zeros(1,length(x)-1);
for n = 1:length(x)-1
t(n+1) = t(n) + sqrt(diffx(n).^2+diffy(n).^2);
end
tj = linspace(t(1),t(end),300);
xj = interp1(t,x,tj,'spline');
yj = interp1(t,y,tj,'spline');
plot(x,y,'b.',xj,yj,'r-')
This creates pretty decent outlines.
What this does is use the fact that a curve in the plane can be approximated by connecting a finite number of points on the curve using line segments to create a polygonal path. Using this we can parametrize the points (x,y) in terms of t. As we only have a few points to create t from, we create more by adding linearly spaced points in between. Using the function interp1, we then find the intermediate values of x and y that correspond to these linearly spaced t, ti.
Here is an example of how to do it using linear interpolation: Interpolating trajectory from unsorted array of 2D points where order matters. This should get you to the same result as plot(x,y).
The idea in that post is to loop through each consecutive pair of points and interpolate between just those points. You might be able to adapt this to work with splines, you need to give it 4 points each time though which could cause problems since they could double back.
To connect the start and end though just do this before interpolating:
x(end+1) = x(1);
y(end+1) = y(1);

Connecting the dots in an image

I have the following image:
Is there a way to connect the dots (draw a line between them) using MATLAB? I tried plot by passing it the image, but didn't work.
Thanks.
UPDATE
The way I'm expecting to connect the dots is roughly as shown through the red line in the image sample below:
There are a number of ways you can do this, for instance as follows:
img=im2double(imread('SWal5.png'));
m = bwmorph(~img,'shrink',Inf);
[ix iy] = find(m);
tri = delaunay(iy,ix);
image(img)
hold on
triplot(tri,iy,ix,'g')
set(gca,'Ydir','reverse')
axis square off
If instead you want something akin to a plot, then you can try this after running the find step:
[ix ii]=sort(ix);
iy = iy(ii);
imshow(img)
hold on
plot(iy,ix,'k')
set(gca,'Ydir','reverse')
To plot a trail through a number of points, you could define an adjacency matrix and use gplot to display.
Here's a snippet to show the idea, although you'll note that with this rather simple code the output is a bit messy. It depends how clean a line you want and if crossovers are allowed - there are probably better ways of creating the adjacency matrix.
This assumes you've extracted the position of your points into a matrix "data" which is size n x 2 (in this case, I just took 200 points out of your image to test).
Basically, the nearest points are found with knnsearch (requires Statistics toolbox), and then the adjacency matrix is filled in by picking a starting point and determining the nearest neighbour not yet used. This results in every point being connected to at maximum two other points, providing that the number of points found with knnsearch is sufficient that you never back yourself into a corner where all the nearest points (100 in this case) have already been used.
datal = length(data);
marked = ones(datal,1);
m = 100; % starting point - can be varied
marked(m)=0; % starting point marked as 'used'
adj = zeros(datal);
IDX = knnsearch(data, data,'K',100);
for n = 1:datal;
x = find(marked(IDX(m,:))==1,1,'first'); % nearest unused neighbour
adj(m,IDX(m,x))=1; % two points marked as connected in adj
marked(IDX(m,x))=0; % point marked as used
m = IDX(m,x);
end
gplot(adj, data); hold on; plot(data(:,1),data(:,2),'rx');

2D color plot using a limited dataset

I couldn't find a solution after trying for a long time.
I have 3 columns of data: x, y, and the stress value (S) at every point (x,y). I want to generate a 2D color plot displaying continuous color change with the magnitude of the stress (S). The stress values increase from -3*10^4 Pa to 4*10^4 Pa. I only have hundreds of data points for an area, but I want to see the stress magnitude (read from the color) at every location (x, y). What Matlab command should I use?
I want to make a 2D color plot showing stress magnitude (S) at every location (x, y) based on continuous color change using limited data points
I'd use patch with interpolated coloring:
% some data, x/y are random
N = 50;
x = rand(N,1);
y = rand(N,1);
S = sin(2*x)+y;
% plotting
tr = delaunay(x,y);
trisurf(tr,x,y,zeros(N,1),S,'FaceColor','interp');
view (2)
Take a look at surf and mesh in the MATLAB documentation
To further contribute on Gunther Struyf answer; assuming it is a FEM analysis you may already have a connectivity matrix say 'M' and 'x' 'y' column vectors with node coordinates. Stress values at the nodes may be contained in a column vector 'S'; then you may use the patch function as stated above:
patch('faces',M,'vertices',[x(:) y(:)],'facevertexcdata',S(:),'FaceColor','interp');
and you will have a 2D plot of your data similar to the one posted by Gunther Struyf.

How to create 3D joint density plot MATLAB?

I 'm having a problem with creating a joint density function from data. What I have is queue sizes from a stock as two vectors saved as:
X = [askQueueSize bidQueueSize];
I then use the hist3-function to create a 3D histogram. This is what I get:
http://dl.dropbox.com/u/709705/hist-plot.png
What I want is to have the Z-axis normalized so that it goes from [0 1].
How do I do that? Or do someone have a great joint density matlab function on stock?
This is similar (How to draw probability density function in MatLab?) but in 2D.
What I want is 3D with x:ask queue, y:bid queue, z:probability.
Would greatly appreciate if someone could help me with this, because I've hit a wall over here.
I couldn't see a simple way of doing this. You can get the histogram counts back from hist3 using
[N C] = hist3(X);
and the idea would be to normalise them with:
N = N / sum(N(:));
but I can't find a nice way to plot them back to a histogram afterwards (You can use bar3(N), but I think the axes labels will need to be set manually).
The solution I ended up with involves modifying the code of hist3. If you have access to this (edit hist3) then this may work for you, but I'm not really sure what the legal situation is (you need a licence for the statistics toolbox, if you copy hist3 and modify it yourself, this is probably not legal).
Anyway, I found the place where the data is being prepared for a surf plot. There are 3 matrices corresponding to x, y, and z. Just before the contents of the z matrix were calculated (line 256), I inserted:
n = n / sum(n(:));
which normalises the count matrix.
Finally once the histogram is plotted, you can set the axis limits with:
xlim([0, 1]);
if necessary.
With help from a guy at mathworks forum, this is the great solution I ended up with:
(data_x and data_y are values, which you want to calculate at hist3)
x = min_x:step:max_x; % axis x, which you want to see
y = min_y:step:max_y; % axis y, which you want to see
[X,Y] = meshgrid(x,y); *%important for "surf" - makes defined grid*
pdf = hist3([data_x , data_y],{x y}); %standard hist3 (calculated for yours axis)
pdf_normalize = (pdf'./length(data_x)); %normalization means devide it by length of
%data_x (or data_y)
figure()
surf(X,Y,pdf_normalize) % plot distribution
This gave me the joint density plot in 3D. Which can be checked by calculating the integral over the surface with:
integralOverDensityPlot = sum(trapz(pdf_normalize));
When the variable step goes to zero the variable integralOverDensityPlot goes to 1.0
Hope this help someone!
There is a fast way how to do this with hist3 function:
[bins centers] = hist3(X); % X should be matrix with two columns
c_1 = centers{1};
c_2 = centers{2};
pdf = bins / (sum(sum(bins))*(c_1(2)-c_1(1)) * (c_2(2)-c_2(1)));
If you "integrate" this you will get 1.
sum(sum(pdf * (c_1(2)-c_1(1)) * (c_2(2)-c_2(1))))