/* JAI-Ext - OpenSource Java Advanced Image Extensions Library * http://www.geo-solutions.it/ * Copyright 2014 GeoSolutions * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package it.geosolutions.jaiext.stats; import it.geosolutions.jaiext.range.Range; import it.geosolutions.jaiext.stats.Statistics.StatsType; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; import com.sun.media.jai.util.PropertyGeneratorImpl; /** * This class is used for retrieving an eventual ROI object passed to the source image by calling the getProperty() method. This method checks if the * ROI is present and if so, its bounds are intersected with the source images bounds, and then passed as a result. If no property was found an * Undefined Property object is returned. */ class StatisticsPropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public StatisticsPropertyGenerator() { super(new String[] { "ROI" }, new Class[] { ROI.class }, new Class[] { RenderedOp.class }); } /** * Returns the ROI saved as a property. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if (opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp) opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. RenderedImage src = pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return java.awt.Image.UndefinedProperty; } ROI srcROI = (ROI) property; // Determine the effective source bounds. Rectangle srcBounds = null; srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); // If necessary, clip the ROI to the effective source bounds. if (!srcBounds.contains(srcROI.getBounds())) { srcROI = srcROI.intersect(new ROIShape(srcBounds)); } // Saves the destination ROI. ROI dstROI = srcROI; // Return the clipped ROI. return dstROI; } return java.awt.Image.UndefinedProperty; } } /** * An <code>OperationDescriptor</code> describing various "Statistical" operation supporting ROI and NoData. * * <p> * The SimpleStats operation takes in input a source image and an array indicating which bands to analyze and which kind of statistics can be done. * More statistics can be computed inside the same image. The possible statistics are: * </p> * * <p> * <ul> * <li>Mean</li> * <li>Sum</li> * <li>Maximum</li> * <li>Minimum</li> * <li>Extrema</li> * <li>Variance</li> * <li>Standard Deviation</li> * <li>Histogram</li> * <li>Mode</li> * <li>Median</li> * </ul> * </p> * * <p> * The source can have all the possible JAI accepted data types. The statistical calculations are performed on every tile and stored inside an object * which is a subclass of the "Statistics" class. For avoiding concurrency issues various techniques are used: for simple statistics, which does not * request an array for storing the values, local statistics are calculated and then accumulated in a synchronized block; for complex statistics, * other techniques are used. The statistical results are returned by calling the getProperty() method. The statistics are calculated only the first * time for avoiding unnecessary calculations. With this setup an advantage is taken by using the internal JAI MultiThreading. * </p> * * <p> * When the results are returned by the getProperty() method as a 2-D array, the user must only select the first index related to one band and the * second index related to a specific computation, as defined at the image creation, and then calling the getResult() method. * </p> * * * <p> * <table border=1> * <caption>Resource List</caption> * <tr> * <th>Name</th> * <th>Value</th> * </tr> * <tr> * <td>GlobalName</td> * <td>Stats</td> * </tr> * <tr> * <td>LocalName</td> * <td>Stats</td> * </tr> * <tr> * <td>Vendor</td> * <td>it.geosolutions.jaiext</td> * </tr> * <tr> * <td>Description</td> * <td>Image operator for calculating simple image statistics like mean supporting ROI and No Data.</td> * </tr> * <tr> * <td>DocURL</td> * <td>Not Defined</td> * </tr> * <tr> * <td>Version</td> * <td>1.0</td> * </tr> * <tr> * <td>arg0Desc</td> * <td>Horizontal subsampling parameter.</td> * </tr> * <tr> * <td>arg1Desc</td> * <td>Vertical subsampling parameter.</td> * </tr> * <tr> * <td>arg2Desc</td> * <td>ROI object used.</td> * </tr> * <tr> * <td>arg3Desc</td> * <td>No Data Range used.</td> * </tr> * <tr> * <td>arg4Desc</td> * <td>Boolean checking if ROI RasterAccessor is used.</td> * </tr> * <tr> * <td>arg5Desc</td> * <td>Array containing the indexes of the bands to calculate.</td> * </tr> * <tr> * <td>arg6Desc</td> * <td>Array indicating which statistical operations must be performed on all the selected bands.</td> * </tr> * <td>arg7Desc</td> * <td>Array indicating the minimum bounds for each statistic types (if needed).</td> * </tr> * <td>arg8Desc</td> * <td>Array indicating the maximum bounds for each statistic types (if needed).</td> * </tr> * <td>arg9Desc</td> * <td>Array indicating the number of bins for each statistic types (if needed).</td> * </tr> * </table> * </p> * * <p> * <table border=1> * <caption>Parameter List</caption> * <tr> * <th>Name</th> * <th>Class Type</th> * <th>Default Value</th> * </tr> * <tr> * <td>xPeriod</td> * <td>Integer</td> * <td>1</td> * <tr> * <td>yPeriod</td> * <td>Integer</td> * <td>1</td> * <tr> * <td>ROI</td> * <td>ROI</td> * <td>null</td> * <tr> * <td>noData</td> * <td>it.geosolutions.jaiext.range.Range</td> * <td>null</td> * <tr> * <td>useRoiAccessor</td> * <td>Boolean</td> * <td>False</td> * <tr> * <td>bands</td> * <td>int[]</td> * <td>null</td> * <tr> * <td>stats</td> * <td>it.geosolutions.jaiext.stats.Statistics.StatsType[]</td> * <td>null</td> * <tr> * <td>lowValue</td> * <td>double[]</td> * <td>null</td> * <tr> * <td>highValue</td> * <td>double[]</td> * <td>null</td> * <tr> * <td>numBins</td> * <td>int[]</td> * <td>null</td> * <tr> * </table> * </p> * */ public class StatisticsDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and specify the parameter list for this operation. */ private static final String[][] resources = { { "GlobalName", "Stats" }, { "LocalName", "Stats" }, { "Vendor", "it.geosolutions.jaiext" }, { "Description", "Image operator for calculating simple image statistics like mean supporting ROI and No Data" }, { "DocURL", "Not defined" }, { "Version", "1.0" }, { "arg0Desc", "Horizontal subsampling parameter" }, { "arg1Desc", "Vertical subsampling parameter" }, { "arg2Desc", "ROI object used" }, { "arg3Desc", "No Data Range used" }, { "arg4Desc", "Boolean checking if ROI RasterAccessor is used" }, { "arg5Desc", "Array containing the indexes of the bands to calculate" }, { "arg6Desc", "Array indicating which statistical operations must be performed on all the selected bands" }, { "arg7Desc", "Array indicating the minimum bounds for each statistic types (if needed)" }, { "arg8Desc", "Array indicating the maximum bounds for each statistic types (if needed)" }, { "arg9Desc", "Array indicating the number of bins for each statistic types (if needed)" } }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Integer.class, java.lang.Integer.class, javax.media.jai.ROI.class, it.geosolutions.jaiext.range.Range.class, java.lang.Boolean.class, int[].class, it.geosolutions.jaiext.stats.Statistics.StatsType[].class, double[].class, double[].class, int[].class, }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "xPeriod", "yPeriod", "ROI", "noData", "useRoiAccessor", "bands", "stats", "lowValue", "highValue", "numBins" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { 1, 1, null, null, false, new int[] { 0 }, null, new double[] {0.0}, new double[] {256.0}, new int[] {256}}; public StatisticsDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns an array of <code>PropertyGenerators</code> implementing property inheritance for the "Stats" operation * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new StatisticsPropertyGenerator(); return pg; } /** * Performs a statistical operation on an image defined by its "stats type" parameter. * * <p> * Creates a <code>ParameterBlockJAI</code> from all supplied arguments except <code>hints</code> and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 <code>RenderedImage</code> source 0. * @param xPeriod Horizontal subsampling. * @param yPeriod Vertical subsampling. * @param ROI Roi object on which the calculation are performed. * @param NoData No Data range used for calculation. * @param useRoiAccessor Boolean indicating if ROI RasterAccessor must be used. * @param bands Array indicating which band to consider. * @param stats Array indicating which statistics to consider. * @param minBounds Array indicating the minimum bounds for each statistic types . * @param maxBounds Array indicating the maximum bounds for each statistic types. * @param numBins Array indicating the number of bins for each statistic types. * @param hints The <code>RenderingHints</code> to use. * @return The <code>RenderedOp</code> destination. * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>. */ public static RenderedOp create(RenderedImage source0, int xPeriod, int yPeriod, ROI roi, Range noData, boolean useRoiAccessor, int[] bands, StatsType[] stats, double[] minBounds, double[] maxBounds, int[] numBins, RenderingHints hints) { // Creation of a parameterBlockJAI containing all the operation parameters ParameterBlockJAI pb = new ParameterBlockJAI("Stats", RenderedRegistryMode.MODE_NAME); // Source image pb.setSource("source0", source0); // Image parameters pb.setParameter("xPeriod", xPeriod); pb.setParameter("yPeriod", yPeriod); pb.setParameter("ROI", roi); pb.setParameter("NoData", noData); pb.setParameter("useRoiAccessor", useRoiAccessor); pb.setParameter("bands", bands); pb.setParameter("stats", stats); if (minBounds != null && maxBounds != null && numBins != null) { pb.setParameter("lowValue", minBounds); pb.setParameter("highValue", maxBounds); pb.setParameter("numBins", numBins); } else if (minBounds != null || maxBounds != null || numBins != null) { throw new IllegalArgumentException("bounds and bins must be declared together"); } // RenderedImage creation return JAI.create("Stats", pb, hints); } /** * Performs a statistical operation on an image defined by its "stats type" parameter. * * <p> * Creates a <code>ParameterBlockJAI</code> from all supplied arguments except <code>hints</code> and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 <code>RenderedImage</code> source 0. * @param xPeriod Horizontal subsampling. * @param yPeriod Vertical subsampling. * @param ROI Roi object on which the calculation are performed. * @param NoData No Data range used for calculation. * @param useRoiAccessor Boolean indicating if ROI RasterAccessor must be used. * @param bands Array indicating which band to consider. * @param stats Array indicating which statistics to consider. * @param hints The <code>RenderingHints</code> to use. * @return The <code>RenderedOp</code> destination. * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>. */ public static RenderedOp create(RenderedImage source0, int xPeriod, int yPeriod, ROI roi, Range noData, boolean useRoiAccessor, int[] bands, StatsType[] stats, RenderingHints hints) { return create(source0, xPeriod, yPeriod, roi, noData, useRoiAccessor, bands, stats, null, null, null, hints); } }