/* 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.artifacts;
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.ROI;
import javax.media.jai.registry.RenderedRegistryMode;
/**
* A Artifacts Filter operation descriptor.
*
* Given an input image and a ROI, set the values of pixels outside the ROI to the background value and transform the pixels along the BORDER of the
* ROI, if less than a specified Luminance threshold value, to a mean of all surrounding pixels within ROI, having Luminance greater than threshold.
* It should be pointed out that users may specify a NoData Range to use in order to avoid to calculate NoData values.
*
* @author Daniele Romagnoli, GeoSolutions SAS
* @author Simone Giannecchini, GeoSolutions SAS
*
*
*
* @source $URL$
*/
public class ArtifactsFilterDescriptor extends OperationDescriptorImpl {
private static final String[] srcImageNames = { "sourceImage" };
private static final Class<?>[][] srcImageClasses = { { RenderedImage.class } };
static final int ROI_ARG = 0;
static final int BACKGROUND_ARG = 1;
static final int THRESHOLD_ARG = 2;
static final int FILTERSIZE_ARG = 3;
static final int NODATA_ARG = 4;
private static final int DEFAULT_FILTER_SIZE = 3;
private static final int DEFAULT_THRESHOLD = 10;
private static final String[] paramNames = { "roi", "backgroundValues", "threshold",
"filterSize", "nodata" };
private static final Class<?>[] paramClasses = { ROI.class, double[].class, Integer.class,
Integer.class, it.geosolutions.jaiext.range.Range.class };
private static final Object[] paramDefaults = { (ROI) null, (double[]) null, DEFAULT_THRESHOLD,
DEFAULT_FILTER_SIZE, null };
/** Constructor. */
public ArtifactsFilterDescriptor() {
super(
new String[][] {
{ "GlobalName", "ArtifactsFilter" },
{ "LocalName", "ArtifactsFilter" },
{ "Vendor", "it.geosolutions.jaiext" },
{ "Description",
"Filter pixels along the ROI BORDER with Luminance value less than threshold" },
{ "DocURL", "" },
{ "Version", "1.0.0" },
{
"arg0Desc",
String.format("%s (default %s) - a ROI defining working area",
paramNames[ROI_ARG], paramDefaults[ROI_ARG]) },
{
"arg1Desc",
String.format(
"%s (default %s) - an array of double that define values "
+ "for the background ",
paramNames[BACKGROUND_ARG], paramDefaults[BACKGROUND_ARG]) },
{
"arg2Desc",
String.format(
"%s (default %s) - an integer defining the luminance threshold value",
paramNames[THRESHOLD_ARG], paramDefaults[THRESHOLD_ARG]) },
{
"arg3Desc",
String.format(
"%s (default %s) - an integer defining the filterSize",
paramNames[FILTERSIZE_ARG], paramDefaults[FILTERSIZE_ARG]) },
{
"arg4Desc",
String.format(
"%s (default %s) - a Range defining the image nodata",
paramNames[NODATA_ARG], paramDefaults[NODATA_ARG]) }, },
new String[] { RenderedRegistryMode.MODE_NAME }, // supported modes
srcImageNames, srcImageClasses, paramNames, paramClasses, paramDefaults, null // valid values (none defined)
);
}
public static RenderedImage create(RenderedImage sourceImage, ROI sourceRoi,
double[] backgroundValues, final int threshold, RenderingHints hints) {
return create(sourceImage, sourceRoi, backgroundValues, threshold, DEFAULT_FILTER_SIZE,
null, hints);
}
public static RenderedImage create(RenderedImage sourceImage, ROI sourceRoi,
double[] backgroundValues, RenderingHints hints) {
return create(sourceImage, sourceRoi, backgroundValues, DEFAULT_THRESHOLD,
DEFAULT_FILTER_SIZE, null, hints);
}
public static RenderedImage create(RenderedImage sourceImage, ROI sourceRoi,
double[] backgroundValues, final int threshold, Range nodata, RenderingHints hints) {
return create(sourceImage, sourceRoi, backgroundValues, threshold, DEFAULT_FILTER_SIZE,
nodata, hints);
}
public static RenderedImage create(RenderedImage sourceImage, ROI sourceRoi,
double[] backgroundValues, Range nodata, RenderingHints hints) {
return create(sourceImage, sourceRoi, backgroundValues, DEFAULT_THRESHOLD,
DEFAULT_FILTER_SIZE, nodata, hints);
}
/**
* Convenience method which constructs a {@link ParameterBlockJAI} and invokes {@code JAI.create("ArtifactsFilter", params) }
*
* @param sourceImage the image to be restored
*
* @param sourceRoi a {@link ROI} defining the working area
* @param backgroundValues double array used for defining background
* @param threshold integer value used for defining the luminance threshold
* @param filterSize size of the filter used for filtering artifacts
* @param nodata a {@link Range} object defining input NoData
*
* @param hints an optional RenderingHints object
*
* @return a RenderedImage
*/
public static RenderedImage create(RenderedImage sourceImage, ROI sourceRoi,
double[] backgroundValues, final int threshold, final int filterSize, Range nodata,
RenderingHints hints) {
ParameterBlockJAI pb = new ParameterBlockJAI("ArtifactsFilter",
RenderedRegistryMode.MODE_NAME);
pb.setSource(srcImageNames[0], sourceImage);
pb.setParameter(paramNames[ROI_ARG], sourceRoi);
pb.setParameter(paramNames[BACKGROUND_ARG], backgroundValues);
pb.setParameter(paramNames[THRESHOLD_ARG], threshold);
pb.setParameter(paramNames[FILTERSIZE_ARG], filterSize);
pb.setParameter(paramNames[NODATA_ARG], nodata);
return JAI.create("ArtifactsFilter", pb, hints);
}
/**
* Returns true to indicate that properties are supported
*/
@Override
public boolean arePropertiesSupported() {
return true;
}
/**
* Checks parameters for the following:
* <ul>
* <li>Number of sources is 1
* <li>Data image bands are valid
* </ul>
*/
@SuppressWarnings("unchecked")
@Override
public boolean validateArguments(String modeName, ParameterBlock pb, StringBuffer msg) {
if (pb.getNumSources() == 0 || pb.getNumSources() > 1) {
msg.append("ArtifactsFilter operator takes 1 source image");
return false;
}
// CHECKING Background values
Object backgroundValues = pb.getObjectParameter(BACKGROUND_ARG);
double[] bgValues = null;
if (!(backgroundValues instanceof double[])) {
msg.append(paramNames[BACKGROUND_ARG] + " arg has to be of type double[]");
return false;
} else {
bgValues = (double[]) backgroundValues;
}
// CHECKING DATA IMAGE
RenderedImage dataImg = pb.getRenderedSource(0);
Rectangle dataBounds = new Rectangle(dataImg.getMinX(), dataImg.getMinY(),
dataImg.getWidth(), dataImg.getHeight());
// CHECKING ROI
Object roiObject = pb.getObjectParameter(ROI_ARG);
if (roiObject != null) {
if (!(roiObject instanceof ROI)) {
msg.append("The supplied ROI is not a supported class");
return false;
}
final ROI roi = (ROI) roiObject;
final Rectangle roiBounds = roi.getBounds();
if (!roiBounds.intersects(dataBounds)) {
msg.append("The supplied ROI does not intersect the source image");
return false;
}
} else {
msg.append("The ROI parameter is missing ");
return false;
}
return true;
}
}