001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2003, 2ie Technologie
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.image.jai;
018:
019: // J2SE dependencies
020: import java.awt.Rectangle;
021: import java.awt.image.Raster;
022: import java.awt.image.RenderedImage;
023: import java.awt.image.WritableRaster;
024: import java.util.Map;
025:
026: // JAI dependencies
027: import javax.media.jai.ImageLayout;
028: import javax.media.jai.UntiledOpImage;
029: import javax.media.jai.iterator.RandomIter;
030: import javax.media.jai.iterator.RandomIterFactory;
031:
032: /**
033: * Effectue un seuillage par hysteresis sur une image.
034: * L'opération de seuillage s'effectue de la manière suivante:
035: * <p>
036: * On dispose d'un seuil haut, <var>sh</var>, et d'un seuil bas, <var>sb</var>.
037: * Si la valeur d'un pixel est supérieur à <var>sh</var>, on la conserve, elle
038: * nous interesse. Si cette valeur est inférieure à <var>sb</var>, on la supprime.
039: * Si elle est entre les deux on dit le pixel indeterminé et on ne le conserve que
040: * s'il est proche d'un pixel dont la valeur est au dessus de <var>sh</var>, ou
041: * proche d'un pixel indéterminé que l'on a précédement trouvé proche d'un pixel
042: * de valeur supérieure à <var>sh</var>. Cette recherche se fait de manière itérative,
043: * jusqu'à ce que le point indéterminé n'est plus de voisins satisfaisants.
044: *
045: * @since 2.1
046: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/image/jai/Hysteresis.java $
047: * @version $Id: Hysteresis.java 20970 2006-08-11 07:53:22Z jgarnett $
048: * @author Lionel Flahaut
049: * @author Martin Desruisseaux
050: */
051: public class Hysteresis extends UntiledOpImage {
052: /**
053: * The low threshold value, inclusive.
054: */
055: private final double low;
056:
057: /**
058: * The high threshold value, inclusive.
059: */
060: private final double high;
061:
062: /**
063: * The value to give to filtered pixel.
064: */
065: private final double padValue;
066:
067: /**
068: * Constructs a new Hysterisis filter for the given image.
069: *
070: * @param source The source image.
071: * @param layout The image layout.
072: * @param map The rendering hints and image properties.
073: * @param low The low threshold value, inclusive.
074: * @param high The high threshold value, inclusive.
075: * @param padValue The value to give to filtered pixel.
076: */
077: protected Hysteresis(final RenderedImage source,
078: final ImageLayout layout, final Map map, final double low,
079: final double high, final double padValue) {
080: super (source, map, layout);
081: this .low = low;
082: this .high = high;
083: this .padValue = padValue;
084: }
085:
086: /**
087: * Computes the whole image.
088: */
089: protected void computeImage(final Raster[] sources,
090: final WritableRaster dest, final Rectangle destRect) {
091: assert sources.length == 1;
092: final Raster source = sources[0];
093: Rectangle sourceRect = mapDestRect(destRect, 0);
094: sourceRect = sourceRect.intersection(source.getBounds());
095: final RandomIter iter = RandomIterFactory.create(source,
096: sourceRect);
097: final int minX = destRect.x; // Minimum inclusive
098: final int minY = destRect.y; // Minimum inclusive
099: final int maxX = destRect.width + minX; // Maximum exclusive
100: final int maxY = destRect.height + minY; // Maximum exclusive
101: final int w = width - 1;
102: final int h = height - 1;
103: final boolean[] accepted = new boolean[destRect.width
104: * destRect.height];
105: final boolean[] rejected = new boolean[destRect.width
106: * destRect.height];
107: for (int band = source.getNumBands(); --band >= 0;) {
108: /*
109: * Find immediately all accepted values (above the high threshold) and rejected values
110: * (below the low threshold). NaN values are both accepted and rejected ("accepted"
111: * since they are going to be copied in the destination image, and "rejected" since
112: * they do not cause the acceptation of neighbor values).
113: */
114: int index = 0;
115: for (int y = minY; y < maxY; y++) {
116: for (int x = minX; x < maxX; x++) {
117: final double current = iter.getSampleDouble(x, y,
118: band);
119: accepted[index] = !(current < high); // Accept NaN values
120: rejected[index] = !(current >= low); // Accept NaN values
121: index++;
122: }
123: }
124: assert index == accepted.length;
125: /*
126: * Complete the mask of "accepted" values. Unknow values (those which are neither
127: * accepted or rejected) are tested for their proximity with an accepted value.
128: * This loop will be run until there is no change.
129: */
130: int sign = +1;
131: boolean changed;
132: do {
133: changed = false;
134: final int stop;
135: if (sign >= 0) {
136: index = 0;
137: stop = accepted.length;
138: } else {
139: index = accepted.length - 1;
140: stop = -1;
141: }
142: while (index != stop) {
143: if (!accepted[index] && !rejected[index]) {
144: int check;
145: final int y = index / width;
146: final int x = index % width;
147: if ((x != 0 && ((accepted[check = index - 1] && !rejected[check])
148: || (y != 0
149: && accepted[check = index - 1
150: - width] && !rejected[check]) || (y != h
151: && accepted[check = index - 1 + width] && !rejected[check])))
152: || (x != w && ((accepted[check = index + 1] && !rejected[check])
153: || (y != 0
154: && accepted[check = index
155: + 1 - width] && !rejected[check]) || (y != h
156: && accepted[check = index + 1
157: + width] && !rejected[check])))
158: || (y != 0 && ((accepted[check = index
159: - width] && !rejected[check])))
160: || (y != w && ((accepted[check = index
161: + width] && !rejected[check])))) {
162: accepted[index] = true;
163: changed = true;
164: }
165: }
166: index += sign;
167: }
168: sign = -sign;
169: } while (changed);
170: /*
171: * Copy all accepted values in the destination raster.
172: * Other values are replaced by NaN.
173: */
174: index = 0;
175: for (int y = minY; y < maxY; y++) {
176: for (int x = minX; x < maxX; x++) {
177: dest.setSample(x, y, band, accepted[index++] ? iter
178: .getSampleDouble(x, y, band) : padValue);
179: }
180: }
181: assert index == accepted.length;
182: }
183: iter.done();
184: }
185: }
|