/* 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.convolve; import it.geosolutions.jaiext.range.Range; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; import com.sun.media.jai.util.AreaOpPropertyGenerator; /** * An <code>OperationDescriptor</code> describing the "Convolve" operation. * * <p> * Convolution is a spatial operation that computes each output sample by multiplying elements of a kernel with the samples surrounding a particular * source sample. * * <p> * For each destination sample, the kernel is rotated 180 degrees and its "key element," or origin, is placed over the source pixel corresponding with * the destination pixel. The kernel elements are multiplied with the source pixels beneath them, and the resulting products are summed together to * produce the destination sample value. * * <p> This operation is able to check if each input pixel is contained inside the provided ROI and if it is not a NoData value. If a pixel in the kernel * is outside ROI or a NoData, the related Kernel value is not calculated and destination No Data value is returned. * * <p> * Pseudocode for the convolution operation on a single sample dst[x][y] is as follows, assuming the kernel is of size width x height and has already * been rotated through 180 degrees. The kernel's Origin element is located at position (xOrigin, yOrigin): * * <pre> * dst[x][y] = 0; * for (int i = -xOrigin; i < -xOrigin + width; i++) { * for (int j = -yOrigin; j < -yOrigin + height; j++) { * dst[x][y] += src[x + i][y + j] * kernel[xOrigin + i][yOrigin + j]; * } * } * </pre> * * <p> * Convolution, like any neighborhood operation, leaves a band of pixels around the edges undefined. For example, for a 3x3 kernel only four kernel * elements and four source pixels contribute to the convolution pixel at the corners of the source image. Pixels that do not allow the full kernel to * be applied to the source are not included in the destination image. A "Border" operation may be used to add an appropriate border to the source * image in order to avoid shrinkage of the image boundaries. * * <p> * The kernel may not be bigger in any dimension than the image data. * * It should be noted that this operation automatically adds a value of <code>Boolean.TRUE</code> for the * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> to the given <code>configuration</code> so that the operation is performed on the pixel values * instead of being performed on the indices into the color map if the source(s) have an <code>IndexColorModel</code>. This addition will take place * only if a value for the <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> has not already been provided by the user. Note that the * <code>configuration</code> Map is cloned before the new hint is added to it. The operation can be smart about the value of the * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> <code>RenderingHints</code>, i.e. while the default value for the * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> is <code>Boolean.TRUE</code>, in some cases the operator could set the default. * * <p> * <table border=1> * <caption>Resource List</caption> * <tr> * <th>Name</th> * <th>Value</th> * </tr> * <tr> * <td>GlobalName</td> * <td>Convolve</td> * </tr> * <tr> * <td>LocalName</td> * <td>Convolve</td> * </tr> * <tr> * <td>Vendor</td> * <td>it.geosolutions.jaiext</td> * </tr> * <tr> * <td>Description</td> * <td>Convolves the source image with the input kernel.</td> * </tr> * <tr> * <td>DocURL</td> * <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ConvolveDescriptor.html</td> * </tr> * <tr> * <td>Version</td> * <td>1.0</td> * </tr> * <tr> * <td>arg0Desc</td> * <td>Input convolution kernel.</td> * </tr> * <tr> * <td>arg1Desc</td> * <td>Optional ROI object to use in computation.</td> * </tr> * <tr> * <td>arg2Desc</td> * <td>Optional Range of NoData values to use in computation.</td> * </tr> * <tr> * <td>arg3Desc</td> * <td>Destination No Data value used when the computation cannot be performed.</td> * </tr> * <tr> * <td>arg4Desc</td> * <td>Boolean indicating if kernels with NoData must be skipped from computation.</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>kernel</td> * <td>javax.media.jai.KernelJAI</td> * <td>NO_PARAMETER_DEFAULT</td> * <tr> * <td>roi</td> * <td>javax.media.jai.ROI</td> * <td>null</td> * <tr> * <td>nodata</td> * <td>it.geosolutions.jaiext.range.Range</td> * <td>null</td> * <tr> * <td>destNoData</td> * <td>Double</td> * <td>0</td> * <tr> * <td>skipNoData</td> * <td>Boolean</td> * <td>true</td> * </table> * </p> */ public class ConvolveDescriptor extends OperationDescriptorImpl { /** serialVersionUID */ private static final long serialVersionUID = 1L; /** * The resource strings that provide the general documentation and specify the parameter list for a Convolve operation. */ private static final String[][] resources = { { "GlobalName", "Convolve" }, { "LocalName", "Convolve" }, { "Vendor", "it.geosolutions.jaiext" }, { "Description", JaiI18N.getString("ConvolveDescriptor0") }, { "DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ConvolveDescriptor.html" }, { "Version", JaiI18N.getString("DescriptorVersion") }, { "arg0Desc", JaiI18N.getString("ConvolveDescriptor1") }, { "arg1Desc", JaiI18N.getString("ConvolveDescriptor2") }, { "arg2Desc", JaiI18N.getString("ConvolveDescriptor3") }, { "arg3Desc", JaiI18N.getString("ConvolveDescriptor4") }, { "arg5Desc", JaiI18N.getString("ConvolveDescriptor5") }}; /** The parameter names for the Convolve operation. */ private static final String[] paramNames = { "kernel", "roi", "nodata", "destNoData", "skipNoData" }; /** The parameter class types for the Convolve operation. */ private static final Class[] paramClasses = { javax.media.jai.KernelJAI.class, javax.media.jai.ROI.class, it.geosolutions.jaiext.range.Range.class, Double.class, Boolean.class }; /** The parameter default values for the Convolve operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, null, null, 0d, true}; /** Constructor. */ public ConvolveDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns an array of <code>PropertyGenerators</code> implementing property inheritance for the "Convolve" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AreaOpPropertyGenerator(); return pg; } /** * Performs kernel-based convolution on an image. * * <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 kernel The convolution kernel. * @param roi Optional ROI to use in computation * @param nodata Optional nodata Range to check for NoData * @param destNoData Double value used for setting destination No Data value when it is not possible to * calculate the convolved result * @param skipNoData Boolean indicating if kernels with NoData must be skipped from computation * @param hints The <code>RenderingHints</code> to use. May be <code>null</code>. * @return The <code>RenderedOp</code> destination. * @throws IllegalArgumentException if <code>source0</code> is <code>null</code>. * @throws IllegalArgumentException if <code>kernel</code> is <code>null</code>. */ public static RenderedOp create(RenderedImage source0, KernelJAI kernel, ROI roi, Range nodata, double destNoData, boolean skipNoData, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Convolve", RenderedRegistryMode.MODE_NAME); // Setting sources pb.setSource("source0", source0); // Setting params pb.setParameter("kernel", kernel); pb.setParameter("roi", roi); pb.setParameter("nodata", nodata); pb.setParameter("destNoData", destNoData); pb.setParameter("skipNoData", skipNoData); return JAI.create("Convolve", pb, hints); } }