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 SzybkiSzkielet implements IOperator { private static final long serialVersionUID = -4016066669227307063L; private IImageChannel input; private float threshold; private int xSize; private int ySize; private boolean[][] skeletonData; private int[] deletionX; private int[] deletionY; private int[] processingX; private int[] processingY; private int processingIndex; private int newProcessingIndex; public SzybkiSzkielet(IImageChannel input) { this(input, 0.5f); } public SzybkiSzkielet(IImageChannel input, float threshold) { this.input = input; this.threshold = threshold; this.xSize = this.input.getXSize(); this.ySize = this.input.getYSize(); } private boolean makeDecissionOnPairForS(boolean[] p, int i, int j) { return (!p[i])&&(p[j]); } private int calculateS(boolean[] 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, boolean[] p) { if (S == 1) { if (step == 1) { if (!(p[2]&&p[4]&&p[6])) { if (!(p[4]&&p[6]&&p[8])) { return true; } } } else { //Do the same as Step 1 above replacing the conditions (c) and (d) if (!(p[2]&&p[4]&&p[8])) { if (!(p[2]&&p[6]&&p[8])) { return true; } } } } return false; } private boolean makeDecissionForN(boolean[] p) { int N = 0; if (p[2]) { N++; } if (p[3]) { N++; } if (p[4]) { N++; } if (p[5]) { N++; } if (p[6]) { N++; } if (p[7]) { N++; } if (p[8]) { N++; } if (p[9]) { N++; } //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(boolean[] p) { return !(p[2]&&p[3]&&p[4]&&p[5]&&p[6]&&p[7]&&p[8]&&p[9]); } private boolean makeDecissionForPixel(int step, int x, int y, boolean[] p) { if (this.skeletonData[x][y]) { p[2] = this.skeletonData[x][y - 1]; p[3] = this.skeletonData[x + 1][y - 1]; p[4] = this.skeletonData[x + 1][y]; p[5] = this.skeletonData[x + 1][y + 1]; p[6] = this.skeletonData[x][y + 1]; p[7] = this.skeletonData[x - 1][y + 1]; p[8] = this.skeletonData[x - 1][y]; p[9] = this.skeletonData[x - 1][y - 1]; 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, boolean[] p ) { int deletionIndex = 0; /*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)) { this.deletionX[deletionIndex] = x; this.deletionY[deletionIndex] = y; deletionIndex++; } } }*/ this.newProcessingIndex = 0; for (int i = 0; i < this.processingIndex; i++) { int x = this.processingX[i]; int y = this.processingY[i]; if (this.makeDecissionForPixel(step, x, y, p)) { this.deletionX[deletionIndex] = x; this.deletionY[deletionIndex] = y; deletionIndex++; } else { this.processingX[this.newProcessingIndex] = x; this.processingY[this.newProcessingIndex] = y; this.newProcessingIndex++; } } this.processingIndex = this.newProcessingIndex; for (int i = 0; i < deletionIndex; i++) { this.skeletonData[this.deletionX[i]][this.deletionY[i]] = false; } return deletionIndex > 0; } public IImageChannel apply() { this.skeletonData = new boolean[this.xSize][this.ySize]; this.deletionX = new int[this.xSize * this.ySize]; this.deletionY = new int[this.xSize * this.ySize]; this.processingX = new int[this.xSize * this.ySize]; this.processingY = new int[this.xSize * this.ySize]; this.processingIndex = 0; 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; if ((x > 0)&&(y > 0)&&(x < this.xSize - 1)&&(y < this.ySize - 1)) { this.processingX[this.processingIndex] = x; this.processingY[this.processingIndex] = y; this.processingIndex++; } } } } boolean repeat = true; boolean[] p = new boolean[10]; int licznik = 0; while (repeat) { boolean continue1 = this.checkImageForDeletion(1, p); boolean continue2 = this.checkImageForDeletion(2, p); repeat = continue1 || continue2; licznik++; } //System.out.println("Powtorzenia szkieletu: " + licznik); 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; } }