outlineDescriptor.FieldAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for outlineDescriptor.FieldAnalyzer.java

Source

/*******************************************************************************
 * @Author David Helekal
 *      Platelocator
 *
 *  Copyright (C) 2015  David Helekal
 *
 *  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 outlineDescriptor;

import org.apache.commons.math3.linear.Array2DRowRealMatrix;

import java.util.ArrayList;
import java.util.Arrays;

@SuppressWarnings("unused")

/**
 * Class for analyzing resulting directional field. Call getCircle() to obtain result
 */
final class FieldAnalyzer {

    private final static byte CANDIDATE = (byte) 1;
    private final static byte PROCESSED = (byte) 2;
    private final static byte MAX = (byte) 4;
    private final static byte EQUAL = (byte) 16;
    private final static byte DISCARDED = (byte) 32;
    private final static double THRESHOLD = 0.4;
    private final static double FPERROR = 1e-5;

    private final int[] flags;
    private final int height;
    private final int count;
    private final double cOffsetX;
    private final double cOffsetY;
    private final double[][] LUT;
    private final double scaleFactor;

    private CellArray inputArray;
    private EllipticalKernelGenerator ek;
    private double[][] coherenceKernel;
    private DensityKernel pointKernel;
    private WeightedMeanKernel radiusKernel;
    private Circle result;

    /**
     * @param inputArray   the CellArray to be analyzed
     * @param searchRadius minimum distance between local maxima
     * @param kernelWidth  kernel X dimension high values may seriously hinder performance
     * @param k            number of maxima to be used to establish dish location
     * @param ev1          eigenvalue parameter for normal distribution function
     */
    public FieldAnalyzer(CellArray inputArray, int searchRadius, int kernelWidth, int k, double ev1) {

        this.inputArray = inputArray;
        this.scaleFactor = (double) kernelWidth / (double) inputArray.getImgWidth();
        this.pointKernel = new DensityKernel((int) (inputArray.getImgHeight() * scaleFactor), kernelWidth);
        this.radiusKernel = new WeightedMeanKernel(pointKernel.getRowDimension(), pointKernel.getColumnDimension());

        this.ek = new EllipticalKernelGenerator(searchRadius, searchRadius, ev1, ev1, new double[] { 0 });
        this.coherenceKernel = inputArray.getCoherenceMap();
        this.count = k;
        this.cOffsetX = inputArray.getCellSide() * scaleFactor / 2;
        this.cOffsetY = inputArray.getCellSide() * scaleFactor / 2;
        this.height = inputArray.getRowDimension();
        this.flags = new int[inputArray.getColumnDimension() * height];
        this.LUT = getLUT();

        analyzeArray();

        int[] maxPoint = getMax(pointKernel);
        int radius = (int) radiusKernel.getValueAt(maxPoint[0], maxPoint[1]);
        result = new Circle((int) (maxPoint[0] / scaleFactor), (int) (maxPoint[1] / scaleFactor),
                radius / scaleFactor, 1);
    }

    private CellReference[] findSortAllMaxima() {

        ArrayList<CellReference> store = new ArrayList<CellReference>();

        for (int x = 0; x < inputArray.getColumnDimension(); x++) {

            for (int y = 0; y < inputArray.getRowDimension(); y++) {

                if (coherenceKernel[x][y] > THRESHOLD) {

                    CellReference pf = new CellReference(x, y, coherenceKernel[x][y]);
                    store.add(pf);
                    flags[getListOffset(x, y)] |= CANDIDATE;

                }
            }
        }

        CellReference[] pt = store.toArray(new CellReference[store.size()]);
        Arrays.sort(pt);
        return pt;
    }

    private double[][] getLUT() {

        double step = Math.PI / inputArray.getDirections();
        double[][] out = new double[2][inputArray.getDirections()];

        for (int i = 0; i < inputArray.getDirections(); i++) {

            out[0][i] = Math.cos(step * (i + 0.5));
            out[1][i] = Math.sin(step * (i + 0.5));

        }

        return out;
    }

    private CellReference[] identifyMaxima(CellReference[] store, Array2DRowRealMatrix kernel) {

        ArrayList<CellReference> out = new ArrayList<CellReference>();
        int maxCount = 0;

        for (CellReference pt : store) {

            //this point has already been processed thus it is not a separate maximum
            if ((flags[getListOffset(pt.x, pt.y)] & (DISCARDED | PROCESSED)) != 0) {
                continue;
            }

            //boolean equalInRange = (flags[getListOffset(pt.x, pt.y)] & EQUAL) != 0;

            //checking the neighboring area
            for (int i = 0; i < kernel.getRowDimension(); i++) {

                double[] bounds = kernel.getRow(i);

                int upperLimit = (int) Math.max(bounds[1], bounds[2]);
                int bottom = (int) Math.min(bounds[1], bounds[2]);

                while (bottom <= upperLimit) {

                    int yPos = pt.y + bottom;
                    int xPos = (int) bounds[0] + pt.x;

                    if (xPos >= 0 && xPos < coherenceKernel.length && yPos >= 0
                            && yPos < coherenceKernel[0].length) {

                        if ((flags[getListOffset(xPos, yPos)] & CANDIDATE) == 0) {
                            //this point isnt being considered for a maximum, continue
                            flags[getListOffset(xPos, yPos)] |= PROCESSED;

                        }
                        //checking for equality within tolerance
                        else if (pt.coh - FPERROR <= coherenceKernel[xPos][yPos]) {
                            flags[getListOffset(xPos, yPos)] |= (EQUAL);
                        } else {

                            flags[getListOffset(xPos, yPos)] |= (PROCESSED | DISCARDED);
                        }
                    }
                    flags[getListOffset(pt.x, pt.y)] |= MAX;
                    bottom++;
                }
            }

            out.add(pt);
            maxCount++;

            if (maxCount >= count) {
                break;
            }
        }
        out.trimToSize();
        return out.toArray(new CellReference[out.size()]);

    }

    private int getListOffset(int x, int y) {
        return x + height * (y % height);
    }

    private void analyzeArray() {

        CellReference[] localMaxima = identifyMaxima(findSortAllMaxima(), ek.getRotatedEllipseBounds(0));

        int[][] dirAr = inputArray.getDirectionalArray();

        for (int i = 0; i < localMaxima.length; i++) {

            for (int j = i + 1; j < localMaxima.length; j++) {

                CellReference pt1 = localMaxima[i];
                CellReference pt2 = localMaxima[j];

                if (dirAr[pt1.x][pt1.y] == dirAr[pt2.x][pt2.y]) {
                    continue;
                }

                int[] intersect = getIntersect(pt1, pt2, dirAr[pt1.x][pt1.y], dirAr[pt2.x][pt2.y]);
                if (testBounds(intersect[0], intersect[1])) {

                    //double weight = 1 / (1 + Math.abs((intersect[2] - intersect[3]) / (intersect[2] + intersect[3]) / 2));
                    ODUtils.ellipseVote(intersect[0], intersect[1], 1, ek, 0, pointKernel);
                    ODUtils.ellipseVote(intersect[0], intersect[1], (intersect[2] + intersect[3]) / 2, ek, 0,
                            radiusKernel);
                }
            }
        }
    }

    private int[] getIntersect(CellReference pt1, CellReference pt2, int dir1, int dir2) {

        int[] out = new int[4];

        double a1, a2;
        double b1, b2;
        double c1, c2;

        double x1 = (inputArray.getCell(pt1.x, pt1.y).getPosX() + cOffsetX) * scaleFactor;
        double y1 = (inputArray.getCell(pt1.x, pt1.y).getPosY() + cOffsetY) * scaleFactor;
        double x2 = (inputArray.getCell(pt2.x, pt2.y).getPosX() + cOffsetX) * scaleFactor;
        double y2 = (inputArray.getCell(pt2.x, pt2.y).getPosY() + cOffsetY) * scaleFactor;

        a1 = LUT[0][dir1];
        a2 = LUT[0][dir2];
        b1 = LUT[1][dir1];
        b2 = LUT[1][dir2];

        c1 = -(a1 * x1 + b1 * y1);
        c2 = -(a2 * x2 + b2 * y2);

        double xIntersect = (b2 * c1 - b1 * c2) / (a2 * b1 - b2 * a1);
        double yIntersect = -(a1 * xIntersect + c1) / b1;

        out[0] = (int) xIntersect;
        out[1] = (int) yIntersect;
        out[2] = (int) Math.sqrt(Math.pow(xIntersect - x1, 2) + Math.pow(yIntersect - y1, 2));
        out[3] = (int) Math.sqrt(Math.pow(xIntersect - x2, 2) + Math.pow(yIntersect - y2, 2));

        if ((out[2] <= 0 && out[3] <= 0)) {

            try {
                throw new Exception("ARITHMETIC ERROR THIS SHOULD NOT HAPPEN");
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        return out;

    }

    private boolean testBounds(int x, int y) {

        return x >= 0 && y >= 0 && x < pointKernel.getColumnDimension() && y < pointKernel.getRowDimension();

    }

    private int[] getMax(Array2DRowRealMatrix field) {

        int[] pos = new int[2];

        for (int x = 0; x < field.getColumnDimension(); x++) {

            for (int y = 0; y < field.getRowDimension(); y++) {

                if (field.getEntry(y, x) > field.getEntry(pos[1], pos[0])) {

                    pos[0] = x;
                    pos[1] = y;

                }
            }
        }
        return pos;
    }

    /**
     * @return kernel used to localize the dish center
     */
    public Array2DRowRealMatrix getPointKernel() {
        return pointKernel;
    }

    /**
     * @return dish location
     */
    public Circle getResult() {
        return result;
    }

    private class CellReference implements Comparable<CellReference> {

        public final int x;
        public final int y;
        public final double coh;

        private CellReference(int x, int y, double coh) {
            this.x = x;
            this.y = y;
            this.coh = coh;
        }

        @Override
        public int compareTo(CellReference o) {
            return (int) (Math.signum(o.coh - this.coh));
        }

    }
}