Plotting an ellipse in MATLAB given in matrix form - matlab

I have an ellipse in 2 dimensions, defined by a positive definite matrix X as follows: a point x is in the ellipse if x'*X*x <= 1. How can I plot this ellipse in matlab? I've done a bit of searching while finding surprisingly little.
Figured out the answer actually: I'd post this as an answer, but it won't let me (new user):
Figured it out after a bit of tinkering. Basically, we express the points on the ellipse border (x'*X*x = 1) as a weighted combination of the eigenvectors of X, which makes some of the math to find the points easier. We can just write (au+bv)'X(au+bv)=1 and work out the relationship between a,b. Matlab code follows (sorry it's messy, just used the same notation that I was using with pen/paper):
function plot_ellipse(X, varargin)
% Plots an ellipse of the form x'*X*x <= 1
% plot vectors of the form a*u + b*v where u,v are eigenvectors of X
[V,D] = eig(X);
u = V(:,1);
v = V(:,2);
l1 = D(1,1);
l2 = D(2,2);
pts = [];
delta = .1;
for alpha = -1/sqrt(l1)-delta:delta:1/sqrt(l1)+delta
beta = sqrt((1 - alpha^2 * l1)/l2);
pts(:,end+1) = alpha*u + beta*v;
end
for alpha = 1/sqrt(l1)+delta:-delta:-1/sqrt(l1)-delta
beta = -sqrt((1 - alpha^2 * l1)/l2);
pts(:,end+1) = alpha*u + beta*v;
end
plot(pts(1,:), pts(2,:), varargin{:})

I stumbled across this post while searching for this topic, and even though it's settled, I thought I might provide another simpler solution, if the matrix is symmetric.
Another way of doing this is to use the Cholesky decomposition of the semi-definite positive matrix E implemented in Matlab as the chol function. It computes an upper triangular matrix R such that X = R' * R. Using this, x'*X*x = (R*x)'*(R*x) = z'*z, if we define z as R*x.
The curve to plot thus becomes such that z'*z=1, and that's a circle. A simple solution is thus z = (cos(t), sin(t)), for 0<=t<=2 pi. You then multiply by the inverse of R to get the ellipse.
This is pretty straightforward to translate into the following code:
function plot_ellipse(E)
% plots an ellipse of the form xEx = 1
R = chol(E);
t = linspace(0, 2*pi, 100); % or any high number to make curve smooth
z = [cos(t); sin(t)];
ellipse = inv(R) * z;
plot(ellipse(1,:), ellipse(2,:))
end
Hope this might help!

Related

Spiral of non-overlapping circles

I want to create a spiral of circle markers which never overlap with each other. This is what I got so far, but it overlaps the first markers, and the last ones are too far apart from each other.
t = pi : pi/20 : 20*pi;
t = asind(1./t);
r = t;
x = r .* cos(t);
y = r .* sin(t);
plot(x,y,'o-');
axis equal; hold on
Plotting without redefining t as asinf(1/t) as follows, is shown in the second plot.
t = pi : pi/20 : 20*pi;
r = t;
x = r .* cos(t);
y = r .* sin(t);
plot(x,y,'o-');
Any ideas on how does the spacing of the angles t must be to accomplish that the markers don't overlap?
You can approximate the arc length, greatly simplifying Gilles-Phillipe's solution. This is a simplification, which means that the distance between the markers is not identical everywhere. However the distances are fairly consistent, especially further out.
The approximation here is to assume that the spiral is, locally, a circle. The arc length then is r*dt at a position in the spiral a distance r from the origin, for a change in angle of dt radian.
We now no longer need to solve symbolic equations. I wrote the code in a loop. I'm sure it's possible to vectorize it, making the whole thing two lines of code, but I'll leave that as an exercise to the reader.
This is the code:
d = 1; % step size
q = 1/(2*pi); % spiral constant -- radius grows by q every 1 radian turn
N = 300; % number of points
t = 0; % initial angle
r = d; % initial radius
p = zeros(100,2);
p(1,:) = [r*cos(t),r*sin(t)]; % first point
for ii=2:N
dt = d/r;
t = t+dt;
r = r+dt*q;
p(ii,:) = [r*cos(t),r*sin(t)];
end
clf
plot(p(:,1),p(:,2),'o-')
axis equal
Try this:
syms s;
scale = 10;
l = scale/2 : scale/2 : 40*scale;
t = double(arrayfun(#(y) vpasolve((0.5*(s*sqrt(1+s^2)+asinh(s)))==y,s), l));
x = t .* cos(t);
y = t .* sin(t);
plot(x,y,'o-');
pbaspect([1 1 1]);
axis(scale*[-5 5 -5 5])
The idea is to parameterize using the arclength of the curve. The arclength of this spiral is l=1/2*(t*sqrt(1+t*t)+asinh(t)) (can be found using Matlab symbolic integration). To place points uniformly, we do a uniform sampling of the arclength, and find the corresponding t by solving the equation. Since it cannot be solved easily symbolically, we use a numerical solver.
Note that the scale and the aspect ratio of the plot is really important for it to look uniform and non-overlapping. This is why I added axis/ratio definition. Since each point is solved numerically, it can take quite some time to evaluate. There may be a faster way to do it, but at least you have a result.
I obtain the following result:

data type to store precision more than double in matlab

I an working in matlab, recently I am doing research in image processing. this time I am implementing a research paper this paper, in which I am getting problem to store precision more than double. kindly check the equation 6 of that paper.
I am facing problem in following code..
img = imread('Einstein.bmp');
exponent = double(zeros(size(img,1),size(img,2)));
s = double(zeros(size(img,1),size(img,2)));
sigma=1;
for i=1:size(img,1)
for j=1:size(img,2)
exponent(i,j) = double(((i^2)+(j^2))/(2*(sigma^2)));
s(i,j) = double(exp(-exponent(i,j)));
end
end
After some values, s(i,j) gives 0 for all values but that values should not be 0. how can I avoid that problem?
Looking at your code, it seems you are trying to create a 2D Gaussian function centered at (0,0). The kernel width you are using is really small: sigma=1, seeing you are computing the function over the entire grid of an image pixels. So no wonder you are getting all zeros except for a tiny portion in the top-left corner of the matrix S.
Here is a more efficient way to create the kernel centered around the middle:
% 100x100 grid
r = 100; c = 100;
[x,y] = ndgrid(1:r,1:c);
% sigma = 20
sigma = max([r c])/5;
% 2D gaussian function
z = sum(bsxfun(#minus, [x(:) y(:)], [r c]./2).^2, 2);
z = exp(-z ./ (2*sigma^2)) ./ (sigma^2 * 2*pi);
z = reshape(z, [r c]);
If you have access to the Statistics toolbox, use mvnpdf to it write as:
z = mvnpdf([x(:) y(:)], [r c]./2, eye(2)*sigma^2);
z = reshape(z, [r c]);
or if you have the Image processing toolbox, simply use fspecial:
z = fspecial('gaussian', [r c], sigma);
The result:
imshow(z, 'DisplayRange',[], 'InitialMag','fit')
axis on, colorbar
Note that the matrix is normalized so that the integral is one (hence the small values). That's why I turned off the default [0,1] range when displaying as an image.
You could also simply map it into [0,1] range using: z = (z - min(z(:))) ./ range(z(:))
Here is the resulting matrix when viewed as a surface:
surf(x,y,z)
axis vis3d
It appears that matlab only uses 64-bit doubles, but you might have luck with one of the arbitrary precision libraries available for matlab.

surfnorm function more efficient way Matlab

After constructing the point cloud I want to get the normal of each point and I used the built-in matlab function surfnorm but its takes a lot of processing time. So if anyone could assist me do this a better and more efficient way.
I wonder if the following code would help you. There are three steps here.
Create 500 randomly spaced points (x,y), and compute a corresponding value z (the height of the surface) for which I chose a sinc like function
Resample the random points using the TriScatteredInterp function - this permits me to obtain points on an evenly sampled grid that "roughly correspond" to the initial surface
Compute the normal to "some points" on that grid (since there are 480x640 points, computing the normal at every point would just create an impossibly dense "forest of vectors"; by sampling "every 10th point" you can actually see what you are doing
The code I used was as follows:
randomX = rand(1,500);
randomY = rand(1,500);
r = 5*sqrt(randomX.^2 + randomY.^2);
randomZ = sin(r) ./ r;
% resample the data:
[xx yy] = meshgrid(linspace(0,1,640), linspace(0,1,480));
F = TriScatteredInterp(randomX(:), randomY(:), randomZ(:));
zz = F(xx, yy);
%% at each point, the normal is cross product of vectors to neighbors
xyz=reshape([xx yy zz],[size(xx) 3]);
xv = 10:30:479; yv = 10:30:639; % points at which to compute normals
dx = xyz(xv, yv+1, :) - xyz(xv, yv, :);
dy = xyz(xv+1, yv, :) - xyz(xv, yv, :);
normVecs = cross(dx, dy); % here we compute the normals.
normVecs = normVecs ./ repmat(sqrt(sum(normVecs.^2, 3)), [1 1 3]);
figure;
quiver3(xx(xv, yv), yy(xv, yv), zz(xv, yv), ...
normVecs(:,:,1), normVecs(:,:,2), normVecs(:,:,3));
axis equal
view([56 22]);
And the resulting plot:

Visualizing a toroidal surface in Matlab

I have a problem which is twofold:
How do I plot a toroidal surface in MATLAB, given a major radius R and a minor radius a? To avoid confusion, it is the toroidal/poloidal coordinate system, illustrated in the picture below, that I'm talking about.
Now, in any point (phi, theta) on this surface, the minor radius will be distorted by some value that I have stored in a matrix. How do I plot this distorted surface? This might be easy once I have the answer to part 1, but this is my actual goal, so any solution to part 1 that cannot handle this is pretty useless to me.
If anyone can tell me how to make the image appear smaller here, please do =)
Addressing your first question: first you will need to define the coordinates of your torus in toroidal coordinates (seems natural!) and then convert to Cartesian coordinates, which is how MATLAB expects all plots to be constructed (unless you are making a polar plot, of course). So we start of by defining our toroidal coordinates:
aminor = 1.; % Torus minor radius
Rmajor = 3.; % Torus major radius
theta = linspace(-pi, pi, 64) ; % Poloidal angle
phi = linspace(0., 2.*pi, 64) ; % Toroidal angle
We just want a single surface of the torus, so the minor radius is a scalar. We now make a 2D mesh of these coordinates:
[t, p] = meshgrid(phi, theta);
and convert to 3D Cartesian coordinates using the formulas given on the Wikipedia page linked to in the question:
x = (Rmajor + aminor.*cos(p)) .* cos(t);
y = (Rmajor + aminor.*cos(p)) .* sin(t);
z = aminor.*sin(p);
Now we have our torus defined in 3D, we can plot it using surf:
surf(x, y, z)
axis equal
Edit: To address your second question, it depends how you have your distortions stored, but say you have a matrix defined at each toroidal and poloidal point, you will just have to multiply the constant that is the minor radius by the distortion. In the following I create a distortion matrix that is the same dimensions as my toroidal and poloidal angle coordinate matrices and that is normally distributed about a mean of 1 with a FWHM of 0.1:
distortion = 1. + 0.1 * randn(64, 64);
x = (Rmajor + aminor .* distortion .*cos(p)) .* cos(t);
y = (Rmajor + aminor .* distortion .* cos(p)) .* sin(t);
z = aminor.* distortion .* sin(p);
surf(x, y, z)
The result of which is:
You can do this with surf - just create matrices with the x,y,z coordinates. The page you linked to has the trig equations to do this (they are exactly what you'd come up with on your own - I wrote the code below before checking your link).
R = 10;
a = 3;
tx=nan(41,21);
ty=nan(41,21);
tz=nan(41,21);
for j=1:21
for i=1:41
phi = (i-1)*2*pi/40;
theta = (j-1)*2*pi/20;
tx(i,j)= cos(phi) * (R+cos(theta)*a);
ty(i,j)= sin(phi) * (R+cos(theta)*a);
tz(i,j)= sin(theta)*a;
end
end
figure
surf(tx,ty,tz)
axis equal
To distort the surface, replace the constant a with a matrix of the desired minor radius values, and index into that matrix - i.e. tz(i,j) = sin(theta)*distortion(i,j) (but in all dimensions, obviously).

Creating a matrix containing a filled ellipse based on a non-contiguous outline

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.