/* 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.lookup; import it.geosolutions.jaiext.range.Range; 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 and destination images bounds, * and then passed as a result. If no property was found an Undefined Property object is returned. * */ class LookupPropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public LookupPropertyGenerator() { 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; // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the warped ROI to the destination bounds. if (!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the warped and possibly clipped ROI. return dstROI; } return java.awt.Image.UndefinedProperty; } } /** * An <code>OperationDescriptor</code> describing the "Lookup" operation. * * <p> * The Lookup operation takes a rendered image and a lookup table, and performs general table lookup by passing the source image through the table. If * ROI or No Data values are set then the lookupTable takes in account this 2 parameters. The out-of-ROI values or No Data values are set to * destination no data. * * <p> * The source may be a single- or multi-banded image of data types <code>byte</code>, <code>ushort</code>, <code>short</code>, or <code>int</code>. * The lookup table may be single- or multi-banded and of any DataBuffer supported data types. The destination image must have the same data type as * the lookup table, and its number of bands is determined based on the number of bands of the source and the table. If the source is single-banded, * the destination has the same number of bands as the lookup table; otherwise, the destination has the same number of bands as the source. * * <p> * If either the source or the table is single-banded and the other one is multi-banded, then the single band is applied to every band of the * multi-banded object. If both are multi-banded, then their corresponding bands are matched up. * * <p> * The table may have a set of offset values, one for each band. This value is subtracted from the source pixel values before indexing into the table * data array. * * <p> * It is the user's responsibility to make certain the lookup table supplied is suitable for the source image. Specifically, the table data covers the * entire range of the source data. Otherwise, the result of this operation is undefined. * * <p > * By the nature of this operation, the destination may have a different number of bands and/or data type from the source. The * <code>SampleModel</code> of the destination is created in accordance with the actual lookup table used in a specific case. * * <p> * The destination pixel values are defined by the pseudocode: * <ul> * <li>If the source image is single-banded and the lookup table is single- or multi-banded, then the destination image has the same number of bands * as the lookup table: * * <pre> * dst[x][y][b] = table[b][src[x][y][0] - offsets[b]] * </pre> * * </li> * * <li>If the source image is multi-banded and the lookup table is single-banded, then the destination image has the same number of bands as the * source image: * * <pre> * dst[x][y][b] = table[0][src[x][y][b] - offsets[0]] * </pre> * * </li> * * <li>If the source image is multi-banded and the lookup table is multi-banded, with the same number of bands as the source image, then the * destination image will have the same number of bands as the source image: * * <pre> * dst[x][y][b] = table[b][src[x][y][b] - offsets[b]] * </pre> * * </li> * </ul> * * <p> * <table border=1> * <caption>Resource List</caption> * <tr> * <th>Name</th> * <th>Value</th> * </tr> * <tr> * <td>GlobalName</td> * <td>Lookup</td> * </tr> * <tr> * <td>LocalName</td> * <td>Lookup</td> * </tr> * <tr> * <td>Vendor</td> * <td>it.geosolutions.jaiext</td> * </tr> * <tr> * <td>Description</td> * <td>Lookup operation 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>The lookup table to use.</td> * </tr> * <tr> * <td>arg1Desc</td> * <td>Destination No Data used for ROI or No Data.</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> * </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>table</td> * <td>it.geosolutions.jaiext.lookup.LookupTable</td> * <td>NO_PARAMETER_DEFAULT</td> * <tr> * <td>destinationNoData</td> * <td>Double</td> * <td>0</td> * <tr> * <td>ROI</td> * <td>javax.media.jai.ROI</td> * <td>NO_PARAMETER_DEFAULT</td> * <tr> * <td>NoData</td> * <td>it.geosolutions.jaiext.range.Range</td> * <td>NO_PARAMETER_DEFAULT</td> * <tr> * <td>useRoiAccessor</td> * <td>Boolean</td> * <td>false</td> * </table> * </p> * */ public class LookupDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and specify the parameter list for this operation. */ private static final String[][] resources = { { "GlobalName", "Lookup" }, { "LocalName", "Lookup" }, { "Vendor", "it.geosolutions.jaiext" }, { "Description", "Lookup operation supporting ROI and No Data" }, { "DocURL", "Not defined" }, { "Version", "1.0" }, { "arg0Desc", "The lookup table to use" }, { "arg1Desc", "Destination No Data used for ROI or No Data" }, { "arg2Desc", "ROI object used" }, { "arg3Desc", "No Data Range used" }, { "arg4Desc", "Boolean checking if ROI RasterAccessor is used" } }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { it.geosolutions.jaiext.lookup.LookupTable.class, java.lang.Double.class, javax.media.jai.ROI.class, it.geosolutions.jaiext.range.Range.class, java.lang.Boolean.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "table", "destinationNoData", "ROI", "NoData", "useRoiAccessor" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { null, 0.0d, null, null, false }; public LookupDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns an array of <code>PropertyGenerators</code> implementing property inheritance for the "Lookup" operation * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new LookupPropertyGenerator(); return pg; } /** * Performs a lookup operation on an integral 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 table The lookuptable used. * @param destinationNoData Destination no data used for ROI or No Data. * @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 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, LookupTable table, double destinationNoData, ROI roi, Range noData, boolean useRoiAccessor, RenderingHints hints) { // Creation of a parameterBlockJAI containing all the operation parameters ParameterBlockJAI pb = new ParameterBlockJAI("Lookup", RenderedRegistryMode.MODE_NAME); // Source image pb.setSource("source0", source0); // Image parameters pb.setParameter("table", table); pb.setParameter("destinationNoData", destinationNoData); pb.setParameter("ROI", roi); pb.setParameter("NoData", noData); pb.setParameter("useRoiAccessor", useRoiAccessor); // RenderedImage creation return JAI.create("Lookup", pb, hints); } }