Fitting an ellipsoid given 2D contours - math

I have coordinates corresponding to a set of 2D contours, each corresponding to different heights. These contours do not draw out a perfect ellipsoid in 3D, and instead what I would like to do is to find the best fitting ellipsoid. I do not have any knowledge on the origin of this ellipsoid.
My first thought was to incorporate some type of least squares algorithm, where I find the ellipsoid parameters that minimize the distance between points. I imagine this would be quite expensive and not too far from a brute force approach. I am convinced there is a more elegant and efficient way of doing this. If there is an existing library that handles this (preferably in Python) that would be even better.
I have already seen a related question (Fitting an ellipsoid to 3D data points), but figured I would ask again as it has been over a decade since that post.

So you have a set of (x,y) values for each contour, which describe a portion of an ellipse (blue dots below).
The best fit ellipse is described by the general equation
A x^2 + B y^2 + 2C x y + 2D x + 2E y = 1
and once the coefficients (A,B,C,D,E) are found, the ellipse of fully described. See below in how to find the the curve coordinates (x,y) from the coefficients and a parameter t=0 .. 1.
To find the coefficients of the ellipse, form 5 vectors, each a column of a n×5 matrix Q
for i = 1 to n
Q(i,1) = x(i)^2
Q(i,2) = y(i)^2
Q(i,3) = 2*x(i)*y(i)
Q(i,4) = 2*x(i)
Q(i,5) = 2*y(i)
next i
and a vector K filled with 1 for the right-hand side
for i = 1 to n
K(i) = 1.0
next i
Find the coefficients using a least-squares fit with some linear algebra
[A,B,C,D,E] = inv(tr(Q)*Q)*tr(Q)*K
where tr(Q) is the transpose of Q and * is matrix/vector product
Now we need to extract the geometric properties of the ellipse from the coefficient. I want to have a the semi-major axis, b the semi-minor axis, φ the rotation angle, xc the x-axis center, yc the y-axis center.
xc = -(B*D-C*E)/(A*B-(C^2))
yc = -(A*E-C*D)/(A*B-(C^2))
φ = atan( 2*C/(A-B) )/2
a = SQRT(2*(A*(B+E^2)+B*D^2-C*(C+2*D*E))/((A*B-C^2)*(A+B-SQRT((A-B)^2+4*C^2))))
b = SQRT(2*(A*(B+E^2)+B*D^2-C*(C+2*D*E))/((A*B-C^2)*(A+B+SQRT((A-B)^2+4*C^2))))
Finally to plot the ellipse you need to generate a set of points (x,y) from the curve parameter t=0..1 using the above 5 coefficients.
Generate the centered aligned coordinates (u,v) with
u = a*cos(2*π*t)
v = b*sin(2*π*t)
Generate the centered rotated coordinates (x',y') with
x' = u*cos(φ) - v*sin(φ)
y' = u*sin(φ) + v*cos(φ)
Generate the ellipse coordinates (x,y) with
x = x' + xc
y = y' + yc
The result is observed above in the first picture.
Now for the total solution, each 2D slice would have its own ellipse. But all the slices would not generate an ellipsoid this way.
Extending the above into 3D coordinates (x,y,z) is doable, but the math is quite involved and I feel [SO] is not a good place to develop such an algorithm. You can hack it together, by finding the average center for each slice (weighted by the ellipse area π*a*b). Additionally, the rotation angle should be the same for all contours, and so another averaging is needed. Finally, the major and minor axis values would fall on an elliptical curve along the z-axis and it would require another least-fit solution. This one is driven by the equation
(x/a)^2 + (y/b)^2 + (z/c)^2 = 1
but rather in the aligned coordinates (u,v,w)
(u/a)^2 + (v/b)^2 + (w/c)^2 = 1

Related

Scaling the X axis of a rotated ellipse

I have a scatter plot and I'm allowing points to be selected by drawing an ellipse like so:
In the above image the X axis and Y axis are scaled differently. I'm trying to implement functionality to allow the scale of each axis to be changed, but I'm having trouble scaling the ellipse shape with the axis. Changing the scale of the X axis to match the Y axis gives this:
I know I can scale the cx value of the ellipse with the X axis to get the new centre of the ellipse, but I don't know how to work out the new rx, ry and rotation angle. So my question is given a rotated ellipse with values cx, cy, rx, ry and rotation in radians, how do I calculate the new values for the ellipse given a scale value s for either the X axis or Y Axis? For clarity the rotation is done about the centre (cx, cy) of the ellipse.
I think I understand your problem. For an axis-aligned ellipse with points (x,y), the shape of (a*x,b*y) is still an ellipse with some arbitrary scaling factors a and b acting on the major and minor radii.
But for a rotated ellipse this is not true. In addition to the major and minor radii scaling differently, the rotation angle might differ also.
So mathematically the problem is as such: The parametric points of a rotated ellipse are
with θ the rotation angle r_1 and r_2 the original ellipse radii. The parameter t goes from 0 to 2π.
Now to find the modified parameters such that the ellipse matches a scaled version of the above
The above two are combined into one equation to be solved for r_1' and r_2' as well as θ' for all values of t.
Unfortunately, there is no solution that I can think of, because the combined 2×2 matrix on the RHS in front of the vector needs to be diagonalized by finding the appropriate rotation θ' to bring it down to this form
which is trivially solved for r_1' and r_2'.
But the rotation needs to satisfy two contradictory equations
tan(θ') = (a/b)*tan(θ) and tan(θ') = (b/a)*tan(θ)
which can only be solved if the two scaling factors are identical => a==b.
I suggest posting this problem into the [Mathematics.SE] as math problem, before trying to implement it as an algorithm. Maybe you will have better luck.
The computation is not that easy.
The equation of a rotated ellipse centered at the origin is
(c x + s y)² / a² + (s x - c y)² / b² = 1 = A x² + 2B xy + C y²
(c, s denote the cosine and sine of the rotation angle.) After development, the quadratic coefficients are
A = c² / a² + s² / b², 2B = 2 cs / a² - 2 sc / b², C = s² / a² + c² / b².
When you have the coefficients, you can retrieve 1/a², 1/b², c, s as the Eigenvalues and first Eigen vector of the 2x2 matrix
|A B|
|B C|
and the rotation angle is given by tan Θ = s / c.
Now let us stretch the abscissas by applying a coefficient r, giving
A' = r² A, B' = r B, C' = C.
The matrix becomes
|r²A rB|
|rB C|
and again, you will find the axis and cosine/sine of the angle by computing the Eigenvalues and first Eigenvector.
The Eigenvalues are roots of the polynomial
z² + (r²A + C) z + r²(AC - B²)
where A, B, C are computed as above, and the Eigenvector follows
(r²A - z) c + rB s = 0
from which you draw s/c.

Draw a 2d plane with scatterplot3d

I am trying to plot a plane with scatterplot3d that is perpendicular to a direction vector described by two angles, say theta and phi. The points are described by the (xyz)-coordinates satisfying the following equation, where R is the distance from the origin.
x cos(theta)cos(phi) + y sin(theta) cos(phi) + z sin(phi) = R
I guess I should use plane3d, but I can't figure out how to get this plane right based on my description. Can anyone help?
In other words, I am trying to plot the plane perpendicular to the blue line at distance R from the origin in this figure.
I assume this should be straightforward, but cannot figure it out.
Using plane3d and calculating the intercept and coefficients, this turned out to be quite straightforward:
spl$plane3d(Intercept, x.coeff, y.coeff, col=5, draw_polygon=T, lty=NULL)
The Intercept would just be R/sin(phi), and the x- and y-coefficients are the coefficients in front of X and Y: x.coeff = cos(theta)/tan(phi) and y.coeff = sin(theta)/tan(phi).
This gives the plane, as desired.

Generating random points on a surface of an n-dimensional torus

I'd like to generate random points being located on the surface of an n-dimensional torus. I have found formulas for how to generate the points on the surface of a 3-dimensional torus:
x = (c + a * cos(v)) * cos(u)
y = (c + a * cos(v)) * sin(u)
z = a * sin(v)
u, v ∈ [0, 2 * pi); c, a > 0.
My question is now: how to extend this formulas to n dimensions. Any help on the matter would be much appreciated.
I guess that you can do this recursively. Start with a full orthonormal basis of your vector space, and let the current location be the origin. At each step, choose a point in the plane spanned by the first two coordinate vectors, i.e. take w1 = cos(t)*v1 + sin(t)*v2. Shift the other basis vectors, i.e. w2 = v3, w3 = v4, …. Also take a step from your current position in the direction w1, with the radius r1 chosen up front. When you only have a single basis vector remaining, then the current point is a point on the n-dimensional torus of the outermost recursive call.
Note that while the above may be used to choose points randomly, it won't choose them uniformly. That would likely be a much harder question, and you definitely should ask about the math of that on Math SE or perhaps on Cross Validated (Statistics SE) to get the math right before you worry about implementation.
An n-torus (n being the dimensionality of the surface of the torus; a bagel or doughnut is therefore a 2-torus, not a 3-torus) is a smooth mapping of an n-rectangle. One way to approach this is to generate points on the rectangle and then map them onto the torus. Aside from the problem of figuring out how to map a rectangle onto a torus (I don't know it off-hand), there is the problem that the resulting distribution of points on the torus is not uniform even if the distribution of points is uniform on the rectangle. But there must be a way to adjust the distribution on the rectangle to make it uniform on the torus.
Merely generating u and v uniformly will not necessarily sample uniformly from a torus surface. An additional step is needed.
J.F. Williamson, "Random selection of points distributed on curved surfaces", Physics in Medicine & Biology 32(10), 1987, describes a general method of choosing a uniformly random point on a parametric surface. It is an acceptance/rejection method that accepts or rejects each candidate point depending on its stretch factor (norm-of-gradient). To use this method for a parametric surface, several things have to be known about the surface, namely—
x(u, v), y(u, v) and z(u, v), which are functions that generate 3-dimensional coordinates from two dimensional coordinates u and v,
The ranges of u and v,
g(point), the norm of the gradient ("stretch factor") at each point on the surface, and
gmax, the maximum value of g for the entire surface.
For the 3-dimensional torus with the parameterization you give in your question, g and gmax are the following:
g(u, v) = a * (c + cos(v) * a).
gmax = a * (a + c).
The algorithm to generate a uniform random point on the surface of a 3-dimensional torus with torus radius c and tube radius a is then as follows (where RNDEXCRANGE(x,y) returns a number in [x,y) uniformly at random, and RNDRANGE(x,y) returns a number in [x,y] uniformly at random):
// Maximum stretch factor for torus
gmax = a * (a + c)
while true
u = RNDEXCRANGE(0, pi * 2)
v = RNDEXCRANGE(0, pi * 2)
x = cos(u)*(c+cos(v)*a)
y = sin(u)*(c+cos(v)*a)
z = sin(v)*a
// Norm of gradient (stretch factor)
g = a*abs(c+cos(v)*a)
if g >= RNDRANGE(0, gmax)
// Accept the point
return [x, y, z]
end
end
If you have n-dimensional torus generating formulas, a similar approach can be used to generate uniform random points on that torus (accept a candidate point if norm-of-gradient equals or exceeds a random number in [0, gmax), where gmax is the maximum norm-of-gradient).

Gradients for 2D geometric shapes

I've been generating islands using a combination of Perlin noise and radial gradients -- as outlined in this awesome Reddit answer. It's working pretty well, but obviously the islands are all circular in shape, and I'd like more variety.
I was wondering if there is a general method of converting equations for geometrical shapes into equations for gradients. Eg. an equation that is to its shape what Euclidean distance is for a circle. Is this possible? Is it obvious?
Defining an implicit surface for a geometric shape, or the Level Set (wiki), is basically what you are asking for. For example, the implicit surface of a circle with radius R is
f(x,y) = x^2 + y^2 - R^2
Note that when f(x,y) = 0, that defines the surface of the circle. When f(x,y) < 0, then x^2 + y^2 < R^2, which means the point (x,y) is inside the circle centered at the origin. Finally f(x,y) > 0 means the point (x,y) is outside the circle. In order to create an image out of this, you would need to iterate over every pixel and evaluate f(x,y), while finding the min and max value, then scale the whole image to have a range between 0 and 1.
This would technically not give you distance. The circle is a special case because the equation for it (x^2 + y^2) is the squared distance function from the origin. This does give you the effect you are most likely looking for your problem - negative inside, positive inside (or vice versa), while the magnitude is scaled w.r.t. to the distance of a given point to the surface.
For any equation you come up with for a geometric shape, you need to get all the terms on one side to form f(x,y). For example, take x*y = log(x). That can become x*y - log(x) = 0, so f(x,y) = x*y - log(x).

Hexagonal Grid Coordinates To Pixel Coordinates

I am working with a hexagonal grid. I have chosen to use this coordinate system because it is quite elegant.
This question talks about generating the coordinates themselves, and is quite useful. My issue now is in converting these coordinates to and from actual pixel coordinates. I am looking for a simple way to find the center of a hexagon with coordinates x,y,z. Assume (0,0) in pixel coordinates is at (0,0,0) in hex coords, and that each hexagon has an edge of length s. It seems to me like x,y, and z should each move my coordinate a certain distance along an axis, but they are interrelated in an odd way I can't quite wrap my head around it.
Bonus points if you can go the other direction and convert any (x,y) point in pixel coordinates to the hex that point belongs in.
For clarity, let the "hexagonal" coordinates be (r,g,b) where r, g, and b are the red, green, and blue coordinates, respectively. The coordinates (r,g,b) and (x,y) are related by the following:
y = 3/2 * s * b
b = 2/3 * y / s
x = sqrt(3) * s * ( b/2 + r)
x = - sqrt(3) * s * ( b/2 + g )
r = (sqrt(3)/3 * x - y/3 ) / s
g = -(sqrt(3)/3 * x + y/3 ) / s
r + b + g = 0
Derivation:
I first noticed that any horizontal row of hexagons (which should have a constant y-coordinate) had a constant b coordinate, so y depended only on b. Each hexagon can be broken into six equilateral triangles with sides of length s; the centers of the hexagons in one row are one and a half side-lengths above/below the centers in the next row (or, perhaps easier to see, the centers in one row are 3 side lengths above/below the centers two rows away), so for each change of 1 in b, y changes 3/2 * s, giving the first formula. Solving for b in terms of y gives the second formula.
The hexagons with a given r coordinate all have centers on a line perpendicular to the r axis at the point on the r axis that is 3/2 * s from the origin (similar to the above derivation of y in terms of b). The r axis has slope -sqrt(3)/3, so a line perpendicular to it has slope sqrt(3); the point on the r axis and on the line has coordinates (3sqrt(3)/4 * s * r, -3/4 * s * r); so an equation in x and y for the line containing the centers of the hexagons with r-coordinate r is y + 3/4 * s * r = sqrt(3) * (x - 3sqrt(3)/4 * s * r). Substituting for y using the first formula and solving for x gives the second formula. (This is not how I actually derived this one, but my derivation was graphical with lots of trial and error and this algebraic method is more concise.)
The set of hexagons with a given r coordinate is the horizontal reflection of the set of hexagons with that g coordinate, so whatever the formula is for the x coordinate in terms of r and b, the x coordinate for that formula with g in place of r will be the opposite. This gives the third formula.
The fourth and fifth formulas come from substituting the second formula for b and solving for r or g in terms of x and y.
The final formula came from observation, verified by algebra with the earlier formulas.

Resources