org.jgrasstools.lesto.modules.vegetation.WatershedAlgorithm Maven / Gradle / Ivy
The newest version!
package org.jgrasstools.lesto.modules.vegetation;
/*
* Watershed algorithm
*
* Copyright (c) 2003 by Christopher Mei ([email protected])
*
* This plugin is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this plugin; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
import static org.jgrasstools.gears.libs.modules.JGTConstants.doubleNovalue;
import java.awt.image.WritableRaster;
import java.util.List;
import javax.media.jai.iterator.RandomIterFactory;
import javax.media.jai.iterator.WritableRandomIter;
import oms3.annotations.Author;
import oms3.annotations.Description;
import oms3.annotations.Documentation;
import oms3.annotations.Execute;
import oms3.annotations.In;
import oms3.annotations.Keywords;
import oms3.annotations.Label;
import oms3.annotations.License;
import oms3.annotations.Name;
import oms3.annotations.Status;
import oms3.annotations.UI;
import org.geotools.coverage.grid.GridCoverage2D;
import org.jgrasstools.gears.libs.modules.JGTConstants;
import org.jgrasstools.gears.libs.modules.JGTModel;
import org.jgrasstools.gears.utils.RegionMap;
import org.jgrasstools.gears.utils.coverage.CoverageUtilities;
import org.jgrasstools.lesto.modules.vegetation.watershed.WatershedFIFO;
import org.jgrasstools.lesto.modules.vegetation.watershed.WatershedPixel;
import org.jgrasstools.lesto.modules.vegetation.watershed.WatershedStructure;
/**
* This algorithm is an implementation of the watershed immersion algorithm
* written by Vincent and Soille (1991).
*
* @Article{Vincent/Soille:1991,
* author = "Lee Vincent and Pierre Soille",
* year = "1991",
* keywords = "IMAGE-PROC SKELETON SEGMENTATION GIS",
* institution = "Harvard/Paris+Louvain",
* title = "Watersheds in digital spaces: An efficient algorithm
* based on immersion simulations",
* journal = "IEEE PAMI, 1991",
* volume = "13",
* number = "6",
* pages = "583--598",
* annote = "Watershed lines (e.g. the continental divide) mark the
* boundaries of catchment regions in a topographical map.
* The height of a point on this map can have a direct
* correlation to its pixel intensity. WIth this analogy,
* the morphological operations of closing (or opening)
* can be understood as smoothing the ridges (or filling
* in the valleys). Develops a new algorithm for obtaining
* the watershed lines in a graph, and then uses this in
* developing a new segmentation approach based on the
* {"}depth of immersion{"}.",
* }
*
* A review of Watershed algorithms can be found at :
* http://www.cs.rug.nl/~roe/publications/parwshed.pdf
*
* @Article{RoeMei00,
* author = "Roerdink and Meijster",
* title = "The Watershed Transform: Definitions, Algorithms and
* Parallelization Strategies",
* journal = "FUNDINF: Fundamenta Informatica",
* volume = "41",
* publisher = "IOS Press",
* year = "2000",
* }
*
* Taken from: http://rsbweb.nih.gov/ij/plugins/watershed.html
*
**/
@Description("Calculates the watershed of a 8-bit images. It uses the immersion algorithm written by Vincent and Soille (1991)")
@Documentation("http://rsbweb.nih.gov/ij/plugins/watershed.html")
@Author(name = "Christopher Mei, Lee Vincent and Pierre Soille 1991, Andrea Antonello (jgt port)", contact = "[email protected]")
@Keywords("IMAGE-PROC SKELETON SEGMENTATION GIS")
@Label(JGTConstants.LESTO + "/vegetation")
@Name("contourscrowner")
@Status(Status.EXPERIMENTAL)
@License("GPL2")
public class WatershedAlgorithm extends JGTModel {
@Description("An elevation raster")
@UI(JGTConstants.FILEIN_UI_HINT)
@In
public String inRaster;
@Description("If true, the borders of the watershed are set, else the content.")
@In
public boolean doBorders = true;
@Description("Watershed raster")
@UI(JGTConstants.FILEOUT_UI_HINT)
@In
public String outRaster;
@Execute
public void process() throws Exception {
checkNull(inRaster, outRaster);
GridCoverage2D inRasterGC = getRaster(inRaster);
RegionMap regionMap = CoverageUtilities.getRegionParamsFromGridCoverage(inRasterGC);
int nCols = regionMap.getCols();
int nRows = regionMap.getRows();
byte[] input = CoverageUtilities.renderedImage2ByteArray(inRasterGC.getRenderedImage(), true);
/** First step : the pixels are sorted according to increasing grey values **/
WatershedStructure watershedStructure = new WatershedStructure(input, nCols, nRows, pm);
/** Start flooding **/
WatershedFIFO queue = new WatershedFIFO();
int curlab = 0;
int heightIndex1 = 0;
int heightIndex2 = 0;
for( int h = 0; h < 256; h++ ) /*Geodesic SKIZ of level h-1 inside level h */{
for( int pixelIndex = heightIndex1; pixelIndex < watershedStructure.size(); pixelIndex++ ) /*mask all pixels at level h*/{
WatershedPixel p = watershedStructure.get(pixelIndex);
if (p.getIntHeight() != h) {
/** This pixel is at level h+1 **/
heightIndex1 = pixelIndex;
break;
}
p.setLabelToMASK();
List neighbours = p.getNeighbours();
for( int i = 0; i < neighbours.size(); i++ ) {
WatershedPixel q = (WatershedPixel) neighbours.get(i);
if (q.getLabel() >= 0) {/*Initialise queue with neighbours at level h of current basins or watersheds*/
p.setDistance(1);
queue.fifo_add(p);
break;
} // end if
} // end for
} // end for
int curdist = 1;
queue.fifo_add_FICTITIOUS();
while( true ) /** extend basins **/
{
WatershedPixel p = queue.fifo_remove();
if (p.isFICTITIOUS())
if (queue.fifo_empty())
break;
else {
queue.fifo_add_FICTITIOUS();
curdist++;
p = queue.fifo_remove();
}
List neighbours = p.getNeighbours();
for( int i = 0; i < neighbours.size(); i++ ) /* Labelling p by inspecting neighbours */{
WatershedPixel q = (WatershedPixel) neighbours.get(i);
/* Original algorithm :
if( (q.getDistance() < curdist) &&
(q.getLabel()>0 || q.isLabelWSHED()) ) {*/
if ((q.getDistance() <= curdist) && (q.getLabel() >= 0)) {
/* q belongs to an existing basin or to a watershed */
if (q.getLabel() > 0) {
if (p.isLabelMASK())
// Removed from original algorithm || p.isLabelWSHED() )
p.setLabel(q.getLabel());
else if (p.getLabel() != q.getLabel())
p.setLabelToWSHED();
} // end if lab>0
else if (p.isLabelMASK())
p.setLabelToWSHED();
} else if (q.isLabelMASK() && (q.getDistance() == 0)) {
q.setDistance(curdist + 1);
queue.fifo_add(q);
}
} // end for, end processing neighbours
} // end while (loop)
/* Detect and process new minima at level h */
for( int pixelIndex = heightIndex2; pixelIndex < watershedStructure.size(); pixelIndex++ ) {
WatershedPixel p = watershedStructure.get(pixelIndex);
if (p.getIntHeight() != h) {
/** This pixel is at level h+1 **/
heightIndex2 = pixelIndex;
break;
}
p.setDistance(0); /* Reset distance to zero */
if (p.isLabelMASK()) { /* the pixel is inside a new minimum */
curlab++;
p.setLabel(curlab);
queue.fifo_add(p);
while( !queue.fifo_empty() ) {
WatershedPixel q = queue.fifo_remove();
List neighbours = q.getNeighbours();
for( int i = 0; i < neighbours.size(); i++ ) /* inspect neighbours of p2*/{
WatershedPixel r = (WatershedPixel) neighbours.get(i);
if (r.isLabelMASK()) {
r.setLabel(curlab);
queue.fifo_add(r);
}
}
} // end while
} // end if
} // end for
}
/** End of flooding **/
WritableRaster outWR = CoverageUtilities.createDoubleWritableRaster(nCols, nRows, null, null, doubleNovalue);
WritableRandomIter outIter = RandomIterFactory.createWritable(outWR, null);
pm.beginTask("Setting watersheds...", watershedStructure.size());
for( int pixelIndex = 0; pixelIndex < watershedStructure.size(); pixelIndex++ ) {
WatershedPixel p = watershedStructure.get(pixelIndex);
int c = p.getX();
int r = p.getY();
if (p.isLabelWSHED() && !p.allNeighboursAreWSHED()) {
if (doBorders)
outIter.setSample(c, r, 0, 1.0);
} else {
if (!doBorders)
outIter.setSample(c, r, 0, 1.0);
}
pm.worked(1);
}
pm.done();
outIter.done();
GridCoverage2D outRasterGC = CoverageUtilities.buildCoverage("normalized", outWR, regionMap,
inRasterGC.getCoordinateReferenceSystem());
dumpRaster(outRasterGC, outRaster);
}
// public static void main( String[] args ) throws Exception {
// GridCoverage2D inRaster =
// getRaster("/home/moovida/data-mega/unibz_aurino/testarea_dtm_dsm/dsm_reverse_blurred_5_0.01.tif");
//
// OmsWatershedAlgorithm w = new OmsWatershedAlgorithm();
// w.inRaster = inRaster;
// w.process();
// GridCoverage2D outRaster2 = w.outRaster;
// dumpRaster(outRaster2,
// "/home/moovida/data-mega/unibz_aurino/testarea_dtm_dsm/dsm_reverse_watershed_gauss.tif");
// }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy