DOWNLOAD
package Algorithms; import Utils.*;import java.util.Arrays; public class AnalyzeSkeleton2{ public static final int NONE = 0; public static final int END_POINT = 1; public static final int JUNCTION = 2; public static final int SLAB = 3; public static final int MAX_BREADTH = 8; public static final int JUNC_VISIT = 16; public static final int SKEL_VISIT = 24; public static class Result { public IntVector[] pointVectors; public IntVector endPoints; public IntVector junctionVoxels; public IntVector singleJunctions; public IntVector slabVoxels; public IntVector startingSlabVoxels; public IntVector toRevisit; public IntVector isolatedJunctions; public IntVector removedJunctions; public IntVector slabList; public IntVector edgesTrees; public IntVector edgesVerts; public IntVector edgesPoints; public DoubleVector edgesLengths; public int treeCount; //public IntVector triplePointCounts; //public IntVector quadruplePointCounts; public DoubleVector totalBranchLengths; public DoubleVector maximumBranchLengths; public IntVector numberOfBranches; public IntVector numberOfSlabs; private static IntVector resetIntVector(IntVector vec) { if (vec == null) return new IntVector(); vec.size = 0; return vec; } private static DoubleVector resetDoubleVector(DoubleVector vec) { if (vec == null) return new DoubleVector(); vec.size = 0; return vec; } public void reset() { if (pointVectors == null) pointVectors = new IntVector[3]; endPoints = resetIntVector(endPoints); junctionVoxels = resetIntVector(junctionVoxels); singleJunctions = resetIntVector(singleJunctions); slabVoxels = resetIntVector(slabVoxels); startingSlabVoxels = resetIntVector(startingSlabVoxels); toRevisit = resetIntVector(toRevisit); isolatedJunctions = resetIntVector(isolatedJunctions); removedJunctions = resetIntVector(removedJunctions); slabList = resetIntVector(slabList); edgesTrees = resetIntVector(edgesTrees); edgesVerts = resetIntVector(edgesVerts); edgesPoints = resetIntVector(edgesPoints); edgesLengths = resetDoubleVector(edgesLengths); treeCount = 0; //triplePointCounts = resetIntVector(triplePointCounts); //quadruplePointCounts = resetIntVector(quadruplePointCounts); totalBranchLengths = resetDoubleVector(totalBranchLengths); maximumBranchLengths = resetDoubleVector(maximumBranchLengths); numberOfBranches = resetIntVector(numberOfBranches); numberOfSlabs = resetIntVector(numberOfSlabs); } } private static class Scratch { public int width; public int height; public int breadth; public int[] imageInfo; public int[] junctionMap2d; public int[] markedImages; // volume public int[] endPointVertexMap; // volume public int[] junctionVertexMap; // volume public void acquire(int width, int height, int breadth) { this.width = width; this.height = height; this.breadth = breadth; int area = width * height; this.imageInfo = IntBufferPool.acquireAsIs(area); this.junctionMap2d = IntBufferPool.acquireZeroed(area); this.markedImages = IntBufferPool.acquireZeroed(area * breadth); this.endPointVertexMap = IntBufferPool.acquireZeroed(area * breadth); this.junctionVertexMap = IntBufferPool.acquireZeroed(area * breadth); } public void release() { this.imageInfo = IntBufferPool.release(imageInfo); this.junctionMap2d = IntBufferPool.release(junctionMap2d); this.markedImages = IntBufferPool.release(markedImages); this.endPointVertexMap = IntBufferPool.release(endPointVertexMap); this.junctionVertexMap = IntBufferPool.release(junctionVertexMap); } } // returns a 26-bit number containing each neighbor within a 3x3x3 vicinity (-1 to exclude the point itself) public static int getBooleanNeighborBits(byte[] planes, int width, int height, int breadth, int x, int y, int z) { int bits = 0; for (int i = 0; i < 27; i++) { if (i == 13) continue; int xx = x + (i % 3) - 1; int yy = y + ((i / 3) % 3) - 1; int zz = z + (i / 9) - 1; int p = 0; if (xx >= 0 && xx < width && yy >= 0 && yy < height && zz >= 0 && zz < breadth) p = (planes[xx + width * yy] >> zz) & 1; bits = (bits << 1) | p; } return bits; } public static void analyze( Result result, byte[] skeletonImages, int width, int height, int breadth ) { result.reset(); breadth = Math.min(Math.max(1, breadth), MAX_BREADTH); Scratch scratch = new Scratch(); scratch.acquire(width, height, breadth); tagImages(scratch, result, skeletonImages, width, height, breadth); int nTrees = markTreesOnImages(scratch, result, skeletonImages, width, height, breadth); if (nTrees <= 0) nTrees = 1; result.treeCount = nTrees; //resizeAndZeroIntVector(result.triplePointCounts, nTrees); //resizeAndZeroIntVector(result.quadruplePointCounts, nTrees); groupJunctions(scratch, result, skeletonImages, width, height, breadth, nTrees); resizeAndZeroDoubleVector(result.totalBranchLengths, nTrees); resizeAndZeroDoubleVector(result.maximumBranchLengths, nTrees); resizeAndZeroIntVector(result.numberOfBranches, nTrees); resizeAndZeroIntVector(result.numberOfSlabs, nTrees); buildSkeletonGraphs(scratch, result, skeletonImages, width, height, breadth, nTrees); isolateDominantJunctions(scratch, result, width, height); scratch.release(); } static void tagImages( Scratch scratch, Result result, byte[] skeletonImages, int width, int height, int breadth ) { result.pointVectors[END_POINT-1] = result.endPoints; result.pointVectors[JUNCTION-1] = result.junctionVoxels; result.pointVectors[SLAB-1] = result.slabVoxels; int area = width * height; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { int idx = x + width * y; int value = 0; for (int z = 0; z < breadth; ++z) { int type = NONE; if (((skeletonImages[idx] >>> z) & 1) != 0) { int numOfNeighbors = Integer.bitCount( getBooleanNeighborBits(skeletonImages, width, height, breadth, x, y, z) ); if (numOfNeighbors < 2) { type = END_POINT; int pos = result.pointVectors[type-1].addThree(x, y, z); scratch.endPointVertexMap[idx + area * z] = pos; } else if (numOfNeighbors > 2) { type = JUNCTION; int pos = result.pointVectors[type-1].addThree(x, y, z); scratch.junctionMap2d[idx] = pos + 1; } else { type = SLAB; result.pointVectors[type-1].addThree(x, y, z); } } value = (value << 2) | type; } scratch.imageInfo[idx] = value; } } } static int markTreesOnImages( Scratch scratch, Result result, byte[] skeletonImages, int width, int height, int breadth ) { final int area = width * height; int nTrees = 0; for (int type = END_POINT; type <= SLAB; type++) { int n = result.pointVectors[type-1].size; for (int i = 0; i < n; i += 3) { int x = result.pointVectors[type-1].buf[i]; int y = result.pointVectors[type-1].buf[i+1]; int z = result.pointVectors[type-1].buf[i+2]; if (scratch.markedImages[x + width * y + area * z] != 0) continue; if (type == SLAB) result.startingSlabVoxels.addThree(x, y, z); int numOfVoxels = 0; scratch.markedImages[x + width * y + area * z] = nTrees + 1; result.toRevisit.size = 0; if (((scratch.imageInfo[x + width * y] >>> (z << 1)) & 3) == JUNCTION) result.toRevisit.addThree(x, y, z); int revisitIdx = 0; boolean wasRevisit = false; boolean didFindPoint; do { didFindPoint = false; for (int j = 0; j < 27; j++) { if (j == 13) continue; int xx = x + (j / 9) - 1; int yy = y + ((j / 3) % 3) - 1; int zz = z + (j % 3) - 1; if (xx < 0 || xx >= width || yy < 0 || yy >= height || zz < 0 || zz >= breadth) continue; if ( ((skeletonImages[xx + width * yy] >>> zz) & 1) != 0 && scratch.markedImages[xx + width * yy + area * zz] == 0 ) { x = xx; y = yy; z = zz; didFindPoint = true; break; } } if (didFindPoint) { ++numOfVoxels; scratch.markedImages[x + width * y + area * z] = nTrees + 1; if (((scratch.imageInfo[x + width * y] >>> (z << 1)) & 3) == JUNCTION) result.toRevisit.addThree(x, y, z); } else { if (wasRevisit) revisitIdx += 3; if (revisitIdx < result.toRevisit.size) { x = result.toRevisit.buf[revisitIdx]; y = result.toRevisit.buf[revisitIdx+1]; z = result.toRevisit.buf[revisitIdx+2]; } } wasRevisit = !didFindPoint; } while (didFindPoint || revisitIdx < result.toRevisit.size); if (type == END_POINT || numOfVoxels != 0) nTrees++; } } return nTrees; } static void groupJunctions( Scratch scratch, Result result, byte[] skeletonImages, int width, int height, int breadth, int nTrees ) { final int area = width * height; int vertexCount = 0; for (int i = 0; i < result.junctionVoxels.size; i += 3) { int x = result.junctionVoxels.buf[i]; int y = result.junctionVoxels.buf[i+1]; int z = result.junctionVoxels.buf[i+2]; if (((scratch.imageInfo[x + width * y] >>> (JUNC_VISIT + z)) & 1) != 0) continue; scratch.imageInfo[x + width * y] |= 1 << (JUNC_VISIT + z); int treeIdx = scratch.markedImages[x + width * y + area * z] - 1; vertexCount++; scratch.junctionVertexMap[x + width * y + area * z] = vertexCount; result.singleJunctions.addThree(x, y, z); result.toRevisit.size = 0; result.toRevisit.addThree(x, y, z); int revisitIdx = 0; boolean wasRevisit = false; int pointFound; do { pointFound = -1; int nBranches = 0; for (int j = 0; j < 27; j++) { if (j == 13) continue; int xx = x + (j / 9) - 1; int yy = y + ((j / 3) % 3) - 1; int zz = z + (j % 3) - 1; if (xx < 0 || xx >= width || yy < 0 || yy >= height || zz < 0 || zz >= breadth) continue; int type = (scratch.imageInfo[xx + width * yy] >>> (zz << 1)) & 3; if ( pointFound == -1 && ((skeletonImages[xx + width * yy] >>> zz) & 1) != 0 && ((scratch.imageInfo[xx + width * yy] >>> (JUNC_VISIT + zz)) & 1) == 0 && type == JUNCTION ) { pointFound = j; } if (type == END_POINT || type == SLAB) nBranches++; } if (pointFound >= 0) { x += (pointFound / 9) - 1; y += ((pointFound / 3) % 3) - 1; z += (pointFound % 3) - 1; scratch.imageInfo[x + width * y] |= 1 << (JUNC_VISIT + z); scratch.junctionVertexMap[x + width * y + area * z] = vertexCount; result.singleJunctions.addThree(x, y, z); result.toRevisit.addThree(x, y, z); /* if (nBranches == 3) result.triplePointCounts.buf[treeIdx]++; else if (nBranches == 4) result.quadruplePointCounts.buf[treeIdx]++; */ } else { if (wasRevisit) revisitIdx += 3; if (revisitIdx < result.toRevisit.size) { x = result.toRevisit.buf[revisitIdx]; y = result.toRevisit.buf[revisitIdx+1]; z = result.toRevisit.buf[revisitIdx+2]; } } wasRevisit = pointFound == -1; } while (pointFound >= 0 || revisitIdx < result.toRevisit.size); } } static void buildSkeletonGraphs( Scratch scratch, Result result, byte[] skeletonImages, int width, int height, int breadth, int nTrees ) { int area = width * height; for (int i = 0; i < result.endPoints.size; i += 3) { int x = result.endPoints.buf[i]; int y = result.endPoints.buf[i+1]; int z = result.endPoints.buf[i+2]; int t = scratch.markedImages[x + width * y + area * z] - 1; if (((scratch.imageInfo[x + width * y] >>> (SKEL_VISIT + z)) & 1) != 0) continue; scratch.imageInfo[x + width * y] |= 1 << (SKEL_VISIT + z); int slabListIdx = result.slabList.size; visitBranch( scratch, result, skeletonImages, width, height, breadth, t, END_POINT, i, slabListIdx, 0.0, x, y, z ); } for (int i = 0; i < result.singleJunctions.size; i += 3) { int x = result.singleJunctions.buf[i]; int y = result.singleJunctions.buf[i+1]; int z = result.singleJunctions.buf[i+2]; int t = scratch.markedImages[x + width * y + area * z] - 1; // no check scratch.imageInfo[x + width * y] |= 1 << (SKEL_VISIT + z); int vertexIdx = scratch.junctionVertexMap[x + width * y + area * z] - 1; for (int j = 0; j < 27; j++) { if (j == 13) continue; int xx = x + (j / 9) - 1; int yy = y + ((j / 3) % 3) - 1; int zz = z + (j % 3) - 1; if (xx < 0 || xx >= width || yy < 0 || yy >= height || zz < 0 || zz >= breadth) continue; if ( ((skeletonImages[xx + width * yy] >>> zz) & 1) != 0 && ((scratch.imageInfo[xx + width * yy] >>> (SKEL_VISIT + zz)) & 1) == 0 ) { //didFindPoint = true; if (((scratch.imageInfo[xx + width * yy] >>> (zz << 1)) & 3) == JUNCTION) { scratch.imageInfo[xx + width * yy] |= 1 << (SKEL_VISIT + zz); continue; } double initialLength = calculateDistance(x, y, z, xx, yy, zz); int slabListIdx = result.slabList.addThree(xx, yy, zz); visitBranch( scratch, result, skeletonImages, width, height, breadth, t, JUNCTION, vertexIdx, slabListIdx, initialLength, xx, yy, zz ); } } } int startingSlabStart = 0; while (startingSlabStart + 2 < result.startingSlabVoxels.size) { int s = startingSlabStart; int x = result.startingSlabVoxels.buf[s]; int y = result.startingSlabVoxels.buf[s+1]; int z = result.startingSlabVoxels.buf[s+2]; int t = scratch.markedImages[x + width * y + area * z] - 1; boolean isSingle = true; startingSlabStart += 3; while (startingSlabStart + 2 < result.startingSlabVoxels.size) { int xx = result.startingSlabVoxels.buf[startingSlabStart]; int yy = result.startingSlabVoxels.buf[startingSlabStart+1]; int zz = result.startingSlabVoxels.buf[startingSlabStart+2]; if (scratch.markedImages[xx + width * yy + area * zz] != t + 1) break; isSingle = false; startingSlabStart += 3; } if (isSingle) { //System.out.println("slab visit on tree " + (t+1) + " / " + nTrees); result.numberOfSlabs.buf[t]++; int slabListIdx = result.slabList.addThree(x, y, z); visitBranch( scratch, result, skeletonImages, width, height, breadth, t, SLAB, s, slabListIdx, 0.0, x, y, z ); } } } static void visitBranch( Scratch scratch, Result result, byte[] skeletonImages, int width, int height, int breadth, int iTree, int mode, int initialVertIdx, int initialSlabListIdx, double initialLength, int xStart, int yStart, int zStart ) { final int area = width * height; double length = initialLength; int type = NONE; int finalVertIdx = initialVertIdx; int x = xStart; int y = yStart; int z = zStart; scratch.imageInfo[x + width * y] |= 1 << (SKEL_VISIT + z); boolean didFindSlabPoint; do { didFindSlabPoint = false; for (int j = 0; j < 27; j++) { if (j == 13) continue; int xx = x + (j / 9) - 1; int yy = y + ((j / 3) % 3) - 1; int zz = z + (j % 3) - 1; if (xx < 0 || xx >= width || yy < 0 || yy >= height || zz < 0 || zz >= breadth) continue; if ( ((skeletonImages[xx + width * yy] >>> zz) & 1) != 0 && ((scratch.imageInfo[xx + width * yy] >>> (SKEL_VISIT + zz)) & 1) == 0 ) { length += calculateDistance(x, y, z, xx, yy, zz); scratch.imageInfo[xx + width * yy] |= 1 << (SKEL_VISIT + zz); type = (scratch.imageInfo[xx + width * yy] >>> (zz << 1)) & 3; x = xx; y = yy; z = zz; switch (type) { case END_POINT: finalVertIdx = scratch.endPointVertexMap[x + width * y + area * z] - 1; break; case JUNCTION: finalVertIdx = scratch.junctionVertexMap[x + width * y + area * z] - 1; //this.auxFinalVertex = this.findPointVertex(this.junctionVertex[iTree], nextPoint); break; case SLAB: result.numberOfSlabs.buf[iTree]++; result.slabList.addThree(x, y, z); didFindSlabPoint = true; break; } break; } } } while (didFindSlabPoint); double lengthBeforeSlabs = length; int modeEnd = mode; if (mode == SLAB) { finalVertIdx = initialVertIdx; } else if (type == SLAB) { finalVertIdx = initialVertIdx; int xExclude = -1; int yExclude = -1; int zExclude = -1; int vertExclude = -1; if (mode == END_POINT) { xExclude = xStart; yExclude = yStart; zExclude = zStart; } else { vertExclude = initialVertIdx; } for (int j = 0; j < 27; j++) { if (j == 13) continue; int xx = x + (j / 9) - 1; int yy = y + ((j / 3) % 3) - 1; int zz = z + (j % 3) - 1; if (xx < 0 || xx >= width || yy < 0 || yy >= height || zz < 0 || zz >= breadth) continue; int info = scratch.imageInfo[xx + width * yy]; int vert = scratch.junctionVertexMap[xx + width * yy + area * zz]; if ( ((skeletonImages[xx + width * yy] >>> zz) & 1) != 0 && ((info >>> (SKEL_VISIT + zz)) & 1) != 0 && ((info >>> (zz << 1)) & 3) == JUNCTION && vert != vertExclude && !(xx == xExclude && yy == yExclude && zz == zExclude) ) { finalVertIdx = vert; modeEnd = JUNCTION; length += calculateDistance(x, y, z, xx, yy, zz); break; } } } result.totalBranchLengths.buf[iTree] += mode == JUNCTION ? lengthBeforeSlabs : length; if (length > result.maximumBranchLengths.buf[iTree]) result.maximumBranchLengths.buf[iTree] = length; result.numberOfBranches.buf[iTree]++; int modeSides = (mode << 2) | modeEnd; int nSlabs = result.slabList.size - initialSlabListIdx; result.edgesTrees.add(iTree); result.edgesVerts.addThree(modeSides, initialVertIdx, finalVertIdx); result.edgesPoints.addTwo(initialSlabListIdx, nSlabs); result.edgesLengths.add(length); } static void isolateDominantJunctions(Scratch scratch, Result result, int width, int height) { for (int i = 0; i < result.junctionVoxels.size; i += 3) { int x = result.junctionVoxels.buf[i]; int y = result.junctionVoxels.buf[i+1]; int pos = scratch.junctionMap2d[x + width * y]; if (pos <= 0) continue; result.isolatedJunctions.add(pos - 1); scratch.junctionMap2d[x + width * y] = 0; for (int j = 0; j < 9; j++) { if (j == 4) continue; int xx = x + (j % 3) - 1; int yy = y + (j / 3) - 1; int p = scratch.junctionMap2d[xx + width * yy]; if (p > 0) { result.removedJunctions.add(p - 1); scratch.junctionMap2d[xx + width * yy] = 0; } } } } static double calculateDistance(int x1, int y1, int z1, int x2, int y2, int z2) { double dx = (double)(x2 - x1); double dy = (double)(y2 - y1); double dz = (double)(z2 - z1); return Math.sqrt((dx * dx) + (dy * dy) + (dz * dz)); } static void resizeAndZeroDoubleVector(DoubleVector vec, int newSize) { vec.resize(newSize); if (newSize > 0) Arrays.fill(vec.buf, 0, newSize, 0.0); } static void resizeAndZeroIntVector(IntVector vec, int newSize) { vec.resize(newSize); if (newSize > 0) Arrays.fill(vec.buf, 0, newSize, 0); }}