Created
July 26, 2013 02:27
-
-
Save dbuscombe-usgs/6085598 to your computer and use it in GitHub Desktop.
Ellipse fitting to 2d points, python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def fitellipse( x, **kwargs ): | |
x = asarray( x ) | |
## Parse inputs | |
## Default parameters | |
kwargs.setdefault( 'constraint', 'bookstein' ) | |
kwargs.setdefault( 'maxits', 200 ) | |
kwargs.setdefault( 'tol', 1e-5 ) | |
if x.shape[1] == 2: | |
x = x.T | |
centroid = mean(x, 1) | |
x = x - centroid.reshape((2,1)) | |
## Obtain a linear estimate | |
if kwargs['constraint'] == 'bookstein': | |
## Bookstein constraint : lambda_1^2 + lambda_2^2 = 1 | |
z, a, b, alpha = fitbookstein(x) | |
## Add the centroid back on | |
z = z + centroid | |
return z | |
def fitbookstein(x): | |
''' | |
function [z, a, b, alpha] = fitbookstein(x) | |
FITBOOKSTEIN Linear ellipse fit using bookstein constraint | |
lambda_1^2 + lambda_2^2 = 1, where lambda_i are the eigenvalues of A | |
''' | |
## Convenience variables | |
m = x.shape[1] | |
x1 = x[0, :].reshape((1,m)).T | |
x2 = x[1, :].reshape((1,m)).T | |
## Define the coefficient matrix B, such that we solve the system | |
## B *[v; w] = 0, with the constraint norm(w) == 1 | |
B = hstack([ x1, x2, ones((m, 1)), power( x1, 2 ), multiply( sqrt(2) * x1, x2 ), power( x2, 2 ) ]) | |
## To enforce the constraint, we need to take the QR decomposition | |
Q, R = linalg.qr(B) | |
## Decompose R into blocks | |
R11 = R[0:3, 0:3] | |
R12 = R[0:3, 3:6] | |
R22 = R[3:6, 3:6] | |
## Solve R22 * w = 0 subject to norm(w) == 1 | |
U, S, V = linalg.svd(R22) | |
V = V.T | |
w = V[:, 2] | |
## Solve for the remaining variables | |
v = dot( linalg.solve( -R11, R12 ), w ) | |
## Fill in the quadratic form | |
A = zeros((2,2)) | |
A.ravel()[0] = w.ravel()[0] | |
A.ravel()[1:3] = 1 / sqrt(2) * w.ravel()[1] | |
A.ravel()[3] = w.ravel()[2] | |
bv = v[0:2] | |
c = v[2] | |
## Find the parameters | |
z, a, b, alpha = conic2parametric(A, bv, c) | |
return z, a, b, alpha | |
def conic2parametric(A, bv, c): | |
''' | |
function [z, a, b, alpha] = conic2parametric(A, bv, c) | |
''' | |
## Diagonalise A - find Q, D such at A = Q' * D * Q | |
D, Q = linalg.eig(A) | |
Q = Q.T | |
## If the determinant < 0, it's not an ellipse | |
if prod(D) <= 0: | |
raise RuntimeError, 'fitellipse:NotEllipse Linear fit did not produce an ellipse' | |
## We have b_h' = 2 * t' * A + b' | |
t = -0.5 * linalg.solve(A, bv) | |
c_h = dot( dot( t.T, A ), t ) + dot( bv.T, t ) + c | |
z = t | |
a = sqrt(-c_h / D[0]) | |
b = sqrt(-c_h / D[1]) | |
alpha = atan2(Q[0,1], Q[0,0]) | |
return z, a, b, alpha |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment