/* * GridNeighborCounterOperator.java * * Created on February 1, 2006, 11:50 PM * */ package ika.geo.grid; import ika.geo.*; import ika.utils.GeometryUtils; /** * * @author Bernhard Jenny, Institute of Cartography, ETH Zurich */ public class GridAspectNeighborCounterOperator implements GridOperator{ /** * Size of the filter. * filterSize must be odd number; */ private int filterSize = 5; /** * Only values with an absolute difference that is smaller than maxDiff * are counted. */ private float maxDiff = 0.35f; /** * The filter scans each ring around its center, starting at the center * towards its borders. The scan stops when there are a certain amount of * small values. * For one ring: fract = nbrValuesLargerThanMaxDiff / totalNbrValues. * The scan stops if fract < minFraction */ private float minFraction = 0.7f; /** * Creates a new instance of GridNeighborCounterOperator */ public GridAspectNeighborCounterOperator() { } public String getName() { return "Aspect Count Filter"; } public ika.geo.GeoObject operate(ika.geo.GeoGrid geoGrid) { /* % Size of the resulting matrices relative to input grid: The filter uses % aspect values that are not (well) defined for border rows and columns. % The size of the input grid to the filtering process is therefore nrows-2 % times ncols-2. The filtering is again not (well) defined on the border % rows and columns of this reduced aspect grid. The size is reduced by % filterSize-1 if filterSize is odd. The number of rows of the resulting % grids therefore is nrows-2-(filterSize-1) = nrows-1-filterSize. */ if (geoGrid == null) throw new IllegalArgumentException(); // make sure filterSize is odd number if (filterSize % 2 != 1) return null; final int halfFilterSize = this.filterSize / 2; // compute the size of the new GeoGrid and create it. final int old_nrows = geoGrid.getRows(); final int old_ncols = geoGrid.getCols(); final int counter_nrows = old_nrows - this.filterSize + 1; final int counter_ncols = old_ncols - this.filterSize + 1; final double meshSize = geoGrid.getCellSize(); GeoGrid counterGrid = new GeoGrid(counter_ncols, counter_nrows, meshSize); counterGrid.setWest(geoGrid.getWest() + meshSize * halfFilterSize); counterGrid.setNorth(geoGrid.getNorth() + meshSize * halfFilterSize); /* loop over each pixel */ float[][] srcGrid = geoGrid.getGrid(); float[][] dstGrid = counterGrid.getGrid(); for (int row = halfFilterSize; row < old_nrows-halfFilterSize; row++) { float[] dstRow = dstGrid[row - halfFilterSize]; for (int col = halfFilterSize; col < old_ncols-halfFilterSize; col++) { int nbrScannedPts = 0; int npts = 0; // loop over each ring around the central pixel for (int i = 1; i <= halfFilterSize; i++) { // count the number of values in the ring that similar // to the central value final int nbrFoundPts = scanRect(col, row, srcGrid, i); // keep track of the number of pixel that have been visited so far // For one ring: npts = 4n-2, here half filter size. nbrScannedPts += 8 * i - 2; // abort scanning when there are too few similar points in // the current ring if (((double)npts + nbrFoundPts) / nbrScannedPts < minFraction) break; // accumulate the number of found points npts += nbrFoundPts; } // store the number of found points in the grid dstRow[col-halfFilterSize] = npts; } } return counterGrid; } /** * Scans a rectangular ring around a central pixel and counts the number of * pixels that are similar to the central pixel. * @param col The column of the central pixel * @param row The row of the central pixel * @param grid The GeoGrid to scan. * @param currentHalfFilterSize The distance in pixel from the central pixel * to the ring to scan. * @return The number of pixels with a value similar to the central pixel. */ private int scanRect(int col, int row, float[][] grid, int currentHalfFilterSize) { int npts = 0; final float centerVal = grid[row][col]; // scan top and bottom row final float[] topRow = grid[row-currentHalfFilterSize]; final float[] botRow = grid[row+currentHalfFilterSize]; for (int c = col-currentHalfFilterSize; c <= col+currentHalfFilterSize; c++) { if (this.isAngleDiffLarge(centerVal, topRow[c])) { npts++; } if (this.isAngleDiffLarge(centerVal, botRow[c])){ npts++; } } // scan left colum and right column. Don't scan corner point, which // have been scanned in the loop above. for (int r = row-currentHalfFilterSize+1; r < row+currentHalfFilterSize; r++) { final float[] srcRow = grid[r]; if (this.isAngleDiffLarge(centerVal, srcRow[col-currentHalfFilterSize])) { npts++; } if (this.isAngleDiffLarge(centerVal, srcRow[col+currentHalfFilterSize])) { npts++; } } return npts; } private boolean isAngleDiffLarge(float a1, float a2) { // compute the difference between two angles. We are interested in the // absolute value only. double dif = Math.abs(a1 - a2); if (dif > Math.PI) dif = 2. * Math.PI - dif; return dif < this.maxDiff; } public int getFilterSize() { return filterSize; } public void setFilterSize(int filterSize) { this.filterSize = filterSize; } public float getMaxDiff() { return maxDiff; } public void setMaxDiff(float maxDiff) { this.maxDiff = maxDiff; } public float getMinFraction() { return minFraction; } public void setMinFraction(float minFraction) { this.minFraction = minFraction; } }