I have been given an assignement for a graphics module, one part of which is to calculate the minimum bounding ellipse of a set of arbitrary shapes. The ellipse doesn\'t hav
Thanks to Jacob's pseudocode I was able to implement the Minimum Volume Enclosing Ellipsoid (MVEE) in Java. There are public methods to get the center point, the "A" matrix, and a method to generate a list of coordinates that can be used to render the ellipse. The latter is based on MatLab code posted by Peter Lawrence in the comments to the original MVEE code. Note that the code references a class called "Eigen" - a modified version of Jama's EigenvalueDecomposition class (I took out Matrix class dependencies). I would add it but there is a 30k character limit to answers...
public class Ellipse {
private double[] center;
private double[][] A;
private double l1;
private double l2;
private double thu;
//**************************************************************************
//** Constructor
//**************************************************************************
/** @param P An array of points. Each entry in the array contains an x,y
* coordinate.
*/
public Ellipse(double[][] P, double tolerance){
// Dimension of the points
double d = 2;
// Number of points
int N = P.length;
// Rotate the array of points
P = transpose(P);
// Add a row of 1s to the 2xN matrix P - so Q is 3xN now.
//Q = [P;ones(1,N)]
double[][] Q = merge(P, ones(1,N));
// Initialize
int count = 1;
double err = 1;
//u is an Nx1 vector where each element is 1/N
//u = (1/N) * ones(N,1)
double[] u = new double[N];
for (int i=0; i tolerance){
// Matrix multiplication:
// diag(u) : if u is a vector, places the elements of u
// in the diagonal of an NxN matrix of zeros
//X = Q*diag(u)*Q'; // Q' - transpose of Q
double[][] X = multiply(multiply(Q,diag(u)), transpose(Q));
// inv(X) returns the matrix inverse of X
// diag(M) when M is a matrix returns the diagonal vector of M
//M = diag(Q' * inv(X) * Q); // Q' - transpose of Q
double[] M = diag(multiply(multiply(transpose(Q), inv(X)), Q));
//Find the value and location of the maximum element in the vector M
double maximum = max(M);
int j = find_maximum_value_location(M, maximum);
// Calculate the step size for the ascent
double step_size = (maximum - d -1)/((d+1)*(maximum-1));
// Calculate the new_u:
// Take the vector u, and multiply all the elements in it by (1-step_size)
double[] new_u = multiply((1 - step_size), u);
// Increment the jth element of new_u by step_size
new_u[j] = new_u[j] + step_size;
// Calculate error by taking finding the square root of the SSD
// between new_u and u
err = Math.sqrt(ssd(new_u, u));
// Increment count and replace u
count = count + 1;
u = new_u;
}
// Compute center point
//c = P * u
double[][] c = multiply(P, u);
center = transpose(c)[0];
// Put the elements of the vector u into the diagonal of a matrix
// U with the rest of the elements as 0
double[][] U = diag(u);
// Compute the A-matrix
//A = (1/d) * inv(P * U * P' - (P * u)*(P*u)' );
double[][] pup = multiply(multiply(P, U) , transpose(P));
double[][] pupu = multiply((multiply(P, u)), transpose(multiply(P, u)));
double[][] pup_pupu = minus(pup, pupu);
A = multiply((1/d), inv(pup_pupu));
// Compute Eigen vectors and values
//A=inv(A);
//[Ve,De]=eig(A);
Eigen eig = new Eigen(inv(A));
double[][] Ve = eig.getV(); //eigenvalues
double[][] De = eig.getD(); //right eigenvectors
reorderEigenVectors(De);
reorderEigenValues(Ve);
//v=sqrt(diag(De));
double[] v = sqrt(diag(De));
//[l1,Ie] = max(v);
l1 = max(v);
int Ie = find_maximum_value_location(v, l1); //off by one from MatLab but I think it's ok here
//veig=Ve(:,Ie);
double[] veig = new double[Ve.length];
for (int i=0; i
1 1 1
1 1 1
* Reference: https://www.mathworks.com/help/matlab/ref/ones.html
*/
private double[][] ones(int rows, int cols){
double[][] arr = new double[rows][];
for (int i=0; iHere an example output using 10 random points and a tolerance of 0.001. The ellipse is rendered using straight lines connecting points generated via the Ellipse.getBoundingCoordinates() method using 50 points.