Reconstruct 3D-Coordinates in Camera Coordinate System from 2D - Pixels with side condition - matlab

I am trying to reconstruct 3D-Coordinates from the 2D-Pixel-Coordinates in a Camera Picture using a side condition (in MatLab). I do have extrinsic and intrinsic camera parameters.
Using homogenous transformation I can transform 3D-Coordinates from a initial World Coordinate System to my Camera Corrdinate System. So here I have my extrinsic parameters in my Transform Matrix R_world_to_Camera:
R_world_to_Camera = [ r_11, r_12, r_13, t1;
r_21, r_22, r_23, t2;
r_31, r_32, r_33, t3;
0, 0, 0, 1];
For intrinsic parameters I used Caltech's "Camera Calibration Toolbox for MatLab" and got these parameters:
Calibration results (with uncertainties):
Focal Length: fc = [ 1017.21523 1012.54901 ] ± [ NaN NaN ]
Principal point: cc = [ 319.50000 239.50000 ] ± [ NaN NaN ]
Skew: alpha_c = [ 0.00000 ] ± [ NaN ] => angle of pixel axes = 90.00000 ± NaN degrees
Distortion: kc = [ 0.00000 0.00000 0.00000 0.00000 0.00000 ] ± [ NaN NaN NaN NaN NaN ]
Pixel error: err = [ 0.11596 0.14469 ]
Note: The numerical errors are approximately three times the standard deviations (for reference).
So I then get the Camera-Calibration-Matrix K (3x3)
K = [1.017215234570303e+03, 0, 3.195000000000000e+02;
0, 1.012549014668498e+03,2.395000000000000e+02;
0, 0, 1.0000];
and using this I can calculate the 3D -> 2D - Projection-Matrix P (3x4) with:
P = K * [eye(3), zeros(3,1)];
When converting a Point in World-Coordinates [X, Y, Z]_World I transform it first to Camera-Coordinates and then project it to 2D:
% Transformation
P_world = [X; Y; Z; 1]; % homogenous coordinates in World coordinate System
P_camera = R_world_to_Camera * [X; Y; Z; 1];
% Projection
P_pixels = P * camera;
P_pixels = P_pixels / P_pixels(3); % normalize coordinates
So my question now is how to reverse these steps? As side condition I want to set the Z-Coordinate to be known (zero in world-coordinates). I tried the solution proposed here on Stackoverflow, but somehow I get wrong coordinates. Any idea? Every help is appreciated a lot!!

You cannot reverse the step in general: depth and scale information is lost when 3D points are projected onto a 2D image. However if, as you indicate, all your 3D points are on the Z=0 plane, then getting them back from their projections is trivial: compute the inverse Ki = K^-1 of the camera matrix, and apply it to the image points in homogeneous coordinates.
P_camera = Ki * [u, v, 1]'
where [u, v] are the image coordinates, and the apostrophe denotes transposition.
The 3D points you want lie on the rays from the camera centre to the P_camera's. Express both in world coordinates:
P_world = [R|t]_camera_to_world * [P_camera, 1]'
C_world = [R|t]_camera_to_world * [0, 0, 0, 1]'
where [R|t] is the 4x4 coordinate transform.
Now, the set of points on each ray is expressed as
P = C_world + lambda * P_world;
where lambda is a scalar (the coordinate along the ray). You can now impose the condition that P(3) = 0 to find the value of lambda that places your points on the Z = 0 plane.

thanks to some red wine and thorough reading I found the answer in a (german) Master Thesis :)
My "testcode" is as follows:
% Clean-Up First
clear all;
close all;
clc;
% Caamera-Calibration-Matrix
K = [1.017215234570303e+03, 0, 3.195000000000000e+02, 0;
0, 1.012549014668498e+03,2.395000000000000e+02, 0;
0, 0, 1.0000, 0;
0, 0, 0, 0];
% Transforma Matrix from 3D-World-Coordinate System to 3D-Camera-Coordinate System (Origin on CCD-Chip)
%
% The Camera is oriented "looking" into positive X-Direction of the World-Coordinate-System. On the picture,
% positive Y-Direction will be to the left, positive Z-Direction to the top. (right hand coordinate system!)
R_World_to_Cam = [-0.0113242625465167 -0.999822053685344 0.0151163536128891 141.173585444427;
0.00842007509644635 -0.0152123858102325 -0.999848810645587 1611.96528372161;
0.999900032304804 -0.0111955728474261 0.00859117128537919 847.090629282911;
0 0 0 1];
% Projection- and Transforma Matrix P
P = K * R_World_to_Cam;
% arbitrary Points X_World in World-Coordinate-System [mm] (homogenous Coordinates)
% forming a square of size 10m x 4m
X_World_1 = [20000; 2000; 0; 1];
X_World_2 = [20000; -2000; 0; 1];
X_World_3 = [10000; 2000; 0; 1];
X_World_4 = [10000; -2000; 0; 1];
% Transform and Project from 3D-World -> 2D-Picture
X_Pic_1 = P * X_World_1;
X_Pic_2 = P * X_World_2;
X_Pic_3 = P * X_World_3;
X_Pic_4 = P * X_World_4;
% normalize homogenous Coordinates (3rd Element has to be 1!)
X_Pic_1 = X_Pic_1 / X_Pic_1(3);
X_Pic_2 = X_Pic_2 / X_Pic_2(3);
X_Pic_3 = X_Pic_3 / X_Pic_3(3);
X_Pic_4 = X_Pic_4 / X_Pic_4(3);
% Now for reverse procedure take arbitrary points in Camera-Picture...
% (for simplicity, take points from above and "go" 30px to the right and 40px down)
X_Pic_backtransform_1 = X_Pic_1(1:3) + [30; 40; 0];
X_Pic_backtransform_2 = X_Pic_2(1:3) + [30; 40; 0];
X_Pic_backtransform_3 = X_Pic_3(1:3) + [30; 40; 0];
X_Pic_backtransform_4 = X_Pic_4(1:3) + [30; 40; 0];
% ... and transform back following the formula from the Master Thesis (in German):
% Ilker Savas, "Entwicklung eines Systems zur visuellen Positionsbestimmung von Interaktionspartnern"
M_Mat = P(1:3,1:3); % Matrix M is the "top-front" 3x3 part
p_4 = P(1:3,4); % Vector p_4 is the "top-rear" 1x3 part
C_tilde = - inv( M_Mat ) * p_4; % calculate C_tilde
% Invert Projection with Side-Condition ( Z = 0 ) and Transform back to
% World-Coordinate-System
X_Tilde_1 = inv( M_Mat ) * X_Pic_backtransform_1;
X_Tilde_2 = inv( M_Mat ) * X_Pic_backtransform_2;
X_Tilde_3 = inv( M_Mat ) * X_Pic_backtransform_3;
X_Tilde_4 = inv( M_Mat ) * X_Pic_backtransform_4;
mue_N_1 = -C_tilde(3) / X_Tilde_1(3);
mue_N_2 = -C_tilde(3) / X_Tilde_2(3);
mue_N_3 = -C_tilde(3) / X_Tilde_3(3);
mue_N_4 = -C_tilde(3) / X_Tilde_4(3);
% Do the inversion of above steps...
X_World_backtransform_1 = mue_N_1 * inv( M_Mat ) * X_Pic_backtransform_1 + C_tilde;
X_World_backtransform_2 = mue_N_2 * inv( M_Mat ) * X_Pic_backtransform_2 + C_tilde;
X_World_backtransform_3 = mue_N_3 * inv( M_Mat ) * X_Pic_backtransform_3 + C_tilde;
X_World_backtransform_4 = mue_N_4 * inv( M_Mat ) * X_Pic_backtransform_4 + C_tilde;
% Plot everything
figure(1);
% First Bird Perspective of World-Coordinate System...
subplot(1,2,1);
xlabel('Y-World');
ylabel('X-World');
grid on;
axis([-3000 3000 0 22000]);
hold on;
plot( -X_World_1(2), X_World_1(1), 'bo' );
plot( -X_World_2(2), X_World_2(1), 'bo' );
plot( -X_World_3(2), X_World_3(1), 'bo' );
plot( -X_World_4(2), X_World_4(1), 'bo' );
line([-X_World_1(2) -X_World_2(2) -X_World_4(2) -X_World_3(2) -X_World_1(2)], [X_World_1(1) X_World_2(1) X_World_4(1) X_World_3(1) X_World_1(1)], 'Color', 'blue' );
plot( -X_World_backtransform_1(2), X_World_backtransform_1(1), 'ro' );
plot( -X_World_backtransform_2(2), X_World_backtransform_2(1), 'ro' );
plot( -X_World_backtransform_3(2), X_World_backtransform_3(1), 'ro' );
plot( -X_World_backtransform_4(2), X_World_backtransform_4(1), 'ro' );
line([-X_World_backtransform_1(2) -X_World_backtransform_2(2) -X_World_backtransform_4(2) -X_World_backtransform_3(2) -X_World_backtransform_1(2)], [X_World_backtransform_1(1) X_World_backtransform_2(1) X_World_backtransform_4(1) X_World_backtransform_3(1) X_World_backtransform_1(1)], 'Color', 'red' );
hold off;
% ...then the camera picture (perspective!)
subplot(1,2,2);
hold on;
image(ones(480,640).*255);
colormap(gray(256));
axis([0 640 -480 0]);
line([X_Pic_1(1) X_Pic_2(1) X_Pic_4(1) X_Pic_3(1) X_Pic_1(1)], -1*[X_Pic_1(2) X_Pic_2(2) X_Pic_4(2) X_Pic_3(2) X_Pic_1(2)], 'Color', 'blue' );
line([X_Pic_backtransform_1(1) X_Pic_backtransform_2(1) X_Pic_backtransform_4(1) X_Pic_backtransform_3(1) X_Pic_backtransform_1(1)], -1*[X_Pic_backtransform_1(2) X_Pic_backtransform_2(2) X_Pic_backtransform_4(2) X_Pic_backtransform_3(2) X_Pic_backtransform_1(2)], 'Color', 'red' );
hold off;

Related

How can I fit a cosine function to 2D data?

Analysing some data, I produced the following curve which I need to fit to a trigonometric function (cosine).
In Matlab, how can I get the parameters of the cosine function to best fit this data? x axis is the angle and y axis is Temperature. Thanks a lot.
EDIT: my data:
angle = [
0
3.316287766
4.730800754
9.134775894
11.91995938
13.21180064
16.97962712
20.87805072
22.34754865
24.61618179
28.18653577
31.58045095
35.07799869
38.5496595
41.97293968
44.56839009
45.3302489
46.10645938
48.67064021
52.08302411
55.54965999
59.04755401
62.28433855
65.72402163
68.63018998
69.36548262
73.19888377
76.8932392
78.40386372
80.91798834
85.28633489
86.74398825
90
]
Temperature = [
998.208
998.35
998.402
998.525
998.604
998.631
998.699
998.776
998.802
998.818
998.842
998.878
998.9
998.92
998.936
998.949
998.946
998.949
998.936
998.918
998.897
998.873
998.837
998.81
998.79
998.777
998.698
998.626
998.595
998.524
998.4
998.348
998.208
]
The following code should do the trick:
% Define the sample data and auxiliary variables...
x = [0 3.316287766 4.730800754 9.134775894 11.91995938 13.21180064 16.97962712 20.87805072 22.34754865 24.61618179 28.18653577 31.58045095 35.07799869 38.5496595 41.97293968 44.56839009 45.3302489 46.10645938 48.67064021 52.08302411 55.54965999 59.04755401 62.28433855 65.72402163 68.63018998 69.36548262 73.19888377 76.8932392 78.40386372 80.91798834 85.28633489 86.74398825 90];
y = [998.208 998.35 998.402 998.525 998.604 998.631 998.699 998.776 998.802 998.818 998.842 998.878 998.9 998.92 998.936 998.949 998.946 998.949 998.936 998.918 998.897 998.873 998.837 998.81 998.79 998.777 998.698 998.626 998.595 998.524 998.4 998.348 998.208];
p2 = 2 * pi();
% Determine the range of X...
x_max = max(x);
x_min = min(x);
x_range = linspace(x_min,x_max);
% Determine the scale of Y...
y_max = max(y);
y_mean = mean(y);
y_range = y_max - min(y);
% Find the zero crossings...
yz = y - y_max + (y_range / 2);
zc = x(yz(:) .* circshift(yz(:),[1 0]) <= 0);
% Define the fitting and the LS functions...
fit = #(b,x) (b(1) .* (sin(((p2 * x) ./ b(2)) + (p2 / b(3))))) + b(4);
ls = #(b) sum((fit(b,x) - y) .^ 2);
% Minimize the LS...
ls_min = fminsearch(ls,[y_range; (2 * mean(diff(zc))); -1; y_mean]);
% Plot the result...
plot(x,y,'b');
hold on;
plot(x_range,fit(ls_min,x_range),'r');
hold off;
Output:
Here's one way using fitnlm:
% your "data"
a=[0 3.316287766 4.730800754 9.134775894 11.91995938 13.21180064 16.97962712 20.87805072 22.34754865 24.61618179 28.18653577 31.58045095 35.07799869 38.5496595 41.97293968 44.56839009 45.3302489 46.10645938 48.67064021 52.08302411 55.54965999 59.04755401 62.28433855 65.72402163 68.63018998 69.36548262 73.19888377 76.8932392 78.40386372 80.91798834 85.28633489 86.74398825 90 ];
T=[998.208 998.35 998.402 998.525 998.604 998.631 998.699 998.776 998.802 998.818 998.842 998.878 998.9 998.92 998.936 998.949 998.946 998.949 998.936 998.918 998.897 998.873 998.837 998.81 998.79 998.777 998.698 998.626 998.595 998.524 998.4 998.348 998.208 ];
F=#(b,data) b(1)+b(2)*cosd(b(3)*data+b(4)); % the fitting function
b0=[T(1),1,1,0]; % initial guess for the fit
opts = statset('maxIter',500,'TolFun',1e-6); % controlling the fit options
mdl=fitnlm(a,T,F,b0,'Options',opts); % doing the fit
coeff=mdl.Coefficients.Estimate; % the fit coefficients (b)
plot(a,T,'o'); hold on % lets see how we did...
plot(a,F(coeff,a),'--')

Moving a star marker along a hexagon trajectory?

I want to move a star marker along hexagon trajectory similar to "Circle trajectory" that I have added at the end of my question. Thanks.
This is the source code to that I have written yet for creating concentric hegzagons but I don't know how to move a star marker which traverses the concentric hexagons, I had written a similar simulation code for circle trajectory but I couldn't do it for hexagon.
%clc; % Clear the command window.
%close all; % Close all figures (except those of imtool.)
%clear; % Erase all existing variables. Or clearvars if you want.
workspace; % Make sure the workspace panel is showing.
format long g;
format compact;
fontSize = 20;
angles = linspace(0, 360, 7);
radii = [20, 35, 50,70];
% First create and draw the hexagons.
numberOfHexagons = 4;
% Define x and y arrays. Each row is one hexagon.
% Columns are the vertices.
x1=radii(1) * cosd(angles)+50;
y1 = radii(1) * sind(angles)+50;
x2=radii(2) * cosd(angles)+50;
y2 = radii(2) * sind(angles)+50;
x3=radii(3) * cosd(angles)+50;
y3 = radii(3) * sind(angles)+50;
x4=radii(4) * cosd(angles)+50;
y4 = radii(4) * sind(angles)+50;
plot(x1 , y1, 'b');
hold on
plot(x2, y2, 'b');
hold on
plot(x3, y3, 'b');
hold on
plot(x4, y4, 'b');
hold on
% Connecting Line:
plot([70 100], [50 50],'color','b')
axis([0 100 0 100])
hold on
Circle trajectory:
% Initialization steps.
format long g;
format compact;
fontSize = 20;
r1 = 50;
r2 = 35;
r3= 20;
xc = 50;
yc = 50;
% Since arclength = radius * (angle in radians),
% (angle in radians) = arclength / radius = 5 / radius.
deltaAngle1 = 5 / r1;
deltaAngle2 = 5 / r2;
deltaAngle3 = 5 / r3;
theta1 = 0 : deltaAngle1 : (2 * pi);
theta2 = 0 : deltaAngle2 : (2 * pi);
theta3 = 0 : deltaAngle3 : (2 * pi);
x1 = r1*cos(theta1) + xc;
y1 = r1*sin(theta1) + yc;
x2 = r2*cos(theta2) + xc;
y2 = r2*sin(theta2) + yc;
x3 = r3*cos(theta3) + xc;
y3 = r3*sin(theta3) + yc;
plot(x1,y1,'color',[1 0.5 0])
hold on
plot(x2,y2,'color',[1 0.5 0])
hold on
plot(x3,y3,'color',[1 0.5 0])
hold on
% Connecting Line:
plot([70 100], [50 50],'color',[1 0.5 0])
% Set up figure properties:
% Enlarge figure to full screen.
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0, 0, 1, 1]);
drawnow;
axis square;
for i = 1 : length(theta1)
plot(x1(i),y1(i),'r*')
pause(0.1)
end
for i = 1 : length(theta2)
plot(x2(i),y2(i),'r*')
pause(0.1)
end
for i = 1 : length(theta3)
plot(x3(i),y3(i),'r*')
pause(0.1)
end
I would generalize your problem with parametric function for the trajectory. In it use rotation kernel which you want here few examples in C++/VCL/GDI (sorry I am not Matlab friendly but the equations should be the same in Matlab too) for circle,square and hexagon rotation kernels:
void getpnt_circle(double &x,double &y,double &pi2,double r,double t) // (x,y) = circle(r,t) t=<0,1>
{
pi2=2.0*M_PI; // circumference(r=1) 6.283185307179586476925286766559
t*=pi2;
x=r*cos(t);
y=r*sin(t);
}
//---------------------------------------------------------------------------
void getpnt_square(double &x,double &y,double &pi2,double r,double t) // (x,y) = square(r,t) t=<0,1>
{
pi2=8.0; // circumference(r=1)
// kernel
const int n=4; // sides
const double x0[n]={+1.0,+1.0,-1.0,-1.0}; // side start point
const double y0[n]={-1.0,+1.0,+1.0,-1.0};
const double dx[n]={ 0.0,-2.0, 0.0,+2.0}; // side tangent
const double dy[n]={+2.0, 0.0,-2.0, 0.0};
int ix;
t-=floor(t); // t = <0, 1.0)
t*=n; // t = <0,n)
ix=floor(t); // side of square
t-=ix; // distance from side start
x=r*(x0[ix]+t*dx[ix]);
y=r*(y0[ix]+t*dy[ix]);
}
//---------------------------------------------------------------------------
void getpnt_hexagon(double &x,double &y,double &pi2,double r,double t) // (x,y) = square(r,t) t=<0,1>
{
pi2=6.0; // circumference(r=1)
// kernel
const int n=6; // sides
const double c60=cos(60.0*M_PI/180.0);
const double s60=sin(60.0*M_PI/180.0);
const double x0[n]={+1.0,+c60,-c60,-1.0,-c60,+c60}; // side start point
const double y0[n]={ 0.0,+s60,+s60, 0.0,-s60,-s60};
const double dx[n]={-c60,-1.0,-c60,+c60,+1.0,+c60}; // side tangent
const double dy[n]={+s60, 0.0,-s60,-s60, 0.0,+s60};
int ix;
t-=floor(t); // t = <0, 1.0)
t*=n; // t = <0,n)
ix=floor(t); // side of square
t-=ix; // distance from side start
x=r*(x0[ix]+t*dx[ix]);
y=r*(y0[ix]+t*dy[ix]);
}
//---------------------------------------------------------------------------
void TMain::draw()
{
if (!_redraw) return;
// clear buffer
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
int e;
double r,t,x,y,c,dr=15.0,dl=15.0;
int xx,yy,rr=3;
bmp->Canvas->MoveTo(xs2,ys2);
bmp->Canvas->Pen->Color=clAqua;
bmp->Canvas->Brush->Color=clBlue;
for (r=dr,t=0.0;;)
{
// get point from selected kernel
// getpnt_circle (x,y,c,r,t);
// getpnt_square (x,y,c,r,t);
getpnt_hexagon(x,y,c,r,t);
// render it
xx=xs2+x;
yy=ys2+y;
bmp->Canvas->LineTo(xx,yy);
bmp->Canvas->Ellipse(xx-rr,yy-rr,xx+rr,yy+rr);
// update position
r+=dr*dr/(r*c);
t+=dl/(r*c); t-=floor(t);
if (r>=xs2) break;
if (r>=ys2) break;
}
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
_redraw=false;
}
//---------------------------------------------------------------------------
You can ignore the VCL/GDI rendering stuff.
xs,ys is full and xs2,ys2 is half resolution of window to scale the plot properly...
dl is distance between markers [pixels]
dr is distance between spiral screws [pixels]
the spiral is sampled with r,t step depending on the actual circumference (that is what the pi2 or c is for). The getpnt_xxxxx function will return x,y coordinate of your shape from parameter t=<0,1> and actual radius r. It also returns the actual value of circumference/r ratio called pi2
Here preview of the 3 kernels used for spiral ...

data fitting an ellipse in 3D space

Forum
I've got a set of data that apparently forms an ellipse in 3D space (not an ellipsoid, but a curve in 3D).
Being inspired by following thread http://au.mathworks.com/matlabcentral/newsreader/view_thread/65773
and with the help from someone ,I manage to get the optimization code running and outputs a set of best parameters x (vector). However, when I try to use this x to replicate the ellipse,the outcomes is a strange straight line in the space. I have been stacked on this for days., still don't know what went wrong....pretty much devastated... I hope someone can shed some light on this. The Mathematica formulations for the ellipse follows the same as in the above thread ,where
The 3D-ellipse is given by: (x;y;z) = (z1;z2;z3) + R(alpha,beta,gamma).(acos(phi); b*sin(phi);0)
where:
* z is the translation vector.
* R is the rotation matrix (using Euler angles, we first rotate alpha rad around the x-axis, then beta rad around the y-axis and finally gamma rad around the z-axis again).
* a is the long axis of the ellipse
* b is the short axis of the ellipse.
Here is my target function for optimization (ellipsefit.m)
function [merit]= ellipsefit(x, vmatrix) % x is the initial parameters, vmatrix stores the datapoints
load vmatrix.txt % In vmatrix, the data are stored: N rows x 3 columns
a = x(1);
b = x(2);c
alpha = x(3);
beta = x(4);
gamma = x(5);
z = [x(6),x(7),x(8)];
t = z'
[dim1, dim2]=size(vmatrix);
% construction of rotation matrix R(alpha,beta,gamma)
R1 = [cos(alpha), sin(alpha), 0; -sin(alpha), cos(alpha), 0; 0, 0, 1];v
R2 = [1, 0, 0; 0, cos(beta), sin(beta); 0, -sin(beta), cos(beta)];
R3 = [cos(gamma), sin(gamma), 0; -sin(gamma), cos(gamma), 0; 0, 0, 1];
R = R3*R2*R1;
% first compute vector phi (in the length of the data) by minimizing for every
% point in the data set the distance of this point to the ellipse
% (with its initial parameters a,b,alpha,beta,gamma, z1,z2,z3 held fixed) with respect to phi.
for i=1:dim1
point=vmatrix(i,:)';
dist=#(phi)sum((R*[a*cos(phi); b*sin(phi); 0]+t-point)).^2;
phi(i)=fminbnd(dist,0,2*pi);
end
v = [a*cos(phi); b*sin(phi); zeros(size(phi))];
P = R*v;
%The targetfunction is: g = (xi1,xi2,xi3)' -(z1,z2,z3)'-R(alpha,beta,gamma)(a cos(phi), b sin(phi),0)'
% Construction of distance function
merit = [vmatrix(:,1)-z(1)-P(1),vmatrix(:,2)-z(2)-P(2),vmatrix(:,3)-z(3)-P(3)];
merit = sqrt(sum(sum(merit.^2)))
end
here is the main function for parameters initialization and call for opts (xfit.m)
function [y] = xfit (x)
x= [1 1 1 1 1 1 1 1] % initial parameters
[x] = fminsearch(#ellipsefit,x) % set the distance minimum as the target function
y=x
end
code to reconstruct the ellipse in scatter points (ellipsescatter.txt)
x= [0.655,0.876,1.449,2.248,1.024,0.201,-0.11,0.002] % values obtained according to above routines
a = x(1);
b = x(2);
alpha = x(3);
beta = x(4);
gamma = x(5);
z = [x(6),x(7),x(8)];
R1 = [cos(alpha), sin(alpha), 0; -sin(alpha), cos(alpha), 0; 0, 0, 1];
R2 = [1, 0, 0; 0, cos(beta), sin(beta); 0, -sin(beta), cos(beta)];
R3 = [cos(gamma), sin(gamma), 0; -sin(gamma), cos(gamma), 0; 0, 0, 1];
R = R3*R2*R1;
phi=linspace(0,2*pi,100)
v = [a*cos(phi); b*sin(phi); zeros(size(phi))];
P = R*v;
u = P'
and last the data points (vmatrix)
0.002037965 0.004225765 0.002020202
0.005766671 0.007269193 0.004040404
0.010004802 0.00995638 0.006060606
0.014444336 0.012502725 0.008080808
0.019083408 0.014909533 0.01010101
0.023967745 0.017144827 0.012121212
0.03019849 0.01969697 0.014591289
0.038857283 0.022727273 0.017839321
0.045443501 0.024730475 0.02020202
0.051213405 0.026346492 0.022222222
0.061038174 0.028787879 0.02555121
0.069408829 0.030575164 0.028282828
0.075785861 0.031818182 0.030321465
0.088818543 0.033954681 0.034343434
0.095538223 0.03490652 0.036363636
0.109421234 0.036499949 0.04040404
0.123800737 0.037746182 0.044444444
0.131206601 0.038218171 0.046464646
0.146438211 0.038868525 0.050505051
0.162243245 0.039117883 0.054545455
0.178662839 0.03893748 0.058585859
0.195740664 0.038296774 0.062626263
0.204545539 0.037790433 0.064646465
0.222781268 0.036340005 0.068686869
0.23715887 0.034848485 0.071748051
0.251787024 0.033009003 0.074747475
0.26196429 0.031542949 0.076767677
0.278510276 0.028787879 0.079919236
0.294365342 0.025757576 0.082799669
0.306221108 0.023197784 0.084848485
0.31843759 0.020305704 0.086868687
0.331291367 0.016967964 0.088888889
0.342989936 0.013636364 0.090622484
0.352806191 0.010606061 0.091993214
0.36201461 0.007575758 0.093211986
0.376385537 0.002386324 0.094949495
0.386214665 -0.001515152 0.096012
0.396173756 -0.005800677 0.096969697
0.406365393 -0.010606061 0.097799682
0.417897899 -0.016666667 0.098516141
0.428059375 -0.022727273 0.098889844
0.436894505 -0.028787879 0.09893196
0.444444123 -0.034848485 0.098652697
0.45074522 -0.040909091 0.098061305
0.455830971 -0.046969697 0.097166076
0.457867157 -0.05 0.096591789
0.46096663 -0.056060606 0.095199991
0.461974832 -0.059090909 0.094368708
0.462821268 -0.063709158 0.092929293
0.46279206 -0.068181818 0.091323015
0.462224312 -0.071212121 0.090097745
0.461247257 -0.074242424 0.088770148
0.459194871 -0.07812596 0.086868687
0.456406121 -0.0818267 0.084848485
0.45309565 -0.085162601 0.082828283
0.449335762 -0.088184223 0.080808081
0.445185841 -0.090933095 0.078787879
0.440695103 -0.093443633 0.076767677
0.435904796 -0.095744683 0.074747475
0.429042582 -0.098484848 0.072052312
0.419877272 -0.101489369 0.068686869
0.41402731 -0.103049401 0.066666667
0.407719192 -0.104545455 0.064554798
0.395265308 -0.106881864 0.060606061
0.388611992 -0.107880111 0.058585859
0.374697979 -0.10945186 0.054545455
0.360058411 -0.11051623 0.050505051
0.352443612 -0.11084211 0.048484848
0.336646801 -0.111097219 0.044444444
0.320085063 -0.110817414 0.04040404
0.31150078 -0.110465333 0.038383838
0.293673303 -0.109300395 0.034343434
0.275417637 -0.107575758 0.030396076
0.265228963 -0.106361993 0.028282828
0.251914589 -0.104545455 0.025603647
0.234385536 -0.101745907 0.022222222
0.223443994 -0.099745394 0.02020202
0.212154519 -0.097501571 0.018181818
0.20046153 -0.09497557 0.016161616
0.188298809 -0.092121085 0.014141414
0.17558878 -0.088883868 0.012121212
0.162241674 -0.085201142 0.01010101
0.148154337 -0.081000773 0.008080808
0.136529019 -0.077272727 0.006507255
0.127611912 -0.074242424 0.005361311
0.116762238 -0.070350086 0.004040404
0.103195122 -0.065151515 0.002507114
0.095734279 -0.062121212 0.001725236
0.081719652 -0.056060606 0.000388246
0 0 0
This answer is not a direct fit in 3D, it instead involves first a rotation of the data so that the plane of the points coincides with the xy plane, then a fit to the data in 2D.
% input: data, a N x 3 array with one set of Cartesian coords per row
% remove the center of mass
CM = mean(data);
datap = data - ones(size(data,1),1)*CM;
% now rotate all points into the xy plane ...
% start by finding the plane:
[u s v]=svd(datap);
% rotate the data into the principal axes frame:
datap = datap*v;
% fit the equation for an ellipse to the rotated points
x= [0.25 0.07 0.037 0 0]'; % initial parameters
options=1;
xopt = fmins(#fellipse,x,options,[],datap) % set the distance minimum as the target function
This is the function fellipse, based on the function provided:
function [merit]= fellipse(x,data) % x is the initial parameters, data stores the datapoints
a = x(1);
b = x(2);
alpha = x(3);
z = x(4:5);
R = [cos(alpha), sin(alpha), 0; -sin(alpha), cos(alpha), 0; 0, 0, 1];
data = data*R;
merit = 0;
[dim1, dim2]=size(data);
for i=1:dim1
dist=#(phi)sum( ( [a*cos(phi);b*sin(phi)] + z - data(i,1:2)').^2 );
phi=fminbnd(dist,0,2*pi);
merit = merit+dist(phi);
end
end
Also, note again that this can be turned into a fit directly in 3D, but this answer is just as good if you can assume the data points lie approx. in a 2D plane. The current solution is likely much more efficient then a solution in 3D with additional parameters.
Hopefully the code is self-explanatory. I recommend looking at the link included in the OP, it explains the purpose of the loop over phi.
And this is how you can inspect the result of the fit:
a = xopt(1);
b = xopt(2);
alpha = xopt(3);
z = [xopt(4:5) ; 0]';
phi = linspace(0,2*pi,100)';
simdat = [a*cos(phi) b*sin(phi) zeros(size(phi))];
R = [cos(alpha), -sin(alpha), 0; sin(alpha), cos(alpha), 0; 0, 0, 1];
simdat = simdat*R + ones(size(simdat,1), 1)*z ;
figure, hold on
plot3(datap(:,1),datap(:,2),datap(:,3),'o')
plot3(simdat(:,1),simdat(:,2),zeros(size(simdat,1),1),'r-')
Edit
The following is a 3D approach. It does not appear to be very robust as it is quite sensitive to the choice of starting parameters. Some improvements may be necessary.
CM = mean(data);
datap = data - ones(size(data,1),1)*CM;
xopt = [ 0.07 0.25 1 -0.408976 0.610120 0 0 0]';
options=1;
xopt = fmins(#fellipse3d,xopt,options,[],datap) % set the distance minimum as the target function
The function fellipse3d is
function [merit]= fellipse3d(x,data) % x is the initial parameters, data stores the datapoints
a = abs(x(1));
b = abs(x(2));
alpha = x(3);
beta = x(4);
gamma = x(5);
z = x(6:8)';
[dim1, dim2]=size(data);
R1 = [cos(alpha), sin(alpha), 0; -sin(alpha), cos(alpha), 0; 0, 0, 1];
R2 = [1, 0, 0; 0, cos(beta), sin(beta); 0, -sin(beta), cos(beta)];
R3 = [cos(gamma), sin(gamma), 0; -sin(gamma), cos(gamma), 0; 0, 0, 1];
R = R3*R2*R1;
data = (data - z(ones(dim1,1),:))*R;
merit = 0;
for i=1:dim1
dist=#(phi)sum( ([a*cos(phi);b*sin(phi);0] - data(i,:)').^2 );
phi=fminbnd(dist,0,2*pi);
merit = merit+dist(phi);
end
end
You can visualize the results with
a = xopt(1);
b = xopt(2);
alpha = -xopt(3);
beta = -xopt(4);
gamma = -xopt(5);
z = xopt(6:8)' + CM;
dim1 = 100;
phi = linspace(0,2*pi,dim1)';
simdat = [a*cos(phi) b*sin(phi) zeros(size(phi))];
R1 = [cos(alpha), sin(alpha), 0; ...
-sin(alpha), cos(alpha), 0; ...
0, 0, 1];
R2 = [1, 0, 0; ...
0, cos(beta), sin(beta); ...
0, -sin(beta), cos(beta)];
R3 = [cos(gamma), sin(gamma), 0; ...
-sin(gamma), cos(gamma), 0; ...
0, 0, 1];
R = R1*R2*R3;
simdat = simdat*R + z(ones(dim1,1),:);
figure, hold on
plot3(data(:,1),data(:,2),data(:,3),'o')
plot3(simdat(:,1),simdat(:,2),simdat(:,3),'r-')

Colouring a Cylinder in MATLAB using the density of points located on it

I have a plot in MATLAB that is currently just a cylinder. I also have a large set of data points from an experiment that lie on this cylinder. I want to colour the cylinder based on the density of these points (ie. Dark red for high density, fading to blue for low density). I am unsure what the best way to do this would be. Currently I draw the points and the mesh for the cylinder separately. The points are not uniformly spaced.
rad = linspace( 0, 1, 100 ) ;
theta = linspace( 0, 2 * pi, 100 ) ;
[r, th] = meshgrid( rad, theta ) ;
x = 190 * cos( th ) ;
y = 115 * sin( th ) ;
z = 1730 * r ;
mesh( x, y, z )
hold on
x = [35.12 -44.44 24.98 -17.05 152.52 109.28 -181.85 -72.26 84.45 -89.96 55.02 70.88 172.08 -144.16 44.24 28.81 -30.14 72.79 -126.75 -37.22]
y = [-113.01 -111.80 -114.00 -114.53 -68.57 -94.07 -33.31 -106.35 -103.01 -101.28 -110.07 -106.69 -48.74 -74.90 -111.83 -113.66 -113.54 -106.22 -85.66 -112.71]
z = [1650.59 767.18 845.06 311.28 1352.75 921.70 1111.35 1572.80 1231.16 89.67 891.30 551.67 547.92 983.57 1746.61 1346.11 810.22 465.33 1564.76 1624.73]
scatter3( x, y, z )
Below is a pictorial example of what I'm trying to achieve:
It's a bit tricky here...
Let's start by estimating the density of the points on the surface of the cylinder, converting their 3D coordinates to 2D (because they are on a 2D surface in 3D space)
xx = [35.12 -44.44 24.98 -17.05 152.52 109.28 -181.85 -72.26 84.45 -89.96 55.02 70.88 172.08 -144.16 44.24 28.81 -30.14 72.79 -126.75 -37.22];
yy = [-113.01 -111.80 -114.00 -114.53 -68.57 -94.07 -33.31 -106.35 -103.01 -101.28 -110.07 -106.69 -48.74 -74.90 -111.83 -113.66 -113.54 -106.22 -85.66 -112.71];
zz = [1650.59 767.18 845.06 311.28 1352.75 921.70 1111.35 1572.80 1231.16 89.67 891.30 551.67 547.92 983.57 1746.61 1346.11 810.22 465.33 1564.76 1624.73];
tt = atan2( yy./115, xx./190 ); %// angle in range [-pi pi]
tt( tt<0 ) = tt( tt<0 ) + 2*pi; %// in range [0..2*pi] for compatibility with definition of `theta`.
%//compute density using hist3
[n c] = hist3( [tt;zz]' ); %'// you can play with the granularity here...
Extrapolate the histogram over the entire plotting surface
d = interpn( c{1}, c{2}, n, th, z, 'linear', 0 );
Now we can use the density to color the cylinder
mesh( x, y, z, d );
Resulting with

Least squares circle fitting using MATLAB Optimization Toolbox

I am trying to implement least squares circle fitting following this paper (sorry I can't publish it). The paper states, that we could fit a circle, by calculating the geometric error as the euclidean distance (Xi'') between a specific point (Xi) and the corresponding point on the circle (Xi'). We have three parametres: Xc (a vector of coordinates the center of circle), and R (radius).
I came up with the following MATLAB code (note that I am trying to fit circles, not spheres as it is indicated on the images):
function [ circle ] = fit_circle( X )
% Kör paraméterstruktúra inicializálása
% R - kör sugara
% Xc - kör középpontja
circle.R = NaN;
circle.Xc = [ NaN; NaN ];
% Kezdeti illesztés
% A köz középpontja legyen a súlypont
% A sugara legyen az átlagos négyzetes távolság a középponttól
circle.Xc = mean( X );
d = bsxfun(#minus, X, circle.Xc);
circle.R = mean(bsxfun(#hypot, d(:,1), d(:,2)));
circle.Xc = circle.Xc(1:2)+random('norm', 0, 1, size(circle.Xc));
% Optimalizáció
options = optimset('Jacobian', 'on');
out = lsqnonlin(#ort_error, [circle.Xc(1), circle.Xc(2), circle.R], [], [], options, X);
end
%% Cost function
function [ error, J ] = ort_error( P, X )
%% Calculate error
R = P(3);
a = P(1);
b = P(2);
d = bsxfun(#minus, X, P(1:2)); % X - Xc
n = bsxfun(#hypot, d(:,1), d(:,2)); % || X - Xc ||
res = d - R * bsxfun(#times,d,1./n);
error = zeros(2*size(X,1), 1);
error(1:2:2*size(X,1)) = res(:,1);
error(2:2:2*size(X,1)) = res(:,2);
%% Jacobian
xdR = d(:,1)./n;
ydR = d(:,2)./n;
xdx = bsxfun(#plus,-R./n+(d(:,1).^2*R)./n.^3,1);
ydy = bsxfun(#plus,-R./n+(d(:,2).^2*R)./n.^3,1);
xdy = (d(:,1).*d(:,2)*R)./n.^3;
ydx = xdy;
J = zeros(2*size(X,1), 3);
J(1:2:2*size(X,1),:) = [ xdR, xdx, xdy ];
J(2:2:2*size(X,1),:) = [ ydR, ydx, ydy ];
end
The fitting however is not too good: if I start with the good parameter vector the algorithm terminates at the first step (so there is a local minima where it should be), but if I perturb the starting point (with a noiseless circle) the fitting stops with very large errors. I am sure that I've overlooked something in my implementation.
For what it's worth, I implemented these methods in MATLAB a while ago. However, I did it apparently before I knew about lsqnonlin etc, as it uses a hand-implemented regression. This is probably slow, but may help to compare with your code.
function [x, y, r, sq_error] = circFit ( P )
%# CIRCFIT fits a circle to a set of points using least sqaures
%# P is a 2 x n matrix of points to be fitted
per_error = 0.1/100; % i.e. 0.1%
%# initial estimates
X = mean(P, 2)';
r = sqrt(mean(sum((repmat(X', [1, length(P)]) - P).^2)));
v_cen2points = zeros(size(P));
niter = 0;
%# looping until convergence
while niter < 1 || per_diff > per_error
%# vector from centre to each point
v_cen2points(1, :) = P(1, :) - X(1);
v_cen2points(2, :) = P(2, :) - X(2);
%# distacnes from centre to each point
centre2points = sqrt(sum(v_cen2points.^2));
%# distances from edge of circle to each point
d = centre2points - r;
%# computing 3x3 jacobean matrix J, and solvign matrix eqn.
R = (v_cen2points ./ [centre2points; centre2points])';
J = [ -ones(length(R), 1), -R ];
D_rXY = -J\d';
%# updating centre and radius
r_old = r; X_old = X;
r = r + D_rXY(1);
X = X + D_rXY(2:3)';
%# calculating maximum percentage change in values
per_diff = max(abs( [(r_old - r) / r, (X_old - X) ./ X ])) * 100;
%# prevent endless looping
niter = niter + 1;
if niter > 1000
error('Convergence not met in 1000 iterations!')
end
end
x = X(1);
y = X(2);
sq_error = sum(d.^2);
This is then run with:
X = [1 2 5 7 9 3];
Y = [7 6 8 7 5 7];
[x_centre, y_centre, r] = circFit( [X; Y] )
And plotted with:
[X, Y] = cylinder(r, 100);
scatter(X, Y, 60, '+r'); axis equal
hold on
plot(X(1, :) + x_centre, Y(1, :) + y_centre, '-b', 'LineWidth', 1);
Giving: