Non-rectangular meshgrids in MATLAB - matlab

I want to create a non-rectangular meshgrid in matlab.
Basically I have a polygon shaped feasible set I need to make a grid of in order to interpolate 3D data points in this set. The function for interpolation is given and requires finite (x, y, z) inputs. Where x is nx1, y is 1xm and z is nxm. Right now I have the mesh set up with linspace and set all NaN (infeasible) values to 0 before using my function, which is wrong of course (third figure).
Is there a simple solution for this?
I added a picture illustrating what I'm currently doing: First plot is the feasible set, second plot are solved sample data points in this set and third plot is the interpolation (currently still with rectangular meshgrid and NaN = 0). What I need is a meshgrid looking like the first figure (red polygon) instead of a rectangular one. In the third plot you can see that the rectangular meshgrid in combination with setting NaN to 0 (=infeasible values, not included in the red polygon set) results in a wrong interpolation along the edges, because it includes infeasible regions.
Here is my code using a rectangular meshgrid:
figure (2) %sample data
plot3(X0(1,:), X0(2,:), U, 'x')
%X0(1,:) and X0(2,:) are vectors corresponding to the Z-Values (blue sample data)
%X0 and U are in the feasible set (red polygon)
xv = linspace(xLb(1), xUb(1), 100);
yv = linspace(xLb(2), xUb(2), 100); %xLb and xUb are upper and lower bounds for the rectangle mesh
[x1,x2] = meshgrid(xv, yv);
Z = griddata(X0(1,:), X0(2,:), U, x1, x2);
%This grid obviously includes values that are not in the feasible set (red polygon) by its rectangular nature
Z(isnan(Z))=0; %set infeasible values to 0, wrong of course
testMPC = someInterpolationFunction([0:length(Z)-1]',[0:length(Z)-1],Z);
testMPC.showInterpolation(20,20)
%this shows figure 3 in the attached picture

Try something like this:
nRows = 100;
nCols = 200;
x1 = #(x) max(0, x-50);
x2 = #(x) min(nCols, nCols - 50 + x);
RR = zeros(nRows, nCols);
CC = zeros(nRows, nCols);
for iRow = 1:nRows
c1 = x1(iRow);
c2 = x2(iRow);
colVec = linspace(c1, c2, nCols);
RR(iRow, :) = iRow;
CC(iRow, :) = colVec;
end
mesh(RR, CC, zeros(size(RR)))
You'd need to redefine the functions for x1 and x2 or course as well as the scaling, but this should give you an idea of how to get started.

Related

Plotting a coloured 3D figure in MATLAB

I have 3 axes: X of size N, Y of size M and Z of size O, which correspond to the coordinates of my data.
I have a matrix: DATA of size MxNxO, which corresponds to the module for each points.
I would like to plot the MATLAB figure of coordinate X, Y, Z and color the point depending of the value of the matrix DATA of size MxNxO.
I tried lots of functions such as scatter3, surf, plot3, but none is working as I wanted.
This is what I tried:
n = 10;
x = linspace(0,10,n);
y = linspace(0,10,n);
z = linspace(0,10,n);
DATA = randn(n,n,n);
scatter3(x,y,z,DATA);
This code didn't work because DATA is not the same size as x, y, z. I also tried with:
[X,Y,Z] = ndgrid(x,y,z)
scatter3(X,Y,Z,DATA);
but this didn't work either.
The trick with scatter3() is to "unroll" your matrices to a column vector, and don't forget that the fourth argument is size, rather than colour:
n = 10;
x = linspace(0,10,n);
y = linspace(0,10,n);
z = linspace(0,10,n);
[X,Y,Z] = ndgrid(x,y,z);
DATA = randn(n,n,n);
% here 3 is the size. You can set it to a different constant, or vary it as well
scatter3(X(:), Y(:), Z(:), 3, DATA(:));
Results in
You can colour a surface, see its documentation, however, it doesn't seem to make much sense in your case, given you have a full cube of data points. A surface is 2D, whereas your data is 3D. For a 2D surface, simply follow the example in the docs:
n = 10;
x = linspace(0,10,n);
y = linspace(0,10,n);
Z = rand(n);
DATA = randn(n);
surf(x, y, Z, DATA);
Images rendered in R2007b, syntax cross-checked with the documentation.
If you've got a surface defined by an M -by- 4 array containing X, Y, Z and Data, you can use delaunay() to create a Delaunay triangulation of your points and then trisurf() to plot that. Note that this still requires a 2D surface, albeit it can vary in three dimensions. The cube of data in your example still doesn't make sense to plot as a surface, even with this method.

Plot a surface only for coordinates that satisfy a specific equation in MATLAB

I have two grid coordinates matrices, X and Y, created by calling [X, Y] = meshgrid(x, y), so their elements represent coordinates. How can I plot a surface on the xy-plane, using heights from matrix V, only for coordinates that satisfy a specific equation? For example, my plot extends up to radius a, but I dont want to plot any data to the set of points that satisfy the equation sqrt(x^2 + (y-c)^2) < b, where b, c (a>b) are given constants and x=X(i,j), y=Y(i,j). Is there an easy way to do this, other than creating the two grid coordinates matrices (up to radius a) and then manually removing elements from X, Y, V, using nested for loops? I have not found any way to limit the plotting area I am interested in by changing x, y.
Using Logical Indexing
Just in case you're still looking for any implementation details. Referencing the comment by #Ander Biguri. I have to add that it might be easier to use mesh parameters X and Y directly in the logical indexing. Here is a little playground script that might help future readers. Below Region_Array is a logical array that specifies where the condition in this case sqrt(X.^2 + (Y-c).^2) < b is true. When true Region_Array is indexed with the value "1" and elsewhere with "0". I've split this into two steps just in case the complementary region is quickly wanted. The images/plots below show the resulting surf() and masks/regions. MATLAB has some thorough documentation and examples overviewing logical indexing: Find Array Elements That Meet a Condition
Trivial Surface Plot:
Masks/Regions Not to be Plotted:
Playground Script:
%Random test axes%
x = linspace(0,100,50);
y = linspace(0,100,50);
[X,Y] = meshgrid(x,y);
%Trivial plot of ones%
V = ones(length(x),length(y));
%Constant parameters%
b = 20;
c = 10;
%Eliminating within the curved region%
figure(1)
Region_Array = sqrt(X.^2 + (Y-c).^2) < b;
V(Region_Array) = NaN;
subplot(1,2,1); surf(X,Y,V);
axis([0 100 0 100]);
title("Eliminating Within the Curved Region");
%Eliminating outside the curved region%
V = ones(length(x),length(y));
V(~Region_Array) = NaN;
subplot(1,2,2); surf(X,Y,V);
axis([0 100 0 100]);
title("Eliminating Outside the Curved Region");
figure(2)
subplot(1,2,1); imshow(~Region_Array,'InitialMagnification',200);
title("Region Array Mask/Map (Inside)")
subplot(1,2,2); imshow(Region_Array,'InitialMagnification',200);
title("Region Array Mask/Map (Outside)")
Ran using MATLAB R2019b

undo rotation of measured vector components after affine transformation of coordinates

Problem
I have a vector field with three dimensions. Each direction of the vector I have to sample seperately and therefore the three grids are slightly unaligned within the measured field (the sample moves, not the measurement grid).
When I realign my measurements my vector components are no longer orthogonal to each other since each has a different transformation. Because the realignment also has a translation, I think each sample point has a slightly different rotation. Basically I want to rotate my sample points but keep my vector directions and make sure they are orthogonal.
Question
How do I correctly 'unrotate' my vectors?
Example
Example MatLab code (note that in my real data I only want to perform the correction in MatLab, I dont perform the transformation in MatLab, just creating fake data to give an example):
n1=10; n2=10; n3=10; %10x10x10 samples for each direction / vector component measurement
sig=5; %smoothness of measured vector field
vec = struct; A = struct; %define structs
%define measurement grid
[vec(1).X,vec(2).X,vec(3).X] = ndgrid(1:n1,1:n2,1:n3); figure;
for itrans = 1:3
vec(itrans).x = imgaussfilt3(rand(n1,n2,n3), sig);%make random smooth vector field
t = rand(1,3); %random translations
r = rand(1,3)*2*pi/50; %random pitch roll yaw
%seperate rotation matrices
R1 = [1, 0, 0;...
0, cos(r(1)), sin(r(1));...
0,-sin(r(1)), cos(r(1))];
R2 = [cos(r(2)), 0,-sin(r(2));...
0, 1, 0;...
sin(r(2)), 0, cos(r(2))];
R3 = [cos(r(3)), sin(r(3)), 0;...
-sin(r(3)), cos(r(3)), 0;...
0, 0, 1];
%make affine component matrices
T = eye(4); T(1,4) = t(1); T(2,4) = t(2); T(3,4) = t(3);%make translation matrix
R = R1*R2*R3; R(4,4) = 1;%combine rotations
S = eye(4); %no skew or scaling
%compose affine transformation
A(itrans).mat = T*R*S;
%apply transformation to coordinates and plot alignment
X = [vec(1).X(:)'; vec(2).X(:)'; vec(3).X(:)';ones(1,numel(vec(3).X))]; XX = A(itrans).mat*X;
subplot(1,3,itrans); scatter3(vec(1).X(:),vec(2).X(:),vec(3).X(:)); hold on; title('displacement measurement 3'); scatter3(XX(1,:)',XX(2,:)',XX(3,:)', 'r'); legend('original', 'displaced')
end
%apply tranformation to data
for itrans = 1:3
vec(itrans).xtrans = interp3(vec(itrans).x ,XX(1,:)',XX(2,:)',XX(3,:)','cubic',0);
end
%plot new vectors
figure; subplot(1,2,1); quiver3(vec(1).X,vec(2).X,vec(3).X,vec(1).x,vec(2).x,vec(3).x); title('original field')
subplot(1,2,2); quiver3(vec(1).X,vec(2).X,vec(3).X,vec(1).xtrans,vec(2).xtrans,vec(3).xtrans); legend('displaced field')
The three realigned coordinates, each component of the vector field has been translated and rotated slightly differently.
The original field, each component of one vector was not really measured at the same location, which I correct for by transforming the coordinates and then interpolating my measurements.
The transformed field, each component of one vector is no longer really along the axis it represents, and they are no longer orthogonal to each other.
Trying to show the problem in 2d with paint. Each arrow shows a measured component, each cross shows a coordinate system. The two blue arrows are the two components that I measure, the two gray arrows are my result after realigning my measurements, the two orange arrows are what I need after somehow 'unrotating' and combining them.
This isn't a very complete answer, and it probably isn't an answer that merits votes; but, at the time the answer is given, there aren't any others, so I had better post what I know.
If your problem were an academic problem, then it would be a graduate-level academic vector-algebra problem. Someone may already have prefabricated a neat formula for it; but, lacking such a formula, if I were in your place, before rotating, I might try to reset all measurements on a regular orthonormal coordinate grid. (I recently instructed a junior-level electromagnetics course in which I had the students do something vaguely similar, but simpler, to discretize Laplace's equation.) Before resetting, one would need to expand each orthonormal component of the continuous function in a multidimensional Taylor series, then fit the nearby measured values to the undetermined coefficients of the series.... In other words, the analysis before you might be painful.
Splines work on a related idea.
Your comment mentioned an affine analysis. Unfortunately, it is not obvious to me how that would help.
Fortunately, once you had done the analysis and coding, the associated calculations should be pretty quick for the computer to complete.
This answer doesn't answer anything, of course. Given 16 hours or so, I could probably do the analysis; then given another 40 hours or so, I could probably come up with some code—by which time I might discover where in the literature someone had already solved the problem in a neater, simpler way. Good luck.
I calculated the displacement for each sampling point D = X - XX and then calculated the rotation of that field with curl(). That showed a constant rotation for all locations and all directions. So probably I was wrong in assuming that I needed a different correction for each sampling point.
Then its probably possible to invert/transpose the rotation matrix and apply it to the measurement (instead of the coordinates of the measurement). Then add the three resulting components of the three measurements together would then give the corrected data.
I added that code to the loop and plotted the displacement and the curl:
n1=10; n2=10; n3=10; sig=5; vec = struct; A = struct; D = struct; C = struct; [vec(1).X,vec(2).X,vec(3).X] = ndgrid(1:n1,1:n2,1:n3); fig1 = figure; fig2 = figure; fig3 = figure;
for itrans = 1:3
t = rand(1,3); r = rand(1,3)*2*pi/50; vec(itrans).x = imgaussfilt3(rand(n1,n2,n3), sig);
R1 = [1, 0, 0; 0, cos(r(1)), sin(r(1)); 0,-sin(r(1)), cos(r(1))];
R2 = [cos(r(2)), 0,-sin(r(2)); 0, 1, 0; sin(r(2)), 0, cos(r(2))];
R3 = [cos(r(3)), sin(r(3)), 0; -sin(r(3)), cos(r(3)), 0; 0, 0, 1];
%make affine component matrices
T = eye(4); T(1,4) = t(1); T(2,4) = t(2); T(3,4) = t(3); R = R1*R2*R3; R(4,4) = 1;S = eye(4);
A(itrans).mat = T*R*S;
%apply transformation to coordinates and plot alignment
X = [vec(1).X(:)'; vec(2).X(:)'; vec(3).X(:)';ones(1,numel(vec(3).X))]; XX = A(itrans).mat*X;
dcenter = sqrt(sum((X(1:3,:)-5).^2)); dcenter = dcenter./max(dcenter(:));
figure(fig1); subplot(1,3,itrans); scatter3(vec(1).X(:),vec(2).X(:),vec(3).X(:)); hold on; title('displacement measurement 3'); scatter3(XX(1,:)',XX(2,:)',XX(3,:)', 'r'); legend('original', 'displaced')
%calculate and plot displacement due to transformation
D(itrans).d = X-XX;
figure(fig2); subplot(1,3,itrans); q = quiver3(vec(1).X(:)',vec(2).X(:)',vec(3).X(:)',D(itrans).d(1,:),D(itrans).d(2,:),D(itrans).d(3,:)); title(['displacement of each voxel measurement ' num2str(itrans)])
currentColormap = colormap(gca); [~, ~, ind] = histcounts(dcenter, size(currentColormap, 1)); cmap = uint8(ind2rgb(ind(:), currentColormap) * 255); cmap(:,:,4) = 255; cmap = permute(repmat(cmap, [1 3 1]), [2 1 3]); set(q.Head, 'ColorBinding', 'interpolated', 'ColorData', reshape(cmap(1:3,:,:), [], 4).'); set(q.Tail, 'ColorBinding', 'interpolated', 'ColorData', reshape(cmap(1:2,:,:), [], 4).');
%calculate and plot rotational part of displacement field
[C(itrans).x1,C(itrans).x2,C(itrans).x3,C(itrans).av] = curl(reshape(vec(1).X(:)', n1, n2, n3),reshape(vec(2).X(:)', n1, n2, n3),reshape(vec(3).X(:)', n1, n2, n3),reshape(D(itrans).d(1,:), n1, n2, n3),reshape(D(itrans).d(2,:), n1, n2, n3),reshape(D(itrans).d(3,:), n1, n2, n3));
figure(fig3); subplot(1,3,itrans); q = quiver3(vec(1).X(:)',vec(2).X(:)',vec(3).X(:)',C(itrans).x1(:)',C(itrans).x2(:)',C(itrans).x3(:)'); title(['curl of each voxel measurement ' num2str(itrans)])
currentColormap = colormap(gca); [~, ~, ind] = histcounts(dcenter, size(currentColormap, 1)); cmap = uint8(ind2rgb(ind(:), currentColormap) * 255); cmap(:,:,4) = 255; cmap = permute(repmat(cmap, [1 3 1]), [2 1 3]); set(q.Head, 'ColorBinding', 'interpolated', 'ColorData', reshape(cmap(1:3,:,:), [], 4).'); set(q.Tail, 'ColorBinding', 'interpolated', 'ColorData', reshape(cmap(1:2,:,:), [], 4).');
%rotation is constant, so not changed by translation or position of sample?
end
Displacement of vectors (color as a function of how close to the edge).
Rotational part of displacement.
Which is equal everywhere:
unique(C(1).x1(:))
ans =
0.1617
0.1617
0.1617
0.1617
So I think I can just apply R' to each measurement and then add them together as the corrected data.
rotmeas = zeros(3, numel(vec(1).x));
for itrans = 1:3
%apply transformation to coordinates and interpolate
vec(itrans).xtrans = interp3(vec(itrans).x(:) ,XX(1,:)',XX(2,:)',XX(3,:)','cubic',0);
%get inverse rotation
Rinv = A(itrans).mat(1:3,1:3)'
curmeas = vec(itrans).xtrans;
compmeas = zeros(3, numel(vec(1).x));
compmeas(itrans,:) = curmeas;
%apply inverse rotation to spread old component over new axes
rotmeas = rotmeas + Rinv*compmeas;
end
Then just reshape it to an array again.
for itrans = 1:3
vec(itrans).xcor = reshape(rotmeas(itrans,:), n1,n2,n3);
end
Now I think the three data sets are aligned and the vector components are orthogonal and aligned with the new coordinate system. Just not sure how to check that. It seems easy to mix up the different components of the measurements and the dimensions of the rotations.

data type to store precision more than double in matlab

I an working in matlab, recently I am doing research in image processing. this time I am implementing a research paper this paper, in which I am getting problem to store precision more than double. kindly check the equation 6 of that paper.
I am facing problem in following code..
img = imread('Einstein.bmp');
exponent = double(zeros(size(img,1),size(img,2)));
s = double(zeros(size(img,1),size(img,2)));
sigma=1;
for i=1:size(img,1)
for j=1:size(img,2)
exponent(i,j) = double(((i^2)+(j^2))/(2*(sigma^2)));
s(i,j) = double(exp(-exponent(i,j)));
end
end
After some values, s(i,j) gives 0 for all values but that values should not be 0. how can I avoid that problem?
Looking at your code, it seems you are trying to create a 2D Gaussian function centered at (0,0). The kernel width you are using is really small: sigma=1, seeing you are computing the function over the entire grid of an image pixels. So no wonder you are getting all zeros except for a tiny portion in the top-left corner of the matrix S.
Here is a more efficient way to create the kernel centered around the middle:
% 100x100 grid
r = 100; c = 100;
[x,y] = ndgrid(1:r,1:c);
% sigma = 20
sigma = max([r c])/5;
% 2D gaussian function
z = sum(bsxfun(#minus, [x(:) y(:)], [r c]./2).^2, 2);
z = exp(-z ./ (2*sigma^2)) ./ (sigma^2 * 2*pi);
z = reshape(z, [r c]);
If you have access to the Statistics toolbox, use mvnpdf to it write as:
z = mvnpdf([x(:) y(:)], [r c]./2, eye(2)*sigma^2);
z = reshape(z, [r c]);
or if you have the Image processing toolbox, simply use fspecial:
z = fspecial('gaussian', [r c], sigma);
The result:
imshow(z, 'DisplayRange',[], 'InitialMag','fit')
axis on, colorbar
Note that the matrix is normalized so that the integral is one (hence the small values). That's why I turned off the default [0,1] range when displaying as an image.
You could also simply map it into [0,1] range using: z = (z - min(z(:))) ./ range(z(:))
Here is the resulting matrix when viewed as a surface:
surf(x,y,z)
axis vis3d
It appears that matlab only uses 64-bit doubles, but you might have luck with one of the arbitrary precision libraries available for matlab.

Set surf minimum for matlab

I have a function which takes a voxel representation of a 3D landscape and can plot a X-Y section to show the middle of the landscape. The voxel representation is stored in a 3 dimensional matrix with a number that represents something important. Obviously the matrix is
1,1,1
2,2,2
in terms of accessing the elements but the actual 3D locations are found in the following method:
(index-1)*resolution+0.5*resolution+minPos;
where resolution is the grid size :
resolution
<-->
__ __ __
|__|__|__|
<- Min pos
and minPos is where the grid starts.
Now in terms of the actual question, i would like to extract a single X-Y section of this voxel representation and display it as a surf. This can be done by just doing this:
surf(voxel(:, :, section))
however then you get this:
The obvious problem is that the grid will start at 0 because that is how the matrix representation is. How can i set the minimum and cell size for surf, ie so that the grid will start at the minimum (shown above) and will have the grid spacing of resolution (shown above).
Read the documentation of surf, you can also provide x and y coordinates corresponding to your data points.
surf(X,Y,Z)
X and Y can be either vectors or matrices:
surf(X,Y,Z) uses Z for the color data and surface height. X and Y are vectors or matrices defining the x and y components of a surface. If X and Y are vectors, length(X) = n and length(Y) = m, where [m,n] = size(Z). In this case, the vertices of the surface faces are (X(j), Y(i), Z(i,j)) triples. To create X and Y matrices for arbitrary domains, use the meshgrid function
Example
Z=[ 0 1 2 3;
7 6 5 4;
8 9 10 11];
x=[-1 0 1 2];
y=[-2 0 2];
surf(x,y,Z);
Of course you have to match Z, x and y matrices/vectors as clearly described in the doc^^
Just remember that elements in columns of Z are surf'ed as values along the y-axis, elements in rows of Z are surf'ed as values along the x-axis. This is clearly to be seen in the example picture.
Solution
I think you switched the x and y-axis around, which you can fix by just transposing z:
s = size(voxel);
xi = (minPosX:resolution:(minPosX+resolution*s(1)-1));
yi = (minPosY:resolution:(minPosY+resolution*s(2)-1));
z = (voxel(:,:,section));
surf(xi, yi, z');
or that you're picking the wrong numbers for constructing xi and yi and it should be this instead:
xi = (minPosX:resolution:(minPosX+resolution*s(2)-1));
yi = (minPosY:resolution:(minPosY+resolution*s(1)-1));
z = (voxel(:,:,section));
surf(xi, yi, z);
So it was easy enough to do:
lets say we have a 3D matrix "voxel";
s = size(voxel);
xi = (minPosX:resolution:(minPosX+resolution*s(1)-1));
yi = (minPosY:resolution:(minPosY+resolution*s(2)-1));
z = (voxel(:,:,section));
[x y] = meshgrid(xi, yi);
x = x';
y = y';
surf(x, y, z);
Provides the following plot:
This is rotated which is annoying, I cant seem to get it to rotate back (I could just visualise around the other way but that's ok)