package fr.unistra.pelican.util.morphology;
import java.awt.Point;
import fr.unistra.pelican.BooleanImage;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.util.Point4D;
/**
* Utility class to create images representing structuring elements
*
* @author lefevre
*
*/
public class FlatStructuringElement2D {
/**
*
*/
public static final long serialVersionUID = 234523;
// present and future bug source:
// the centre is kept as X,Y being : COLUMS, ROWS.
// Careful with the transformations
// TODO E.A : we should use a single standard...this is far too messy.
/**
* Create a horizontal line of length length. The center is at length/2.
*
* @param length
* @param center
* @return
*/
public static BooleanImage createHorizontalLineFlatStructuringElement(
int length) {
BooleanImage se = new BooleanImage(length, 1, 1, 1, 1);
se.resetCenter();
se.fill(true);
return se;
}
/**
* Create a vertical line of length length. The center is at length/2
*
* @param length
* @return a vertical line shaped SE
*/
public static BooleanImage createVerticalLineFlatStructuringElement(int length) {
BooleanImage se = new BooleanImage(1, length, 1, 1, 1);
se.resetCenter();
se.fill(true);
return se;
}
/**
* Create a vertical line of length length. The center is at length/2
*
* @param length
* @param center
* @return a vertical line shaped SE
*/
public static BooleanImage createVerticalLineFlatStructuringElement(int length, Point center) {
BooleanImage se = new BooleanImage(1, length, 1, 1, 1);
se.setCenter(center);
se.fill(true);
return se;
}
/**
* Create a left diagonal line of length length (odd). The center is at length /
* 2
*
* @param length
* @return a left diagonal shaped SE
*/
public static BooleanImage createLeftDiagonalLineFlatStructuringElement(
int length) {
BooleanImage se = new BooleanImage(length, length, 1, 1, 1);
se.resetCenter();
for (int i = 0; i < length; i++)
se.setPixelXYBoolean(i, i, true);
return se;
}
/**
* Create a right diagonal line of length length (odd). The center is at
* length / 2
*
* @param length
* @return
*/
public static BooleanImage createRightDiagonalLineFlatStructuringElement(
int length) {
BooleanImage se = new BooleanImage(length, length, 1, 1, 1);
se.resetCenter();
for (int i = 0; i < length; i++)
se.setPixelXYBoolean(i, length - 1 - i, true);
return se;
}
/**
* Create a line of length length of a given orientation (in degrees)
*
* @param length
* @param orientation
* @return
*/
public static BooleanImage createLineFlatStructuringElement(int length,
double orientation) {
double cos = Math.cos(Math.toRadians(orientation));
double sin = Math.sin(Math.toRadians(orientation));
double abscos = Math.abs(cos);
double abssin = Math.abs(sin);
double epsilon = 0.0000001;
double sumcos = abscos;
double sumsin = abssin;
int i = 0, j = 0;
int center = (length - 1) / 2;
BooleanImage se = new BooleanImage(length, length, 1, 1, 1);
se.setCenter(new Point(center, center));
se.setPixelXYBoolean(center, center, true);
for (int l = 1; l <= center; l++) {
if (sumcos + epsilon >= sumsin) {
i = cos > 0 ? i + 1 : i - 1;
sumsin += abssin;
}
if (sumsin + epsilon >= sumcos) {
j = sin > 0 ? j + 1 : j - 1;
sumcos += abscos;
}
se.setPixelXYBoolean(center + i, center + j, true);
se.setPixelXYBoolean(center - i, center - j, true);
}
return se;
}
/**
* Create a horizontal line of length length. The center is at center.
*
* @param length
* @param center
* @return
*/
public static BooleanImage createHorizontalLineFlatStructuringElement(
int length, Point center) {
BooleanImage se = new BooleanImage(length, 1, 1, 1, 1);
se.setCenter(center);
se.fill(true);
return se;
}
/**
* Create a cross line of radius radius (plus the pixel at the cross).
*
* @param radius
* @return
*/
public static BooleanImage createCrossFlatStructuringElement(int radius) {
BooleanImage se = new BooleanImage(2 * radius + 1, 2 * radius + 1, 1, 1, 1);
se.resetCenter();
for (int i = 0; i < 2 * radius + 1; i++) {
se.setPixelXYBoolean(i, radius, true);
se.setPixelXYBoolean(radius, i, true);
}
return se;
}
/**
* Create a circle of radius radius (plus the pixel at the cross).
*
* @param radius
* @return
*/
public static BooleanImage createCircleFlatStructuringElement(int radius) {
BooleanImage se = new BooleanImage(2 * radius + 1, 2 * radius + 1, 1, 1, 1);
se.resetCenter();
for (int i = 0; i < 2 * radius + 1; i++) {
for (int j = 0; j < 2 * radius + 1; j++) {
if (Math.sqrt(Math.pow(i - radius, 2) + Math.pow(j - radius, 2)) <= radius + 0.000001)
se.setPixelXYBoolean(i, j, true);
}
}
return se;
}
/**
* Create a empty circle of radius radius (plus the pixel at the cross).
*
* @param radius
* @return
*/
public static BooleanImage createEmptyCircleFlatStructuringElement(int radius) {
BooleanImage se = new BooleanImage(2 * radius + 1, 2 * radius + 1, 1, 1, 1);
se.resetCenter();
for (int i = 0; i < 2 * radius + 1; i++) {
for (int j = 0; j < 2 * radius + 1; j++) {
if (Math.rint(Math.sqrt(Math.pow(i - radius, 2) + Math.pow(j - radius, 2))) == radius)
se.setPixelXYBoolean(i, j, true);
}
}
return se;
}
/**
* @return The transpose of the structuring element.
*/
public static BooleanImage transpose(BooleanImage se) {
BooleanImage res = new BooleanImage(se.ydim, se.xdim, se.zdim, se.tdim,
se.bdim);
for (int j = 0; j < res.ydim; j++)
for (int i = 0; i < res.xdim; i++)
res.setPixelXYBoolean(i, j, se.getPixelXYBoolean(j, i));
Point4D p = se.getCenter();
res.setCenter(new Point4D(p.y, p.x, p.z, p.t));
return res;
}
public static BooleanImage reflect(BooleanImage se) {
int xdim = se.getXDim();
int ydim = se.getYDim();
int zdim = se.getZDim();
int tdim = se.getTDim();
int bdim = se.getBDim();
BooleanImage res = new BooleanImage(se);
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++)
res.setPixelXYZTBBoolean(x, y, z, t, b, se
.getPixelXYZTBBoolean(xdim - 1 - x, ydim - 1 - y, zdim - 1
- z, tdim - 1 - t, b));
Point4D p = se.getCenter();
res.setCenter(new Point4D(xdim - 1 - p.x, ydim - 1 - p.y, zdim - 1 - p.z,
tdim - 1 - p.t));
return res;
}
/**
* Perform a rotation of the se around its middle (spatial center)
*
* @param se
* the input SE
* @param degree
* the angle of rotation in degrees
* @return the rotated SE
*/
public static BooleanImage rotateMiddle(BooleanImage se, double degree) {
if(degree==180)
System.out.println("ok");
double angleradian = Math.toRadians(degree);
double xinput = se.xdim;
double yinput = se.ydim;
double tcos = Math.cos(-angleradian);
double tsin = Math.sin(-angleradian);
double atcos = Math.cos(angleradian);
double atsin = Math.sin(angleradian);
int xoutput = (int) Math.round(xinput * Math.abs(tcos) + yinput
* Math.abs(tsin));
int youtput = (int) Math.round(xinput * Math.abs(tsin) + yinput
* Math.abs(tcos));
// System.out.println(xoutput + " " + youtput + " ... "
// + (xinput * Math.abs(tcos) + yinput * Math.abs(tsin)) + " "
// + (xinput * Math.abs(tsin) + yinput * Math.abs(tcos)));
int xm = (int) (xinput / 2);
int ym = (int) (yinput / 2);
int xmprime = xoutput / 2;
int ymprime = youtput / 2;
BooleanImage res = new BooleanImage(xoutput, youtput, 1, 1, 1);
//res.setCenter(new Point(xoutput / 2, youtput / 2));
res.fill(false);
for (int x = 0; x < se.xdim; x++)
for (int y = 0; y < se.ydim; y++) {
int xprime = (int) Math.round((x - xm) * atcos - (y - ym) * atsin
+ xmprime);
int yprime = (int) Math.round((x - xm) * atsin + (y - ym) * atcos
+ ymprime);
if (xprime < 0) {
xprime = 0;
}
if (xprime >= xoutput) {
xprime = xoutput - 1;
}
if (yprime < 0) {
yprime = 0;
}
if (yprime >= youtput) {
yprime = youtput - 1;
}
res.setPixelXYBoolean(xprime, yprime, se.getPixelXYBoolean(x, y));
}
int centreX = (int) se.getCenter().getX();
int centreY = (int) se.getCenter().getY();
int centreXprime = (int) Math.round((centreX - xm) * atcos - (centreY - ym)
* atsin + xmprime);
int centreYprime = (int) Math.round((centreX - xm) * atsin + (centreY - ym)
* atcos + ymprime);
if (centreXprime < 0) {
centreXprime = 0;
}
if (centreXprime >= xoutput) {
centreXprime = xoutput - 1;
}
if (centreYprime < 0) {
centreYprime = 0;
}
if (centreYprime >= youtput) {
centreYprime = youtput - 1;
}
res.setCenter(new Point(centreXprime, centreYprime));
// System.out.println(res.getCenter());
return res;
}
/**
* Perform a rotation of the SE around its center (not necessarily in the middle)
*
* @param se
* the input SE
* @param degree
* the angle of rotation in degrees
* @return the rotated SE
*/
public static BooleanImage rotate(BooleanImage se, double degree) {
double angleradian = Math.toRadians(degree);
double atcos = Math.cos(angleradian);
double atsin = Math.sin(angleradian);
Point4D[] fg=se.foreground();
int xm = (int) se.getCenter().getX();
int ym = (int) se.getCenter().getY();
int minx=xm;
int miny=ym;
int maxx=xm;
int maxy=ym;
for (int i=0;i<fg.length;i++) {
int x=(int) fg[i].getX();
int y=(int) fg[i].getY();
int xprime = (int) Math.round((x - xm) * atcos - (y - ym) * atsin+xm
);
int yprime = (int) Math.round((x - xm) * atsin + (y - ym) * atcos+ym
);
fg[i].setLocation(xprime,yprime,0,0);
if(xprime<minx)
minx=xprime;
if(yprime<miny)
miny=yprime;
if(xprime>maxx)
maxx=xprime;
if(yprime>maxy)
maxy=yprime;
}
int xoutput = maxx-minx+1;
int youtput = maxy-miny+1;
BooleanImage res = new BooleanImage(xoutput, youtput, 1, 1, 1);
res.fill(false);
for (int i=0;i<fg.length;i++) {
int x=(int) fg[i].getX();
int y=(int) fg[i].getY();
res.setPixelXYBoolean(x-minx,y-miny,true);
}
res.setCenter(new Point(xm-minx,ym-miny));
return res;
}
/**
* Create a square of edge's length size. Center is at (size/2, size/2).
*
* @param size
* @return a square SE
*/
public static BooleanImage createSquareFlatStructuringElement(int size) {
BooleanImage se = new BooleanImage(size, size, 1, 1, 1);
se.resetCenter();
se.fill(true);
return se;
}
/**
* Create a diamond of a given height (width is the same). Center is at
* (size/2, size/2).
*
* @param size
* @return a square SE
*/
public static BooleanImage createDiamondFlatStructuringElement(int size) {
BooleanImage se = new BooleanImage(size, size, 1, 1, 1);
se.resetCenter();
for (int x = 0; x < se.getXDim(); x++)
for (int y = 0; y < se.getYDim(); y++)
if (x + y + 1 > size / 2 && y <= size / 2 + x
&& x + y + 1 <= size + size / 2 && x - y <= size / 2)
se.setPixelXYBoolean(x, y, true);
else
se.setPixelXYBoolean(x, y, false);
return se;
}
/**
* Create a rectangle. Center is at (height/2, width/2).
*
* @param rows
* @param cols
* @return a rectangle shaped SE
*/
public static BooleanImage createRectangularFlatStructuringElement(int xdim,
int ydim) {
BooleanImage se = new BooleanImage(xdim, ydim, 1, 1, 1);
se.resetCenter();
se.fill(true);
return se;
}
/**
* Create a hollow square SE element size x size. Center is at (size/2,
* size/2).
*
* @param size
* @return a hollow square SE
*/
public static BooleanImage createHollowSquareFlatStructuringElement(int size) {
BooleanImage se = new BooleanImage(size, size, 1, 1, 1);
se.resetCenter();
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
if (x == 0 || x == size - 1 || y == 0 || y == size - 1)
se.setPixelXYBoolean(x, y, true);
}
}
return se;
}
/**
* Create a frame rectangle. Center is at (height/2, width/2).
*
* @param rows
* @param cols
* @return
*/
public static BooleanImage createFrameFlatStructuringElement(int ydim,
int xdim) {
BooleanImage se = new BooleanImage(xdim, ydim, 1, 1, 1);
se.resetCenter();
for (int i = 0; i < se.xdim; i++) {
se.setPixelXYBoolean(i, 0, true);
se.setPixelXYBoolean(i, se.ydim - 1, true);
}
for (int i = 0; i < se.ydim; i++) {
se.setPixelXYBoolean(0, i, true);
se.setPixelXYBoolean(se.xdim - 1, i, true);
}
return se;
}
/**
* Create a frame square of edge's length size. Center is at (size/2, size/2).
*
* @param size
* @return
*/
public static BooleanImage createFrameFlatStructuringElement(int size) {
return createFrameFlatStructuringElement(size, size);
}
public static void print(BooleanImage se) {
for (int j = 0; j < se.ydim; j++) {
for (int i = 0; i < se.xdim; i++) {
if (se.getCenter().x == i && se.getCenter().y == j) {
if (se.getPixelXYBoolean(i, j))
System.out.print("+ ");
else
System.out.print("O ");
} else if (se.getPixelXYBoolean(i, j)) {
System.out.print("0 ");
} else {
System.out.print(" ");
}
}
System.out.println();
}
}
// /**
// * Dessine l'lment structurant sur une image
// *
// * @param im
// * Image dans laquelle dessiner
// * @param p
// * Translation de l'lment structurant
// */
// public void draw(Image im, Point p) {
// draw(im, p.x, p.y);
// }
//
// /**
// * Dessine l'lment structurant sur une image
// *
// * @param im
// * Image dans laquelle dessiner
// * @param x
// * Translation de l'lment structurant en x
// * @param y
// * Translation de l'lment structurant en y
// */
// public void draw(Image im, int x, int y) {
// int cX = x - centre.x;
// int cY = y - centre.x;
// for (int i = 0; i < rows; i++)
// for (int j = 0; j < cols; j++) {
// int valX = i + cX;
// int valY = j + cY;
// if (getValue(i, j) && valX >= 0 && valX < im.getXDim() && valY >= 0
// && valY < im.getYDim())
// im.setPixelXYBoolean(valX, valY, true);
// }
// }
}