package fr.unistra.pelican.algorithms.morphology.binary;
import fr.unistra.pelican.*;
import fr.unistra.pelican.util.Point4D;
import fr.unistra.pelican.util.buffers.BooleanBuffers;
/**
* Performs a binary erosion with a flat structuring element.
* @author ?, Jonathan Weber, Régis Witz ( mask management + optimization )
*/
public class BinaryErosion extends Algorithm {
///////////////
// CONSTANTS //
///////////////
/** Constant for ignoring out-of-image pixels. */
public static final int IGNORE = 0;
/** Constant for setting to white out-of-image pixels. */
public static final int WHITE = 1;
/** Constant for setting to black out-of-image pixels. */
public static final int BLACK = 2;
////////////
// FIELDS //
////////////
/** Image to be processed. */
public Image inputImage;
/** Structuring Element used for the erosion. */
public BooleanImage se;
/** Resulting picture. */
public Image outputImage;
/** Option for considering out-of-image pixels. */
public Integer option = IGNORE;
/** Indicates if structuring element is convex or not. */
public Boolean convexSEFlag = false;
public int optimization =
fr.unistra.pelican.algorithms.morphology.gray.GrayErosion.NO_OPTIMIZATION;
/////////////
// PROFILE //
/////////////
/** Algorithm profile. */
public BinaryErosion() {
super.inputs = "inputImage,se";
super.options = "option,convexSEFlag,optimization";
super.outputs = "outputImage";
}
////////////////////
// "EXEC" METHODS //
////////////////////
/** Performs a binary erosion with a flat structuring element.
* @param <T> Type of <tt>image</tt>.
* @param image Image to be processed.
* @param se Structuring element.
* @return Eroded picture.
*/
@SuppressWarnings("unchecked")
public static <T extends Image> T exec(T image, BooleanImage se) {
return (T) new BinaryErosion().process( image,se );
}
/** Performs a binary erosion with a flat structuring element.
* @param <T> Type of <tt>image</tt>.
* @param image Image to be processed.
* @param se Structuring element.
* @param option How to consider out-of-image pixels.
* @return Eroded picture.
*/
@SuppressWarnings("unchecked")
public static <T extends Image> T exec( T image, BooleanImage se, Integer option ) {
return (T) new BinaryErosion().process( image,se,option );
}
/** Performs a binary erosion with a flat structuring element.
* @param <T> Type of <tt>image</tt>.
* @param image Image to be processed.
* @param se Structuring element.
* @param flag If structuring element is convex or not.
* @return Eroded picture.
*/
@SuppressWarnings("unchecked")
public static <T extends Image> T exec( T image, BooleanImage se, Boolean flag ) {
return (T) new BinaryErosion().process( image,se,null,flag );
}
/** Performs a binary erosion with a flat structuring element.
* @param <T> Type of <tt>image</tt>.
* @param image Image to be processed.
* @param se Structuring element.
* @param opt Way of optimize things. Should be one of this class XXX_OPTIMIZATION constants.
* @return Eroded picture.
*/
@SuppressWarnings("unchecked")
public static <T extends Image> T exec( T image,
BooleanImage se,
int opt ) {
return (T) new BinaryErosion().process( image,se,null,null,opt );
}
/** Performs a binary erosion with a flat structuring element.
* @param <T> Type of <tt>image</tt>.
* @param image Image to be processed.
* @param se Structuring element.
* @param option How to consider out-of-image pixels.
* @param flag If structuring element is convex or not.
* @return Eroded picture.
*/
@SuppressWarnings("unchecked")
public static <T extends Image> T exec( T image,
BooleanImage se,
int option,
Boolean flag ) {
return (T) new BinaryErosion().process( image,se,option,flag );
}
/** Performs a binary erosion with a flat structuring element.
* @param <T> Type of <tt>image</tt>.
* @param image Image to be processed.
* @param se Structuring element.
* @param option How to consider out-of-image pixels.
* @param flag If structuring element is convex or not.
* @param opt Way of optimize things. Should be one of this class XXX_OPTIMIZATION constants.
* @return Eroded picture.
*/
@SuppressWarnings("unchecked")
public static <T extends Image> T exec( T image,
BooleanImage se,
int option,
Boolean flag,
int opt ) {
return (T) new BinaryErosion().process( image,se,option,flag,opt );
}
/////////////////////
// "LAUNCH" METHOD //
/////////////////////
/** @see fr.unistra.pelican.Algorithm#launch() */
public void launch() throws AlgorithmException {
switch ( this.optimization ) {
case fr.unistra.pelican.algorithms.morphology.gray.GrayErosion.RECTANGLE_OPTIMIZATION:
this.rectangleErosion();
break;
case fr.unistra.pelican.algorithms.morphology.gray.GrayErosion.HLINE_OPTIMIZATION:
this.standardErosion();
break;
case fr.unistra.pelican.algorithms.morphology.gray.GrayErosion.VLINE_OPTIMIZATION:
this.standardErosion();
break;
case fr.unistra.pelican.algorithms.morphology.gray.GrayErosion.VANHERK_HLINE_OPTIMIZATION:
this.horizontalErosion();
break;
case fr.unistra.pelican.algorithms.morphology.gray.GrayErosion.VANHERK_VLINE_OPTIMIZATION:
this.verticalErosion();
break;
default : // try to find a possible optimization ...
int opt = fr.unistra.pelican.algorithms.morphology.gray.GrayErosion
.wichOptimization( this.se, this.inputImage );
if ( opt == fr.unistra.pelican.algorithms.morphology.gray.GrayErosion.NO_OPTIMIZATION )
this.standardErosion();
else this.outputImage =
BinaryErosion.exec( this.inputImage,this.se,option,convexSEFlag,opt );
}
}
///////////////////
// OTHER METHODS //
///////////////////
private void standardErosion() {
int xDim = this.inputImage.getXDim();
int yDim = this.inputImage.getYDim();
int tDim = this.inputImage.getTDim();
int bDim = this.inputImage.getBDim();
int zDim = this.inputImage.getZDim();
this.outputImage = this.inputImage.copyImage(false);
Point4D[] points = this.se.foreground();
boolean isHere;
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++ ) {
isHere = this.inputImage.isPresent( x,y,z,t,b );
if ( !isHere && this.option == IGNORE ) continue;
// if ( this.convexSEFlag == true
// && ( ( !isHere && this.option == BLACK )
// || ( isHere && !this.inputImage.getPixelBoolean( x,y,z,t,b ) ) )
// ) continue;
// this.outputImage.setPixelBoolean( x,y,z,t,b, getMin( x,y,z,t,b, points ) );
if ( isHere )
this.outputImage.setPixelBoolean( x,y,z,t,b, this.getMin( x,y,z,t,b, points ) );
else this.outputImage.setPixelBoolean( x,y,z,t,b, false );
}
}
/** Gets the min value for the pixels under the structuring element when the
* last is at the given coordinates.
* @param x X coordinate.
* @param y Y coordinate.
* @param z Z coordinate.
* @param t T coordinate.
* @param b B coordinate.
* @param points Present points of {@link #se}.
* @return Min value.
*/
public boolean getMin(int x, int y, int z, int t, int b, Point4D[] points) {
boolean flag = false;
for (int i = 0; i < points.length; i++) {
int valX = x - se.getCenter().x + points[i].x;
int valY = y - se.getCenter().y + points[i].y;
int valZ = z - se.getCenter().z + points[i].z;
int valT = t - se.getCenter().t + points[i].t;
if (valX >= 0 && valX < inputImage.getXDim() && valY >= 0
&& valY < inputImage.getYDim() && valZ >= 0
&& valZ < inputImage.getZDim() && valT >= 0
&& valT < inputImage.getTDim()) {
flag = true;
boolean p = inputImage.getPixelBoolean( valX,valY,valZ,valT,b );
if ( !inputImage.isPresent( valX,valY,valZ,valT,b ) ) {
if ( this.option == BLACK ) return false;
} else if ( !p ) return false;
} else {
if (option == BLACK) {
return false;
}
}
}
// FIXME: Strange, if nothing is under the se, what is the right way?
return (flag == true) ? true : inputImage.getPixelBoolean(x, y, z, t, b);
}
//
// RECTANGLE EROSION METHOD
/** Performs a faster erosion with a square structuring element. */
private void rectangleErosion() {
this.outputImage = this.inputImage.copyImage( false );
int xdim = this.se.getXDim();
int ydim = this.se.getYDim();
BooleanImage optSe = fr.unistra.pelican.util.morphology.FlatStructuringElement2D.
createHorizontalLineFlatStructuringElement(
xdim, new java.awt.Point( this.se.getCenter().x, 0) );
this.outputImage = BinaryErosion.exec( this.inputImage,optSe,option,convexSEFlag );
optSe = fr.unistra.pelican.util.morphology.FlatStructuringElement2D.
createVerticalLineFlatStructuringElement(
ydim, new java.awt.Point( 0, this.se.getCenter().y ) );
this.outputImage = BinaryErosion.exec( this.outputImage,optSe,option,convexSEFlag );
} // endfunc
//
// LINES EROSION MATERIAL
/** Initializes buffers corresponding to line <tt>y</tt>.
* @param x Column of {@link inputImage} that <tt>buffers</tt> must represent at call's end.
* @param z Z coordinate.
* @param t T coordinate.
* @param b B coordinate.
* @param buffers Up and down buffers.
* @param len Length of horizontal line structuring element {@link #se}.
*/
private void initColumnBuffers( int x, int z, int t, int b, BooleanBuffers buffers, int len ) {
boolean px;
boolean isHere;
for ( int y = 0 ; y < buffers.size ; y++ ) { // fill g
isHere = this.inputImage.isPresent( x,y,z,t,b );
if ( isHere ) px = this.inputImage.getPixelXYZTBBoolean( x,y,z,t,b );
else px = true;
if ( y%len == 0 ) buffers.g[y] = px;
else buffers.g[y] = buffers.g[y-1] && px ;
} // rof x
for ( int y = buffers.size-1 ; y >= 0 ; y-- ) { // fill h
isHere = this.inputImage.isPresent( x,y,z,t,b );
if ( isHere ) px = this.inputImage.getPixelXYZTBBoolean( x,y,z,t,b );
else px = true;
if ( y%len == len-1 ) buffers.h[y] = px;
else {
if ( y+1 < buffers.size ) buffers.h[y] = buffers.h[y+1] && px;
else buffers.h[y] = px;
}
} // rof x
} // endfunc
/** Initializes buffers corresponding to line <tt>y</tt>.
* @param y Line of {@link inputImage} that <tt>buffers</tt> must represent at call's end.
* @param z Z coordinate.
* @param t T coordinate.
* @param b B coordinate.
* @param buffers Left and right buffers.
* @param len Length of horizontal line structuring element {@link #se}.
*/
private void initRowBuffers( int y, int z, int t, int b, BooleanBuffers buffers, int len ) {
boolean px;
boolean isHere;
for ( int x = 0 ; x < buffers.size ; x++ ) { // fill g
isHere = this.inputImage.isPresent( x,y,z,t,b );
if ( isHere ) px = this.inputImage.getPixelXYZTBBoolean( x,y,z,t,b );
else px = true;
if ( x%len == 0 ) buffers.g[x] = px;
else buffers.g[x] = buffers.g[x-1] && px;
} // rof x
for ( int x = buffers.size-1 ; x >= 0 ; x-- ) { // fill h
isHere = this.inputImage.isPresent( x,y,z,t,b );
if ( isHere ) px = this.inputImage.getPixelXYZTBBoolean( x,y,z,t,b );
else px = true;
if ( x%len == len-1 ) buffers.h[x] = px;
else {
if ( x+1 < buffers.size ) buffers.h[x] = buffers.h[x+1] && px;
else buffers.h[x] = px;
}
} // rof x
} // endfunc
/** Performs a faster erosion (van Herk,1992) with a horizontal structuring element.
* <p>
* M. van Herk, <i>A fast algorithm for local minimum and maximum filters on rectangular and
* octogonal kernels</i> (1992).
* Algorithm leeched from P. Soille, <i>Morphological Image Analysis</i> (3.9.1).
*/
private void horizontalErosion() {
this.outputImage = this.inputImage.copyImage( false );
int xdim = this.inputImage.getXDim();
int ydim = this.inputImage.getYDim();
int zdim = this.inputImage.getZDim();
int tdim = this.inputImage.getTDim();
int bdim = this.inputImage.getBDim();
int size = xdim;
BooleanBuffers buffers = new BooleanBuffers( size );
int lambda = this.se.getXDim(); // lambada !
int o = this.se.getCenter().x;
assert buffers.size%lambda == 0;
boolean px;
int m,n;
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++ ) {
this.initRowBuffers( y,z,t,b, buffers,lambda );
for ( int x = 0 ; x < xdim ; x++ ) {
m = x+lambda-o-1;
n = x-o;
if ( m >= xdim ) {
if ( n < 0 ) px = true;
else px = buffers.h[ n ];
} else {
if ( n < 0 ) px = buffers.g[ m ];
else px = buffers.g[ m ] && buffers.h[ n ];
}
this.outputImage.setPixelBoolean( x,y,z,t,b, px );
} // rof x
} // rof
} // endfunc
/** Performs a faster erosion (van Herk,1992) with a vertical structuring element.
* <p>
* M. van Herk, <i>A fast algorithm for local minimum and maximum filters on rectangular and
* octogonal kernels</i> (1992).
* Algorithm leeched from P. Soille, <i>Morphological Image Analysis</i> (3.9.1).
*/
private void verticalErosion() {
this.outputImage = this.inputImage.copyImage( false );
int xdim = this.inputImage.getXDim();
int ydim = this.inputImage.getYDim();
int zdim = this.inputImage.getZDim();
int tdim = this.inputImage.getTDim();
int bdim = this.inputImage.getBDim();
int size = ydim;
BooleanBuffers buffers = new BooleanBuffers( size );
int lambda = this.se.getYDim();
int o = this.se.getCenter().y;
assert buffers.size%lambda == 0;
boolean px;
int m,n;
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++ ) {
this.initColumnBuffers( x,z,t,b, buffers,lambda );
for ( int y = 0 ; y < ydim ; y++ ) {
m = y+lambda-o-1;
n = y-o;
if ( m >= ydim ) {
if ( n < 0 ) px = true;
else px = buffers.h[ n ];
} else {
if ( n < 0 ) px = buffers.g[ m ];
else px = buffers.g[ m ] && buffers.h[ n ];
}
this.outputImage.setPixelBoolean( x,y,z,t,b, px );
} // rof x
} // rof
} // endfunc
}