/* This plugin is written by Joost Willemse * For any questions concerning the plugin you can contact me at jwillemse@biology.leidenuniv.nl * * The reason for developping this plugin is to allow thresholding on other statistics than only on intensity * A paper explaining the methods used and potential implementations is in preparation */ package LeidenUniv.Tools; import ij.IJ; import ij.ImagePlus; import ij.plugin.*; import ij.gui.*; import ij.plugin.filter.*; import java.awt.*; import ij.process.*; import java.awt.event.*; import ij.ImageJ; import javax.swing.JProgressBar; public class Special_Threshold implements PlugIn, DialogListener, ActionListener { private ImagePlus image, overlay; private ImageProcessor ip; private GenericDialog gd; private int prevA, prevT, prevC; private FloatProcessor bt; // image property members private int width; private int height; private Button okB; private Thread t; private Roi ro; private boolean prevBack, firstChange; private String [] Choices = {"Mean", "Median", "Modal", "Standard deviation", "Maximum grey value", "Minimm grey value", "Integrated density", "Skewness", "Kurtosis", "Harmonic Mean", "Geometric Mean", "Winsorized Mean", "Quadruple Mean", "Cubic Mean","Left to Right symmetry","Top to Bottom symmetry","Top-left to Right-bottom symmetry","Bottom-left to Top-right symmetry","Cubic Symmetry","FFT Stdev","FFT Mean","FFT Kurtosis", "FFT Skewness"}; ImageJ cij = IJ.getInstance(); JProgressBar pb =new JProgressBar(); public void run(String arg) { int test =5; image = IJ.getImage(); ip = image.getProcessor(); // get width and height width = ip.getWidth(); height = ip.getHeight(); bt = new FloatProcessor(width, height); overlay = new ImagePlus("overlay", bt); //overlay.show(); gd = new GenericDialog("Filter"); prevA=3; prevC= 0; prevT=50; prevBack=false; gd.addSlider("Kernel layers ", 1, 21, 3); gd.addMessage("1 means only the pixel itself, \n2 means also 1 neighbouring cell, \n3 means 2 neighbouring cells\nand so on"); gd.addChoice("Filter based on: ", Choices, Choices[0]); gd.addSlider("Threshold in % ", 0, 100, 50); gd.setModal(false); gd.addCheckbox("Dark background", prevBack); gd.addDialogListener(this); gd.addMessage("Progress"); gd.add(pb); gd.showDialog(); Button [] bts = gd.getButtons(); okB = bts[0]; okB.addActionListener(this); bts[1].addActionListener(this); } // for implementation in macro, info needed is ImagePlus, Kernel size, dark/light background, threshold in %, segmentation method public ImagePlus getMask(ImagePlus im, int ks, boolean bck, int Thresh, int Method){ image = im; prevA = ks; prevT=Thresh; ip = image.getProcessor(); // get width and height width = ip.getWidth(); height = ip.getHeight(); bt = new FloatProcessor(width, height); overlay = new ImagePlus("overlay", bt); getThreshold(prevT,Method, bck, true); while (t.isAlive()){ IJ.log("running"); try { Thread.sleep(100); } catch(InterruptedException ex){} } //overlay.show(); overlay.setRoi(ro); //IJ.run(overlay, "Clear Outside", ""); //IJ.setForegroundColor(255, 255, 255); //IJ.run(overlay, "Fill", "slice"); //wfud.show(); //IJ.run(overlay, "Remove Overlay", ""); //wfud.show(); ByteProcessor maskbp =overlay.createRoiMask(); ImagePlus mask = new ImagePlus("Mask",(ImageProcessor)maskbp); return mask; } public boolean dialogItemChanged(GenericDialog gd1, AWTEvent ev){ Scrollbar Selected=(Scrollbar)(gd1.getSliders().elementAt(0)); Scrollbar SelectedT=(Scrollbar)(gd1.getSliders().elementAt(1)); int a=Selected.getValue(); int T=SelectedT.getValue(); int c =gd1.getNextChoiceIndex(); boolean back = gd1.getNextBoolean(); boolean recalc = true; if (a!=prevA){ prevA=a; } else if (c!=prevC){ prevC=c; } else if (T!=prevT){ prevT=T; recalc=false; } else if (back!=prevBack){ prevBack =back;// in all cases we need to update the mask, either the method, the size or the value has changed. recalc=false; } getThreshold(T,c, back, recalc); return true; } public void getThreshold(int Tval, int Cval, boolean back, boolean recalc){ ImageWindow iw = overlay.getWindow(); final int cCval=Cval; // needs to be final to be called from inner class final int cTval=Tval; final boolean cback=back; if (recalc){ // clear the progressbar initially t = new Thread(){ public void run(){ int xw=0,yw=0; for (int i=0; iwidth-1){ // if endpoint falls outside xw = width-i+prevA; } else if (i-prevA<0) { // cannot start lower than 0 but should not take the whole region then xw = prevA+i; } else { xw=2*prevA-1; } if (ye>height-1){ // if endpoint falls outside yw = height-j+prevA; } else if (j-prevA<0){ yw = prevA+j; } else { yw=2*prevA-1; } Roi cr = new Roi(xm,ym,xw,yw); image.setRoi(cr); ImageStatistics is = cr.getStatistics(); switch (cCval){ //{"Mean", "Median", "Modal", "Standard deviation", "Maximum grey value", "Minimm grey value", "Integrated density", "Skewness", "Kurtosis" // "Harmonic Mean", "Geometric Mean", "Winsorized Mean", "Quadruple Mean", "Cubic Mean","Left to Right symmetry","Top to Bottom symmetry","Top-left to Right-bottom symmetry","Bottom-left to Top-right symmetry" case 0: bt.putPixelValue(i,j,is.mean); break; case 1:bt.putPixelValue(i,j,is.median); break; case 2:bt.putPixelValue(i,j,is.mode); break; case 3:bt.putPixelValue(i,j,is.stdDev); break; case 4:bt.putPixelValue(i,j,is.max); break; case 5:bt.putPixelValue(i,j,is.min); break; case 6:bt.putPixelValue(i,j,is.mean*xw*yw); break; case 7:bt.putPixelValue(i,j,is.skewness); break; case 8:bt.putPixelValue(i,j,is.kurtosis); break; case 9:bt.putPixelValue(i,j,getHarmonicMean(xm,ym,xw,yw,ip)); // for harmonic mean break; case 10:bt.putPixelValue(i,j,getGeometricMean(xm,ym,xw,yw,ip)); // for geometric mean break; case 11:bt.putPixelValue(i,j,(is.min+is.max)/2); // for winsorized mean break; case 12:bt.putPixelValue(i,j,getPowerMean(xm,ym,xw,yw,ip,2)); // for quadruple mean break; case 13:bt.putPixelValue(i,j,getPowerMean(xm,ym,xw,yw,ip,3)); // for cubic mean break; case 14:bt.putPixelValue(i,j,getLRSymmetry(xm,ym,xw,yw, ip)); //Left Right symmetry break; case 15:bt.putPixelValue(i,j,getTBSymmetry(xm,ym,xw,yw, ip)); // Top Bottom symmetry break; case 16:bt.putPixelValue(i,j,getTLBRSymmetry(xm,ym,xw,yw,ip)); // top-left to right bottom symmetry break; case 17:bt.putPixelValue(i,j,getTRBLSymmetry(xm,ym,xw,yw,ip)); // bottom left to top right symmetry break; case 18:bt.putPixelValue(i,j,Math.pow(Math.pow(getTRBLSymmetry(xm,ym,xw,yw,ip),2)+Math.pow(getLRSymmetry(xm,ym,xw,yw,ip),2)+Math.pow(getTBSymmetry(xm,ym,xw,yw,ip),2)+Math.pow(getTLBRSymmetry(xm,ym,xw,yw,ip),2), 0.25)); // all symmertry cubic combined break; case 19:bt.putPixelValue(i,j,getFFTStdev(xm,ym,xw,yw,ip)); // all symmertry cubic combined break; case 20:bt.putPixelValue(i,j,getFFTMean(xm,ym,xw,yw,ip)); // all symmertry cubic combined break; case 21:bt.putPixelValue(i,j,getFFTKurt(xm,ym,xw,yw,ip)); // all symmertry cubic combined break; case 22:bt.putPixelValue(i,j,getFFTSkew(xm,ym,xw,yw,ip)); // all symmertry cubic combined break; }//switch } //j } //i IJ.run(overlay, "Enhance Contrast", "saturated=0.0"); ImageStatistics ois = overlay.getStatistics(); double minS = ois.min; double maxS = ois.max; double diff = maxS-minS; double steps = diff/100; if (!cback){ IJ.setRawThreshold(overlay, ois.min, ois.min+prevT*steps, null); } else { IJ.setRawThreshold(overlay, ois.min+prevT*steps, ois.max, null); } ro = ThresholdToSelection.run(overlay); Overlay ov = new Overlay(ro); ov.setFillColor(Color.red); image.setOverlay(ov); } // public void }; //thread t.start(); } //iw.updateImage(overlay); //overlay.updateAndDraw(); IJ.run(overlay, "Enhance Contrast", "saturated=0.0"); ImageStatistics ois = overlay.getStatistics(); double minS = ois.min; double maxS = ois.max; double diff = maxS-minS; double steps = diff/100; if (!back){ IJ.setRawThreshold(overlay, ois.min, ois.min+prevT*steps, null); } else { IJ.setRawThreshold(overlay, ois.min+prevT*steps, ois.max, null); } ro = ThresholdToSelection.run(overlay); Overlay ov = new Overlay(ro); ov.setFillColor(Color.red); image.setOverlay(ov); } public double getFFTKurt(int xm, int ym, int xw, int yw, ImageProcessor ip){ ImagePlus Fimp = FFT.forward(image); //IJ.log(""+Fimp.getStatistics(0x40000).kurtosis); return Fimp.getStatistics(0x40000).kurtosis; } public double getFFTSkew(int xm, int ym, int xw, int yw, ImageProcessor ip){ ImagePlus Fimp = FFT.forward(image); return Fimp.getStatistics(0x20000).skewness; } public double getFFTMean(int xm, int ym, int xw, int yw, ImageProcessor ip){ ImagePlus Fimp = FFT.forward(image); return Fimp.getStatistics().mean; } public double getFFTStdev(int xm, int ym, int xw, int yw, ImageProcessor ip){ ImagePlus Fimp = FFT.forward(image); return Fimp.getStatistics().stdDev; } public double getPowerMean(int xm, int ym, int xw, int yw, ImageProcessor ip, double pow){ double sum=0; for (int j=0;j