Java tutorial
/******************************************************************************* * @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)); } } }