plugins.SURFPixelMatching.java Source code

Java tutorial

Introduction

Here is the source code for plugins.SURFPixelMatching.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 plugins;

import jopensurf.*;
import static java.lang.Math.*;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.math3.linear.*;
import org.apache.commons.math3.linear.SingularValueDecomposition;
import whitebox.geospatialfiles.WhiteboxRaster;
import whitebox.geospatialfiles.ShapeFile;
import whitebox.geospatialfiles.shapefile.PointsList;
import whitebox.geospatialfiles.shapefile.PolyLine;
import whitebox.geospatialfiles.shapefile.ShapeFileRecord;
import whitebox.geospatialfiles.shapefile.ShapeType;
import whitebox.geospatialfiles.shapefile.attributes.DBFField;
import whitebox.structures.XYPoint;
import whitebox.stats.PolynomialLeastSquares2DFitting;
import whitebox.structures.KdTree;
import whitebox.structures.RowPriorityGridCell;
import photogrammetry.Normalize2DHomogeneousPoints;

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

    public static void main(String[] args) {
        SURFPixelMatching surf = new SURFPixelMatching();
        surf.run();
    }

    private void run() {

        try {
            // variables
            int a, b, c, i, j, k, n, r;
            int row, col;
            int progress, oldProgress;
            double x, y, z, newX, newY;
            double x2, y2;
            double north, south, east, west;
            double newNorth, newSouth, newEast, newWest;
            double rightNodata;
            double leftNodata;
            Object[] rowData;
            whitebox.geospatialfiles.shapefile.Point outputPoint;
            ShapeFile rightTiePoints;
            ShapeFile leftTiePoints;
            ShapeFile rightFiducials;
            ShapeFile leftFiducials;
            XYPoint xyPoint;
            ArrayList<XYPoint> leftTiePointsList = new ArrayList<>();
            ArrayList<XYPoint> rightTiePointsList = new ArrayList<>();

            float balanceValue = 0.81f;
            float threshold = 0.004f;
            int octaves = 4;
            double maxAllowableRMSE = 1.0;
            double matchingThreshold = 0.6;

            // left image
            //String leftImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/GuelphCampus_C6430-74072-L9_253_Blue_clipped.dep";
            //String leftImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/Guelph_A19409-82_Blue.dep";
            //String leftImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/GuelphCampus 253.dep";
            //String leftImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/GuelphCampus 253 epipolar.dep";
            String leftImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/Guelph_A19409-82_Blue low res.dep";
            //String leftImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/tmp1.dep";

            // right image
            //String rightImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/GuelphCampus_C6430-74072-L9_254_Blue_clipped.dep";
            //String rightImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/Guelph_A19409-83_Blue.dep";
            //String rightImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/GuelphCampus 254.dep";
            //String rightImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/GuelphCampus 254 epipolar.dep";
            String rightImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/Guelph_A19409-83_Blue low res.dep";
            //String rightImageName = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/GuelphCampus 253.dep";

            String leftOutputHeader = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/tmp4.shp";

            String rightOutputHeader = "/Users/johnlindsay/Documents/Teaching/GEOG2420/airphotos/tmp4_2.shp";

            DBFField[] fields = new DBFField[5];
            fields[0] = new DBFField();
            fields[0].setName("FID");
            fields[0].setDataType(DBFField.DBFDataType.NUMERIC);
            fields[0].setDecimalCount(4);
            fields[0].setFieldLength(10);

            fields[1] = new DBFField();
            fields[1].setName("ORIENT");
            fields[1].setDataType(DBFField.DBFDataType.NUMERIC);
            fields[1].setDecimalCount(4);
            fields[1].setFieldLength(10);

            fields[2] = new DBFField();
            fields[2].setName("SCALE");
            fields[2].setDataType(DBFField.DBFDataType.NUMERIC);
            fields[2].setDecimalCount(4);
            fields[2].setFieldLength(10);

            fields[3] = new DBFField();
            fields[3].setName("LAPLAC");
            fields[3].setDataType(DBFField.DBFDataType.NUMERIC);
            fields[3].setDecimalCount(4);
            fields[3].setFieldLength(10);

            fields[4] = new DBFField();
            fields[4].setName("RESID");
            fields[4].setDataType(DBFField.DBFDataType.NUMERIC);
            fields[4].setDecimalCount(4);
            fields[4].setFieldLength(10);

            // read the input data
            WhiteboxRaster leftImage = new WhiteboxRaster(leftImageName, "r");
            //            leftImage.setForceAllDataInMemory(true);
            int nRowsLeft = leftImage.getNumberRows();
            int nColsLeft = leftImage.getNumberColumns();
            leftNodata = leftImage.getNoDataValue();

            WhiteboxRaster rightImage = new WhiteboxRaster(rightImageName, "r");
            //rightImage.setForceAllDataInMemory(true);
            //            int nRowsRight = rightImage.getNumberRows();
            //            int nColsRight = rightImage.getNumberColumns();
            rightNodata = rightImage.getNoDataValue();

            //            ArrayList<InterestPoint> interest_points;
            //            double threshold = 600;
            //            double balanceValue = 0.9;
            //            int octaves = 4;
            //            ISURFfactory mySURF = SURF.createInstance(leftImage, balanceValue, threshold, octaves);
            //            IDetector detector = mySURF.createDetector();
            //            interest_points = detector.generateInterestPoints();
            //            System.out.println("Interest points generated");
            //            IDescriptor descriptor = mySURF.createDescriptor(interest_points);
            //            descriptor.generateAllDescriptors();
            System.out.println("Performing SURF analysis on left image...");
            Surf leftSurf = new Surf(leftImage, balanceValue, threshold, octaves);
            //            if (leftSurf.getNumberOfPoints() > 500000) {
            //                System.out.println("Number of points exceeds limit, reset threshold: " + leftSurf.getNumberOfPoints());
            //                return;
            //            }
            if (leftSurf.getNumberOfPoints() == 0) {
                System.out
                        .println("Number of points equals zero, reset threshold: " + leftSurf.getNumberOfPoints());
                return;
            }

            System.out.println("Performing SURF analysis on right image...");
            Surf rightSurf = new Surf(rightImage, balanceValue, threshold, octaves);
            if (rightSurf.getNumberOfPoints() == 0) {
                System.out
                        .println("Number of points equals zero, reset threshold: " + leftSurf.getNumberOfPoints());
                return;
            }

            System.out.println("Matching points of interest...");
            Map<SURFInterestPoint, SURFInterestPoint> matchingPoints = leftSurf.getMatchingPoints(rightSurf,
                    matchingThreshold, false);

            int numTiePoints = matchingPoints.size();
            if (numTiePoints < 3) {
                System.err.println(
                        "The number of potential tie points is less than 3. Adjust your threshold parameters and retry.");
                return;
            }
            System.out.println(numTiePoints + " potential tie points located");
            System.out.println("Trimming outlier tie points...");

            boolean flag;
            do {
                flag = false;
                leftTiePointsList.clear();
                rightTiePointsList.clear();
                i = 0;
                for (SURFInterestPoint point : matchingPoints.keySet()) {
                    x = point.getX();
                    y = point.getY();

                    SURFInterestPoint target = matchingPoints.get(point);
                    x2 = target.getX();
                    y2 = target.getY();

                    leftTiePointsList.add(new XYPoint(x, y));
                    rightTiePointsList.add(new XYPoint(x2, y2));

                    i++;
                }

                PolynomialLeastSquares2DFitting overallFit = new PolynomialLeastSquares2DFitting(leftTiePointsList,
                        rightTiePointsList, 1);

                double maxDist = 0;
                SURFInterestPoint mostInfluentialPoint = null;
                i = 0;
                for (SURFInterestPoint point : matchingPoints.keySet()) {
                    leftTiePointsList.clear();
                    rightTiePointsList.clear();
                    for (SURFInterestPoint point2 : matchingPoints.keySet()) {
                        if (point2 != point) {
                            x = point2.getX();
                            y = point2.getY();

                            SURFInterestPoint target = matchingPoints.get(point2);
                            x2 = target.getX();
                            y2 = target.getY();

                            leftTiePointsList.add(new XYPoint(x, y));
                            rightTiePointsList.add(new XYPoint(x2, y2));
                        }
                    }
                    PolynomialLeastSquares2DFitting newFit = new PolynomialLeastSquares2DFitting(leftTiePointsList,
                            rightTiePointsList, 1);

                    x = point.getX();
                    y = point.getY();
                    XYPoint pt1 = overallFit.getForwardCoordinates(x, y);
                    XYPoint pt2 = newFit.getForwardCoordinates(x, y);
                    double dist = pt1.getSquareDistance(pt2);
                    if (dist > maxDist) {
                        maxDist = dist;
                        mostInfluentialPoint = point;
                    }
                }

                if (maxDist > 10 && mostInfluentialPoint != null) {
                    matchingPoints.remove(mostInfluentialPoint);
                    flag = true;
                }
                System.out.println(maxDist);
            } while (flag);

            int numPoints = matchingPoints.size();

            // create homogeneous points matrices
            double[][] leftPoints = new double[3][numPoints];
            double[][] rightPoints = new double[3][numPoints];

            i = 0;
            for (SURFInterestPoint point : matchingPoints.keySet()) {
                leftPoints[0][i] = point.getX();
                leftPoints[1][i] = point.getY();
                leftPoints[2][i] = 1;

                SURFInterestPoint target = matchingPoints.get(point);

                rightPoints[0][i] = target.getX();
                rightPoints[1][i] = target.getY();
                rightPoints[2][i] = 1;
                i++;
            }

            double[][] normalizedLeftPoints = Normalize2DHomogeneousPoints.normalize(leftPoints);
            RealMatrix Tl = MatrixUtils.createRealMatrix(Normalize2DHomogeneousPoints.T);
            double[][] normalizedRightPoints = Normalize2DHomogeneousPoints.normalize(rightPoints);
            RealMatrix Tr = MatrixUtils.createRealMatrix(Normalize2DHomogeneousPoints.T);

            RealMatrix pnts1 = MatrixUtils.createRealMatrix(normalizedLeftPoints);
            RealMatrix pnts2 = MatrixUtils.createRealMatrix(normalizedRightPoints);

            RealMatrix A = MatrixUtils.createRealMatrix(buildA(normalizedLeftPoints, normalizedRightPoints));

            //RealMatrix ata = A.transpose().multiply(A);
            SingularValueDecomposition svd = new SingularValueDecomposition(A);

            RealMatrix V = svd.getV();
            RealVector V_smallestSingularValue = V.getColumnVector(8);
            RealMatrix F = MatrixUtils.createRealMatrix(3, 3);
            for (i = 0; i < 9; i++) {
                F.setEntry(i / 3, i % 3, V_smallestSingularValue.getEntry(i));
            }

            for (i = 0; i < V.getRowDimension(); i++) {
                System.out.println(V.getRowVector(i).toString());
            }

            SingularValueDecomposition svd2 = new SingularValueDecomposition(F);
            RealMatrix U = svd2.getU();
            RealMatrix S = svd2.getS();
            V = svd2.getV();
            RealMatrix m = MatrixUtils.createRealMatrix(
                    new double[][] { { S.getEntry(1, 1), 0, 0 }, { 0, S.getEntry(2, 2), 0 }, { 0, 0, 0 } });
            F = U.multiply(m).multiply(V).transpose();

            // Denormalise
            F = Tr.transpose().multiply(F).multiply(Tl);
            for (i = 0; i < F.getRowDimension(); i++) {
                System.out.println(F.getRowVector(i).toString());
            }

            svd2 = new SingularValueDecomposition(F);
            //[U,D,V] = svd(F,0);
            RealMatrix e1 = svd2.getV().getColumnMatrix(2); //hnormalise(svd2.getV(:,3));
            RealMatrix e2 = svd2.getU().getColumnMatrix(2); //e2 = hnormalise(U(:,3));

            e1.setEntry(0, 0, (e1.getEntry(0, 0) / e1.getEntry(2, 0)));
            e1.setEntry(1, 0, (e1.getEntry(1, 0) / e1.getEntry(2, 0)));
            e1.setEntry(2, 0, 1);

            e2.setEntry(0, 0, (e2.getEntry(0, 0) / e2.getEntry(2, 0)));
            e2.setEntry(1, 0, (e2.getEntry(1, 0) / e2.getEntry(2, 0)));
            e2.setEntry(2, 0, 1);

            System.out.println("");

            //                boolean[] removeTiePoint = new boolean[numTiePoints];
            //                double[] residuals = null;
            //                double[] residualsOrientation = null;
            //                boolean flag;
            //                do {
            //                    // perform the initial tie point transformation
            //                    leftTiePointsList.clear();
            //                    rightTiePointsList.clear();
            //                    int numPointsIncluded = 0;
            //                    i = 0;
            //                    for (SURFInterestPoint point : matchingPoints.keySet()) {
            //                        if (i < numTiePoints && !removeTiePoint[i]) {
            //                            x = point.getX();
            //                            y = point.getY();
            //
            //                            SURFInterestPoint target = matchingPoints.get(point);
            //                            x2 = target.getX();
            //                            y2 = target.getY();
            //
            //                            leftTiePointsList.add(new XYPoint(x, y));
            //                            rightTiePointsList.add(new XYPoint(x2, y2));
            //
            //                            numPointsIncluded++;
            //                        }
            //                        i++;
            //                    }
            //
            //                    PolynomialLeastSquares2DFitting plsFit = new PolynomialLeastSquares2DFitting(
            //                            leftTiePointsList, rightTiePointsList, 1);
            //
            //                    double rmse = plsFit.getOverallRMSE();
            //                    System.out.println("RMSE: " + rmse + " with " + numPointsIncluded + " points included.");
            //
            //                    flag = false;
            //                    residuals = plsFit.getResidualsXY();
            //                    residualsOrientation = plsFit.getResidualsOrientation();
            //                    if (rmse > maxAllowableRMSE) {
            //                        i = 0;
            //                        for (k = 0; k < numTiePoints; k++) {
            //                            if (!removeTiePoint[k]) {
            //                                if (residuals[i] > 3 * rmse) {
            //                                    removeTiePoint[k] = true;
            //                                    flag = true;
            //                                }
            //                                i++;
            //                            }
            //                        }
            //                    }
            //                } while (flag);
            //
            //                i = 0;
            //                for (k = 0; k < numTiePoints; k++) {
            //                    if (!removeTiePoint[k]) {
            //                        i++;
            //                    }
            //                }
            System.out.println(numPoints + " tie points remain.");

            System.out.println("Outputing tie point files...");

            ShapeFile leftOutput = new ShapeFile(leftOutputHeader, ShapeType.POINT, fields);

            ShapeFile rightOutput = new ShapeFile(rightOutputHeader, ShapeType.POINT, fields);

            i = 0;
            k = 0;
            for (SURFInterestPoint point : matchingPoints.keySet()) {
                //                    if (i < numTiePoints && !removeTiePoint[i]) {
                x = leftImage.getXCoordinateFromColumn((int) point.getX());
                y = leftImage.getYCoordinateFromRow((int) point.getY());

                SURFInterestPoint target = matchingPoints.get(point);
                x2 = rightImage.getXCoordinateFromColumn((int) target.getX());
                y2 = rightImage.getYCoordinateFromRow((int) target.getY());

                outputPoint = new whitebox.geospatialfiles.shapefile.Point(x, y);
                rowData = new Object[5];
                rowData[0] = new Double(k + 1);
                rowData[1] = new Double(point.getOrientation());
                rowData[2] = new Double(point.getScale());
                rowData[3] = new Double(point.getLaplacian());
                rowData[4] = new Double(0); //residuals[k]);
                leftOutput.addRecord(outputPoint, rowData);

                outputPoint = new whitebox.geospatialfiles.shapefile.Point(x2, y2);
                rowData = new Object[5];
                rowData[0] = new Double(k + 1);
                rowData[1] = new Double(target.getOrientation());
                rowData[2] = new Double(target.getScale());
                rowData[3] = new Double(target.getLaplacian());
                rowData[4] = new Double(0); //residuals[k]);
                rightOutput.addRecord(outputPoint, rowData);

                k++;
                //                    }
                i++;
            }

            leftOutput.write();
            rightOutput.write();

            System.out.println("Done!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private double[][] buildA(double[][] points1, double[][] points2) {
        int numPoints = points1[0].length;
        double[][] result = new double[numPoints][9];
        for (int i = 0; i < numPoints; i++) {
            double x1i = points1[0][i];
            double x2i = points2[0][i];
            double y1i = points1[1][i];
            double y2i = points2[1][i];

            double[] row = new double[] { x1i * x2i, y1i * x2i, x2i, x1i * y2i, y1i * y2i, y2i, x1i, y1i, 1 };
            System.arraycopy(row, 0, result[i], 0, 9);
        }
        return result;
    }
}