interpolating coordinates of 3D spline - scipy

I have a dataframe df1 containing points which describe a 3D curve (x, y, z coordinates and the arc length).
In a second data frame df2 I have the arc length (with different steps than in df1) and a value g. I need to find the g associated coordinates x, y and z.
My idea was to use splprep and splev to create a b_spline representation and interpolate via a Parametrization. Unfortunately this does not quite work, can someone help me?
Edit: This is my Code so far. I don't get any Errors, but the calculated x,y,z coordinates are wrong.
# df1 with x, y, z values and arc length
x = df1['x'].values
y = df1['y'].values
z = df1['z'].values
s = df1['arc_length'].values
# df2 with arc length intervals and gamma values
s_int = df2['arc_length'].values
gamma = df2['g'].values
# Use splprep to create a smooth curve
spl = splprep([x, y, z], s=0, k=3)
# Get the x, y, and z coordinates for a given arc length value
target_arc_lengths = s_int
points = splev(target_arc_lengths, spl[0])
x_coordinates = points[0]
y_coordinates = points[1]
z_coordinates = points[2]
Edit2: When creating the minimal example I noticed that my data is incorrect, the script works fine

Related

How to integrate over a discrete 2D surface in MATLAB?

I have a function z = f(x, y), where z is the value at point (x, y). How may I integrate z over the x-y plane in MATLAB?
By function above, I actually mean I have something similar to a hash table. That is, given a (x, y) pair, I can look up the table to find the corresponding z value.
The problem would be rather simple, if the points were uniformly distributed over x-y plane, in which case I can simply sum up all the z values, multiply it with the bottom area, and finally divide it by the number of points I have. However, the distribution is not uniform as shown below. So I am actually asking for the computation method that minimises the error.
The currently accepted answer will only work for gridded data. If your data is scattered you can use the following approach instead:
scatteredInterpolant + integral2:
f = scatteredInterpolant(x(:), y(:), z(:), 'linear');
int = integral2(#(x,y) f(x,y), xmin, xmax, ymin, ymax);
This defines the linear interpolant f of the data z(i) = f(x(i),y(i)) and uses it as an argument to integral2. Note that ymin and ymax, instead of doubles, can be function handles depending on x. So usually you will be integrating rectangles, but this could be used for integration regions a bit more complicated.
If your integration area is rather complicated or has holes, you should consider triangulating your data.
DIY using triangulation:
Let's say your integration area is given by the triangulation trep, which for example could be obtained by trep = delaunayTriangulation(x(:), y(:)). If you have your values z corresponding to z(i) = f(trep.Points(i,1), trep.Points(i,2)), you can use the following integration routine. It computes the exact integral of the linear interpolant. This is done by evaluating the areas of all the triangles and then using these areas as weights for the midpoint(mean)-value on each triangle.
function int = integrateTriangulation(trep, z)
P = trep.Points; T = trep.ConnectivityList;
d21 = P(T(:,2),:)-P(T(:,1),:);
d31 = P(T(:,3),:)-P(T(:,1),:);
areas = abs(1/2*(d21(:,1).*d31(:,2)-d21(:,2).*d31(:,1)));
int = areas'*mean(z(T),2);
If you have a discrete dataset for which you have all the x and y values over which z is defined, then just obtain the Zdata matrix corresponding to those (x,y) pairs. Save this matrix, and then you can make it a continuous function using interp2:
function z_interp = fun(x,y)
z_interp = interp2(Xdata,Ydata,Zdata,x,y);
end
Then you can use integral2 to find the integral:
q = integral2(#fun,xmin,xmax,ymin,ymax)
where #fun is your function handle that takes in two inputs.
I had to integrate a biavariate normal distribution recently in MatLab. The idea is very simple. Matlab defines a surface through a meshgrid, so from x, y you need to do this:
x = -10:0.05:10;
y = x;
[X,Y] = meshgrid(x',y');
...for example. Then, let's call FX the function that defines the value at each point of the surface. To calculate the integral you just need to do this:
surfint = zeros(length(X),1);
for a = 1:length(X)
surfint(a,1) = trapz(x,FX(:,a));
end
trapz(x, surfint)
For me, this is the simplest way.

Point Cloud Generation

I have a 3-D geometrical shape which I have to convert into a point cloud.
The resultant point cloud can be considered equivalent to a point cloud output from a Laser Scan of the object.
No mesh generation is neeeded
The points generated may be evenly spaced, or maybe just randomly spaced - doesn't matter
The 3-D shape can be provided in the form of a 3-D mathematical formula
This has to be done using MATLAB
It's difficult to answer without an example but it sounds like you just want to perform a montecarlo simulation?
Lets say your shape is defined by the function f and that you have X, Y limits stored in two element vector e.g. xlim = [-10 10] i.e. all possible x values of this shape lie between x = -10 and x = 10 then I would suggest that you make f return some sort of error code if there is no value for a specific x-y pair. I'm going to assume that will be NaN. So f(x,y) is a function you are writing that either returns a z if it can or NaN if it can't
n= 10000;
counter = 1;
shape = nan(n, 3)
while counter < n
x = rand*diff(xlim) + mean(xlmin);
y = rand*diff(ylim) + mean(ylim);
z = f(x,y)
if ~isnan(z)
shape(counter, :) = [x, y, z];
counter = counter + 1
end
end
So the above code will generate 10000 (non unique, but that's easily adapted for) points randomly sample across your shape.
Now after typing this I realise that perhaps your shape is actually not all that big and maybe you can uniformly sample it rather than randomly:
for x = xlim(1):xstep:xlim(2)
for y = ylim(1):ystep:ylim(2)
shape(counter, :) = [x, y, f(x,y)];
end
end
or if you write f to be vectorized (preferable)
shape = [(xlim(1):xstep:xlim(2))', (ylim(1):ystep:ylim(2))', f(xlim(1):xstep:xlim(2), ylim(1):ystep:ylim(2));
and then either way
shape(isnan(shape(:, 3), :) = []; %remove the points that fell outside the shape
Here is the code to create a Cloud image with a Depth image from a PrimeSense Camera.
The input/Ouput of this function :
-inputs
depth -depth map
topleft -topleft coordinates of the segmented image in the whole image
-outputs
pclouds -3d point clouds
MatLab code :
depth = double(depth);
% Size of camera image
center = [320 240];
[imh, imw] = size(depth);
constant = 570.3;
% convert depth image to 3d point clouds
pclouds = zeros(imh,imw,3);
xgrid = ones(imh,1)*(1:imw) + (topleft(1)-1) - center(1);
ygrid = (1:imh)'*ones(1,imw) + (topleft(2)-1) - center(2);
pclouds(:,:,1) = xgrid.*depth/constant;
pclouds(:,:,2) = ygrid.*depth/constant;
pclouds(:,:,3) = depth;
distance = sqrt(sum(pclouds.^2,3));
Edit : This source is from this current article http://www.cs.washington.edu/rgbd-dataset/software.html
You can find some other Cloud function in MatLab and C++ that can be interest you.

Error while generating cubic splines for image processing in MATLAB - "data sites should be distinct"

I'm developing an image processing program in MATLAB. One part of it involves sampling some points at fixed intervals from the input image (which is basically a binary line drawing with 1-pixel-wide edges, similar to those made by the "pencil" tool in Paint / GIMP / Photoshop), and then generating a cubic spline with the sampled pixels as its knots.
Now, I know how to generate a cubic spline in MATLAB given a set of (x, y) points, but the problem is this: an image is not a true function of x (say x is the row number of the pixel), because it may have multiple "values" at each x - the values being the numbers of all columns in that row that are black. Because of this MATLAB's spline() function complains that "the data sites should be distinct."
How do I get around this?
I suggest converting your function to parametric form and then doing two splines, on x and y:
y = f(x) =>
y(t),x(t)
In Matlab you can do it in the following way:
t=1:numel(x);
xs = interp1(t,x, ti);
ys = interp1(t,y, ti);
Where xi and yi are the points that you want to interpolate.
Here is an example with data that has multiple y values per x:
y = -10:10;
x = y.^2;
figure;plot(x,y,'rv');
t = 1:numel(x);
ti = 1:0.05:numel(x);
xi = interp1(t,x,ti );
yi = interp1(t,y,ti );
hold on ;plot(xi,yi);

How to use TriScatteredInterp in MatLab?

I am having a problem with TriScatteredInterp in MatLab.
I am using a set of coordinate points with the corresponding temperature at that location. They are all in degrees in the form (long, lat, temp). I want to make an interpolant on these points so I can find out the values at other points and build a grid.
This is what I have done so far:
long = data(:,1)
lat = data(:,2)
values = data(:,3)
lat = lat.*(pi/180)
long = long.*(pi/180)
X = cos(lat).*cos(long)
Y = cos(lat).*sin(long)
Z = sin(lat)
F = TriScatteredInterp(X,Y,Z,values)
[long1 lat1] = meshgrid(-pi:pi/360:pi, -pi/2:pi/360:pi/2);
X1 = cos(lat1).*cos(long1)
Y1 = cos(lat1).*sin(long1)
Z1 = sin(lat1);
F.Method = 'natural'
InterpVals = F(X1,Y1,Z1);
mesh(long1, lat1, InterpVals)
As you can see for every (long, lat) point, I have computed the corresponding point on the sphere and have used the 3d version of TriScatteredInterp.
The problem is that the interpolation only works for the 'nearest' method, as for the linear or natural is producing just NaN's. As I have read this happens when the points that I want to interpolate are outside of the convex hull of the triangulation, but as the points needed are exactly on the sphere, and the input points are covering the entire range (Long : -180 to 180, Lat : -90 to 90), I just don't see how all the points could be outside the convex hull. Any help will be appreciated , ty.
You should interpolate values on the bi-dimensional original data (long, lat), not on the tri-dimensional one (X, Y, Z).
Note that I included some dummy data generator, for the readers that do not have access to your data()!
n = 100;
long = rand(n,1)*720-360;
lat = rand(n,1)*180-90;
values = rand(n,1)*30-5;
lat = lat.*(pi/180);
long = long.*(pi/180);
F = TriScatteredInterp(long,lat,values);
[long1 lat1] = meshgrid(-pi:pi/36:pi, -pi/2:pi/24:pi/2);
InterpVals = F(long1,lat1);
X1 = cos(lat1).*cos(long1);
Y1 = cos(lat1).*sin(long1);
Z1 = sin(lat1);
mesh(X1,Y1,Z1,InterpVals); %note here the meshing on the regular grid (X1,Y1,Z1)
There is still an issue on the edges of the map, as the interpolator doesn't know that the data "wraps" around. The content of InterpVals on those edges will be.. NaN!
Edit: suggestions for the wrapping:
1) rewrite TriScatteredInterp so that it uses modulos;
2) mirror the data around the "edges" of the map, interpolate, then crop it back to original size;
3) check out the Matlab Mapping Toolbox, which analyze and visualize geographic information.

Create 2D grid from vector data in Matlab

I am trying to create a 2-D grid from a vector.
So, for example I have:
x = 1:1:10;
z = 2:2:20;
Now, I want to create a grid which has x on both side of the grid cell and z as grid cell value.
I tried doing it as :
[X,Y] = meshgrid(x, x);
newZ = griddata(x, x ,z, X, Y);
But this gives me error:
The underlying triangulation is empty - the points may be
collinear.
Need help solving this.
In a high level, griddata() takes a 2d surface with variable z-value at each point as the first part of the input, and the query points as the second part of the input. To be more specific, when we look into the definition of the function:
vq = griddata(x,y,v,xq,yq)
x and y specifies the range of x and y values, v is like z-value in a plane, and xq and yq together are query points. Here, v (in your case, z) is expected to be a 2d matrix, to be more specific, the size of v is [length(x), length(y)], whereas in your case, you put z as a vector. Matlab generates the warning since the size doesn't match.
For your reference: http://www.mathworks.com/help/matlab/ref/griddata.html?refresh=true