package cz.cuni.lf1.lge.ThunderSTORM.rendering; import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.Molecule; import ij.ImagePlus; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * This class processes rendering tasks serially. It consists of a queue and a * thread that takes tasks out of the queue and processes them. Can perform a * user defined task after every [frequency] invocations. */ public class RenderingQueue { private ThreadPoolExecutor executor = null; Runnable repaintTask; public IncrementalRenderingMethod method; int taskCounter = 0; int repaintFrequency; /** * Example: new RenderingQueue(method, new Runnable(){public void * run(){image.repaint();}},10) will call image.repaint() every 10 tasks * executed * * @param method rendering method * @param repaintTask Runnable, which will be called after executing every * repaintFrequency tasks. Or null if no periodic action should be run. * @param repaintFrequency specifies how often to call repaintTask.run(). */ public RenderingQueue(IncrementalRenderingMethod method, Runnable repaintTask, int repaintFrequency) { if(method != null && repaintFrequency > 0) { this.repaintTask = repaintTask; this.method = method; this.repaintFrequency = repaintFrequency; //executor with one thread, that will die after not being used for 30 seconds executor = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); executor.allowCoreThreadTimeOut(true); } } public void renderLater(List<Molecule> fits) { if(executor != null) { executor.execute(new RenTask(fits)); } } public void repaintLater() { if(executor != null) { executor.execute(repaintTask); } } public void invokeLater(Runnable r) { if(executor != null) { executor.execute(r); } } public void resetLater() { if(executor != null) { executor.execute(new Runnable() { @Override public void run() { method.reset(); } }); } } public void shutdown() { if(executor != null) { executor.shutdown(); } } class RenTask implements Runnable { List<Molecule> fits; public RenTask(List<Molecule> fits) { this.fits = fits; } @Override public void run() { method.addToImage(fits); taskCounter++; // no need to use atomic counter because this method will be run only from single threaded executor if(taskCounter % repaintFrequency == 0) { repaintTask.run(); } } } public static class DefaultRepaintTask implements Runnable { ImagePlus renderedImage; public DefaultRepaintTask(ImagePlus renderedImage) { this.renderedImage = renderedImage; } @Override public void run() { renderedImage.show(); if(renderedImage.isVisible()) { double upperRange = findQuantileHisto(renderedImage, 0.99); //IJ.log("upper image range: " + upperRange); renderedImage.setDisplayRange(0, upperRange); renderedImage.updateAndDraw(); } } /* private static Object [] getFloatImageArray(ImagePlus imp) { ImageStack stack = imp.getStack(); ImageStack f_stack = new ImageStack(imp.getWidth(), imp.getHeight()); for(int z = 0, zm = stack.getSize(); z < zm; z++) { f_stack.addSlice(null, stack.getProcessor(z+1).convertToFloat()); } return new ImagePlus(null, f_stack).getStack().getImageArray(); } */ private static double findMaxStackValue(ImagePlus imp) { Object[] stack = imp.getStack().getImageArray();//getFloatImageArray(imp); double max = 0; for(int i = 0; i < stack.length; i++) { float[] pixels = (float[]) stack[i]; if(pixels != null) { for(int j = 0; j < pixels.length; j++) { double val = pixels[j]; if(val > max) { max = val; } } } } return max; } private static double findQuantileHisto(ImagePlus imp, double quantile) { assert imp.getType() == ImagePlus.GRAY32; assert quantile > 0 && quantile < 1; double max = findMaxStackValue(imp); int nBins = 1000; double binSize = max / nBins; int[] binCounts = new int[nBins + 1]; Object[] stack = imp.getStack().getImageArray();//getFloatImageArray(imp); int totalNonZeroPixels = 0; for(int i = 0; i < stack.length; i++) { float[] pixels = (float[]) stack[i]; if(pixels != null) { for(int j = 0; j < pixels.length; j++) { double val = pixels[j]; if(val != 0) { totalNonZeroPixels++; int bin = (int) (val / binSize); binCounts[bin]++; } } } } int requiredPixels = (int) (totalNonZeroPixels * (quantile)); int cumulativeCount = 0; for(int i = 0; i <= nBins; i++) { cumulativeCount += binCounts[i]; if(cumulativeCount > requiredPixels) { return (i + 1) * binSize; } } return max; } } }