package fr.unistra.pelican.algorithms.segmentation; import java.awt.Point; import java.util.ArrayList; import fr.unistra.pelican.Algorithm; import fr.unistra.pelican.Image; import fr.unistra.pelican.IntegerImage; /* EA (13.03.05) - total rewrite..same algo (Vincent-Soille) - added multiband support - eliminated getBin method...use WatershedLine algorithm instead JW (21.03.10) - convert to ArrayList - merging of watershed lines into existing regions TODO - dont ignore ArrayIndexOutOfBoundsException... calculate the proper array instead - Biggest region merging FIXME (Lefevre) remis dans PELICAN car "bonnes frontières" */ public class Watershed2 extends Algorithm { /** * Original Image (often a gradient) */ public Image input; /** * Method used for merging watershed lines * into regions (optional) */ public int wshedMergingMethod=NONE; /** * Watershed result */ public IntegerImage output; private int xDim; private int yDim; private int bDim; public static final int WSHED = 0; private static final int INIT = -3; private static final int MASK = -2; private static final int INQUEUE = -1; public static final int NONE = -1; public static final int CLOSESTVALUE = 0; public static final int BIGGESTREGION = 1; public Watershed2() { super.inputs = "input"; super.options = "wshedMergingMethod"; super.outputs = "output"; } public static IntegerImage exec(Image input) { return (IntegerImage) new Watershed2().process(input); } public static IntegerImage exec(Image input, int watershedMerging) { return (IntegerImage) new Watershed2().process(input,watershedMerging); } public void launch() { xDim = input.getXDim(); yDim = input.getYDim(); bDim = input.getBDim(); // initialize labels to INIT output = new IntegerImage(xDim, yDim, 1, 1, bDim); output.fill(INIT); for (int b = 0; b < bDim; b++) { boolean flag = false; int x, y; Fifo fifo = new Fifo(); Point p; int currentLabel = WSHED; // pixel value distribution, // so that we dont have to check the entire image ArrayList<ArrayList<Point>> distro = calculateDistro(b); for (int i = 0; i < 256; i++) { // geodesic SKIZ of level i - 1 inside level i ArrayList<Point> pointsList = distro.get(i); int size = pointsList.size(); for (int j = 0; j < size; j++) { p = pointsList.get(j); x = (int) p.getX(); y = (int) p.getY(); output.setPixelXYBInt(x, y, b, MASK); if (areThereLabelledNeighbours(x, y, b) == true) { output.setPixelXYBInt(x, y, b, INQUEUE); fifo.add(p); } } while (fifo.isEmpty() == false) { p = fifo.retrieve(); x = (int) p.getX(); y = (int) p.getY(); // for every pixel in the 8-neighbourhood of p for (int j = y - 1; j <= y + 1; j++) { for (int k = x - 1; k <= x + 1; k++) { if (k < 0 || k >= xDim || j < 0 || j >= yDim) continue; // if the pixel is already labelled if (!(j == y && k == x) && output.getPixelXYBInt(k, j, b) > WSHED) { if (output.getPixelXYBInt(x, y, b) == INQUEUE || (output.getPixelXYBInt(x, y, b) == WSHED && flag == true)) output.setPixelXYBInt(x, y, b, output .getPixelXYBInt(k, j, b)); else if (output.getPixelXYBInt(x, y, b) > WSHED && output.getPixelXYBInt(x, y, b) != output .getPixelXYBInt(k, j, b)) { output.setPixelXYBInt(x, y, b, WSHED); flag = false; } } else if (output.getPixelXYBInt(k, j, b) == WSHED && output.getPixelXYBInt(x, y, b) == INQUEUE) { output.setPixelXYBInt(x, y, b, WSHED); flag = true; } else if (output.getPixelXYBInt(k, j, b) == MASK) { output.setPixelXYBInt(k, j, b, INQUEUE); fifo.add(new Point(k, j)); } } } } // check for new minima size = pointsList.size(); for (int j = 0; j < size; j++) { p = pointsList.get(j); x = (int) p.getX(); y = (int) p.getY(); if (output.getPixelXYBInt(x, y, b) == MASK) { currentLabel++; fifo.add(p); output.setPixelXYBInt(x, y, b, currentLabel); while (fifo.isEmpty() == false) { p = fifo.retrieve(); x = (int) p.getX(); y = (int) p.getY(); // for every pixel in the 8-neighbourhood of p for (int l = y - 1; l <= y + 1; l++) { for (int k = x - 1; k <= x + 1; k++) { if (k < 0 || k >= xDim || l < 0 || l >= yDim) continue; if (!(k == x && l == y) && output.getPixelXYBInt(k, l, b) == MASK) { fifo.add(new Point(k, l)); output.setPixelXYBInt(k, l, b, currentLabel); } } } } } } } } switch(wshedMergingMethod) { case CLOSESTVALUE: wshedMergedInRegionOfClosestValue(); break; case BIGGESTREGION : System.err.println("This merging is not managed yet"); break; } } private void wshedMergedInRegionOfClosestValue() { IntegerImage newWSHEDValues = output.newIntegerImage(); newWSHEDValues.fill(-1); for(int y=0;y<yDim;y++) for(int x=0;x<xDim;x++) { if(output.getPixelXYInt(x, y)==WSHED) { int smallestDistance=Integer.MAX_VALUE; int closestRegion=-1; int localValue = input.getPixelXYByte(x, y); for(int yN=-1;yN<=1;yN++) for(int xN=-1;xN<=1;xN++) { int xLoc=x+xN; int yLoc=y+yN; if(xLoc>=0&&xLoc<xDim&&yLoc>=0&&yLoc<yDim) { if(output.getPixelXYInt(xLoc, yLoc)!=WSHED) { int localDistance=Math.abs(input.getPixelXYByte(xLoc, yLoc)-localValue); if(localDistance<smallestDistance) { smallestDistance=localDistance; closestRegion=output.getPixelXYInt(xLoc, yLoc); } } } } // A watershed pixel surrounded by watershed pixel, weird but it happens... if(closestRegion==-1) { for(int yN=-1;yN<=1;yN++) for(int xN=-1;xN<=1;xN++) { int xLoc=x+xN; int yLoc=y+yN; if(xLoc>=0&&xLoc<xDim&&yLoc>=0&&yLoc<yDim) { if(newWSHEDValues.getPixelXYInt(xLoc,yLoc)!=-1) { int localDistance=Math.abs(input.getPixelXYByte(xLoc, yLoc)-localValue); if(localDistance<smallestDistance) { smallestDistance=localDistance; closestRegion=newWSHEDValues.getPixelXYInt(xLoc, yLoc); } } } } } //End of watershed surrounded watershed pixel treatment newWSHEDValues.setPixelXYInt(x, y, closestRegion); } } for(int i=0;i<newWSHEDValues.size();i++) { if(newWSHEDValues.getPixelInt(i)!=-1) { output.setPixelInt(i, newWSHEDValues.getPixelInt(i)); } } } private ArrayList<ArrayList<Point>> calculateDistro(int b) { ArrayList<ArrayList<Point>> distro = new ArrayList<ArrayList<Point>>(); for (int i = 0; i < 256; i++) distro.add(new ArrayList<Point>()); for (int x = 0; x < xDim; x++) { for (int y = 0; y < yDim; y++) distro.get(input.getPixelXYBByte(x, y, b)).add(new Point(x, y)); } return distro; } private boolean areThereLabelledNeighbours(int x, int y, int b) { for (int j = y - 1; j <= y + 1; j++) { for (int i = x - 1; i <= x + 1; i++) { if (i < 0 || i >= xDim || j < 0 || j >= yDim) continue; if (!(i == x && j == y) && output.getPixelXYBInt(i, j, b) >= WSHED) return true; } } return false; } private class Fifo { private ArrayList<Point> v; Fifo() { v = new ArrayList<Point>(); } void add(Point o) { v.add(o); } Point retrieve() { Point o = v.get(0); v.remove(0); return o; } boolean isEmpty() { return v.size() == 0; } } }