package fr.unistra.pelican.algorithms.morphology.gray;
import java.awt.Point;
import java.util.Vector;
import fr.unistra.pelican.AlgorithmDeprecated;
import fr.unistra.pelican.AlgorithmException;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.IntegerImage;
import fr.unistra.pelican.InvalidNumberOfParametersException;
import fr.unistra.pelican.InvalidTypeOfParameterException;
/*
EA (13.03.05)
- total rewrite..same algo (Vincent-Soille)
- added multiband support
- eliminated getBin method...use WatershedLine algorithm instead
TODO
- dont ignore ArrayIndexOutOfBoundsException...
calculate the proper array instead
*/
public class Watershed2 implements AlgorithmDeprecated
{
private Image input;
private 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 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++){
int current_label = WSHED;
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
Vector[] distro = calculateDistro(b);
for(int i = 0; i < 256; i++){
// geodesic SKIZ of level i - 1 inside level i
int size = distro[i].size();
for(int j = 0; j < size; j++){
p = (Point)distro[i].elementAt(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 = (Point)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 = distro[i].size();
for(int j = 0; j < size; j++){
p = (Point)distro[i].elementAt(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 = (Point)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);
}
}
}
}
}
}
}
}
}
private Vector[] calculateDistro(int b)
{
Vector[] distro = new Vector[256];
for(int i = 0; i < 256; i++)
distro[i] = new Vector();
for(int x = 0; x < xDim; x++){
for(int y = 0; y < yDim; y++)
distro[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;
}
/* (non-Javadoc)
* @see fr.unistra.pelican.Algorithm#setInput(java.util.Vector)
*/
public void setInput(Vector inputVector)
throws InvalidNumberOfParametersException, InvalidTypeOfParameterException {
// Check the number of parameters.
if (inputVector.size() != 1)
throw new InvalidNumberOfParametersException("Need one parameter!");
Object o=inputVector.firstElement();
// Check types of each parameter.
if ((o instanceof fr.unistra.pelican.Image) == false)
throw new InvalidTypeOfParameterException(
"Input param 1 need to be instance of fr.unistra.pelican.Image");
// When type is checked, store the parameter.
input = (Image)o;
}
public Vector getOutput()
{
Vector v=new Vector(1);
v.add(output);
return v;
}
public String[] getInputTypes()
{
String[] tab = new String[1];
tab[0]="fr.unistra.pelican.Image";
return tab;
}
public String[] getOutputTypes()
{
String[] tab = new String[1];
tab[0]="fr.unistra.pelican.Image";
return tab;
}
/* (non-Javadoc)
* @see fr.unistra.pelican.Algorithm#help()
*/
public String help() {
return "This class realize a watershed segmentation.\n"
+"Image inputImage\n"
+"\n"
+"Image segmentedImage\n"
+"\n"
+"This class realize a watershed segmentation. "
+"This class work on a byte resolution. "
+"The maximum number of created segment is 2^31-1. "
+"It return an IntegerImage, the first segment as label Integer.MIN_VALUE. "
+"It use the Soille algorithm with a fifo stack described in Morphological Image Analysis from Soille.";
}
/** Static fonction that use this algorithm.
*
* Each algorithm can have one or more of theses static fonction.
* It's more convenient for coding.
*
* @param image
* @return result
* @throws InvalidTypeOfParameterException
* @throws AlgorithmException
* @throws InvalidNumberOfParametersException
*/
public static Image process(Image image) throws InvalidTypeOfParameterException, AlgorithmException, InvalidNumberOfParametersException {
Watershed2 algo = new Watershed2();
Vector inputs = new Vector();
inputs.add(image);
algo.setInput(inputs);
algo.launch();
return (Image)algo.getOutput().firstElement();
}
private class Fifo
{
private Vector v;
Fifo()
{
v = new Vector();
}
void add(Object o)
{
v.add(o);
}
Object retrieve()
{
Object o = v.firstElement();
v.remove(0);
return o;
}
boolean isEmpty()
{
return v.size() == 0;
}
int size()
{
return v.size();
}
}
}