package fr.unistra.pelican.algorithms.statistics; import Jama.EigenvalueDecomposition; import Jama.Matrix; import fr.unistra.pelican.Algorithm; import fr.unistra.pelican.AlgorithmException; import fr.unistra.pelican.DoubleImage; import fr.unistra.pelican.Image; /** * This class performs the principal components transformation.. (aka Karhunen Loeve or * Hotelling transformation) still in experimental mode...but works all the * same. Done according to wiki : * http://en.wikipedia.org/wiki/Principal_components_analysis dont forget to * scale the doubleImage for visualisation. * * @author Abdullah * */ public class PCA extends Algorithm { /** * The input image */ public Image input; /** * the output images */ public Image output; /** * the eigenvectors */ public double[][] eigenvectors = null; /** * the eigen values */ public double[] eigenvalues = null; /** * the means */ public double[] mean = null; /** * This method performs the Hotelling transform * @param image The input image * @return the eigen images sorted according to their variances in descending order */ public static Image exec(Image image) { return (Image)new PCA().process(image); } /** * Constructor * */ public PCA() { super(); super.inputs = "input"; super.outputs = "output,eigenvectors,eigenvalues,mean"; } /* * (non-Javadoc) * * @see fr.unistra.pelican.Algorithm#launch() */ public void launch() throws AlgorithmException { int bDim = input.getBDim(); int xDim = input.getXDim(); int yDim = input.getYDim(); int size = xDim * yDim; if (bDim == 1) { output = input.copyImage(true); return; } // transform the channels into columns of a 2D matrix... double[][] pixels = new double[bDim][xDim * yDim]; for (int b = 0; b < bDim; b++) for (int x = 0; x < xDim; x++) for (int y = 0; y < yDim; y++) pixels[b][y * xDim + x] = input.getPixelXYBDouble(x, y, b); // get the mean of each dimension mean = new double[bDim]; for (int i = 0; i < bDim; i++) { mean[i] = 0.0; for (int j = 0; j < xDim * yDim; j++) mean[i] += pixels[i][j]; mean[i] = mean[i] / size; } // substract the mean from the input.. for (int i = 0; i < bDim; i++) for (int j = 0; j < size; j++) pixels[i][j] = pixels[i][j] - mean[i]; // covariance matrix...first multiply with transpose Matrix M = new Matrix(pixels, bDim, size); Matrix Mt = M.transpose(); M = M.times(Mt); System.gc(); // then normalize M = M.times(1 / (double) (size - 1)); /* * double[][] tmp = M.getArray(); * * for(int i = 0; i < bDim; i++){ System.err.print("DEBUG PCA: cov "); * * for(int j = 0; j < bDim; j++) System.err.print(tmp[i][j] + " "); * * System.err.println(" "); } */ EigenvalueDecomposition ev = new EigenvalueDecomposition(M); Matrix V = ev.getV(); eigenvalues = ev.getRealEigenvalues(); eigenvectors = V.getArray(); sort(eigenvalues, eigenvectors, mean, bDim); V = new Matrix(eigenvectors); M = Mt.times(V); M = M.transpose(); System.gc(); // the eigen images double[][] eigenImages = M.getArray(); /* * for(int i = 0; i < bDim; i++){ System.err.print("DEBUG PCA : * eigenvectors "); for(int j = 0; j < bDim; j++){ * System.err.print(eigenvectors[i][j] + " "); } System.err.println(" * "); } */ // cleanup for (int j = 0; j < eigenImages.length; j++) { if (eigenvalues[j] < 0) { eigenvalues[j] = 0.0; for (int i = 0; i < eigenImages[j].length; i++) eigenImages[j][i] = 0; } } // prepare output.. output = new DoubleImage(input, false); for (int b = 0; b < bDim; b++) { for (int x = 0; x < xDim; x++) { for (int y = 0; y < yDim; y++) { double d = eigenImages[b][y * xDim + x]; output.setPixelXYBDouble(x, y, b, d); } } } } /* public static double[] getVariances(double[] vals) { double totalVariance = 0.0; double[] variances = new double[vals.length]; // add up the eigen values..(= variances..sort of) for (int j = 0; j < vals.length; j++) totalVariance += vals[j]; // variance percentage of each channel for (int j = 0; j < vals.length; j++) variances[j] = vals[j] / totalVariance; return variances; } */ private void sort(double[] vals, double[][] eigenvectors, double[] mean, int n) { for (int i = 0; i < n; i++) { int k = i; double p = vals[k]; for (int j = i + 1; j < n; j++) { if (vals[j] >= p) { k = j; p = vals[k]; } } if (k != i) { vals[k] = vals[i]; vals[i] = p; // p = mean[i]; // mean[i] = mean[k]; // mean[k] = p; for (int j = 0; j < n; j++) { p = eigenvectors[j][i]; eigenvectors[j][i] = eigenvectors[j][k]; eigenvectors[j][k] = p; } } } } }