/** * Saito-Toriwaki algorithm for Euclidian Distance Transformation. Direct application of Algorithm * 1. Bob Dougherty 8/8/2006 * * <ul> * <li>Version S1A: lower memory usage. * <li>Version S1A.1 A fixed indexing bug for 666-bin data set * <li>Version S1A.2 Aug. 9, 2006. Changed noResult value. * <li>Version S1B Aug. 9, 2006. Faster. * <li>Version S1B.1 Sept. 6, 2006. Changed comments. * <li>Version S1C Oct. 1, 2006. Option for inverse case. <br> * Fixed inverse behavior in y and z directions. * <li>Version D July 30, 2007. Multithread processing for step 2. * </ul> * * <p>This version assumes the input stack is already in memory, 8-bit, and outputs to a new * 32-bit stack. Versions that are more stingy with memory may be forthcoming. * * @param imp 8-bit (binary) ImagePlus */ private float[][] geometryToDistanceMap(ImagePlus imp, boolean inv) { final int w = imp.getWidth(); final int h = imp.getHeight(); final int d = imp.getStackSize(); int nThreads = Runtime.getRuntime().availableProcessors(); // Create references to input data ImageStack stack = imp.getStack(); byte[][] data = new byte[d][]; for (int k = 0; k < d; k++) data[k] = (byte[]) stack.getPixels(k + 1); // Create 32 bit floating point stack for output, s. Will also use it // for g in Transformation 1. float[][] s = new float[d][]; for (int k = 0; k < d; k++) { ImageProcessor ipk = new FloatProcessor(w, h); s[k] = (float[]) ipk.getPixels(); } float[] sk; // Transformation 1. Use s to store g. IJ.showStatus("EDT transformation 1/3"); Step1Thread[] s1t = new Step1Thread[nThreads]; for (int thread = 0; thread < nThreads; thread++) { s1t[thread] = new Step1Thread(thread, nThreads, w, h, d, inv, s, data); s1t[thread].start(); } try { for (int thread = 0; thread < nThreads; thread++) { s1t[thread].join(); } } catch (InterruptedException ie) { IJ.error("A thread was interrupted in step 1 ."); } // Transformation 2. g (in s) -> h (in s) IJ.showStatus("EDT transformation 2/3"); Step2Thread[] s2t = new Step2Thread[nThreads]; for (int thread = 0; thread < nThreads; thread++) { s2t[thread] = new Step2Thread(thread, nThreads, w, h, d, s); s2t[thread].start(); } try { for (int thread = 0; thread < nThreads; thread++) { s2t[thread].join(); } } catch (InterruptedException ie) { IJ.error("A thread was interrupted in step 2 ."); } // Transformation 3. h (in s) -> s IJ.showStatus("EDT transformation 3/3"); Step3Thread[] s3t = new Step3Thread[nThreads]; for (int thread = 0; thread < nThreads; thread++) { s3t[thread] = new Step3Thread(thread, nThreads, w, h, d, inv, s, data); s3t[thread].start(); } try { for (int thread = 0; thread < nThreads; thread++) { s3t[thread].join(); } } catch (InterruptedException ie) { IJ.error("A thread was interrupted in step 3 ."); } // Find the largest distance for scaling // Also fill in the background values. float distMax = 0; final int wh = w * h; float dist; for (int k = 0; k < d; k++) { sk = s[k]; for (int ind = 0; ind < wh; ind++) { if (((data[k][ind] & 255) < 128) ^ inv) { sk[ind] = 0; } else { dist = (float) Math.sqrt(sk[ind]); sk[ind] = dist; distMax = (dist > distMax) ? dist : distMax; } } } IJ.showProgress(1.0); IJ.showStatus("Done"); return s; }
/** * DistanceRidgetoLocalThickness * * <p>Input: Distance Ridge (32-bit stack) (Output from Distance Ridge.java) Output: Local * Thickness. Overwrites the input. * * <ul> * <li>Version 1: September 6, 2006. * <li>Version 2: September 25, 2006. Fixed several bugs that resulted in non-symmetrical output * from symmetrical input. * <li>Version 2.1 Oct. 1, 2006. Fixed a rounding error that caused some points to be missed. * <li>Version 3 July 31, 2007. Parallel processing version. * <li>Version 3.1 Multiplies the output by 2 to conform with the definition of local thickness * </ul> * * @param imp */ private void distanceRidgetoLocalThickness(ImagePlus imp, float[][] s) { final int w = imp.getWidth(); final int h = imp.getHeight(); final int d = imp.getStackSize(); float[] sk; // Count the distance ridge points on each slice int[] nRidge = new int[d]; int ind, nr, iR; IJ.showStatus("Local Thickness: scanning stack "); for (int k = 0; k < d; k++) { sk = s[k]; nr = 0; for (int j = 0; j < h; j++) { final int wj = w * j; for (int i = 0; i < w; i++) { ind = i + wj; if (sk[ind] > 0) nr++; } } nRidge[k] = nr; } int[][] iRidge = new int[d][]; int[][] jRidge = new int[d][]; float[][] rRidge = new float[d][]; // Pull out the distance ridge points int[] iRidgeK, jRidgeK; float[] rRidgeK; float sMax = 0; for (int k = 0; k < d; k++) { nr = nRidge[k]; iRidge[k] = new int[nr]; jRidge[k] = new int[nr]; rRidge[k] = new float[nr]; sk = s[k]; iRidgeK = iRidge[k]; jRidgeK = jRidge[k]; rRidgeK = rRidge[k]; iR = 0; for (int j = 0; j < h; j++) { final int wj = w * j; for (int i = 0; i < w; i++) { ind = i + wj; if (sk[ind] > 0) {; iRidgeK[iR] = i; jRidgeK[iR] = j; rRidgeK[iR++] = sk[ind]; if (sk[ind] > sMax) sMax = sk[ind]; sk[ind] = 0; } } } } int nThreads = Runtime.getRuntime().availableProcessors(); final Object[] resources = new Object[d]; // For synchronization for (int k = 0; k < d; k++) { resources[k] = new Object(); } LTThread[] ltt = new LTThread[nThreads]; for (int thread = 0; thread < nThreads; thread++) { ltt[thread] = new LTThread(thread, nThreads, w, h, d, nRidge, s, iRidge, jRidge, rRidge, resources); ltt[thread].start(); } try { for (int thread = 0; thread < nThreads; thread++) { ltt[thread].join(); } } catch (InterruptedException ie) { IJ.error("A thread was interrupted ."); } // Fix the square values and apply factor of 2 IJ.showStatus("Local Thickness: square root "); for (int k = 0; k < d; k++) { sk = s[k]; for (int j = 0; j < h; j++) { final int wj = w * j; for (int i = 0; i < w; i++) { ind = i + wj; sk[ind] = (float) (2 * Math.sqrt(sk[ind])); } } } IJ.showStatus("Local Thickness complete"); return; }
/** * Create a Thread[] array as large as the number of processors available. From Stephan * Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.java;hb=HEAD */ private Thread[] newThreadArray() { int n_cpus = Runtime.getRuntime().availableProcessors(); return new Thread[n_cpus]; }