B-Spline in 3D can be extended to produce a scalar function of three parameters:
The B-Spline is defined by 64 control points (the data values within a 4X4X4 voxel neighborhood), and evaluated inside the unit cube bounded by the eight central voxels, with t, s and r representing distances in the x, y and z direction respectively.
based on above notes I wrote my function in Matlab:
function [TO]=bspline_matrixform(I,valr,vals,valt,i,j,k)
% I : Input
% i : Index of grid point in x direction
% j : Index of grid point in y direction
% k : Index of grid point in z direction
r = valr; % Val r value in the grid
s = vals; % Val s value in the grid
t = valt; % Val t value in the grid
M = 1/6*[-1 3 -3 1;3 -6 3 0;-3 0 3 0;1 4 1 0];
R = [ r^3 r^2 r 1]; % 0<=r<=1
S = [ s^3 s^2 s 1]; % 0<=s<=1
T = [ t^3 t^2 t 1]; % 0<=w<=1
index = 1;
QK = zeros(4,1);
for ki= k-1:k+2
Q = QCalculation(I,i,j,ki);
QK(index) = S*M*Q*M'*T';
index = index+1;
end
TO = R*M*[QK(1) QK(2) QK(3) QK(4)]';
end
function Q = QCalculation (I,i,j,k)
Q = [I(i-1,j-1,k) I(i,j-1,k) I(i+1,j-1,k) I(i+2,j-1,k)
I(i-1,j,k) I(i,j,k) I(i+1,j,k) I(i+2,j,k)
I(i-1,j+1,k) I(i,j+1,k) I(i+1,j+1,k) I(i+2,j+1,k)
I(i-1,j+2,k) I(i,j+2,k) I(i+1,j+2,k) I(i+2,j+2,k)];
end
if my function is true, how should I call my function now? I dont know how should i calculate r,s and t.
P.S: to run this function, i have control points and ngrid in 3D, E.g from X-Y point of view, my control point( blue) and grid(red) are like this:
based on above notes, s,r and t are distances values between control points and the grid in above picture?
Anybody can help me to find out am I going through the right way and understanding? any alternative way or example code that can help im in 3D Bspline?
P.S: I am doing image registration (3d MRI), and my control points are my motion fields of registration result. I need to apply B-Spline to make my registration more smooth.
Thanks a lot
Related
My code is not plotting the correct contour plot for a plane perpendicular to a ring of charge running through its center properly. My problem is that the contour plot is not filling 2D space.
I've made two versions of code, one uses a for loop to calculate a
Riemann sum and the other simply uses the sum command. Both rely on the
'subs' command for substituting values from a meshgrid into my expression for V (electric potential).
Version 1 (using for loop):
%% Computing a symbolic expression for V for anywhere in space
syms x y z % phiprime is angle that an elemental dq of the circular
charge is located at, x,y and z are arbitrary points in space outside the
charge distribution
N = 200; % number of increments to sum
R = 2; % radius of circle is 2 meters
dphi = 2*pi/N; % discretizing the circular line of charge which spans 2pi
integrand = 0;
for phiprime = 0:dphi:2*pi
% phiprime ranges from 0 to 2pi in increments of dphi
integrand = integrand + dphi./(sqrt(((x - R.*cos(phiprime) )).^2 + ((y -
R.*sin(phiprime) ).^2) + z.^2));
end
intgrl = sum(integrand);
% uncessary but harmless step that I leave to show that I am using the
sum of the above expression for each dphi
eps0 = 8.854e-12;
kC = 1/(4*pi*eps0);
rhol = 1*10^-9; % linear charge density
Vtot = kC*rhol*R.*intgrl; % symbolic expression for Vtot
%% Graphing V & E in plane perpedicular to ring & passing through center
[Y1, Z1] = meshgrid(-4:.5:4, -4:.5:4);
Vcont1 = subs(Vtot, [x,y,z], {0,Y1,Z1}); % Vcont1 stands for V contour; 1
is because I do the plane of the ring next
contour(Y1,Z1,Vcont1)
xlabel('y - axis [m]')
ylabel('z - axis [m]')
title('V in a plane perpendicular to a ring of charge (N = 200)')
str = {'Red line is side view', 'of ring of charge'};
text(-1,2,str)
hold on
% visually displaying line of charge on plot
circle = rectangle('Position',[-2 0 4 .1],'Curvature',[1,1]);
set(circle,'FaceColor',[1, 0, 0],'EdgeColor',[1, 0, 0]);
% taking negative gradient of V and finding symbolic equations for Ex, Ey
and Ez
g = gradient(-1.*(kC*rhol*R.*intgrl),[x,y,z]);
%% now substituting all the values of the 2D coordinate system for the
symbolic x and y variables to get numeric values for Ex and Ey
Ey1 = subs(g(2), [x y z], {0,Y1,Z1});
Ez1 = subs(g(3), [x y z], {0,Y1,Z1});
E1 = sqrt(Ey1.^2 + Ez1.^2); % full numeric magnitude of E in y-z plane
Eynorm1 = Ey1./E1; % This normalizes the electric field lines
Eznorm1 = Ez1./E1;
quiver(Y1,Z1,Eynorm1,Eznorm1);
hold off
Version 2 (using sum command):
syms x y z
R = 2; % radius of circle is 2 meters
N=100;
dphi = 2*pi/N; % discretizing the circular line of charge which spans 2pi
phiprime = 0:dphi:2*pi; %phiprime ranges from 0 to 2pi in increments of
dphi
integrand = dphi./(sqrt(((x - R.*cos(phiprime) )).^2 + ((y -
R.*sin(phiprime) ).^2) + z.^2));
phiprime = 0:dphi:2*pi;
intgrl = sum(integrand); % Reimann sum performed here
eps0 = 8.854e-12;
kC = 1/(4*pi*eps0);
rhol = 1*10^-9; % linear charge density
Vtot = kC*rhol*R.*intgrl; % symbolic expression for Vtot
Everything else after that point for version 2 is the same as version 1 (substituting for the symbols x,y,z etc)
I would post images of what the code produces but apparently you need 10 reputation for that. Thanks stackoverflow. This will be much more confusing to understand without the images.
The vector field produced by my code is correct while the contour plot seems to use only a few points around the ends of the ring and connect them with straight lines in a strange diamond shape. I can't get it to fill space.
I receive no error messages. The contour lines accumulate around the ends of the ring (where the potential would approach infinity) in a strange diamond shape but aren't graphed anywhere else. I need the contour plot to fill the 2D grid
I received a solution to this question from MATLAB's community and posted about it here:
https://scicomp.stackexchange.com/questions/32834/graphing-electric-potential-of-a-ring-of-charge-using-matlab-help/32842#32842
I would post here but this "you can't post images because you don't have enough reputation" thing would make my explanation too abstract and difficult to understand so go take a look if you are having MATLAB contour plot issues and want to see my problem and solution
I have to plot the speed vector of an object orbiting around a central body. This is a Keplerian context. The trajectory of object is deduced from the classical formula ( r = p/(1+e*cos(theta)) with e=eccentricity.
I manage into plotting the elliptical orbit but now, I would like to plot for each point of this orbit the velocity speed of object.
To compute the velocity vector, I start from classical formulas (into polar coordinates), below the 2 components :
v_r = dr/dt and v_theta = r d(theta)/dt
To take a time step dt, I extract the mean anomaly which is proportional to time.
And Finally, I compute the normalization of this speed vector.
clear % clear variables
e = 0.8; % eccentricity
a = 5; % semi-major axis
b = a*sqrt(1-e^2); % semi-minor axis
P = 10 % Orbital period
N = 200; % number of points defining orbit
nTerms = 10; % number of terms to keep in infinite series defining
% eccentric anomaly
M = linspace(0,2*pi,N); % mean anomaly parameterizes time
% M varies from 0 to 2*pi over one orbit
alpha = zeros(1,N); % preallocate space for eccentric anomaly array
%%%%%%%%%%
%%%%%%%%%% Calculations & Plotting
%%%%%%%%%%
% Calculate eccentric anomaly at each point in orbit
for j = 1:N
% initialize eccentric anomaly to mean anomaly
alpha(j) = M(j);
% include first nTerms in infinite series
for n = 1:nTerms
alpha(j) = alpha(j) + 2 / n * besselj(n,n*e) .* sin(n*M(j));
end
end
% calcualte polar coordiantes (theta, r) from eccentric anomaly
theta = 2 * atan(sqrt((1+e)/(1-e)) * tan(alpha/2));
r = a * (1-e^2) ./ (1 + e*cos(theta));
% Compute cartesian coordinates with x shifted since focus
x = a*e + r.*cos(theta);
y = r.*sin(theta);
figure(1);
plot(x,y,'b-','LineWidth',2)
xlim([-1.2*a,1.2*a]);
ylim([-1.2*a,1.2*a]);
hold on;
% Plot 2 focus = foci
plot(a*e,0,'ro','MarkerSize',10,'MarkerFaceColor','r');
hold on;
plot(-a*e,0,'ro','MarkerSize',10,'MarkerFaceColor','r');
% compute velocity vectors
for i = 1:N-1
vr(i) = (r(i+1)-r(i))/(P*(M(i+1)-M(i))/(2*pi));
vtheta(i) = r(i)*(theta(i+1)-theta(i))/(P*(M(i+1)-M(i))/(2*pi));
vrNorm(i) = vr(i)/norm([vr(i),vtheta(i)],1);
vthetaNorm(i) = vtheta(i)/norm([vr(i),vtheta(i)],1);
end
% Plot velocity vector
quiver(x(30),y(30),vrNorm(30),vthetaNorm(30),'LineWidth',2,'MaxHeadSize',1);
% Label plot with eccentricity
title(['Elliptical Orbit with e = ' sprintf('%.2f',e)]);
Unfortunately, once plot performed, it seems that I get a bad vector for speed. Here for example the 30th element of vrNorm and vthetaNorm arrays :
As you can see, the vector has the wrong direction (If I assume to take 0 for theta from the right axis and positive variation like into trigonometrics).
If someone could see where is my error, this would nice.
UPDATE 1: Has this vector representing the speed on elliptical orbit to be tangent permanently to the elliptical curve ?
I would like to represent it by taking the right focus as origin.
UPDATE 2:
With the solution of #MadPhysicist, I have modified :
% compute velocity vectors
vr(1:N-1) = (2*pi).*diff(r)./(P.*diff(M));
vtheta(1:N-1) = (2*pi).*r(1:N-1).*diff(theta)./(P.*diff(M));
% Plot velocity vector
for l = 1:9 quiver(x(20*l),y(20*l),vr(20*l)*cos(vtheta(20*l)),vr(20*l)*sin(vtheta(20*l)),'LineWidth',2,'MaxHeadSize',1);
end
% Label plot with eccentricity
title(['Elliptical Orbit with e = ' sprintf('%.2f',e)]);
I get the following result :
On some parts of the orbit, I get wrong directions and I don't understand why ...
There are two issues with your code:
The normalization is done incorrectly. norm computes the generalized p-norm for a vector, which defaults to the Euclidean norm. It expects Cartesian inputs. Setting p to 1 means that it will just return the largest element of your vector. In your case, the normalization is meaningless. Just set vrNorm as
vrNorm = vr ./ max(vr)
It appears that you are passing in the polar coordinates vrNorm and vthetaNorm to quiver, which expects Cartesian coordinates. It's easy to make the conversion in a vectorized manner:
vxNorm = vrNorm * cos(vtheta);
vyNorm = vrNorm * sin(vtheta);
This assumes that I understand where your angle is coming from correctly and that vtheta is in radians.
Note
The entire loop
for i = 1:N-1
vr(i) = (r(i+1)-r(i))/(P*(M(i+1)-M(i))/(2*pi));
vtheta(i) = r(i)*(theta(i+1)-theta(i))/(P*(M(i+1)-M(i))/(2*pi));
vrNorm(i) = vr(i)/norm([vr(i),vtheta(i)],1);
vthetaNorm(i) = vtheta(i)/norm([vr(i),vtheta(i)],1);
end
can be rewritten in a fully vectorized manner:
vr = (2 * pi) .* diff(r) ./ (P .* diff(M))
vtheta = (2 * pi) .* r .* diff(theta) ./ (P .* diff(M))
vrNorm = vr ./ max(vr)
vxNorm = vrNorm * cos(vtheta);
vyNorm = vrNorm * sin(vtheta);
Note 2
You can call quiver in a vectorized manner, on the entire dataset, or on a subset:
quiver(x(20:199:20), y(20:199:20), vxNorm(20:199:20), vyNorm(20:199:20), ...)
I have a piecewise function, where domain changes for each case. The function is as follows:
For
(x,y)greater than Divider v= f(x,y) (A1)
(x,y)less than Divider v = g(x,y) (A2)
The location of the divider changes with tilt angle of the rectangle given in figures 1 and 2.Figure 1 & 2 The divider will always be a bisector of the rectangle. For example, the divider makes an angle (alpha + 90) with the horizontal.
If the rectangle makes an angle 0, it's easy to implement above functions as I can create meshgrid from
x =B to C & y = A to D for A1
x =A to B & y = A to D for A2
However, when the angles for the rectangle are different, I can't figure out how to create the mesh to calculate the function v using the algorithm A1 and A2 above.
I was thinking of using some inequality and using the equation of the line (as I have the co-ordinates for the center of the rectangle and the angle of tilt). But, I can't seem to think of a way to do it for all angles (for example , slope of pi/2 as in the first figure, yields infinity). Even if I do create some kind of inequality, I can't create a mesh.
1Please help me with this problem. I have wasted a lot of time on this. It seems to be out of my reach
%% Constants
Angle1=0;
Angle1=Angle1.*pi./180;
rect_center=0; % in m
rect_length=5; % in m
rect_width=1; % in m
rect_strength=1.8401e-06;
Angle2=0;
Angle2 =Angle2.*pi./180;
%% This code calculates the outer coordinates of the rectangle by using the central point
% the following code calculates the vertices
vertexA=rect_center+(-rect_width./2.*exp(1i.*1.5708)-rect_length./2).*exp(1i.*Angle2);
vertexA=[vertexA,vertexA+2.*(rect_width./2.*exp(1i.*1.5708)).*exp(1i.*Angle2)];
vertexB=rect_center+(-rect_width./2.*exp(1i.*1.5708)+rect_length./2).*exp(1i.*Angle2);
vertexB=[vertexB,vertexB+2.*(rect_width./2.*exp(1i.*1.5708)).*exp(1i.*Angle2)];
za1=vertexA(1:numel(vertexA)/2);
za2=vertexA(1+numel(vertexA)/2:numel(vertexA));
zb1=vertexB(1:numel(vertexB)/2);
zb2=vertexB(1+numel(vertexB)/2:numel(vertexB));
arg1=exp(-1i.*Angle2);
%% This Section makes the two equations necessary for making the graphs
syms var_z
% Equation 1
Eqn1(var_z)=1.5844e-07.*exp(-1i.*Angle1).*var_z./9.8692e-13;
% subparts of the Equation 2
A = 1.0133e+12.*(-1i.*rect_strength.*exp(-1i*Angle2)./(2*pi.*rect_length.*rect_width*0.2));
ZA1 = var_z+za1-2*rect_center;
ZA2 = var_z+za2-2*rect_center;
ZB1 = var_z+zb1-2*rect_center;
ZB2 = var_z+zb2-2*rect_center;
ZAA2 = log(abs(ZA2)) + 1i*mod(angle(ZA2),2*pi);
ZAA1 = log(abs(ZA1)) + 1i*mod(angle(ZA1),2*pi);
ZBB1 = log(abs(ZB1)) + 1i*mod(angle(ZB1),2*pi);
ZBB2 = log(abs(ZB2)) + 1i*mod(angle(ZB2),2*pi);
%Equation 2 ; this is used for the left side of the center
Eqn2= A*(ZA2*(log(ZA2)-1)-(ZA1*(log(ZA1)-1))+(ZB1*(log(ZB1)-1))-(ZB2*(log(ZB2)-1)));
%Equation 3 ; this is used for the right side of the center
Eqn3 = A.*(ZA2*(ZAA2-1)-(ZA1*(ZAA1-1))+(ZB1*(ZBB1-1))-(ZB2*(ZBB2-1)));
%Equation 4 :Add Equation 2 and Equation 1; this is used for the left side of the center
Eqn4 = matlabFunction(Eqn1+Eqn2,'vars',var_z);
%Equation 5: Add Equation 3 and Equation 1; this is used for the right side of the center
Eqn5 = matlabFunction(Eqn1+Eqn3,'vars',var_z);
%% Prepare for making the plots
minx=-10; %min x coordinate
maxx=10; %max x coordinate
nr_x=1000; %nr x points
miny=-10; %min y coordinate
maxy=10; %max y coordinate
nr_y=1000; %nr y points
%This vector starts from left corner (minx) to the middle of the plot surface,
%The middle of the plot surface lies at the center of the rectange
%created earlier
xvec1=minx:(rect_center-minx)/(0.5*nr_x-1):rect_center;
%This vector starts from middle to the right corner (maxx) of the plot surface,
%The middle of the plot surface lies at the center of the rectange
%created earlier
xvec2=rect_center:(maxx-rect_center)/(0.5*nr_x-1):maxx;
%the y vectors start from miny to maxy
yvec1=miny:(maxy-miny)/(nr_y-1):maxy;
yvec2=miny:(maxy-miny)/(nr_y-1):maxy;
% create mesh from above vectors
[x1,y1]=meshgrid(xvec1,yvec1);
[x2,y2]=meshgrid(xvec2,yvec2);
z1=x1+1i*y1;
z2=x2+1i*y2;
% Calculate the above function using equation 4 and equation 5 using the mesh created above
r1 = -real(Eqn5(z1));
r2 = -real(Eqn4(z2));
%Combine the calculated functions
Result = [r1 r2];
%Combine the grids
x = [x1 x2];
y = [y1 y2];
% plot contours
[c,h]=contourf(x,y,Result(:,:,1),50,'LineWidth',1);
% plot the outerboundary of the rectangle
line_x=real([vertexA;vertexB]);
line_y=imag([vertexA;vertexB]);
line(line_x,line_y,'color','r','linestyle',':','linewidth',5)
The final Figure is supposed to look like this.Final Expected Figure.
I'm not sure which angle defines the dividing line so I assume it's Angle1. It looks like logical indexing is the way to go here. Instead of creating two separate mesh grids we simply create the entire mesh grid then partition it into two sets and operate on each independently.
%% Prepare for making the plots
minx=-10; %min x coordinate
maxx=10; %max x coordinate
nr_x=1000; %nr x points
miny=-10; %min y coordinate
maxy=10; %max y coordinate
nr_y=1000; %nr y points
% create full mesh grid
xvec=linspace(minx,maxx,nr_x);
yvec=linspace(miny,maxy,nr_y);
[x,y]=meshgrid(xvec,yvec);
% Partition mesh based on divider line
% Assumes the line passes through (ox,oy) with normal vector defined by Angle1
ox = rect_center;
oy = rect_center;
a = cos(Angle1);
b = sin(Angle1);
c = -(a*ox + b*oy);
% use logical indexing to opperate on the appropriate parts of the mesh
idx1 = a*x + b*y + c < 0;
idx2 = ~idx1;
z = zeros(size(x));
z(idx1) = x(idx1) + 1i*y(idx1);
z(idx2) = x(idx2) + 1i*y(idx2);
% Calculate the above function using equation 4 and equation 5
% using the mesh created above
Result = zeros(size(z));
Result(idx1) = -real(Eqn5(z(idx1)));
Result(idx2) = -real(Eqn4(z(idx2)));
For example with Angle1 = 45 and Angle2 = 45 we get the following indexing
>> contourf(x,y,idx1);
>> line(line_x,line_y,'color','r','linestyle',':','linewidth',5);
where the yellow region uses Eqn5 and the blue region uses Eqn4. This agrees with the example you posted but I don't know what the resulting contour map for other cases is supposed to look like.
Hope this helps.
I'm trying to create a matrix of 0 values, with 1 values filling a ellipse shape. My ellipse was generated using minVolEllipse.m (Link 1) which returns a matrix of the ellipse equation in the 'center form' and the center of the ellipse. I then use a snippet of code from Ellipse_plot.m (from the aforementioned link) to parameterize the vector into major/minor axes, generate a parametric equation, and generate a matrix of transformed coordinates. You can see their code to see how this is done. The result is a matrix that has index locations for points along the ellipse. It does not encompass every value along the outline of the ellipse unless I set the number of grid points, N, to a ridiculously high value.
When I use the MATLAB plot or patch commands I see exactly the result I'm looking for. However, I want this represented as a matrix of 0 values with 1s where patch 'fills in' the blanks. It is apparent that MATLAB has this functionality, but I have yet to find the code to execute it. What I am looking for is similar to how bwfill of the image processing toolbox works (Link 2). bwfill does not work for me because my ellipse is not contiguous, so the function returns a matrix filled completely with 1 values.
Hopefully I have outlined the problem well enough, if not please comment and I can edit the post to clarify.
EDIT:
I have devised a strategy using the 2-D X vector from Ellipse_plot.m as an input to EllipseDirectFit.m (Link 3). This function returns the coefficients for the ellipse function ax^2+bxy+cy^2+dx+dy+f=0. Using these coefficients I calculate the angle between the x-axis and the major axis of the ellipse. This angle, along with the center and major/minor axes are passed into ellipseMatrix.m (Link 4), which returns a filled matrix. Unfortunately, the matrix appears to be out of rotation from what I want. Here is the portion of my code:
N = 20; %Number of grid points in ellipse
ellipsepoints = clusterpoints(clusterpoints(:,1)==i,2:3)';
[A,C] = minVolEllipse(ellipsepoints,0.001,N);
%%%%%%%%%%%%%%
%
%Adapted from:
% Ellipse_plot.m
% Nima Moshtagh
% nima#seas.upenn.edu
% University of Pennsylvania
% Feb 1, 2007
% Updated: Feb 3, 2007
%%%%%%%%%%%%%%
%
%
% "singular value decomposition" to extract the orientation and the
% axes of the ellipsoid
%------------------------------------
[U D V] = svd(A);
%
% get the major and minor axes
%------------------------------------
a = 1/sqrt(D(1,1))
b = 1/sqrt(D(2,2))
%theta values
theta = [0:1/N:2*pi+1/N];
%
% Parametric equation of the ellipse
%----------------------------------------
state(1,:) = a*cos(theta);
state(2,:) = b*sin(theta);
%
% Coordinate transform
%----------------------------------------
X = V * state;
X(1,:) = X(1,:) + C(1);
X(2,:) = X(2,:) + C(2);
% Output: Elip_Eq = [a b c d e f]' is the vector of algebraic
% parameters of the fitting ellipse:
Elip_Eq = EllipseDirectFit(X')
% http://mathworld.wolfram.com/Ellipse.html gives the equation for finding the angle theta (teta).
% The coefficients from EllipseDirectFit are rescaled to match what is expected in the wolfram link.
Elip_Eq(2) = Elip_Eq(2)/2;
Elip_Eq(4) = Elip_Eq(4)/2;
Elip_Eq(5) = Elip_Eq(5)/2;
if Elip_Eq(2)==0
if Elip_Eq(1) < Elip_Eq(3)
teta = 0;
else
teta = (1/2)*pi;
endif
else
tetap = (1/2)*acot((Elip_Eq(1)-Elip_Eq(3))/(Elip_Eq(2)));
if Elip_Eq(1) < Elip_Eq(3)
teta = tetap;
else
teta = (pi/2)+tetap;
endif
endif
blank_mask = zeros([height width]);
if teta < 0
teta = pi+teta;
endif
%I may need to switch a and b, depending on which is larger (so that the fist is the major axis)
filled_mask1 = ellipseMatrix(C(2),C(1),b,a,teta,blank_mask,1);
EDIT 2:
As a response to the suggestion from #BenVoigt, I have written a for-loop solution to the problem, here:
N = 20; %Number of grid points in ellipse
ellipsepoints = clusterpoints(clusterpoints(:,1)==i,2:3)';
[A,C] = minVolEllipse(ellipsepoints,0.001,N);
filled_mask = zeros([height width]);
for y=0:1:height
for x=0:1:width
point = ([x;y]-C)'*A*([x;y]-C);
if point < 1
filled_mask(y,x) = 1;
endif
endfor
endfor
Although this is technically a solution to the problem, I am interested in a non-iterative solution. I'm running this script over many large images, and need it to be very fast and parallel.
EDIT 3:
Thanks #mathematical.coffee for this solution:
[X,Y] = meshgrid(0:width,0:height);
fill_mask=arrayfun(#(x,y) ([x;y]-C)'*A*([x;y]-C),X,Y) < 1;
However, I believe there is yet a better way to do this. Here is a for-loop implementation that I did that runs faster than both above attempts:
ellip_mask = zeros([height width]);
[U D V] = svd(A);
a = 1/sqrt(D(1,1));
b = 1/sqrt(D(2,2));
maxab = ceil(max(a,b));
xstart = round(max(C(1)-maxab,1));
xend = round(min(C(1)+maxab,width));
ystart = round(max(C(2)-maxab,1));
yend = round(min(C(2)+maxab,height));
for y = ystart:1:yend
for x = xstart:1:xend
point = ([x;y]-C)'*A*([x;y]-C);
if point < 1
ellip_mask(y,x) = 1;
endif
endfor
endfor
Is there a way to accomplish this goal (the total image size is still [width height]) without this for-loop? The reason this is faster is because I don't have to iterate over the entire image to determine if my point is within the ellipse. Instead, I can simply iterate over a square region that is the length of the center +/- the largest principle axis.
Expanding the matrix multiply, which is an elliptic norm, gives a fairly simply vectorized expression:
[X,Y] = meshgrid(0:width,0:height);
X = X - C(1);
Y = Y - C(2);
fill_mask = X.^2 * A(1,1) + X.*Y * (A(1,2) + A(2,1)) + Y.^2 * A(2,2) < 1;
This is what I intended by my original comment.
I would like to reproduce the following figure in MATLAB:
There are two classes of points with X and Y coordinates. I'd like to surround each class with an ellipse with one parameter of standard deviation, which determine how far the ellipse will go along the axis.
The figure was created with another software and I don't exactly understand how it calculates the ellipse.
Here is the data I'm using for this figure. The 1st column is class, 2nd - X, 3rd - Y. I can use gscatter to draw the points itself.
A = [
0 0.89287 1.54987
0 0.69933 1.81970
0 0.84022 1.28598
0 0.79523 1.16012
0 0.61266 1.12835
0 0.39950 0.37942
0 0.54807 1.66173
0 0.50882 1.43175
0 0.68840 1.58589
0 0.59572 1.29311
1 1.00787 1.09905
1 1.23724 0.98834
1 1.02175 0.67245
1 0.88458 0.36003
1 0.66582 1.22097
1 1.24408 0.59735
1 1.03421 0.88595
1 1.66279 0.84183
];
gscatter(A(:,2),A(:,3),A(:,1))
FYI, here is the SO question on how to draw ellipse. So, we just need to know all the parameters to draw it.
Update:
I agree that the center can be calculated as the means of X and Y coordinates. Probably I have to use principal component analysis (PRINCOMP) for each class to determine the angle and shape. Still thinking...
Consider the code:
%# generate data
num = 50;
X = [ mvnrnd([0.5 1.5], [0.025 0.03 ; 0.03 0.16], num) ; ...
mvnrnd([1 1], [0.09 -0.01 ; -0.01 0.08], num) ];
G = [1*ones(num,1) ; 2*ones(num,1)];
gscatter(X(:,1), X(:,2), G)
axis equal, hold on
for k=1:2
%# indices of points in this group
idx = ( G == k );
%# substract mean
Mu = mean( X(idx,:) );
X0 = bsxfun(#minus, X(idx,:), Mu);
%# eigen decomposition [sorted by eigen values]
[V D] = eig( X0'*X0 ./ (sum(idx)-1) ); %#' cov(X0)
[D order] = sort(diag(D), 'descend');
D = diag(D);
V = V(:, order);
t = linspace(0,2*pi,100);
e = [cos(t) ; sin(t)]; %# unit circle
VV = V*sqrt(D); %# scale eigenvectors
e = bsxfun(#plus, VV*e, Mu'); %#' project circle back to orig space
%# plot cov and major/minor axes
plot(e(1,:), e(2,:), 'Color','k');
%#quiver(Mu(1),Mu(2), VV(1,1),VV(2,1), 'Color','k')
%#quiver(Mu(1),Mu(2), VV(1,2),VV(2,2), 'Color','k')
end
EDIT
If you want the ellipse to represent a specific level of standard deviation, the correct way of doing is by scaling the covariance matrix:
STD = 2; %# 2 standard deviations
conf = 2*normcdf(STD)-1; %# covers around 95% of population
scale = chi2inv(conf,2); %# inverse chi-squared with dof=#dimensions
Cov = cov(X0) * scale;
[V D] = eig(Cov);
I'd try the following approach:
Calculate the x-y centroid for the center of the ellipse (x,y in the linked question)
Calculate the linear regression fit line to get the orientation of the ellipse's major axis (angle)
Calculate the standard deviation in the x and y axes
Translate the x-y standard deviations so they're orthogonal to the fit line (a,b)
I'll assume there is only one set of points given in a single matrix, e.g.
B = A(1:10,2:3);
you can reproduce this procedure for each data set.
Compute the center of the ellipsoid, which is the mean of the points. Matlab function: mean
Center your data. Matlab function bsxfun
Compute the principal axis of the ellipsoid and their respective magnitude. Matlab function: eig
The successive steps are illustrated below:
Center = mean(B,1);
Centered_data = bsxfun(#minus,B,Center);
[AX,MAG] = eig(Centered_data' * Centered_data);
The columns of AX contain the vectors describing the principal axis of the ellipsoid while the diagonal of MAG contains information on their magnitude.
To plot the ellipsoid, scale each principal axis with the square root of its magnitude.