package fr.unistra.pelican.algorithms.segmentation.flatzones;
import java.util.ArrayList;
import fr.unistra.pelican.Algorithm;
import fr.unistra.pelican.BooleanImage;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.IntegerImage;
import fr.unistra.pelican.algorithms.segmentation.labels.LabelsToRandomColors;
import fr.unistra.pelican.algorithms.visualisation.Viewer2D;
/**
* This class performs a labeling of a binary image into connected components.
*
* It uses a fast 2-pass algorithm relying on a correspondance table and offers
* two options : the connexity used (either CONNEXITY4 or CONNEXITY8) and the
* possiblity to label background pixels
*
* TODO: add support to integer images (not only boolean images)
*
* @author Lefevre
*/
public class BooleanConnectedComponentsLabeling extends Algorithm {
/**
* A constant representing the 4-connexity mode
*/
public static int CONNEXITY4 = 0;
/**
* A constant representing the 8-connexity mode
*/
public static int CONNEXITY8 = 1;
/**
* Input Image
*/
public Image input;
/**
* The type of connexity considered (either CONNEXITY4 or CONNEXITY8)
*/
public int connexity = CONNEXITY8;
/**
* Flag to determine if background pixels should also be labeled
*/
public boolean background = false;
/**
* Label image
*/
public IntegerImage output;
/**
* Number of labels used
*/
public int countLabels;
/*
* Private attributes
*/
private int nbLabels = 0;
private ArrayList<Integer> labels = new ArrayList<Integer>();
/**
* Constructor
*/
public BooleanConnectedComponentsLabeling() {
super.inputs = "input";
super.options = "connexity,background";
super.outputs = "output,countLabels";
}
/**
* Performs a labeling of a binary image into connected components.
*
* @param input
* The input image
* @param connexity
* The type of connexity considered (either CONNEXITY4 or CONNEXITY8)
* @param background
* Flag to determine if background pixels should also be labeled
* @return The label image
*/
public static IntegerImage exec(Image input, int connexity, boolean background) {
return (IntegerImage) new BooleanConnectedComponentsLabeling().process(input,
connexity, background);
}
public static IntegerImage exec(Image input, boolean background) {
return (IntegerImage) new BooleanConnectedComponentsLabeling().process(input,
null, background);
}
public static IntegerImage exec(Image input, int connexity) {
return (IntegerImage) new BooleanConnectedComponentsLabeling().process(input,
connexity);
}
/**
* Performs a labeling of a binary image into connected components.
*
* @param input
* The input image
* @return The label image
*/
public static IntegerImage exec(Image input) {
return (IntegerImage) new BooleanConnectedComponentsLabeling().process(input);
}
public void launch() {
int xDim = input.getXDim();
int yDim = input.getYDim();
int zDim = input.getZDim();
int tDim = input.getTDim();
int bDim = input.getBDim();
output = new IntegerImage(xDim, yDim, zDim, tDim, bDim);
labels.add(0);
// Cas binaire
if (input instanceof BooleanImage) {
// Initialisation
for (int i = 0; i < input.size(); i++)
if (background || input.getPixelBoolean(i))
this.output.setPixelInt(i, Integer.MAX_VALUE);
else
this.output.setPixelInt(i, 0);
// Premier parcours
for (int b = 0; b < bDim; b++)
for (int t = 0; t < tDim; t++)
for (int z = 0; z < zDim; z++)
for (int y = 0; y < yDim; y++)
for (int x = 0; x < xDim; x++)
if (background || input.getPixelBoolean(x, y, z, t, b)) {
if (connexity == CONNEXITY4)
output.setPixelInt(x, y, z, t, b, get4Connexity(x, y, z, t,
b));
else if (connexity == CONNEXITY8)
output.setPixelInt(x, y, z, t, b, get8Connexity(x, y, z, t,
b));
}
// Simplification de la table d'equivalence
ArrayList<Integer> alreadyTreatedLabels = new ArrayList<Integer>();
for (int i = nbLabels; i >= 0; i--)
{
int j = i;
alreadyTreatedLabels.add(j);
while (labels.get(j) != j)
{
j = labels.get(j);
// loop treatment
if(alreadyTreatedLabels.contains(j))
{
for(int label=0;label<alreadyTreatedLabels.size();label++)
{
labels.set(alreadyTreatedLabels.get(label), i);
}
j = i;
} else
{
alreadyTreatedLabels.add(j);
}
}
labels.set(i, j);
alreadyTreatedLabels.clear();
}
// Calcul du nombre de labels et requantification des labels
int[] labels2 = new int[nbLabels + 1];
countLabels = 0;
int firstLabel=0;
for (int i = firstLabel; i < nbLabels + 1; i++)
if (i == labels.get(i)) {
labels2[i] = countLabels;
countLabels++;
}
output.setProperty("nbRegions", countLabels);
// Second parcours
for (int b = 0; b < bDim; b++)
for (int t = 0; t < tDim; t++)
for (int z = 0; z < zDim; z++)
for (int y = 0; y < yDim; y++)
for (int x = 0; x < xDim; x++)
if (background || input.getPixelBoolean(x, y, z, t, b))
output.setPixelInt(x, y, z, t, b, labels2[labels.get(output
.getPixelInt(x, y, z, t, b))]);
}
// Cas d'une image label : fonctionne pour des ByteImage et IntegerImage (label)
else {
int bg=Integer.MIN_VALUE;
if(input instanceof IntegerImage)
bg=0;
// Initialisation
for (int i = input.size(); --i>=0 ;)
if (background || input.getPixelInt(i)!=bg)
this.output.setPixelInt(i, Integer.MAX_VALUE);
else
this.output.setPixelInt(i, 0);
// Premier parcours
for (int b = 0; b < bDim; b++)
for (int t = 0; t < tDim; t++)
for (int z = 0; z < zDim; z++)
for (int y = 0; y < yDim; y++)
for (int x = 0; x < xDim; x++)
if (background || input.getPixelInt(x, y, z, t, b) != bg) {
if (connexity == CONNEXITY4)
output.setPixelInt(x, y, z, t, b, get4ConnexityLabel(x, y,
z, t, b));
else if (connexity == CONNEXITY8)
output.setPixelInt(x, y, z, t, b, get8ConnexityLabel(x, y,
z, t, b));
}
// Simplification de la table d'equivalence
ArrayList<Integer> alreadyTreatedLabels = new ArrayList<Integer>();
for (int i = nbLabels; i >= 0; i--)
{
int j = i;
alreadyTreatedLabels.add(j);
while (labels.get(j) != j)
{
j = labels.get(j);
// loop treatment
if(alreadyTreatedLabels.contains(j))
{
for(int label=0;label<alreadyTreatedLabels.size();label++)
{
labels.set(alreadyTreatedLabels.get(label), i);
}
j = i;
} else
{
alreadyTreatedLabels.add(j);
}
}
labels.set(i, j);
alreadyTreatedLabels.clear();
}
// Calcul du nombre de labels et requantification des labels
int[] labels2 = new int[nbLabels + 1];
countLabels = 0;
int firstLabel=0;
for (int i = firstLabel; i < nbLabels + 1; i++)
if (i == labels.get(i)) {
labels2[i] = countLabels;
countLabels++;
}
output.setProperty("nbRegions", countLabels);
// Second parcours
for (int b = 0; b < bDim; b++)
for (int t = 0; t < tDim; t++)
for (int z = 0; z < zDim; z++)
for (int y = 0; y < yDim; y++)
for (int x = 0; x < xDim; x++)
if (background || input.getPixelInt(x, y, z, t, b) != bg)
output.setPixelInt(x, y, z, t, b, labels2[labels.get(output
.getPixelInt(x, y, z, t, b))]);
}
}
private final int get4Connexity(int x, int y, int z, int t, int b) {
boolean current = input.getPixelBoolean(x, y, z, t, b);
int min = Integer.MAX_VALUE;
boolean val1 = false, val2 = false;
if (y - 1 >= 0 && current == input.getPixelBoolean(x, y - 1, z, t, b)) {
min = Math.min(min, labels.get(output.getPixelInt(x, y - 1, z, t, b)));
val1 = true;
}
if (x - 1 >= 0 && current == input.getPixelBoolean(x - 1, y, z, t, b)) {
min = Math.min(min, labels.get(output.getPixelInt(x - 1, y, z, t, b)));
val2 = true;
}
if (val1 && labels.get(output.getPixelInt(x, y - 1, z, t, b)) != min)
setTableMin(output.getPixelInt(x, y - 1, z, t, b), min);
if (val2 && labels.get(output.getPixelInt(x - 1, y, z, t, b)) != min)
setTableMin(output.getPixelInt(x - 1, y, z, t, b), min);
if (min == Integer.MAX_VALUE) {
labels.add(++nbLabels);
return nbLabels;
} else
return min;
}
private final int get8Connexity(int x, int y, int z, int t, int b) {
boolean current = input.getPixelBoolean(x, y, z, t, b);
int min = Integer.MAX_VALUE;
boolean val1 = false, val2 = false, val3 = false, val4 = false;
if (x - 1 >= 0 && y - 1 >= 0
&& current == input.getPixelBoolean(x - 1, y - 1, z, t, b)) {
min = Math
.min(min, labels.get(output.getPixelInt(x - 1, y - 1, z, t, b)));
val1 = true;
}
if (y - 1 >= 0 && current == input.getPixelBoolean(x, y - 1, z, t, b)) {
min = Math.min(min, labels.get(output.getPixelInt(x, y - 1, z, t, b)));
val2 = true;
}
if (y - 1 >= 0 && x + 1 < input.getXDim()
&& current == input.getPixelBoolean(x + 1, y - 1, z, t, b)) {
min = Math
.min(min, labels.get(output.getPixelInt(x + 1, y - 1, z, t, b)));
val3 = true;
}
if (x - 1 >= 0 && current == input.getPixelBoolean(x - 1, y, z, t, b)) {
min = Math.min(min, labels.get(output.getPixelInt(x - 1, y, z, t, b)));
val4 = true;
}
if (val1 && labels.get(output.getPixelInt(x - 1, y - 1, z, t, b)) != min)
setTableMin(output.getPixelInt(x - 1, y - 1, z, t, b), min);
if (val2 && labels.get(output.getPixelInt(x, y - 1, z, t, b)) != min)
setTableMin(output.getPixelInt(x, y - 1, z, t, b), min);
if (val3 && labels.get(output.getPixelInt(x + 1, y - 1, z, t, b)) != min)
setTableMin(output.getPixelInt(x + 1, y - 1, z, t, b), min);
if (val4 && labels.get(output.getPixelInt(x - 1, y, z, t, b)) != min)
setTableMin(output.getPixelInt(x - 1, y, z, t, b), min);
if (min == Integer.MAX_VALUE) {
labels.add(++nbLabels);
return nbLabels;
} else
return min;
}
private final int get4ConnexityLabel(int x, int y, int z, int t, int b) {
int current = input.getPixelInt(x, y, z, t, b);
int min = Integer.MAX_VALUE;
boolean val1 = false, val2 = false;
if (y - 1 >= 0 && current == input.getPixelInt(x, y - 1, z, t, b)) {
min = Math.min(min, labels.get(output.getPixelInt(x, y - 1, z, t, b)));
val1 = true;
}
if (x - 1 >= 0 && current == input.getPixelInt(x - 1, y, z, t, b)) {
min = Math.min(min, labels.get(output.getPixelInt(x - 1, y, z, t, b)));
val2 = true;
}
if (val1 && labels.get(output.getPixelInt(x, y - 1, z, t, b)) != min)
setTableMin(output.getPixelInt(x, y - 1, z, t, b), min);
if (val2 && labels.get(output.getPixelInt(x - 1, y, z, t, b)) != min)
setTableMin(output.getPixelInt(x - 1, y, z, t, b), min);
if (min == Integer.MAX_VALUE) {
labels.add(++nbLabels);
return nbLabels;
} else
return min;
}
private final int get8ConnexityLabel(int x, int y, int z, int t, int b) {
int current = input.getPixelInt(x, y, z, t, b);
int min = Integer.MAX_VALUE;
boolean val1 = false, val2 = false, val3 = false, val4 = false;
if (x - 1 >= 0 && y - 1 >= 0
&& current == input.getPixelInt(x - 1, y - 1, z, t, b)) {
min = Math
.min(min, labels.get(output.getPixelInt(x - 1, y - 1, z, t, b)));
val1 = true;
}
if (y - 1 >= 0 && current == input.getPixelInt(x, y - 1, z, t, b)) {
min = Math.min(min, labels.get(output.getPixelInt(x, y - 1, z, t, b)));
val2 = true;
}
if (y - 1 >= 0 && x + 1 < input.getXDim()
&& current == input.getPixelInt(x + 1, y - 1, z, t, b)) {
min = Math
.min(min, labels.get(output.getPixelInt(x + 1, y - 1, z, t, b)));
val3 = true;
}
if (x - 1 >= 0 && current == input.getPixelInt(x - 1, y, z, t, b)) {
min = Math.min(min, labels.get(output.getPixelInt(x - 1, y, z, t, b)));
val4 = true;
}
if (val1 && labels.get(output.getPixelInt(x - 1, y - 1, z, t, b)) != min)
setTableMin(output.getPixelInt(x - 1, y - 1, z, t, b), min);
if (val2 && labels.get(output.getPixelInt(x, y - 1, z, t, b)) != min)
setTableMin(output.getPixelInt(x, y - 1, z, t, b), min);
if (val3 && labels.get(output.getPixelInt(x + 1, y - 1, z, t, b)) != min)
setTableMin(output.getPixelInt(x + 1, y - 1, z, t, b), min);
if (val4 && labels.get(output.getPixelInt(x - 1, y, z, t, b)) != min)
setTableMin(output.getPixelInt(x - 1, y, z, t, b), min);
if (min == Integer.MAX_VALUE) {
labels.add(++nbLabels);
return nbLabels;
} else
return min;
}
private final void setTableMin(int u, int min) {
int v = labels.get(u);
while (u != v) {
labels.set(u, min);
u = v;
v = labels.get(v);
}
labels.set(u, min);
}
}