rotating a vector in matlab and check that angle - matlab

I would like rotate a vector in MATLAB and right after check the angle between the original and the rotated one:
v = [-1 -12 5]; %arbitrarily rotated vector
theta =30; %arbitrary angle to rotate with
R = [cosd(theta) -sind(theta) 0; sind(theta) cosd(theta) 0; 0 0 1]; %rotate around Z axis
vR = R*v'; %calculate the new vector
angle=atan2d(norm(cross(v,vR)),dot(v,vR));
%the angle between the old and rotated vector, also do normalisation before.
%atan2d is better to resolve extremely small angle
angle =
27.6588
%THIS is the problem
As you can see I rotated with 30° but when checking back it's different.

You are not actually calculating the same angle. Consider the situation where your input vector is v = [0, 0, 1] (i.e., a vertical line). If you rotate the vertical line about the z-axis by 30 deg then you just get the same vertical line again, so vR = [0, 0, 1]. The angle between v and vR would be 0 based on your analysis because you are calculating the actual angle between two vectors that intersect somewhere.
So, if you want to calculate the angle between two vectors then I believe your code is correct. But, if you want to calculate the amount of rotation in a specific frame (i.e., the z-axis), then you'll have to project v and vR onto the x-y plane first before using your formula:
v = [-1 -12 5]; %arbitrarily rotated vector
theta =30; %arbitrary angle to rotate with
R = [cosd(theta) -sind(theta) 0; sind(theta) cosd(theta) 0; 0 0 1]; %rotate around Z axis
vR = R*v'; %calculate the new vector
angle=atan2d(norm(cross(v,vR)),dot(v,vR)); %calculate the angle between the vectors
% copy over the vectors and remove the z-component to project onto the x-y
% plane
v_xy = v;
v_xy(3) = 0;
vR_xy = vR;
vR_xy(3) = 0;
angle_xy=atan2d(norm(cross(v_xy,vR_xy)),dot(v_xy,vR_xy)); %calculate the angle between the vectors in the xy-plane
Edit: Note you still won't be able to get the angle for the vertical line case (there is a singularity in the solution there). Also, my suggestion works only for the case of a z-axis rotation. To do this for any general rotation axis just requires a bit more math:
Say you have an axis defined by unit vector a, then rotate vector v about axis a to get the new vector vR. Project v and vR onto the plane for which a is normal and you get vectors p and pR:
p = v - dot(v,a)*a;
pR = vR - dot(vR,a)*a;
Then you find the angle between these projected vectors based on your formula:
angle = atan2d(norm(cross(p,pR)),dot(p,pR));

Related

Rotating a matrix by different angles in 2d in matlab

I have a set of data points, and I would like to rotate each data counterclockwise in the plane by a random angle about different points in the same plane. In first try, I could rotate them counterclockwise in the plane by a certain angle about different points in the same plane:
x = 16:25;
y = 31:40;
% create a matrix of these points, which will be useful in future calculations
v = [x;y];
center = [6:15;1:10];
% define a 60 degree counter-clockwise rotation matrix
theta = pi/3; % pi/3 radians = 60 degrees
R = [cos(theta) -sin(theta); sin(theta) cos(theta)];
% do the rotation...
vo = R*(v - center) + center;
% pick out the vectors of rotated x- and y-data
x_rotated = vo(1,:);
y_rotated = vo(2,:);
% make a plot
plot(x, y, 'k-', x_rotated, y_rotated, 'r-');
Then I tried to generalize it to rotate by random angels, but there is a problem which I can not solve in second code:
x = 16:25;
y = 31:40;
% create a matrix of these points, which will be useful in future calculations
v = [x;y];
center = [6:15;1:10]; %center of rotation
% define random degree counter-clockwise rotation matrix
theta = pi/3*(rand(10,1)-0.5); % prandom angle
R = [cos(theta) -sin(theta); sin(theta) cos(theta)];
% do the rotation...
vo = R*(v - center) + center;
% pick out the vectors of rotated x- and y-data
x_rotated = vo(1,:);
y_rotated = vo(2,:);
% make a plot
plot(x, y, 'k-', x_rotated, y_rotated, 'r-');
The problem is, when I try to rotate the matrix, the rotation matrix dimension is not equal as it should. I don't know how should I create the rotation matrix in this case.
Could anyone suggest how to solve the problem? Any answer is highly appreciated.
Your problem is that you are creating a 20x2 matrix in R. To see why, consider
theta % is a 10x1 vector
cos(theta) % is also going to be a 10x1 vector
[cos(theta) -sin(theta);...
sin(theta) cos(theta)]; % is going to be a 2x2 matrix of 10x1 vectors, or a 20x2 matrix
What you want is to have access to every 2x2 rotation matrix. One way of doing it is
R1 = [cos(theta) -sin(theta)] % Create a 10x2 matrix
R2 = [sin(theta) cos(theta)] % Create a 10x2 matrix
R = cat(3,R1,R2) % Cocatenate ("paste") both matrix along the 3 dimension creating a 10x2x2 matrix
R = permute(R,[3,2,1]) % Shift dimensions so the matrix shape is 2x2x10, this will be helpful in the next step.
Now you need to multiply each data point by its corresponding rotation matrix. Multiplication is only defined for 2D matrices, so I don't know a better method to do this than to loop over each point.
for i = 1:length(v)
vo(:,i) = R(:,:,i)*(v(:,i) - center(:,i)) + center(:,i);
end
Why don't you simply rotate with imrotate.
For example, you want to rotate 30 degrees:
newmat = imrotate(mat, 30, 'crop')
will rotate 30 degrees clockwise and keep the dimension same. To increase the size you can use 'full' option in imresize
To input a random value in the rotation matrix
rn = rand*90; %0-90 degrees
newmat = imrotate(mat, rn, 'crop')

MATLAB - Rotating an equilateral triangle around its centre point

I am new to MATLAB, but have worked with javascript and other programming languages.
I am writing a MATLAB program that will generate an equilateral triangle given the side length, an x coordinate, a y coordinate and an angle of rotation. It is working as intended except for the rotations.
I am using a rotation matrix to rotate the triangle. This works, except it rotates around the origin instead of rotating on the spot. (see below example).
90 degree Rotations Example
In order to rotate it on the spot I think I need to calculate the centre of the triangle and then rotate around that(somehow). I am not sure how to do this or if there is an easier/better way to do this. I did see there is a rotate function, but from what I have seen, it is for spherical space, not Cartesian planes.
Code is below, sorry for the mess:
function [ side, coord1,coord2 ] = equilateral(side, x,y, rotation)
%EQUILATERAL- given a side length and x,y, coordinates as inputs, the
%function plots an equilateral triangle an angle of rotation can be
%given as an input as well. This will rotate the trianlge around the x
%and y coordinates given.
%rotation argument is not required. If not given, angle is 0
if(exist('rotation','var'))
angle = rotation;
else
angle = 0;
end
%rotation matrix
R = [cos(angle), -sin(angle); sin(angle), cos(angle)];
%Make the axis equal so the triangles look equilateral
axis equal;
%max horizontal x coordinate
x2 = x + side;
%max horiontal y coordinate (equal to original y coordinate)
y2 = y;
%height of the triangle at midpoint (perpendicular height)
h = side*sin(pi/3) + y;
%coordinates of midpoint/top vertice
mid = [x2-(0.5*side), h];
%min coordinates
coord1 = [x,y];
%max coordinates
coord2 = [x2,y2];
if (angle > 0)
coord1 = coord1*R;
coord2 = coord2*R;
mid = mid*R;
end
%plot the base of the triangle
plot(linspace(coord1(1),coord2(1)), linspace(coord1(2),coord2(2)));
hold on
%plot the first side from inital coords to midpoint
plot(linspace(coord1(1),mid(1)), linspace(coord1(2),mid(2)));
%plot second side from mid point to max coords
plot(linspace(mid(1),coord2(1)), linspace(mid(2),coord2(2)));
end
I am open to any suggestions for improvements to the code/help to clean it up as well as help with the rotation issues. Thanks for the help.
The rotation matrix
[cos(theta), -sin(theta)
sin(theta), cos(theta)]
rotates a set of points of an angle theta about the origin of the axes.
In order to rotate a point around a given point, you have to:
-shift your points so that the rotatioin point concides with the origin of the axes
- rotate the point using the rotation matrix
- shift back your points
The following code is a modified version of your function in which the proposed approach has been implemented.
In the code I've set the rotation point as the centre of gravity
XR=x+side/2
YR=y+h*.3
nevertheless, you can compute them in a different way.
function [ side, coord1,coord2 ] = equilateral(side, x,y, rotation)
%EQUILATERAL- given a side length and x,y, coordinates as inputs, the
%function plots an equilateral triangle an angle of rotation can be
%given as an input as well. This will rotate the trianlge around the x
%and y coordinates given.
%rotation argument is not required. If not given, angle is 0
if(exist('rotation','var'))
% angle = rotation;
% Convert the angle from deg to rad
angle = rotation*pi/180;
else
angle = 0;
end
%rotation matrix
R = [cos(angle), -sin(angle); sin(angle), cos(angle)];
%Make the axis equal so the triangles look equilateral
axis equal;
%max horizontal x coordinate
x2 = x + side;
%max horiontal y coordinate (equal to original y coordinate)
y2 = y;
%height of the triangle at midpoint (perpendicular height)
h = side*sin(pi/3) + y;
%coordinates of midpoint/top vertice
mid = [x2-(0.5*side), h];
%min coordinates
coord1 = [x,y];
%max coordinates
coord2 = [x2,y2];
plot([coord1(1) coord2(1) mid(1) coord1(1)],[coord1(2) coord2(2) mid(2) coord1(2)],'r')
hold on
%
% Define the coord of the point aroud with to turn
%
XR=x+side/2
YR=y+h*.3
plot(XR,YR,'o','markerfacecolor','k','markeredgecolor','k')
if (angle > 0)
% coord1 = coord1*R;
% coord2 = coord2*R;
% mid = mid*R;
% Shift the triangle so that the rotation point coincides with the origin
% of the axes
r_coord1 = (coord1-[XR YR])*R+[XR YR];
r_coord2 = (coord2-[XR YR])*R+[XR YR];
r_mid = (mid-[XR YR])*R+[XR YR];
%
% Plot the rotated triangle
plot([r_coord1(1) r_coord2(1) r_mid(1) r_coord1(1)],[r_coord1(2) r_coord2(2) r_mid(2) r_coord1(2)],'r')
end
% % %
% % % %plot the base of the triangle
% % % plot(linspace(coord1(1),coord2(1)), linspace(coord1(2),coord2(2)));
% % % hold on
% % % %plot the first side from inital coords to midpoint
% % % plot(linspace(coord1(1),mid(1)), linspace(coord1(2),mid(2)));
% % % %plot second side from mid point to max coords
% % % plot(linspace(mid(1),coord2(1)), linspace(mid(2),coord2(2)));
end
I've also made a couple of additional modification:
I've added a conversion of the input angle from deg to rad; you can discard it if you assume the input is already in rad
I've updated the way to plot the triangle.
As a suggestion, you can use nargin to thest the number of input to your function and varargin to inspect them instead of testing in they exist.
Hope this helps.
Qapla'

Drawing 2D grid in matlab

I am trying to get a 2D grid using matlab with x >= -1 and y <= 1 with step size of 0.1
But I'm getting 3D grid with no proper step sizes. Any ideas?
My code:
[x, y] = meshgrid(-1:0.1:5, 0:0.1:1);
surf(x,y)
Do you just want to plot a bunch of 2D points? You use plot. Using your example, you would take your x,y points and simply put dot markers for each point. Convert them into 1D arrays first before you do this:
[X,Y] = meshgrid(-1:0.1:5, 0:0.1:1);
X = X(:);
Y = Y(:);
plot(X,Y,'b.');
xlabel('X'); % // Label the X and Y axes
ylabel('Y');
This is what I get:
Edit based on comments
If you want to rotate this grid by an angle, you would use a rotation matrix and multiply this with each pair of (x,y) co-ordinates. If you recall from linear algebra, to rotate a point counter-clockwise, you would perform the following matrix multiplication:
[x'] = [cos(theta) -sin(theta)][x]
[y'] [sin(theta) cos(theta)][y]
x,y are the original co-ordinates while x',y' are the output co-ordinates after rotation of an angle theta. If you want to rotate -30 degrees (which is 30 degrees clockwise), you would just specify theta = -30 degrees. Bear in mind that cos and sin take in their angles as radians, so this is actually -pi/6 in radians. What you need to do is place each of your points into a 2D matrix. You would then use the rotation matrix and apply it to each point. This way, you're vectorizing the solution instead of... say... using a for loop. Therefore, you would do this:
theta = -pi/6; % // Define rotation angle
rot = [cos(theta) -sin(theta); sin(theta) cos(theta)]; %// Define rotation matrix
rotate_val = rot*[X Y].'; %// Rotate each of the points
X_rotate = rotate_val(1,:); %// Separate each rotated dimension
Y_rotate = rotate_val(2,:);
plot(X_rotate, Y_rotate, 'b.'); %// Show the plot
xlabel('X');
ylabel('Y');
This is what I get:
If you wanted to perform other transformations, like scaling each axis, you would just multiply either the X or Y co-ordinates by an appropriate scale:
X_scaled = scale_x*X;
Y_scaled = scale_y*Y;
X_scaled and Y_scaled are the scaled versions of your co-ordinates, with scale_x and scale_y are the scales in each dimension you want. If you wanted to translate the co-ordinates, you would add or subtract each of the dimensions by some number:
X_translate = X + X_shift; %// Or -
Y_translate = Y + Y_shift; %// Or -
X_translate and Y_translate are the translated co-ordinates, while X_shift and Y_shift are the amount of shifts you want per dimension. Note that you either do + or -, depending on what you want.

Shade a subset of a sphere's surface in MATLAB

I'm attempting to generate a plot of a hemisphere with a shaded area on the surface bound by max/min values of elevation and azimuth. Essentially I'm trying to reproduce this:
Generating the hemisphere is easy enough, but past that I'm stumped. Any ideas?
Here's the code I used to generate this sphere:
[x,y,z] = sphere;
x = x(11:end,:);
y = y(11:end,:);
z = z(11:end,:);
r = 90;
surf(r.*x,r.*y,r.*z,'FaceColor','yellow','FaceAlpha',0.5);
axis equal;
If you want to highlight a certain area of your hemisphere, first you decide the minimum and maximum azimuth (horizontal sweep) and elevation (vertical sweep) angles. Once you do this, take your x,y,z co-ordinates and convert them into their corresponding angles in spherical co-ordinates. Once you do that, you can then subset your x,y,z co-ordinates based on these angles. To convert from Cartesian to spherical, you would thus do:
Source: Wikipedia
theta is your elevation while phi is your azimuth. r would be the radius of the sphere. Because sphere generates co-ordinates for a unit sphere, r = 1. Therefore, to calculate the angles, we simply need to do:
theta = acosd(z);
phi = atan2d(y, x);
Take note that the elevation / theta is restricted 0 to 180 degrees, while the azimuth / phi is restricted between -180 to 180 degrees. Because you're only creating half of a sphere, the elevation should simply vary from 0 to 90 degrees. Also note that acosd and atan2d return the result in degrees. Now that we're here, you just have to subset what part of the sphere you want to draw. For example, let's say we wanted to restrict the sphere such that the min. and max. azimuth span from -90 to 90 degrees while the elevation only spans from 0 to 45 degrees. As such, let's find those x,y,z co-ordinates that satisfy these constraints, and ensure that anything outside of this range is set to NaN so that these points aren't drawn on the sphere. As such:
%// Change your ranges here
minAzimuth = -90;
maxAzimuth = 90;
minElevation = 0;
maxElevation = 45;
%// Compute angles - assuming that you have already run the code for sphere
%// [x,y,z] = sphere;
%// x = x(11:end,:);
%// y = y(11:end,:);
%// z = z(11:end,:);
theta = acosd(z);
phi = atan2d(y, x);
%%%%%// Begin highlighting logic
ind = (phi >= minAzimuth & phi <= maxAzimuth) & ...
(theta >= minElevation & theta <= maxElevation); % // Find those indices
x2 = x; y2 = y; z2 = z; %// Make a copy of the sphere co-ordinates
x2(~ind) = NaN; y2(~ind) = NaN; z2(~ind) = NaN; %// Set those out of bounds to NaN
%%%%%// Draw our original sphere and then the region we want on top
r = 90;
surf(r.*x,r.*y,r.*z,'FaceColor','white','FaceAlpha',0.5); %// Base sphere
hold on;
surf(r.*x2,r.*y2,r.*z2,'FaceColor','red'); %// Highlighted portion
axis equal;
view(40,40); %// Adjust viewing angle for better view
... and this is what I get:
I've made the code modular so that all you have to do is change the four variables that are defined at the beginning of the code, and the output will highlight that desired part of the hemisphere that are bounded by those min and max ranges.
Hope this helps!
One option is to create an array with the corresponding color you want to attribute to each point.
A minimal example (use trigonometry to convert your azimuth and elevation to logical conditions on x, y, and z):
c=(y>0).*(x>0).*(z>0.1).*(z<0.5);
c(c==0)=NaN;
surf(r.*x,r.*y,r.*z,c ,'FaceAlpha',0.5); axis equal;
yields this:
Note: this only works with the resolution of the grid. (i.e each 'patch' of the surface can have a different color). To exactly reproduce your plot, you might want to superpose the grid sphere with another one that has a much larger number of grid points on which you apply the above code.

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.