package vesselThickness; import Batch.ComputeStep; import Utils.Utils; import ij.IJ; import ij.ImagePlus; import ij.ImageStack; import ij.Prefs; import ij.gui.GenericDialog; import ij.plugin.filter.PlugInFilter; import ij.process.FloatProcessor; import ij.process.ImageProcessor; import java.util.ArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; public class EDT_S1D implements PlugInFilter { private ImagePlus imp; public byte[][] data; public int w; public int h; public int d; public int thresh; public boolean inverse; ImagePlus impOut; final ThreadPoolExecutor threadPool; public EDT_S1D(ThreadPoolExecutor threadPool) { this.threadPool = threadPool; } public int setup(String arg, ImagePlus imp) { this.imp = imp; return 1; } public void run(ImageProcessor ip) { long start = System.currentTimeMillis(); ImageStack stack = this.imp.getStack(); this.w = stack.getWidth(); this.h = stack.getHeight(); this.d = this.imp.getStackSize(); int nThreads = Runtime.getRuntime().availableProcessors(); this.thresh = 200; this.data = new byte[this.d][]; for(int k = 0; k < this.d; ++k) { this.data[k] = (byte[])stack.getPixels(k + 1); } ImageStack sStack = new ImageStack(this.w, this.h); float[][] s = new float[this.d][]; for(int k = 0; k < this.d; ++k) { ImageProcessor ipk = new FloatProcessor(this.w, this.h); sStack.addSlice(null, ipk); s[k] = (float[])ipk.getPixels(); } float[] sk = null; long s1 = System.currentTimeMillis(); if (!Utils.isReleaseVersion) { System.out.println("EDT transformation 1/3"); } EDT_S1D.Step1Thread[] s1t = new EDT_S1D.Step1Thread[nThreads]; for(int thread = 0; thread < nThreads; ++thread) s1t[thread] = new EDT_S1D.Step1Thread(thread, nThreads, this.w, this.h, this.d, this.thresh, s, this.data); runAndJoinAll(threadPool, s1t, 1); long s2 = System.currentTimeMillis(); if (!Utils.isReleaseVersion) { System.out.println("EDT transformation 2/3"); } //ForkStep2 fs2 = new ForkStep2(); //ArrayList f = fs2.thin(s[0], this.w, this.h); //s[0] = f.get(1); ComputeStep.thin(threadPool, nThreads, s[0], this.w, this.h); long s3 = System.currentTimeMillis(); if (!Utils.isReleaseVersion) { System.out.println("EDT transformation 3/3"); } EDT_S1D.Step3Thread[] s3t = new EDT_S1D.Step3Thread[nThreads]; for(int thread = 0; thread < nThreads; ++thread) s3t[thread] = new EDT_S1D.Step3Thread(thread, nThreads, this.w, this.h, this.d, s, this.data); runAndJoinAll(threadPool, s3t, 3); float distMax = 0.0F; int wh = this.w * this.h; for(int k = 0; k < this.d; ++k) { sk = s[k]; for(int ind = 0; ind < wh; ++ind) { if ((this.data[k][ind] & 255) < this.thresh ^ this.inverse) { sk[ind] = 0.0F; } else { float dist = (float)Math.sqrt((double)sk[ind]); sk[ind] = dist; distMax = dist > distMax ? dist : distMax; } } } String title = this.stripExtension(this.imp.getTitle()); this.impOut = new ImagePlus(title + "EDT", sStack); this.impOut.getProcessor().setMinAndMax(0.0, (double)distMax); long end = System.currentTimeMillis(); } static void runAndJoinAll(ThreadPoolExecutor pool, Runnable[] tasks, int stepNumber) { Future[] futures = new Future[tasks.length]; for (int i = 0; i < tasks.length; i++) futures[i] = pool.submit(tasks[i]); for (int i = 0; i < tasks.length; i++) { try { futures[i].get(); } catch (InterruptedException ex) { System.err.println("A thread was interrupted in step " + stepNumber); } catch (ExecutionException ex) { System.err.println("A thread in step " + stepNumber + " threw " + ex.getCause().getClass().getSimpleName()); } } } public ImagePlus getImageResult() { return this.impOut; } String stripExtension(String name) { if (name != null) { int dotIndex = name.lastIndexOf("."); if (dotIndex >= 0) { name = name.substring(0, dotIndex); } } return name; } boolean getScale() { this.thresh = (int)Prefs.get("edtS1.thresh", 128.0); this.inverse = Prefs.get("edtS1.inverse", false); GenericDialog gd = new GenericDialog("EDT...", IJ.getInstance()); gd.addNumericField("Threshold (1 to 255; value < thresh is background)", (double)this.thresh, 0); gd.addCheckbox("Inverse case (background when value >= thresh)", this.inverse); gd.showDialog(); if (gd.wasCanceled()) { return false; } else { this.thresh = (int)gd.getNextNumber(); this.inverse = gd.getNextBoolean(); Prefs.set("edtS1.thresh", this.thresh); Prefs.set("edtS1.inverse", this.inverse); return true; } } class Step1Thread implements Runnable { int thread; int nThreads; int w; int h; int d; int thresh; float[][] s; byte[][] data; public Step1Thread(int thread, int nThreads, int w, int h, int d, int thresh, float[][] s, byte[][] data) { this.thread = thread; this.nThreads = nThreads; this.w = w; this.h = h; this.d = d; this.thresh = thresh; this.data = data; this.s = s; } @Override public void run() { int n = this.w; if (this.h > n) { n = this.h; } if (this.d > n) { n = this.d; } int noResult = 3 * (n + 1) * (n + 1); boolean[] background = new boolean[n]; for(int k = this.thread; k < this.d; k += this.nThreads) { IJ.showProgress((double)k / (1.0 * (double)this.d)); float[] sk = this.s[k]; byte[] dk = this.data[k]; for(int j = 0; j < this.h; ++j) { for(int i = 0; i < this.w; ++i) { background[i] = (dk[i + this.w * j] & 255) < this.thresh ^ EDT_S1D.this.inverse; } for(int i = 0; i < this.w; ++i) { int min = noResult; for(int x = i; x < this.w; ++x) { if (background[x]) { int test = i - x; test *= test; min = test; break; } } for(int x = i - 1; x >= 0; --x) { if (background[x]) { int test = i - x; test *= test; if (test < min) { min = test; } break; } } sk[i + this.w * j] = (float)min; } } } } } class Step3Thread implements Runnable { int thread; int nThreads; int w; int h; int d; float[][] s; byte[][] data; public Step3Thread(int thread, int nThreads, int w, int h, int d, float[][] s, byte[][] data) { this.thread = thread; this.nThreads = nThreads; this.w = w; this.h = h; this.d = d; this.s = s; this.data = data; } @Override public void run() { int n = this.w; if (this.h > n) { n = this.h; } if (this.d > n) { n = this.d; } int noResult = 3 * (n + 1) * (n + 1); int[] tempInt = new int[n]; int[] tempS = new int[n]; for(int j = this.thread; j < this.h; j += this.nThreads) { IJ.showProgress((double)j / (1.0 * (double)this.h)); for(int i = 0; i < this.w; ++i) { boolean nonempty = false; for(int k = 0; k < this.d; ++k) { tempS[k] = (int)this.s[k][i + this.w * j]; if (tempS[k] > 0) { nonempty = true; } } if (nonempty) { int zStart = 0; while(zStart < this.d - 1 && tempS[zStart] == 0) { ++zStart; } if (zStart > 0) { --zStart; } int zStop = this.d - 1; while(zStop > 0 && tempS[zStop] == 0) { --zStop; } if (zStop < this.d - 1) { ++zStop; } for(int k = 0; k < this.d; ++k) { if ((this.data[k][i + this.w * j] & 255) >= EDT_S1D.this.thresh ^ EDT_S1D.this.inverse) { int min = noResult; int zBegin = zStart; int zEnd = zStop; if (zStart > k) { zBegin = k; } if (zStop < k) { zEnd = k; } int delta = k - zBegin; for(int z = zBegin; z <= zEnd; ++z) { int test = tempS[z] + delta * delta--; if (test < min) { min = test; } } tempInt[k] = min; } } for(int k = 0; k < this.d; ++k) { this.s[k][i + this.w * j] = (float)tempInt[k]; } } } } } } }