Search code examples
pythonleast-squares

How to fit a 2D ellipse to given points and a given focus


This question is similar to the question, How to fit a 2D ellipse (in x-y plane) to given points? (See the link below) (How to fit a 2D ellipse to given points)

Now, we know how to use least square method to fit a 2D ellipse with given points by the code provided by Casey. (The code is also provided below.) Based on this code, if I want to fit not only the given points but also a given focus at (0,0), how could I do it? Or is there any better way to do it?

I was thinking about if we can derive the focus (as a function of x and y) based on the ellipse equation, Ax^2 + Bxy + Cy^2 + Dx + Ey=1 , where A, B, C, D, E are coefficients, and use it as a constrain to the least square method. Buts sadly, I don't know how to derive the focus either.

CODE

import numpy as np
import matplotlib.pyplot as plt
alpha = 5
beta = 3
N = 500
DIM = 2

np.random.seed(2)

# Generate random points on the unit circle by sampling uniform angles
theta = np.random.uniform(0, 2*np.pi, (N,1))
eps_noise = 0.2 * np.random.normal(size=[N,1])
circle = np.hstack([np.cos(theta), np.sin(theta)])

# Stretch and rotate circle to an ellipse with random linear tranformation
B = np.random.randint(-3, 3, (DIM, DIM))
noisy_ellipse = circle.dot(B) + eps_noise

# Extract x coords and y coords of the ellipse as column vectors
X = noisy_ellipse[:,0:1]
Y = noisy_ellipse[:,1:]

# Formulate and solve the least squares problem ||Ax - b ||^2
A = np.hstack([X**2, X * Y, Y**2, X, Y])
b = np.ones_like(X)
x = np.linalg.lstsq(A, b)[0].squeeze()

# Print the equation of the ellipse in standard form
print('The ellipse is given by {0:.3}x^2 + {1:.3}xy+{2:.3}y^2+{3:.3}x+{4:.3}y = 1'.format(x[0], x[1],x[2],x[3],x[4]))

# Plot the noisy data
plt.scatter(X, Y, label='Data Points')

# Plot the original ellipse from which the data was generated
phi = np.linspace(0, 2*np.pi, 1000).reshape((1000,1))
c = np.hstack([np.cos(phi), np.sin(phi)])
ground_truth_ellipse = c.dot(B)
plt.plot(ground_truth_ellipse[:,0], ground_truth_ellipse[:,1], 'k--', label='Generating Ellipse')

# Plot the least squares ellipse
x_coord = np.linspace(-5,5,300)
y_coord = np.linspace(-5,5,300)
X_coord, Y_coord = np.meshgrid(x_coord, y_coord)
Z_coord = x[0] * X_coord ** 2 + x[1] * X_coord * Y_coord + x[2] * Y_coord**2 + x[3] * X_coord + x[4] * Y_coord
plt.contour(X_coord, Y_coord, Z_coord, levels=[1], colors=('r'), linewidths=2)

plt.legend()
plt.xlabel('X')
plt.ylabel('Y')
plt.show()

Solution

  • For a ellipse with a focus at (0,0), the ellipse's equation in polar coordinate can be written as the following:

    Given 𝑎 = semi-major axis, 𝑒 = eccentricity, the other focus at the angle 𝜙,

    enter image description here

    Now, we can use the data points (𝑟𝑖,𝜃𝑖) to fit the equation above by non-linear least square method (ex: Levenberg–Marquardt algorithm).

    i.e. Find the minimum for the following equation:

    enter image description here