whitebox.stats.PolynomialLeastSquares2DFitting.java Source code

Java tutorial

Introduction

Here is the source code for whitebox.stats.PolynomialLeastSquares2DFitting.java

Source

/*
 * Copyright (C) 2013 Dr. John Lindsay <jlindsay@uoguelph.ca>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package whitebox.stats;

import java.util.ArrayList;
import org.apache.commons.math3.linear.*;
import whitebox.structures.XYPoint;

/**
 *
 * @author johnlindsay
 */
public class PolynomialLeastSquares2DFitting {

    private int polyOrder = 1;
    private double[] forwardRegressCoeffX;
    private double[] forwardRegressCoeffY;
    private double[] backRegressCoeffX;
    private double[] backRegressCoeffY;
    private int numCoefficients;
    private double[] xCoords1;
    private double[] yCoords1;
    private double[] xCoords2;
    private double[] yCoords2;
    private double[] residualsXY;
    private double[] residualsOrientation;
    //private boolean[] useGCP;
    private double xMin1;
    private double yMin1;
    private double xMin2;
    private double yMin2;
    private double overallRMSE = 0.0;

    public PolynomialLeastSquares2DFitting() {
    }

    public PolynomialLeastSquares2DFitting(double[] X1, double[] Y1, double[] X2, double[] Y2, int polyOrder) {
        this.polyOrder = polyOrder;

        addData(X1, Y1, X2, Y2);
    }

    public PolynomialLeastSquares2DFitting(ArrayList<XYPoint> data1, ArrayList<XYPoint> data2, int polyOrder) {
        this.polyOrder = polyOrder;

        double[] X1 = new double[data1.size()];
        double[] Y1 = new double[data1.size()];
        double[] X2 = new double[data2.size()];
        double[] Y2 = new double[data2.size()];

        int i = 0;
        for (XYPoint xy : data1) {
            X1[i] = xy.x;
            Y1[i] = xy.y;
            i++;
        }

        i = 0;
        for (XYPoint xy : data2) {
            X2[i] = xy.x;
            Y2[i] = xy.y;
            i++;
        }

        addData(X1, Y1, X2, Y2);
    }

    // properties
    public int getPolyOrder() {
        return polyOrder;
    }

    public void setPolyOrder(int polyOrder) {
        if (polyOrder < 1) {
            polyOrder = 1;
        }
        if (polyOrder > 10) {
            polyOrder = 10;
        }
        this.polyOrder = polyOrder;
    }

    public double[] getForwardRegressCoeffX() {
        return forwardRegressCoeffX;
    }

    public double[] getForwardRegressCoeffY() {
        return forwardRegressCoeffY;
    }

    public double[] getBackRegressCoeffX() {
        return backRegressCoeffX;
    }

    public double[] getBackRegressCoeffY() {
        return backRegressCoeffY;
    }

    public double[] getResidualsXY() {
        return residualsXY;
    }

    public double[] getResidualsOrientation() {
        return residualsOrientation;
    }

    public double getOverallRMSE() {
        return overallRMSE;
    }

    // methods
    public void addData(ArrayList<XYPoint> data1, ArrayList<XYPoint> data2) {
        double[] X1 = new double[data1.size()];
        double[] Y1 = new double[data1.size()];
        double[] X2 = new double[data2.size()];
        double[] Y2 = new double[data2.size()];

        int i = 0;
        for (XYPoint xy : data1) {
            X1[i] = xy.x;
            Y1[i] = xy.y;
            i++;
        }

        i = 0;
        for (XYPoint xy : data2) {
            X2[i] = xy.x;
            Y2[i] = xy.y;
            i++;
        }

        addData(X1, Y1, X2, Y2);
    }

    public final void addData(double[] X1, double[] Y1, double[] X2, double[] Y2) {
        int n = X1.length;
        if (Y1.length != n || X2.length != n || Y2.length != n) {
            return;
        }
        xCoords1 = new double[n];
        yCoords1 = new double[n];
        xCoords2 = new double[n];
        yCoords2 = new double[n];

        System.arraycopy(X1, 0, xCoords1, 0, n);
        System.arraycopy(Y1, 0, yCoords1, 0, n);
        System.arraycopy(X2, 0, xCoords2, 0, n);
        System.arraycopy(Y2, 0, yCoords2, 0, n);

        xMin1 = Double.POSITIVE_INFINITY;
        yMin1 = Double.POSITIVE_INFINITY;
        xMin2 = Double.POSITIVE_INFINITY;
        yMin2 = Double.POSITIVE_INFINITY;
        for (int i = 0; i < n; i++) {
            if (X1[i] < xMin1) {
                xMin1 = X1[i];
            }
            if (Y1[i] < yMin1) {
                yMin1 = Y1[i];
            }
            if (X2[i] < xMin2) {
                xMin2 = X2[i];
            }
            if (Y2[i] < yMin2) {
                yMin2 = Y2[i];
            }
        }

        calculateEquations();
    }

    public void calculateEquations() {
        try {
            int m, i, j, k;

            int n = xCoords2.length;

            // How many coefficients are there?
            numCoefficients = 0;

            for (j = 0; j <= polyOrder; j++) {
                for (k = 0; k <= (polyOrder - j); k++) {
                    numCoefficients++;
                }
            }

            //            for (i = 0; i < n; i++) {
            //                xCoords1[i] -= xMin1;
            //                yCoords1[i] -= yMin1;
            //                xCoords2[i] -= xMin2;
            //                yCoords2[i] -= yMin2;
            //            }

            // Solve the forward transformation equations
            double[][] forwardCoefficientMatrix = new double[n][numCoefficients];
            for (i = 0; i < n; i++) {
                m = 0;
                for (j = 0; j <= polyOrder; j++) {
                    for (k = 0; k <= (polyOrder - j); k++) {
                        forwardCoefficientMatrix[i][m] = Math.pow(xCoords1[i], j) * Math.pow(yCoords1[i], k);
                        m++;
                    }
                }
            }

            RealMatrix coefficients = new Array2DRowRealMatrix(forwardCoefficientMatrix, false);
            DecompositionSolver solver = new SingularValueDecomposition(coefficients).getSolver();
            //DecompositionSolver solver = new QRDecomposition(coefficients).getSolver();

            // do the x-coordinate first
            RealVector constants = new ArrayRealVector(xCoords2, false);
            RealVector solution = solver.solve(constants);
            forwardRegressCoeffX = new double[n];
            for (int a = 0; a < numCoefficients; a++) {
                forwardRegressCoeffX[a] = solution.getEntry(a);
            }

            double[] residualsX = new double[n];
            double SSresidX = 0;
            for (i = 0; i < n; i++) {
                double yHat = 0.0;
                for (j = 0; j < numCoefficients; j++) {
                    yHat += forwardCoefficientMatrix[i][j] * forwardRegressCoeffX[j];
                }
                residualsX[i] = xCoords2[i] - yHat;
                SSresidX += residualsX[i] * residualsX[i];
            }

            double sumX = 0;
            double SSx = 0;
            for (i = 0; i < n; i++) {
                SSx += xCoords2[i] * xCoords2[i];
                sumX += xCoords2[i];
            }
            double varianceX = (SSx - (sumX * sumX) / n) / n;
            double SStotalX = (n - 1) * varianceX;
            double rsqX = 1 - SSresidX / SStotalX;

            // now the y-coordinate 
            constants = new ArrayRealVector(yCoords2, false);
            solution = solver.solve(constants);
            forwardRegressCoeffY = new double[numCoefficients];
            for (int a = 0; a < numCoefficients; a++) {
                forwardRegressCoeffY[a] = solution.getEntry(a);
            }

            double[] residualsY = new double[n];
            residualsXY = new double[n];
            residualsOrientation = new double[n];
            double SSresidY = 0;
            for (i = 0; i < n; i++) {
                double yHat = 0.0;
                for (j = 0; j < numCoefficients; j++) {
                    yHat += forwardCoefficientMatrix[i][j] * forwardRegressCoeffY[j];
                }
                residualsY[i] = yCoords2[i] - yHat;
                SSresidY += residualsY[i] * residualsY[i];
                residualsXY[i] = Math.sqrt(residualsX[i] * residualsX[i] + residualsY[i] * residualsY[i]);
                residualsOrientation[i] = Math.atan2(residualsY[i], residualsX[i]);
            }

            double sumY = 0;
            double sumR = 0;
            double SSy = 0;
            double SSr = 0;
            for (i = 0; i < n; i++) {
                SSy += yCoords2[i] * yCoords2[i];
                SSr += residualsXY[i] * residualsXY[i];
                sumY += yCoords2[i];
                sumR += residualsXY[i];
            }
            double varianceY = (SSy - (sumY * sumY) / n) / n;
            double varianceResiduals = (SSr - (sumR * sumR) / n) / n;
            double SStotalY = (n - 1) * varianceY;
            double rsqY = 1 - SSresidY / SStotalY;
            overallRMSE = Math.sqrt(varianceResiduals);

            //System.out.println("y-coordinate r-square: " + rsqY);

            //            // Print the residuals.
            //            System.out.println("\nResiduals:");
            //            for (i = 0; i < n; i++) {
            //                System.out.println("Point " + (i + 1) + "\t" + residualsX[i]
            //                        + "\t" + residualsY[i] + "\t" + residualsXY[i]);
            //            }

            // Solve the backward transformation equations
            double[][] backCoefficientMatrix = new double[n][numCoefficients];
            for (i = 0; i < n; i++) {
                m = 0;
                for (j = 0; j <= polyOrder; j++) {
                    for (k = 0; k <= (polyOrder - j); k++) {
                        backCoefficientMatrix[i][m] = Math.pow(xCoords2[i], j) * Math.pow(yCoords2[i], k);
                        m++;
                    }
                }
            }

            coefficients = new Array2DRowRealMatrix(backCoefficientMatrix, false);
            //DecompositionSolver solver = new SingularValueDecomposition(coefficients).getSolver();
            solver = new QRDecomposition(coefficients).getSolver();

            // do the x-coordinate first
            constants = new ArrayRealVector(xCoords1, false);
            solution = solver.solve(constants);
            backRegressCoeffX = new double[numCoefficients];
            for (int a = 0; a < numCoefficients; a++) {
                backRegressCoeffX[a] = solution.getEntry(a);
            }

            // now the y-coordinate 
            constants = new ArrayRealVector(yCoords1, false);
            solution = solver.solve(constants);
            backRegressCoeffY = new double[n];
            for (int a = 0; a < numCoefficients; a++) {
                backRegressCoeffY[a] = solution.getEntry(a);
            }
        } catch (Exception e) {
            e.printStackTrace();
            //            showFeedback("Error in ImageRectificationDialog.calculateEquations: "
            //                    + e.getMessage());
        }
    }

    public XYPoint getForwardCoordinates(XYPoint point) {
        return getForwardCoordinates(point.x, point.y);
    }

    public XYPoint getForwardCoordinates(double x, double y) {
        XYPoint ret;
        int j, k, m;
        double x_transformed = 0; //mapXMin;
        double y_transformed = 0; //mapYMin;
        double term;
        m = 0;
        for (j = 0; j <= polyOrder; j++) {
            for (k = 0; k <= (polyOrder - j); k++) {
                term = Math.pow(x, j) * Math.pow(y, k);
                x_transformed += term * forwardRegressCoeffX[m];
                y_transformed += term * forwardRegressCoeffY[m];
                m++;
            }
        }

        ret = new XYPoint(x_transformed, y_transformed);

        return ret;
    }

    public XYPoint getBackwardCoordinates(XYPoint point) {
        return getBackwardCoordinates(point.x, point.y);
    }

    public XYPoint getBackwardCoordinates(double x, double y) {
        XYPoint ret;
        int j, k, m;
        double x_transformed = 0; //imageXMin;
        double y_transformed = 0; //imageYMin;
        double term;
        m = 0;
        for (j = 0; j <= polyOrder; j++) {
            for (k = 0; k <= (polyOrder - j); k++) {
                term = Math.pow(x, j) * Math.pow(y, k);
                x_transformed += term * backRegressCoeffX[m];
                y_transformed += term * backRegressCoeffY[m];
                m++;
            }
        }

        ret = new XYPoint(x_transformed, y_transformed);

        return ret;
    }
}