package edu.oregonstate.cartography.grid; import edu.oregonstate.cartography.grid.operators.GridDiffOperator; import edu.oregonstate.cartography.grid.operators.GridDivOperator; import edu.oregonstate.cartography.grid.operators.GridGaussLowPassOperator; import edu.oregonstate.cartography.grid.operators.GridScaleToRangeOperator; import edu.oregonstate.cartography.grid.operators.GridStandardDeviationOperator; /** * Local hypsometric tints: A high-pass filtered grid divided by local standard * deviation. See: * http://cartographicperspectives.org/index.php/journal/article/view/cp74-huffman-patterson/623 * * @author Bernhard Jenny, Cartography and Geovisualization Group, Oregon State * University */ class LocalGridModel { /** * un-filtered grid */ private Grid originalGrid; /** * minimum and maximum values in original grid */ private float[] originalGridMinMax; /** * Laplacian pyramid of the original grid */ private LaplacianPyramid originalGridLaplacianPyramid; /** * The high-pass filtered grid. */ private Grid filteredGrid; /** * The amount of details in highPassGrid. Example values: 0: All values are * 0 1: The first level of the Laplacian pyramid is included. 1.2: The first * level and 0.2 times level 2 are included. */ private double highPassWeight = 1.5; /** * High-pass filtered version of the original grid. */ private Grid highPassGrid; private int localGridStandardDeviationLevels = 3; /** * grid with standard deviation values for the original grid. */ private Grid stdGrid; public LocalGridModel() { } public void setGrid(Grid grid, float[] minMax, LaplacianPyramid laplacianPyramid) { originalGrid = grid; originalGridMinMax = minMax; originalGridLaplacianPyramid = laplacianPyramid; highPassGrid = null; stdGrid = null; filteredGrid = null; } public Grid getFilteredGrid() { if (filteredGrid == null) { updateFilteredGrid(); } return filteredGrid; } private void updateHighPassGrid() { if (originalGrid == null) { return; } // if highPassWeight < 1: compute Gaussian blur (low-pass) and compute // difference to the original grid. // if highPassWeight >= 1: combine levels in Lalacian pyramid to simulate // Gaussian blur. Laplacian pyramids are faster than Gaussian blur for // larger filter size. // Note: if highPassWeight < 1 the Laplacian pyramid cannot be used, as // only the most detailed level in the Laplacian pyramid would be scaled // by highPassWeight, which corresponds to a vertical scaling, but not a // frequency filter. if (highPassWeight < 1) { // the minimum standard deviation (for highPassWeight = 0) double minStdDev = 0.3; // the standard deviation for (highPassWeight = 1) such that a smooth // transition is created with the Laplacian pyramid low pass filter double stdDev_1 = 1.2; // compute standard deviation for Gauss low pass filter double stdDev = highPassWeight * stdDev_1 + minStdDev; GridGaussLowPassOperator op = new GridGaussLowPassOperator(stdDev); // System.out.println(op.kernelToString()); Grid lowPassGrid = op.operate(originalGrid); // high pass grid is the difference between the original grid // and the low-pass filtered grid. highPassGrid = new GridDiffOperator().operate(originalGrid, lowPassGrid); } else { // compute weights for constructing high-pass grid from Laplacian pyramid float[] w = originalGridLaplacianPyramid.createConstantWeights(0); for (int i = 0; i < w.length; i++) { if (Math.floor(highPassWeight) == i) { w[i] = (float) (highPassWeight - i); } else if (Math.floor(highPassWeight) > i) { w[i] = 1f; } } highPassGrid = originalGridLaplacianPyramid.sumLevels(w, true); } } private void updateStdGrid() { if (originalGrid != null) { //long startTime = System.nanoTime(); //System.out.println("std dev: start"); stdGrid = new GridStandardDeviationOperator(localGridStandardDeviationLevels, originalGridLaplacianPyramid).operate(originalGrid); //System.out.println("std dev: end " + (System.nanoTime() - startTime) / 1000 / 1000 + "ms"); } } /** * Computes filteredGrid, which is a high-pass filtered grid divided by * local standard deviation. Local hypsometric tints by Huffman & Patterson, * see * http://cartographicperspectives.org/index.php/journal/article/view/cp74-huffman-patterson/623 */ private void updateFilteredGrid() { if (originalGrid == null) { return; } if (highPassGrid == null) { updateHighPassGrid(); } if (stdGrid == null) { updateStdGrid(); } filteredGrid = new GridDivOperator().operate(highPassGrid, stdGrid, 1f); new GridScaleToRangeOperator(originalGridMinMax).operate(filteredGrid, filteredGrid); } /** * @return the highPassWeight */ public double getHighPassWeight() { return highPassWeight; } /** * @param highPassWeight the highPassWeight to set */ public void setHighPassWeight(double highPassWeight) { if (highPassWeight >= 0); this.highPassWeight = highPassWeight; updateHighPassGrid(); updateFilteredGrid(); } /** * @return the localGridStandardDeviationLevels */ public int getLocalGridStandardDeviationLevels() { return localGridStandardDeviationLevels; } /** * @param localGridStandardDeviationFilterSize the * localGridStandardDeviationFilterSize to set */ public void setLocalGridStandardDeviationLevels(int levels) { assert (levels > 0); this.localGridStandardDeviationLevels = levels; updateStdGrid(); updateFilteredGrid(); } }