Get ellipse parameters from conic coefficient matrix

geometryprojective-geometry

Given a conic coefficient matrix that we can assume that represents an ellipse, in homogeneous coordinates:

import numpy as np


Q = np.array([[  a, b/2, d/2],
              [b/2,   c, e/2],
              [d/2, e/2,   f]])

Q = Q / Q[2,2] # Divide everything by f, to scale for centre

How can I find, the:

  1. Centre of ellipse (centre_x, centre_y)?
  2. Length of major and minor axis?
  3. Angle of ellipse relative to x?

According to this answer, the centre is just the last row/column of Q*. Is this correct?

Also to find the length of the major and minor axis, I have found some suggestions to use the eigenvalues/eigenvectors, but I am not sure how could I do that in practice. Can I calculate the eigenvalues/eigenvectors of the top 2×2 part of Q and use that information to calculate the lengths?

For the angle I think probably the eigenvector can also indicate the direction of the major axis, is this true?

Edit

Using the below answer and @Ng Chung Tak comment. I ended up with the following code:

# Get the coefficients
A = Q[0, 0]
B = Q[0, 1] * 2.0
C = Q[1, 1]
D = Q[0, 2] * 2.0
E = Q[1, 2] * 2.0
F = Q[2, 2]

# Check if it is indeed an ellipse
if np.linalg.det(Q) == 0:
    raise ValueError("Degenerate conic found!")

if np.linalg.det(Q[:2,:2]) <= 0: # According to Wikipedia
    raise ValueError("These parameters do not define an ellipse!")

# Get centre
denominator = B**2 - 4*A*C
centre_x = (2*C*D - B*E) / denominator
centre_y = (2*A*E - B*D) / denominator
print("Centre x:{} y:{}".format(centre_x, centre_y))

# Get major and minor axes
K = - np.linalg.det(Q[:3,:3]) / np.linalg.det(Q[:2,:2])
root = math.sqrt(((A - C)**2 + B**2))
a = math.sqrt(2*K / (A + C - root))
b = math.sqrt(2*K / (A + C + root))
print("Major:{} minor:{}".format(a, b))

# Get angle
angle = math.atan2(C - A + root, B)
angle *= 180.0/math.pi # Convert angle to degrees
print("Angle in degrees: {}".format(angle))

Example of projecting a red sphere into an ellipse and then drawing the ellipse in green:

enter image description here

Best Answer

Here is an answer in Python language:

    if A*C <= 0 or B*B-4*A*C >= 0 or D*D + E*E <= 4*(A+C)*F:
        raise ValueError("These parameters do not define an ellipse.")
    Q = np.array([[2*A, B, D], [B, 2*C, E], [D, E, 2*F]])
    if np.linalg.det(Q) == 0:
        raise ValueError("These parameters do not define an ellipse.")
    M0 = np.array([F, D/2, E/2, D/2, A, B/2, E/2, B/2, C]).reshape((3,3))
    M = np.array([[A, B/2], [B/2, C]])
    eigvals = np.linalg.eigvals(M)
    detM0 = np.linalg.det(M0)
    detM = np.linalg.det(M)
    a = sqrt(-detM0 / (detM * eigvals[0]))
    b = sqrt(-detM0 / (detM * eigvals[1]))
    xy = np.array([B*E - 2*C*D, B*D - 2*A*E])
    phi = 0.0 if A == C else (atan(B/(A-C))/2 if abs(C) > abs(A) else pi/2 - atan(-B/(A-C))/2)
    center = xy/(4*A*C - B*B)
    major_radius = max(a, b)
    minor_radius = min(a, b)
    angle = phi % pi