2D Body Transformation and Rotation in Matlab - matlab

I defined the following rectangle in Matlab:
A = [-4,-4,4,4,-4;-2,2,2,-2,-2;]
And I defined a transformation matrix (Special Euclidean (2)) like this:
function T = se2(x, y, theta)
T = [cosd(theta), -sind(theta), x;
sind(theta), cosd(theta), y;
0, 0, 1];
Now, I want to rotate my shape by 45 degrees counter-clockwise about its center and move it with respect to the new coordinate frame by 2 unit in the new y direction.
First problem is: when doing like the following:...
B = se2(0,2,45)*[A;1 1 1 1 1]
...it will correctly rotate but incorrectly move my shape.
Here is my rectangle(blue), incorrect transformation(red) and correct transformation(green):
Second problem is: Suppose I translated the shape by 6 direction in the y direction. I want to just rotate the rectangle by -30 degrees about it's new center but doing as I've shown, yields rotation about the former center.
How can I get around these problems in Matlab? Is there a predefined function in doing these tasks?
My code for plotting the shapes:
A =
-4 -4 4 4 -4
-2 2 2 -2 -2
plot(A(1,:),A(2,:),'blue')

Regarding your 1st problem:
Essentially you want to do a translation of A to it's centroid before you do the rotation. This is because the rotation assumes that you're rotating about the origin. Therefore you need to "centre" it about the point you plan to rotate it about before you rotate it. Then you need to translate it back after you finish the rotation. Refer to this reference for details.
% Define A
A = [-2,-2,6,6,-2; -2,2,2,-2,-2; 1 1 1 1 1];
% Define Translation Matrix
trans = #(x,y,z) repmat([x; y; z],[1 5]);
% Define Rotation Matrix
se2 = #(x, y, theta) [
cosd(theta), -sind(theta), x;
sind(theta), cosd(theta), y;
0, 0, 1];
% Calculate Rotated Rect
B = se2(0,0,45) * (A - trans(2,0,0) ) + trans(2,0,0);
% Plot Rectangles
figure; plot(A(1,:),A(2,:),'b')
hold on;
plot(B(1,:),B(2,:),'r')
hold off;
axis equal
The function trans will translate it before the rotation.
Result:
>> A
A =
-2 -2 6 6 -2
-2 2 2 -2 -2
1 1 1 1 1
>> B
B =
0.5858 -2.2426 3.4142 6.2426 0.5858
-4.2426 -1.4142 4.2426 1.4142 -4.2426
1.0000 1.0000 1.0000 1.0000 1.0000
Here is the A/B if rotated at the centre.
Here is the A/B with the offset.
Regarding your 2nd problem:
Same solution as the first problem except you use the new centroid and -30 deg instead for the parameters of B.

Related

Draw a circle inside a convex hull with its centre at the origin

I am trying to draw a circle inside a convex hull with its center at the origin. I have attached the code generating the points and the convex hull. In what way can I generate the incircle with its center at the origin.
A=[0 0;0 -5; 5 -5; 5 5;0 5;-2 2;-5 -5;5 8;-5 -8;-8 0;8 0]
x_axis=A(:,1)
y_axis=A(:,2)
k=convhull(x_axis,y_axis);
figure;
plot(x_axis(k),y_axis(k),'r-',x_axis,y_axis,'b*');
Trying to get a circle inscribed like this. With its center at the origin. ( drawing edited in paint, not matlab)
Find the min distance from center(origin) to all edges and plot the circle.
clear all;
A=[0 0;0 -5; 5 -5; 5 5;0 5;-2 2;-5 -5;5 8;-5 -8;-8 0;8 0]
x_axis=A(:,1)
y_axis=A(:,2)
k=convhull(x_axis,y_axis);
figure;
hold on
plot(x_axis(k),y_axis(k),'r-',x_axis,y_axis,'b*');
Centre = [0 0 0];
ConvexHull = [x_axis(k) y_axis(k) zeros(size(k))];
radius = Inf;
for i = 1:size(k)-1
radius = min(radius,Distance(Centre, ConvexHull(i,:), ConvexHull(i+1,:)));
end
radius = min(radius,Distance(Centre, ConvexHull(1,:), ConvexHull(size(k),:)));
viscircles([0 0],radius);
function d = Distance(pt, v1, v2)
a = v1 - v2;
b = pt - v2;
d = norm(cross(a,b)) / norm(a);
end
Add axis equal if the axes don't have same scale.
viscircles

Matlab generate smooth curve between scatter points

I need to generate a curve between scatter points then identify the unit normal of the curve at each point. Here is an example of a point cloud
figure
x = [1 2 0 0 1 1 2 3 4 2];
y = [4 6 9 1 1 2 4 9 2 3];
scatter(x,y)
hold on
line(x,y)
xlim([0 4])
ylim([0 10])
NOTE: the 2 points along the y-axis are connected
Instead of a line between the points, I'd like to create a smooth curve. I'm not sure how to do this when points in x and y repeat. An attempt using spline fails. After I know the curve, I need to find the unit normals at each point. How do I go about this?
EDIT:
Basically I want to do what is show here for polyfit in the matlab docs. Assuming that x was unique in my case, this wouldn't be an issue. I could identify the polynomial and then, I believe, determine the unit normals from the polynomial function evaluated at that point. But in my case, the x and y data repeat so a straight forward application doesn't work.
One way to get a smooth path is to treat this as a parametric function and interpolate x and y separately.
x = [1 2 0 0 1 1 2 3 4 2];
y = [4 6 9 1 1 2 4 9 2 3];
t = 1:numel(x);
tq = 1:0.1:t(end);
xq = interp1(t,x,tq,'v5cubic');
yq = interp1(t,y,tq,'v5cubic');
plot(x,y,' ob',xq,yq,'-r');
To estimate the normals you can take the average normal of the two line segments around the sample points. This code is a bit ugly but it gets the job done.
n = zeros(2,numel(x));
for tidx = 1:numel(t)
tt = t(tidx);
idx1 = find(tq <= tt,1,'last');
idx0 = idx1 - 1;
idx2 = idx1 + 1;
if idx0 > 0
n1 = [yq(idx1) - yq(idx0); xq(idx0) - xq(idx1)];
n(:,tidx) = n(:,tidx) + n1/norm(n1);
end
if idx2 <= numel(tq)
n2 = [yq(idx2) - yq(idx1); xq(idx1) - xq(idx2)];
n(:,tidx) = n(:,tidx) + n2/norm(n2);
end
n(:,tidx) = n(:,tidx) / norm(n(:,tidx));
end
plot(x,y,' ob',xq,yq,'-r',[x.' x.'+n(1,:).'].', [y.' y.'+n(2,:).'].',' -k');
axis equal;
If you use pchip instead of v5cubic for the interpolation method then you get more symmetry around the sample points. However, it appears that any sharp turns (90 degrees or greater) are not smoothed.

how to plot x,y,z in matlab?

I am making the Gauss-Jordan method in matlab and I want to plot these equations
x + y + 4*z = -1
-2*x – y + z= -5
3*x-2*y+3*z=-4
To see in what point of the graph they intersect, but I do not know how to plot in matlab
Is this what you are looking for?
clc
clear
close all
%// Generate x and y values to plot from.
[x,y] = meshgrid(linspace(0,10,100),linspace(0,10,100));
%// Get equation for plane; i.e. z position
z1 = 0.25.*(-1-x-y);
z2 = -5+y+2*x;
z3 = (-4-3.*x+2.*y)./3;
%// Use surf to generate surface plots
figure;
surf(x,y,z1,'linestyle','none','facealpha',0.4)
hold on
surf(x,y,z2,'linestyle','none','facealpha',0.4)
surf(x,y,z3,'linestyle','none','facealpha',0.4)
hold off
%// Use to manually rotate the plot
rotate3d on
Which gives this:
You can play around with the 'FaceAlpha' property of course to make things clearer. Have a look at the surf function for more options.
EDIT:
Alternatively to #rayryeng solution to solve for x,y and z you can use mldivide:
A = [1 1 4;-2 -1 1;3 -2 3];
B = [-1;-5;-4];
X = mldivide(A,B)
X =
1.0000
2.0000
-1.0000
Here is how i would plot those planes.
The 4-th argument of surf lets you specify the color.
% // create function handles of form z = f(x,y)
f1 = #(X,Y) 1/4*(-1 - X -Y);
f2 = #(X,Y) -5 + 2*X + Y;
f3 = #(X,Y) 1/3*(-4 -3*X + 2*Y);
% // create a 2d-grid to plot the functions over
x = linspace(-5, 5, 10);
y = linspace(-10, 10, 20);
[X,Y] = meshgrid(x,y);
% // plot the planes in different colors (4th argument) and without edges
figure
surf(X, Y, f1(X,Y), ones(size(f1(X,Y))), 'EdgeColor', 'None');
hold on
surf(X, Y, f2(X,Y), ones(size(f2(X,Y)))*2, 'EdgeColor', 'None');
surf(X, Y, f3(X,Y), ones(size(f3(X,Y)))*3, 'EdgeColor', 'None');
legend('plane1', 'plane2', 'plane3')
xlabel('x'), ylabel('y'), zlabel('z')
Though this is not plotting, perhaps this is also something you can use. If you want to determine the simultaneous solution to those equations, consider using solve
syms x y z
A = solve('x + y + 4*z == -1', '-2*x - y + z == -5', '3*x - 2*y + 3*z == -4')
disp([A.x A.y A.z]);
[ 1, 2, -1]
This will check to see whether your Gauss-Jordan elimination is correct. If you don't like using solve, you can use linear algebra to help you solve this for you. Simply place your coefficients for your system in a matrix and vector, then find the inverse of the matrix and multiply by the vector.
A = [1 1 4; -2 -1 1; 3 -2 3];
b = [-1;-5;-4];
x = A \ b
x =
1.0000
2.0000
-1.0000
... and even another method is to use rref to reduce your system into row-reduced echelon form. This would be the result after you successfully apply Gauss-Jordan elimination to your linear system. As such:
A = [1 1 4; -2 -1 1; 3 -2 3];
b = [-1;-5;-4];
rref([A b])
ans =
1 0 0 1
0 1 0 2
0 0 1 -1
Reading the above answer, x = 1, y = 2, z = -1. This represents the augmented system where the first 3 columns denote the coefficients of your system and the fourth column denotes the right hand side of your linear system of equations.

Homographic image transformation distortion issue

I am trying to transform an image using a 3D transformation matrix and assuming my camera is orthonormal.
I am defining my homography using the plane-induced homography formula H=R-t*n'/d (with d=Inf so H=R) as given in Hartley and Zisserman Chapter 13.
What I am confused about is when I use a rather modest rotation, the image seems to be distorting much more than I expect (I'm sure I'm not confounding radians and degrees).
What could be going wrong here?
I've attached my code and example output.
n = [0;0;-1];
d = Inf;
im = imread('cameraman.tif');
rotations = [0 0.01 0.1 1 10];
for ind = 1:length(rotations)
theta = rotations(ind)*pi/180;
R = [ 1 0 0 ;
0 cos(theta) -sin(theta);
0 sin(theta) cos(theta)];
t = [0;0;0];
H = R-t*n'/d;
tform = maketform('projective',H');
imT = imtransform(im,tform);
subplot(1,5,ind) ;
imshow(imT)
title(['Rot=' num2str(rotations(ind)) 'deg']);
axis square
end
The formula H = R-t*n'/d has one assumption which is not met in your case:
This formula implies that you are using pinhole camera model with focal length=1
But in your case, for your camera to be more real and for your code to work, you should set the focal length to some positive number much greater than 1. (focal length is the distance from your camera center to the image plane)
To do this you can define a calibration matrix K which handles the focal length. You just need to change your formula to
H=K R inv(K) - 1/d K t n' inv(K)
in which K is a 3-by-3 identity matrix whose two first elements along the diagonal are set to the focal length (e.g. f=300). The formula can be easily derived if you assume a projective camera.
Below is the corrected version of your code, in which the angles make sense.
n = [0;0;-1];
d = Inf;
im = imread('cameraman.tif');
rotations = [0 0.01 0.1 30 60];
for ind = 1:length(rotations)
theta = rotations(ind)*pi/180;
R = [ 1 0 0 ;
0 cos(theta) -sin(theta);
0 sin(theta) cos(theta)];
t = [0;0;0];
K=[300 0 0;
0 300 0;
0 0 1];
H=K*R/K-1/d*K*t*n'/K;
tform = maketform('projective',H');
imT = imtransform(im,tform);
subplot(1,5,ind) ;
imshow(imT)
title(['Rot=' num2str(rotations(ind)) 'deg']);
axis square
end
You can see the result in the image below:
You can also rotate the image around its center. For it to happen you should set the image plane origin to the center of the image which I think is not possible with that method of matlab (maketform).
You can use the method below instead.
imT=imagehomog(im,H','c');
Note that if you use this method, you'll have to change some settings in n, d, t and R to get the appropriate result.
That method can be found at: https://github.com/covarep/covarep/blob/master/external/voicebox/imagehomog.m
The result of the program with imagehomog and some changes in n, d, t , and R is shown below which seems more real.
New settings are:
n = [0 0 1]';
d = 2;
t = [1 0 0]';
R = [cos(theta), 0, sin(theta);
0, 1, 0;
-sin(theta), 0, cos(theta)];
Hmm... I'm not 100% percent on this stuff, but it was an interesting question and relevant to my work, so I thought I'd play around and give it a shot.
EDIT: I tried this once using no built-ins. That was my original answer. Then I realized that you could do it your way pretty easily:
The easy answer to your question is to use the correct rotation matrix about the z-axis:
R = [cos(theta) -sin(theta) 0;
sin(theta) cos(theta) 0;
0 0 1];
Here's another way to do it (my original answer):
I'm going to share what I did; hopefully this is useful to you. I only did it in 2D (though that should be easy to expand to 3D). Note that if you want to rotate the image in plane, you will need to use a different rotation matrix that you have currently coded. You need to rotate about the Z-axis.
I did not use those matlab built-ins.
I referred to http://en.wikipedia.org/wiki/Rotation_matrix for some info.
im = double(imread('cameraman.tif')); % must be double for interpn
[x y] = ndgrid(1:size(im,1), 1:size(im,2));
rotation = 10;
theta = rotation*pi/180;
% calculate rotation matrix
R = [ cos(theta) -sin(theta);
sin(theta) cos(theta)]; % just 2D case
% calculate new positions of image indicies
tmp = R*[x(:)' ; y(:)']; % 2 by numel(im)
xi = reshape(tmp(1,:),size(x)); % new x-indicies
yi = reshape(tmp(2,:),size(y)); % new y-indicies
imrot = interpn(x,y,im,xi,yi); % interpolate from old->new indicies
imagesc(imrot);
My own question now is: "How do you change the origin about which you are rotating the image? Clearly, I'm rotating about (0,0), the top left corner.
EDIT 2 In response to the asker's comment, I've tried again.
This time I fixed a couple of things. Now I'm using the same transformation matrix (about x) as in the original question.
I rotated about the center of the image by redoing the way i do the ndgrids (put 0,0,0) in the center of the image. I also decided to show 3 planes of the image. This was not in the original question. The middle plane is the plane of interest. To get just the middle plane, you can leave out the zero-padding and redefine the 3rd ndgrid option to be just 1 instead of -1:1.
im = double(imread('cameraman.tif')); % must be double for interpn
im = padarray(im, [0 0 1],'both');
[x y z] = ndgrid(-floor(size(im,1)/2):floor(size(im,1)/2)-1, ...
-floor(size(im,2)/2):floor(size(im,2)/2)-1,...
-1:1);
rotation = 1;
theta = rotation*pi/180;
% calculate rotation matrix
R = [ 1 0 0 ;
0 cos(theta) -sin(theta);
0 sin(theta) cos(theta)];
% calculate new positions of image indicies
tmp = R*[x(:)'; y(:)'; z(:)']; % 2 by numel(im)
xi = reshape(tmp(1,:),size(x)); % new x-indicies
yi = reshape(tmp(2,:),size(y)); % new y-indicies
zi = reshape(tmp(3,:),size(z));
imrot = interpn(x,y,z,im,xi,yi,zi); % interpolate from old->new indicies
figure;
subplot(3,1,1);imagesc(imrot(:,:,1)); axis image; axis off;
subplot(3,1,2);imagesc(imrot(:,:,2)); axis image; axis off;
subplot(3,1,3);imagesc(imrot(:,:,3)); axis image; axis off;
You are performing rotations around the x-axis: in your matrix, the 1st component (x) is left unchanged by the rotation matrix. This is confirmed by the perspective deformations from your examples.
The actual amount of deformation will then depend on the distance between the camera and the image plane (or more accurately on its value relative to the focal length of the camera). It can be important when the cameraman image plane is located near the camera.

3d grayscale volume projection onto 2D plane

I have a 3-D grayscale volume corresponding to ultrasound data. In Matlab this 3-D volume is simply a 3-D matrix of MxNxP. The structure I'm interested in is not oriented along the z axis, but along a local coordinate system already known (x'y'z'). What I have up to this point is something like the figure shown below, depicting the original (xyz) and the local coordinate systems (x'y'z'):
I want to obtain the 2-D projection of this volume (i.e. an image) through a specific plane on the local coordinate system, say at z' = z0. How can I do this?
If the volume was oriented along the z axis this projection could be readily achieved. i.e. if the volume, in Matlab, is V, then:
projection = sum(V,3);
thus, the projection can be computed just as the sum along the 3rd dimension of the array. However with a change of orientation the problem becomes more complicated.
I've been looking at radon transform (2D, that applies only to 2-D images and not volumes) and also been considering ortographic projections, but at this point I'm clueless as to what to do!
Thanks for any advice!
New attempt at solution:
Following the tutorial http://blogs.mathworks.com/steve/2006/08/17/spatial-transformations-three-dimensional-rotation/ and making some small changes, I might have something which could help you. Bear in mind, I have little or no experience with volumetric data in MATLAB, so the implementation is quite hacky.
In the below code I use tformarray() to rotate the structure in space. First, the data is centered, then rotated using rotationmat3D to produce the spacial transformation, before the data is moved back to its original position.
As I have never used tformarray before, I handeled datapoints falling outside the defined region after rotation by simply padding the data matrix (NxMxP) with zeros all around. If anyone know a better way, please let us know :)
The code:
%Synthetic dataset, 25x50x25
blob = flow();
%Pad to allow for rotations in space. Bad solution,
%something better might be possible to better understanding
%of tformarray()
blob = padarray(blob,size(blob));
f1 = figure(1);clf;
s1=subplot(1,2,1);
p = patch(isosurface(blob,1));
set(p, 'FaceColor', 'red', 'EdgeColor', 'none');
daspect([1 1 1]);
view([1 1 1])
camlight
lighting gouraud
%Calculate center
blob_center = (size(blob) + 1) / 2;
%Translate to origin transformation
T1 = [1 0 0 0
0 1 0 0
0 0 1 0
-blob_center 1];
%Rotation around [0 0 1]
rot = -pi/3;
Rot = rotationmat3D(rot,[0 1 1]);
T2 = [ 1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1];
T2(1:3,1:3) = Rot;
%Translation back
T3 = [1 0 0 0
0 1 0 0
0 0 1 0
blob_center 1];
%Total transform
T = T1 * T2 * T3;
%See http://blogs.mathworks.com/steve/2006/08/17/spatial-transformations-three-dimensional-rotation/
tform = maketform('affine', T);
R = makeresampler('linear', 'fill');
TDIMS_A = [1 2 3];
TDIMS_B = [1 2 3];
TSIZE_B = size(blob);
TMAP_B = [];
F = 0;
blob2 = ...
tformarray(blob, tform, R, TDIMS_A, TDIMS_B, TSIZE_B, TMAP_B, F);
s2=subplot(1,2,2);
p2 = patch(isosurface(blob2,1));
set(p2, 'FaceColor', 'red', 'EdgeColor', 'none');
daspect([1 1 1]);
view([1 1 1])
camlight
lighting gouraud
The arbitrary visualization below is just to confirm that the data is rotated as expected, plotting a closed surface when the data passed the value '1'. With blob2, you should know be able to project by using simple sums.
figure(2)
subplot(1,2,1);imagesc(sum(blob,3));
subplot(1,2,2);imagesc(sum(blob2,3));
Assuming you have access to the coordinate basis R=[x' y' z'], and that those vectors are orthonormal, you can simply extract the representation in this basis by multiplying your data with the the 3x3 matrix R, where x',y',z' are column vectors.
With the data stored in D (Nx3), you can get the representation with R, by multiplying by it:
Dmarked = D*R;
and now D = Dmarked*inv(R), so going back and forth is stragihtforward.
The following code might provide help to see the transformation. Here I create a synthetic dataset, rotate it, and then rotate it back. Doing sum(DR(:,3)) would then be your sum along z'
%#Create synthetic dataset
N1 = 250;
r1 = 1;
dr1 = 0.1;
dz1 = 0;
mu1 = [0;0];
Sigma1 = eye(2);
theta1 = 0 + (2*pi).*rand(N1,1);
rRand1 = normrnd(r1,dr1,1,N1);
rZ1 = rand(N1,1)*dz1+1;
D = [([rZ1*0 rZ1*0] + repmat(rRand1',1,2)).*[sin(theta1) cos(theta1)] rZ1];
%Create roation matrix
rot = pi/8;
R = rotationmat3D(rot,[0 1 0]);
% R = 0.9239 0 0.3827
% 0 1.0000 0
% -0.3827 0 0.9239
Rinv = inv(R);
%Rotate data
DR = D*R;
%#Visaulize data
f1 = figure(1);clf
subplot(1,3,1);
plot3(DR(:,1),DR(:,2),DR(:,3),'.');title('Your data')
subplot(1,3,2);
plot3(DR*Rinv(:,1),DR*Rinv(:,2),DR*Rinv(:,3),'.r');
view([0.5 0.5 0.2]);title('Representation using your [xmarked ymarked zmarked]');
subplot(1,3,3);
plot3(D(:,1),D(:,2),D(:,3),'.');
view([0.5 0.5 0.2]);title('Original data before rotation');
If you have two normalized 3x1 vectors x2 and y2 corresponding to your local coordinate system (x' and y').
Then, for a position P, its local coordinate will be xP=P'x2 and yP=P'*y2.
So you can try to project your volume using accumarray:
[x y z]=ndgrid(1:M,1:N,1:P);
posP=[x(:) y(:) z(:)];
xP=round(posP*x2);
yP=round(posP*y2);
xP=xP+min(xP(:))+1;
yP=yP+min(yP(:))+1;
V2=accumarray([xP(:),yP(:)],V(:));
If you provide your data, I will test it.