Bounding ellipse

后端 未结 2 387
南旧
南旧 2020-11-30 02:50

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

2条回答
  •  囚心锁ツ
    2020-11-30 03:13

    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; i 1171.18 658.33 658.33 1039.55 * Credit: https://github.com/rchen8/Algorithms/blob/master/Matrix.java */ private double[][] inv1(double[][] matrix){ double[][] inverse = new double[matrix.length][matrix.length]; // minors and cofactors for (int i = 0; i < matrix.length; i++) for (int j = 0; j < matrix[i].length; j++) inverse[i][j] = Math.pow(-1, i + j) * determinant(minor(matrix, i, j)); // adjugate and determinant double det = 1.0 / determinant(matrix); for (int i = 0; i < inverse.length; i++) { for (int j = 0; j <= i; j++) { double temp = inverse[i][j]; inverse[i][j] = inverse[j][i] * det; inverse[j][i] = temp * det; } } return inverse; } private static double determinant(double[][] matrix) { if (matrix.length != matrix[0].length) throw new IllegalStateException("invalid dimensions"); if (matrix.length == 2) return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]; double det = 0; for (int i = 0; i < matrix[0].length; i++) det += Math.pow(-1, i) * matrix[0][i] * determinant(minor(matrix, 0, i)); return det; } private static double[][] minor(double[][] matrix, int row, int column) { double[][] minor = new double[matrix.length - 1][matrix.length - 1]; for (int i = 0; i < matrix.length; i++) for (int j = 0; i != row && j < matrix[i].length; j++) if (j != column) minor[i < row ? i : i - 1][j < column ? j : j - 1] = matrix[i][j]; return minor; } //************************************************************************** //** inv2 //************************************************************************** /** Returns the inverse of a matrix. This implementation successfully * executes but does not pass the inverse check. * Credit: https://www.sanfoundry.com/java-program-find-inverse-matrix/ */ public static double[][] inv2(double a[][]){ int n = a.length; double x[][] = new double[n][n]; double b[][] = new double[n][n]; int index[] = new int[n]; for (int i=0; i=0; --j){ x[j][i] = b[index[j]][i]; for (int k=j+1; k c1) c1 = c0; } c[i] = c1; } // Search the pivoting element from each column int k = 0; for (int j=0; j pi1) { pi1 = pi0; k = i; } } // Interchange rows according to the pivoting order int itmp = index[j]; index[j] = index[k]; index[k] = itmp; for (int i=j+1; i

    Here 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.

提交回复
热议问题