package ie.dcu.image.binary; import ie.dcu.matrix.*; import ie.dcu.matrix.Matrix.Type; /** * Base class for binary morphological operations. * * Implementations of different morphological operations are given as the static * inner classes. * * @author Kevin McGuinness */ public abstract class MorphOp extends AbstractBinaryImageOp { /** * The structuring element for the morph. */ protected ByteMatrix structuringElement; /** * Get the structuring element. * * @return the structuring element (may be null). */ public ByteMatrix getStructuringElement() { return structuringElement; } /** * Set the structuring element. * * @param se * A structuring element (can be null). */ public void setStructuringElement(ByteMatrix se) { this.structuringElement = se; } /** * Perform a basic morph-op on the given matrix. * * The operation will be either an erode or dilate depending * on the values of the a and b parameters. * * @param matrix * The matrix to operate on (will be modified). * @param a * Either the foregroundValue or the backgroundValue * @param b * Either the foregroundValue or the backgroundValue * @return * The modified input matrix. */ protected static ByteMatrix morph( final ByteMatrix matrix, final byte a, final byte b ) { final byte[] im = matrix.values; // Right pixel for (int i = 0; i < matrix.rows; i++) { final int off = i * matrix.cols; for (int j = 0; j < matrix.cols - 1; j++) { final int idx = off + j; if (im[idx] == b && im[idx+1] == a) { im[idx] = a; } } } // Left pixel for (int i = 0; i < matrix.rows; i++) { final int off = i * matrix.cols; for (int j = matrix.cols - 1; j > 0; j--) { final int idx = off + j; if (im[idx] == b && im[idx-1] == a) { im[idx] = a; } } } // Below pixel for (int i = 0; i < matrix.rows - 1; i++) { final int off = i * matrix.cols; for (int j = 0; j < matrix.cols; j++) { final int idx = off + j; if (im[idx] == b && im[idx + matrix.cols] == a) { im[idx] = a; } } } // Above pixel for (int i = matrix.rows - 1; i > 0; i--) { final int off = i * matrix.cols; for (int j = 0; j < matrix.cols; j++) { final int idx = off + j; if (im[idx] == b && im[idx - matrix.cols] == a) { im[idx] = a; } } } return matrix; } /** * Perform a binary morphological operation with a structuring element. * * @param matrix * The input matrix to morph (not modified). * @param se * The structuring element. * @param a * Either the foreground or background value. * @param b * Either the foreground or background value. * @return A matrix containing the result of the morph. */ protected static ByteMatrix morph( final ByteMatrix matrix, final ByteMatrix se, final byte a, final byte b) { final ByteMatrix result = new ByteMatrix(matrix.rows, matrix.cols); result.fill(a); for (int i = 0, k = 0; i < matrix.rows; i++) { for (int j = 0; j < matrix.cols; j++, k++) { if (matrix.values[k] == b) { byte value = b; int im_i0 = i - (se.rows >> 1); int im_j0 = j - (se.cols >> 1); int se_i0 = im_i0 < 0 ? -im_i0 : 0; int se_j0 = im_j0 < 0 ? -im_j0 : 0; if (im_i0 < 0) { im_i0 = 0; } if (im_j0 < 0) { im_j0 = 0; } // Iterate over structuring element outer: for (int se_i = se_i0, im_i = im_i0; se_i < se.rows && im_i < matrix.rows; se_i++, im_i++) { int off1 = se_i * se.cols; int off2 = im_i * matrix.cols; for (int se_j = se_j0, im_j = im_j0; se_j < se.cols && im_j < matrix.cols; se_j++, im_j++) { if (se.values[off1 + se_j] != 0 && matrix.values[off2 + im_j] == a) { value = a; break outer; } } } // Set value result.values[k] = value; } } } return result; } /** * Applies the morphological dilate operator. * * @param matrix * The matrix to operate on (will be modified). * @return the input matrix */ protected ByteMatrix dilate(ByteMatrix matrix) { return morph(matrix, foregroundValue, backgroundValue); } /** * Perform a morphological dilation using the given structuring element * * @param matrix * The matrix to operate on (will not be modified). * @param se * The structuring element * @return A matrix containing the result of the morph. */ protected ByteMatrix dilate(ByteMatrix matrix, ByteMatrix se) { if (se == null) { return dilate(matrix.clone()); } return morph(matrix, se, foregroundValue, backgroundValue); } /** * Applies the morphological erode operator. * * @param matrix * The matrix to operate on (will be modified). * @return the input matrix */ protected ByteMatrix erode(ByteMatrix matrix) { return morph(matrix, backgroundValue, foregroundValue); } /** * Perform a morphological erosion using the given structuring element * * @param matrix * The matrix to operate on (will not be modified). * @param se * The structuring element * @return A matrix containing the result of the morph. */ protected ByteMatrix erode(ByteMatrix matrix, ByteMatrix se) { if (se == null) { return erode(matrix.clone()); } return morph(matrix, se, backgroundValue, foregroundValue); } /** * Applies the morphological open operator. * * @param matrix * The matrix to operate on (will be modified). * @return the input matrix */ protected ByteMatrix open(ByteMatrix matrix) { return dilate(erode(matrix)); } /** * Perform a morphological open using the given structuring element * * @param matrix * The matrix to operate on (will not be modified). * @param se * The structuring element * @return A matrix containing the result of the morph. */ protected ByteMatrix open(ByteMatrix matrix, ByteMatrix se) { if (se == null) { return open(matrix.clone()); } return dilate(erode(matrix, se), se); } /** * Applies the morphological open operator. * * @param matrix * The matrix to operate on (will be modified). * @return the input matrix */ protected ByteMatrix close(ByteMatrix matrix) { return erode(dilate(matrix)); } /** * Perform a morphological close using the given structuring element * * @param matrix * The matrix to operate on (will not be modified). * @param se * The structuring element * @return A matrix containing the result of the morph. */ protected ByteMatrix close(ByteMatrix matrix, ByteMatrix se) { if (se == null) { return close(matrix.clone()); } return erode(dilate(matrix, se), se); } /** * Applies the morphological smooth operator. * * @param matrix * The matrix to operate on (will be modified). * @return the input matrix */ protected ByteMatrix smooth(ByteMatrix matrix) { return close(open(matrix)); } /** * Perform a morphological smooth using the given structuring element * * @param matrix * The matrix to operate on (will not be modified). * @param se * The structuring element * @return A matrix containing the result of the morph. */ protected ByteMatrix smooth(ByteMatrix matrix, ByteMatrix se) { if (se == null) { return smooth(matrix.clone()); } return close(open(matrix, se), se); } /** * Applies the morphological edge operator. * * @param matrix * The matrix to operate on (will be modified). * @return the input matrix */ protected ByteMatrix edge(ByteMatrix matrix) { // Dilate dilate(matrix); // Erode to temporary ByteMatrix erosion = erode(matrix.clone()); // Remove eroded mask for (int i = 0; i < matrix.size; i++) { if (erosion.values[i] == foregroundValue) { matrix.values[i] = backgroundValue; } } return matrix; } /** * Perform a morphological edge using the given structuring element * * @param matrix * The matrix to operate on (will not be modified). * @param se * The structuring element * @return A matrix containing the result of the morph. */ protected ByteMatrix edge(ByteMatrix matrix, ByteMatrix se) { if (se == null) { return edge(matrix.clone()); } // Dilate dilate(matrix, se); // Erode to temporary ByteMatrix erosion = erode(matrix.clone(), se); // Remove eroded mask for (int i = 0; i < matrix.size; i++) { if (erosion.values[i] == foregroundValue) { matrix.values[i] = backgroundValue; } } return matrix; } /* * (non-Javadoc) * @see ie.dcu.cdvp.matrix.MatrixProvider#getDefaultMatrixType() */ public Type getDefaultMatrixType() { return Matrix.Type.Byte; } /** * Binary morphological erosion operation. */ public static class Erode extends MorphOp { @Override protected ByteMatrix processImage(ByteMatrix input) { if (structuringElement == null) { return erode(input); } return erode(input, structuringElement); } } /** * Binary morphological dilation operation. */ public static class Dilate extends MorphOp { @Override protected ByteMatrix processImage(ByteMatrix input) { if (structuringElement == null) { return dilate(input); } return dilate(input, structuringElement); } } /** * Binary morphological open operation. */ public static class Open extends MorphOp { @Override protected ByteMatrix processImage(ByteMatrix input) { if (structuringElement == null) { return open(input); } return open(input, structuringElement); } } /** * Binary morphological close operation. */ public static class Close extends MorphOp { @Override protected ByteMatrix processImage(ByteMatrix input) { if (structuringElement == null) { return close(input); } return close(input, structuringElement); } } /** * Binary morphological smooth operation. */ public static class Smooth extends MorphOp { @Override protected ByteMatrix processImage(ByteMatrix input) { if (structuringElement == null) { return smooth(input); } return smooth(input, structuringElement); } } /** * Binary morphological edge operation. */ public static class Edge extends MorphOp { @Override protected ByteMatrix processImage(ByteMatrix input) { if (structuringElement == null) { return edge(input); } return edge(input, structuringElement); } } }