Suppose I have 3+ coplanar but not collinear points in R^4. To find the 2D plane (not hyperplane) in which they all lie, I used the following plane fit algorithm from MatlabCentral:
function [n,V,p] = affine_fit(X)
% Computes the plane that fits best (least square of the normal distance
% to the plane) a set of sample points.
% INPUTS:
% X: a N by 3 matrix where each line is a sample point
%OUTPUTS:
%n : a unit (column) vector normal to the plane
%V : a 3 by 2 matrix. The columns of V form an orthonormal basis of the plane
%p : a point belonging to the plane
%NB: this code actually works in any dimension (2,3,4,...)
%Author: Adrien Leygue
%Date: August 30 2013
% the mean of the samples belongs to the plane
p = mean(X,1);
% The samples are reduced:
R = bsxfun(#minus,X,p);
% Computation of the principal directions of the samples cloud
[V,D] = eig(R'*R);
% Extract the output from the eigenvectors
n = V(:,1);
V = V(:,2:end);
end
I employed the algorithm in a higher dimension than specified, so X is a 4x4 matrix which holds 4 points in 4 coordinate dimensions. The generated output is something like this.
[n,V,p] = affine_fit(X);
n = -0.0252
-0.0112
0.9151
-0.4024
V = 0.9129 -0.3475 0.2126
0.3216 0.2954 -0.8995
0.1249 0.3532 0.1493
0.2180 0.8168 0.3512
p = -0.9125 1.0526 0.2325 -0.0621
What I want to do now is find out if other points of my choosing are part of the plane, too. I'm sure it's fairly easy given the information above, yet at this point I only know that I need two linear equations to describe a 2D plane in 4D or parametric equations of two variables. I can set them up in theory, but writing up the code has been problematic. Perhaps there is a more straightforward way to test this in matlab?
You can use the Matlab function pca (see for example here). For example, you can determine the basis of your plane, the normal vectors to your plane and a point m on the plane as follows:
coeff = pca(X);
basis = coeff(:,1:2);
normals = coeff(:,3:4);
m = mean(X);
To check if a point p lies in this plane, it suffices to verify that m-p is orthogonal (dot product equal to zero) to the normal vectors onto the plane using dot.
Related
I have started to learn Machine Learning, and programming in matlab.
I want to plot a matrix sized m*d where d=3 and m are the number of points.
with y binary vector I'd like to color each point with blue/red.
and plot a plane which is described with the vertical vector to it w.
The problem I trying to solve is to give some kind of visual representation of the data and the linear predictor.
All I know is how to single points with plot3, but no any number of points.
Thanks.
Plot the points using scatter3()
scatter3(X(y,1),X(y,2),X(y,3),'filled','fillcolor','red');
hold on;
scatter3(X(~y,1),X(~y,2),X(~y,3),'filled','fillcolor','blue');
or using plot3()
plot(X(y,1),X(y,2),X(y,3),' o','MarkerEdgeColor','red','MarkerFaceColor','red');
hold on;
plot(X(~y,1),X(~y,2),X(~y,3),' o','MarkerEdgeColor','blue','MarkerFaceColor','blue');
There are a few ways to plot a plane. As long as w(3) isn't very close to 0 then the following will work okay. I'm assuming your plane is defined by x'*w+b=0 where b is a scalar and w and x are column vectors.
x1min = min(X(:,1)); x2min = min(X(:,2));
x1max = max(X(:,1)); x2max = max(X(:,2));
[x1,x2] = meshgrid(linspace(x1min,x1max,20), linspace(x2min, x2max, 20));
x3 = -(w(1)*x1 + w(2)*x2 + b)/w(3);
surf(x1,x2,x3,'FaceColor',[0.6,0.6,0.6],'FaceAlpha',0.7,'EdgeColor',[0.4,0.4,0.4],'EdgeAlpha',0.4);
xlabel('x_1'); ylabel('x_2'); zlabel('x_3'); axis('vis3d');
Resulting plot
I need to calculate the angle between two plane. One plane is the hand plane and the second is the forearm plane. I calculated the normals of that planes and used the formula atan2(norm(cross(var.n1,var.n2)),dot(var.n1,var.n2)); in MATLAB. I want to see the flexion/extension angle of the wrist that is characterised by positive and negative peaks but with this formula I obtain only positive peaks.
%% Script to compute the angles of wrist flexion/extension and adduction/abduction based on Vicon data
% REF: Cheryl et al., March 2008
% Order of the markers: 1.WRR 2.WRU 3.FAU 4.FAR 5.CMC2 6.CMC5 7.MCP5 8.MCP2
clc
close all
clear all
%% Initialization
% dir_kinematic = input('Path of the folder containing the kinematic files: ','s');
dir_kinematic = 'C:\Users\Utente\Desktop\TESI\Vicon\test.mat';
cd(dir_kinematic);
fileList = getAllFiles(dir_kinematic,0); % Get names of all kinematic files
f=1;
%% Conversion to angles
for f = 1:length(fileList)
if ~isempty(strfind(fileList{f},'mat')) % Take only mat files
% 0. Loading
load(fileList{f});
% 1. Filtering
frameRate = kinematic.framerate;
n = 9;
Wn = 2/(frameRate/2);
ftype = 'low';
[b,a] = butter(n,Wn,ftype);
kinematic.x = filtfilt(b,a,kinematic.x);
kinematic.y = filtfilt(b,a,kinematic.y);
kinematic.z = filtfilt(b,a,kinematic.z);
% 2. Create vectors
var.n=length(kinematic.x);
% Forearm plane
var.FAU_WRU=[kinematic.x(:,2)-kinematic.x(:,3),kinematic.y(:,2)-kinematic.y(:,3),kinematic.z(:,2)-kinematic.z(:,3)];
var.WRR_WRU=[kinematic.x(:,2)-kinematic.x(:,1),kinematic.y(:,2)-kinematic.y(:,1),kinematic.z(:,2)-kinematic.z(:,1)];
% Hand plane
var.CMC5_MCP5=[kinematic.x(:,7)-kinematic.x(:,6),kinematic.y(:,7)-kinematic.y(:,6),kinematic.z(:,7)-kinematic.z(:,6)];
var.MCP2_MCP5=[kinematic.x(:,7)-kinematic.x(:,8),kinematic.y(:,7)-kinematic.y(:,8),kinematic.z(:,7)-kinematic.z(:,8)];
% Transpose
var.FAU_WRU = var.FAU_WRU';
var.WRR_WRU = var.WRR_WRU';
var.CMC5_MCP5 = var.CMC5_MCP5';
var.MCP2_MCP5 = var.MCP2_MCP5';
% 3. Calculate angle of wrist flexion/extension
% Cross vector function for all time => create normal vector plane
var.forearm_n=[];
var.hand_n=[];
var.theta_rad=[];
for i = 1:var.n % Loop through experiment
% vector x and y of the forearm plane
var.v1=var.FAU_WRU(:,i); % take x,y,z of the vector for every time
var.v2=var.WRR_WRU(:,i);
% vector x and y of the hand plane
var.v3=var.CMC5_MCP5(:,i);
var.v4=var.MCP2_MCP5(:,i);
var.forearm_n= [var.forearm_n, cross(var.v1,var.v2)];
var.hand_n=[var.hand_n, cross(var.v3,var.v4)];
end
% Calculate angle
for i = 1:var.n
var.n1=(var.forearm_n(:,i));
var.n2=var.hand_n(:,i);
var.scalar_product(i) = dot(var.n1,var.n2);
%Equation (2) of the paper
var.theta_rad=[var.theta_rad, atan2(norm(cross(var.n1,var.n2)),dot(var.n1,var.n2))]; % result in radian
angle.flex_deflex_wrist{f}=(var.theta_rad*180)/pi;
end
% 4. Calculate angle of wrist adduction/abduction
% Projection vector onto plane
var.MCP2_MCP5_forearmproj=[];
var.WRR_WRU_forearmproj=[];
var.rad_ul_angle_rad=[];
for i=1:var.n
%take x,y,z of the vector for each time
var.v1=var.MCP2_MCP5(:,i);
var.v2=var.WRR_WRU(:,i);
% vector x and y of the forearm plane
var.vfx=var.FAU_WRU(:,i); % take x,y,z of the vector for every time
var.vfy=var.WRR_WRU(:,i);
%projection of vector MCP2_MCP5 and WRR_WRU onto forearm plane
var.squNorm1=(norm(var.vfx)*norm(var.vfx));
var.squNorm2=(norm(var.vfy)*norm(var.vfy));
var.MCP2_MCP5_forearmproj=[var.MCP2_MCP5_forearmproj,((((var.v1')*var.vfx)*var.vfx/var.squNorm1)+(((var.v1')*var.vfy)*var.vfy/var.squNorm2))];
var.WRR_WRU_forearmproj=[var.WRR_WRU_forearmproj,((var.vfx*((var.v2')*var.vfx/var.squNorm1))+(var.vfy*((var.v2')*var.vfy/var.squNorm2)))];
end
% Calculate angle
for i=1:var.n
var.n1=var.MCP2_MCP5_forearmproj(:,i)';
var.n2=var.WRR_WRU_forearmproj(:,i);
var.product=var.n1*var.n2;
var.rad_ul_angle_rad=[var.rad_ul_angle_rad, atan2(norm(cross(var.n1,var.n2)),dot(var.n1,var.n2))];% en rad
angle.rad_ul_wrist{f}=(var.rad_ul_angle_rad*180)/pi;
end
end
end
I want to know why my angles are always positive? I need to see positive and negative peaks...
Thanks for the help!
Since the angle between two planes is the same as that between it's normals, I will constrain the discussion to angle between two vectors.
We know that the cross product between two vectors (a and b) is another vector, perpendicular to both of them.
We are dealing with the problem of distinguishing angles -T and +T, where T is some angle.
Using the formula you used, these two angles would give the same result, due to the fundamental formula itself that is used:
atan2 (|a x b|, a.b)
This is because while a.b is the same in both cases, a x b differs, exactly in the sign of the normal to the two vectors, and nothing else (verify yourself using hand rules). When we compute the norm of this vector, information about its sign is lost, due to which the function always returns positive values.
What you can do about it
You need to keep track of the sign of a x b, to determine if the angle is positive or negative.
Note: As I'm replying from my phone I am unable to add better formatting or code, will update the answer soon.
I have an nx2 matrix r in Matlab reporting n draws from a bivariate normal distribution
n=1000;
m1=0.3;
m2=-m1;
v1=0.2;
n=10000;
v2=2;
rho=0.5;
mu = [m1, m2];
sigma = [v1,rho*sqrt(v1)*sqrt(v2);rho*sqrt(v1)*sqrt(v2),v2];
r = mvnrnd(mu,sigma,n);
I want to normalise these draws to the unit square [0,1]^2
First option
rmax1=max(r(:,1));
rmin1=min(r(:,1));
rmax2=max(r(:,2));
rmin2=min(r(:,2));
rnew=zeros(n,2);
for i=1:n
rnew(i,1)=(r(i,1)-rmin1)/(rmax1-rmin1);
rnew(i,2)=(r(i,2)-rmin2)/(rmax2-rmin2);
end
Second option
rmin1, rmax1, rmin2, rmax2 may be quite variable due to the sampling process. An alternative is applying the 68–95–99.7 rule (here) and I am asking for some help on how to generalise it to a bivariate normal (in particular Step 1 below). Here's my idea
%Step 1: transform the draws in r into draws from a bivariate normal
%with variance-covariance matrix equal to the 2x2 identity matrix
%and mean equal to mu
%How?
%Let t be the transformed vector
%Step 2: apply the 68–95–99.7 rule to each column of t
tmax1=mu(1)+3*1;
tmin1=mu(1)-3*1;
tmax2=mu(2)+3*1;
tmin2=mu(2)-3*1;
tnew=zeros(n,2);
for i=1:n
tnew(i,1)=(t(i,1)-tmin1)/(tmax1-tmin1);
tnew(i,2)=(t(i,1)-tmin2)/(tmax2-tmin2);
end
%Step 3: discard potential values (very few) outside [0,1]
In your case the x and y coordinates of the random vector are correlated, so it's not just a transformation in x and in y independently. You first need to rotate your samples so that x and y become uncorrelated (then the covariance matrix will be diagonal. You don't need it to be the identity, since anywya you normalize later). Then you can apply the transformation you call "2nd option" to the new x and y independently. Shortly, you need to diagonalize the covariance matrix.
As a side note, your code adds/subtracts 3 times 1, instead of 3 times the standard deviation. Also, you can avoid the for loop, using (e.g) Matlab's bsxfun which applies an operation between matrix and vector:
t = bsxfun(#minus,r,mean(r,1)); % center the data
[v, d] = eig(sigma); % find the directions for projection
t = t * v; % the projected data is uncorrelated
sigma_new = sqrt(diag(d)); % that's the std in the new coordinates
% now transform each coordinate independently
tmax1 = 3*sigma_new(1);
tmin1 = -3*sigma_new(1);
tmax2 = 3*sigma_new(2);
tmin2 = -3*sigma_new(2);
tnew = bsxfun(#minus, t, [tmin1, tmin2]);
tnew = bsxfun(#rdivide, tnew, [tmax1-tmin1, tmax2-tmin2]);
You still need to discard the few samples which are out of [0,1], as you wrote.
This question is from my last post,
But I will just focus on a particular part,
If I have a 3d matrix M, 256*256*124,and a plane defined by 3 points that intersect the 3d matrix, A,B,C, how to find the distance of all points in M to the plane defined by A,B,C
Here is the suggestion I got,
[X Y Z] = meshgrid(1:256,1:256,1:124)
A = [X(:)';Y(:)';Z(:)'];
n = cross([coor_A-coor_B],[coor_B-coor_C]); %The norm vector of the plane
d = norm(mean([coor_A,coor_B,coor_C]),2); %The distance from origin to the plane
According to Hesse normal form,
L = ((n*A)-d); %The distance from all points in M to the plane
But all the values in L are huge, which indicates that no points are found on the intersection plane.
Can anyone tell me what's wrong?
You missed one line on the Wikipedia page
where
So add this line
n0 = n / norm(n);
and change the final line to
L = ((n0*A)-d);
I have a camera and its K matrix (calibration matrix) also I have image of plane, I know the real points of the 4 corners and thier correspondence pixel. I know how to compute the H matrix if z=0 (H is homography matrix between Image and the real plane).
And Now I try to get the real point of the plane (3D point) with the rotation matrix and the transltion vector
I follow this paper :Calibrating an Overhead Video Camera by Raul Rojas in section 3 - 3.3.
My code is:
ImagePointsScreen=[16,8,1;505,55,1;505,248,1;44,301,1;];
screenImage=imread( 'screen.jpg');
RealPointsMirror=[0,0,1;9,0,1;9,6,1;0,6,1]; %Mirror
RealPointsScreen=[0,0,1;47.5,0,1;47.5,20,1;0,20,1];%Screen
imagesc(screenImage);
hold on
for i=1:4
drawBubble(ImagePointsScreen(i,1),ImagePointsScreen(i,2),1,'g',int2str(i),'r')
end
Points3DScreen=Get3DpointSurface(RealPointsScreen,ImagePointsScreen,'Screen');
figure
hold on
plot3(Points3DScreen(:,1),Points3DScreen(:,2),Points3DScreen(:,3));
for i=1:4
drawBubble(Points3DScreen(i,1),Points3DScreen(i,2),1,'g',int2str(i),'r')
end
function [ Points3D ] = Get3DpointSurface( RealPoints,ImagePoints,name)
M=zeros(8,9);
for i=1:4
M((i*2)-1,1:3)=-RealPoints(i,:);
M((i*2)-1,7:9)=RealPoints(i,:)*ImagePoints(i,1);
M(i*2,4:6)=-RealPoints(i,:);
M(i*2,7:9)=RealPoints(i,:)*ImagePoints(i,2);
end
[U S V] = svd(M);
X = V(:,end);
H(1,:)=X(1:3,1)';
H(2,:)=X(4:6,1)';
H(3,:)=X(7:9,1)';
K=[680.561906875074,0,360.536967117290;0,682.250270165388,249.568615725655;0,0,1;];
newRO=pinv(K)*H;
h1=newRO(1:3,1);
h2=newRO(1:3,2);
scaleFactor=(norm(h1)+norm(h2))/2;
newRO=newRO./scaleFactor;
r1=newRO(1:3,1);
r2=newRO(1:3,2);
r3=cross(r1,r2);
r3=r3/norm(r3);
R=[r1,r2,r3];
RInv=pinv(R);
O=-RInv*newRO(1:3,3);
M=K*[R,-R*O];
for i=1:4
res=pinv(M)* [ImagePoints(i,1),ImagePoints(i,2),1]';
res=res';
res=res*(1/res(1,4));
Points3D(i,:)=res';
end
Points3D(i+1,:)=Points3D(1,:); %just add the first point to the end of the array for draw square
end
My result is :
Now I have two problem :
1.The point 1 is at (0,0,0) and this is not the real location
2.the points are upside down
What I am doing worng?
A homography is normally the transform of a plane in two positions/rotations.
The position in camera coordinates of a plane is normally called pose or extrinsic parameters
opencv has a solvePnP() function which uses Ransac to estimate the position of a known plane.
ps. Sorry don't know the equivalent matlab but Bouguet has a matlab version of the openCV 3D functions on his site
I found the answer in the paper: Calibrating an Overhead Video Camera by Raul Rojas in section 3 - 3.3.
for the start: H=K^-1*H
Given four points in the image and their known coordinates in the world, the
matrix H can be recovered, up to a scaling factor . We know that the first
two columns of the rotation matrix R must be the first two columns of the
transformation matrix. Let us denote by h1, h2, and h3 the three columns of
the matrix H.Due to the scaling factor we then have that
xr1 = h1
and
xr2 = h2
Since |r1| = 1, then x= |h1|/|r1| = |h1| and x = |h2|/|r2| = |h2|. We can thus
compute the factor and eliminate it from the recovered matrix H. We just set
H'= H/x
In this way we recover the first two columns of the rotation matrix R.
The third column of R can be found remembering that any column in a rotation
matrix is the cross product of the other two columns (times the appropriate
plus or minus sign). In particular
r3 = r1 × r2
Therefore, we can recover from H the rotation matrix R. We can also recover
the translation vector (the position of the camera in field coordinates). Just
remember that
h'3 = −R^t
Therefore the position vector of the camera pin-hole t is given by
t = −R^-1 h3