package dokumenty; import pl.wroc.pwr.IOperator; import pl.wroc.pwr.imagechannel.IImageChannel; import pl.wroc.pwr.imagechannel.basic.CopyChannel; import pl.wroc.pwr.imagechannel.model.ImageChannel; /** * Thinning is a morphological operation that is used to remove selected foreground pixels from binary * images, somewhat like erosion or opening. It can be used for several applications, but is * particularly useful for skeletonization. In this mode it is commonly used to tidy up the output of * edge detectors by reducing all lines to single pixel thickness. Thinning is normally only applied * to binary images, and produces another binary image as output. The thinning operation is related to * the hit-and-miss transform, and so it is helpful to have an understanding of that operator before * reading on. * * http://homepages.inf.ed.ac.uk/rbf/HIPR2/thin.htm * * Implemented according to Biomedical Image Analysis, page 548-549 */ public class SzybkiSzkieletTest implements IOperator { private static final long serialVersionUID = -4016066669227307063L; private IImageChannel input; private float threshold; private int xSize; private int ySize; private boolean[][] deletionData; private boolean[][] skeletonData; public SzybkiSzkieletTest(IImageChannel input) { this(input, 0.5f); } public SzybkiSzkieletTest(IImageChannel input, float threshold) { this.input = input; this.threshold = threshold; this.xSize = this.input.getXSize(); this.ySize = this.input.getYSize(); } private boolean makeDecissionOnPairForS(int [] p, int i, int j) { return (p[i] == 0)&&(p[j] == 1); } private int calculateS(int [] p) { int S = 0; if (makeDecissionOnPairForS(p, 2, 3)) S++; if (makeDecissionOnPairForS(p, 3, 4)) S++; if (makeDecissionOnPairForS(p, 4, 5)) S++; if (makeDecissionOnPairForS(p, 5, 6)) S++; if (makeDecissionOnPairForS(p, 6, 7)) S++; if (makeDecissionOnPairForS(p, 7, 8)) S++; if (makeDecissionOnPairForS(p, 8, 9)) S++; if (makeDecissionOnPairForS(p, 9, 2)) S++; return S; } private boolean makeDecissionForS(int step, int S, int [] p) { if (S == 1) { if (step == 1) { if (p[2] * p[4] * p[6] == 0) { if (p[4] * p[6] * p[8] == 0) { return true; } } } else { //Do the same as Step 1 above replacing the conditions (c) and (d) if (p[2] * p[4] * p[8] == 0) { if (p[2] * p[6] * p[8] == 0) { return true; } } } } return false; } private boolean makeDecissionForN(int [] p) { int N = p[2] + p[3] + p[4] + p[5] + p[6] + p[7] + p[8] + p[9]; return (N >= 2)&&(N <= 6); } private boolean makeDecissionForP(int [] p) { return (p[2] == 0)||(p[3] == 0)||(p[4] == 0)||(p[5] == 0)|| (p[6] == 0)||(p[7] == 0)||(p[8] == 0)||(p[9] == 0); } private boolean makeDecissionForPixel(int step, int x, int y, int[] p) { if (this.skeletonData[x][y]) { p[2] = this.skeletonData[x][y - 1] ? 1 : 0; p[3] = this.skeletonData[x + 1][y - 1] ? 1 : 0; p[4] = this.skeletonData[x + 1][y] ? 1 : 0; p[5] = this.skeletonData[x + 1][y + 1] ? 1 : 0; p[6] = this.skeletonData[x][y + 1] ? 1 : 0; p[7] = this.skeletonData[x - 1][y + 1] ? 1 : 0; p[8] = this.skeletonData[x - 1][y] ? 1 : 0; p[9] = this.skeletonData[x - 1][y - 1] ? 1 : 0; if (this.makeDecissionForP(p)) { if (this.makeDecissionForN(p)) { int S = this.calculateS(p); return this.makeDecissionForS(step, S, p); } } } return false; } private boolean checkImageForDeletion(int step, int[] p ) { boolean repetition = false; for (int x = 1; x < this.xSize - 1; x++) { for (int y = 1; y < this.ySize - 1; y++) { if (this.makeDecissionForPixel(step, x, y, p)) { repetition = true; this.deletionData[x][y] = true; } } } if (repetition) { for (int x = 1; x < this.xSize - 1; x++) { for (int y = 1; y < this.ySize - 1; y++) { if (this.deletionData[x][y]) { this.skeletonData[x][y] = false; } } } } return repetition; } public IImageChannel apply() { this.deletionData = new boolean[this.xSize][this.ySize]; this.skeletonData = new boolean[this.xSize][this.ySize]; for (int x = 0; x < this.xSize; x++) { for (int y = 0; y < this.ySize; y++) { if (this.input.getValue(x, y) > this.threshold) { this.skeletonData[x][y] = true; } } } boolean repeat = true; int[] p = new int[10]; while (repeat) { boolean continue1 = this.checkImageForDeletion(1, p); boolean continue2 = this.checkImageForDeletion(2, p); repeat = continue1 || continue2; } IImageChannel result = new ImageChannel(this.input.getSize()); for (int x = 0; x < this.xSize; x++) { for (int y = 0; y < this.ySize; y++) { if (this.skeletonData[x][y]) { result.setValue(x, y, this.input.getValue(x, y)); } } } return result; } }