package fr.unistra.pelican.algorithms.spatial;
import fr.unistra.pelican.Algorithm;
import fr.unistra.pelican.AlgorithmException;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.IntegerImage;
import fr.unistra.pelican.algorithms.conversion.GrayToPseudoColors;
import fr.unistra.pelican.algorithms.visualisation.Viewer2D;
/**
* Computes the distance transform of a binary image
*
* @author Lefevre
*/
public class DistanceTransform extends Algorithm {
/**
* Input image
*/
public Image input;
/**
* Output image
*/
public IntegerImage output;
/**
* (optionnally) flag to push background borders around the image
*/
public boolean border = false;
/**
* Constructor
*
*/
public DistanceTransform() {
super.inputs = "input";
super.outputs = "output";
super.options = "border";
}
/*
* (non-Javadoc)
*
* @see fr.unistra.pelican.Algorithm#launch()
*/
public void launch() throws AlgorithmException {
output = new IntegerImage(input, false);
xDim = input.getXDim();
yDim = input.getYDim();
zDim = input.getZDim();
tDim = input.getTDim();
bDim = input.getBDim();
// Initialisation du masque de distance
// a < b < c < d et d < a+b et c < 2a et e < min(b+c,a+d)
int valMax = setDistanceMask(5, 7, 9, 11, 13);
// int valMax=setDistanceMask(1,1,1,1,1);
for (int i = 0; i < input.size(); i++)
if (!input.getPixelBoolean(i))
output.setPixelInt(i, valMax);
else
output.setPixelInt(i, 0);
if (border)
pushBorder(output, 5);
// 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 (!input.getPixelBoolean(x, y, z, t, b)) {
output.setPixelInt(x, y, z, t, b, getForwardDistance(x, y, z,
t, b));
}
// Second parcours (inverse)
for (int b = bDim - 1; b >= 0; b--)
for (int t = tDim - 1; t >= 0; t--)
for (int z = zDim - 1; z >= 0; z--)
for (int y = yDim - 1; y >= 0; y--)
for (int x = xDim - 1; x >= 0; x--)
if (!input.getPixelBoolean(x, y, z, t, b)) {
output.setPixelInt(x, y, z, t, b, getBackwardDistance(x, y, z,
t, b));
}
}
private int dist[][][][][] = new int[2][2][2][2][2];
private int xDim, yDim, zDim, tDim, bDim;
private void pushBorder(Image im, int v) {
xDim = input.getXDim();
yDim = input.getYDim();
zDim = input.getZDim();
tDim = input.getTDim();
bDim = input.getBDim();
// B
if (bDim > 1)
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++) {
im.setPixelInt(x, y, z, t, 0, v);
im.setPixelInt(x, y, z, t, bDim - 1, v);
}
// T
if (tDim > 1)
for (int b = 0; b < bDim; b++)
for (int z = 0; z < zDim; z++)
for (int y = 0; y < yDim; y++)
for (int x = 0; x < xDim; x++) {
im.setPixelInt(x, y, z, 0, b, v);
im.setPixelInt(x, y, z, tDim - 1, b, v);
}
// Z
if (zDim > 1)
for (int b = 0; b < bDim; b++)
for (int t = 0; t < tDim; t++)
for (int y = 0; y < yDim; y++)
for (int x = 0; x < xDim; x++) {
im.setPixelInt(x, y, 0, t, b, v);
im.setPixelInt(x, y, bDim - 1, t, b, v);
}
// Y
if (yDim > 1)
for (int b = 0; b < bDim; b++)
for (int t = 0; t < tDim; t++)
for (int z = 0; z < zDim; z++)
for (int x = 0; x < xDim; x++) {
im.setPixelInt(x, 0, z, t, b, v);
im.setPixelInt(x, yDim - 1, z, t, b, v);
}
// X
if (xDim > 1)
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++) {
im.setPixelInt(0, y, z, t, b, v);
im.setPixelInt(xDim - 1, y, z, t, b, v);
}
}
private int setDistanceMask(int a, int b, int c, int d, int e) {
dist[0][0][0][0][0] = 0;
dist[1][0][0][0][0] = a;
dist[0][1][0][0][0] = a;
dist[0][0][1][0][0] = a;
dist[0][0][0][1][0] = a;
dist[0][0][0][0][1] = a;
dist[1][1][0][0][0] = b;
dist[1][0][1][0][0] = b;
dist[1][0][0][1][0] = b;
dist[1][0][0][0][1] = b;
dist[0][1][1][0][0] = b;
dist[0][1][0][1][0] = b;
dist[0][1][0][0][1] = b;
dist[0][0][1][1][0] = b;
dist[0][0][1][0][1] = b;
dist[0][0][0][1][1] = b;
dist[0][0][1][1][1] = c;
dist[0][1][0][1][1] = c;
dist[0][1][1][0][1] = c;
dist[0][1][1][1][0] = c;
dist[1][0][0][1][1] = c;
dist[1][0][1][0][1] = c;
dist[1][0][1][1][0] = c;
dist[1][1][0][0][1] = c;
dist[1][1][0][1][0] = c;
dist[1][1][1][0][0] = c;
dist[1][1][1][1][0] = d;
dist[1][1][1][0][1] = d;
dist[1][1][0][1][1] = d;
dist[1][0][1][1][1] = d;
dist[0][1][1][1][1] = d;
dist[1][1][1][1][1] = e;
return max(xDim, yDim, zDim, tDim, bDim) * e;
}
private int max(int a, int b, int c, int d, int e) {
int m = a;
if (b > m)
m = b;
if (c > m)
m = c;
if (d > m)
m = d;
if (e > m)
m = e;
return m;
}
private int getForwardDistance(int x, int y, int z, int t, int b) {
int min = output.getPixelInt(x, y, z, t, b);
boolean stop = false;
for (int bb = -1; bb <= 1; bb++)
for (int tt = -1; tt <= 1; tt++)
for (int zz = -1; zz <= 1; zz++)
for (int yy = -1; yy <= 1; yy++)
for (int xx = -1; xx <= 1; xx++)
if (!stop) {
if (xx == 0 && yy == 0 && zz == 0 && tt == 0 && bb == 0)
stop = true;
else if (x + xx >= 0 && y + yy >= 0 && z + zz >= 0
&& t + tt >= 0 && b + bb >= 0 && x + xx < xDim
&& y + yy < yDim && z + zz < zDim && t + tt < tDim
&& b + bb < bDim) {
min = Math.min(min, output.getPixelInt(x + xx, y + yy,
z + zz, t + tt, b + bb)
+ dist[Math.abs(xx)][Math.abs(yy)][Math.abs(zz)][Math
.abs(tt)][Math.abs(bb)]);
if (min == Integer.MIN_VALUE)
System.out.println("xxx");
}
}
return min;
}
private int getBackwardDistance(int x, int y, int z, int t, int b) {
int min = output.getPixelInt(x, y, z, t, b);
boolean stop = false;
for (int bb = 1; bb >= -1; bb--)
for (int tt = 1; tt >= -1; tt--)
for (int zz = 1; zz >= -1; zz--)
for (int yy = 1; yy >= -1; yy--)
for (int xx = 1; xx >= -1; xx--)
if (!stop) {
if (xx == 0 && yy == 0 && zz == 0 && tt == 0 && bb == 0)
stop = true;
else if (x + xx >= 0 && y + yy >= 0 && z + zz >= 0
&& t + tt >= 0 && b + bb >= 0 && x + xx < xDim
&& y + yy < yDim && z + zz < zDim && t + tt < tDim
&& b + bb < bDim) {
min = Math.min(min, output.getPixelInt(x + xx, y + yy,
z + zz, t + tt, b + bb)
+ dist[Math.abs(xx)][Math.abs(yy)][Math.abs(zz)][Math
.abs(tt)][Math.abs(bb)]);
}
}
return min;
}
public static IntegerImage exec(Image input) {
return (IntegerImage) new DistanceTransform().process(input);
}
public static IntegerImage exec(Image input, boolean border) {
return (IntegerImage) new DistanceTransform().process(input, border);
}
}