View Javadoc
1   package com.github.celldynamics.quimp.plugin.dic;
2   
3   import java.util.Arrays;
4   
5   import org.slf4j.Logger;
6   import org.slf4j.LoggerFactory;
7   
8   import com.github.celldynamics.quimp.plugin.IQuimpPluginFilter;
9   import com.github.celldynamics.quimp.plugin.ParamList;
10  import com.github.celldynamics.quimp.plugin.QuimpPluginException;
11  import com.github.celldynamics.quimp.registration.Registration;
12  import com.github.celldynamics.quimp.utils.QuimPArrayUtils;
13  
14  import ij.IJ;
15  import ij.ImagePlus;
16  import ij.ImageStack;
17  import ij.gui.GenericDialog;
18  import ij.process.ImageProcessor;
19  import ij.process.ShortProcessor;
20  
21  /**
22   * Main implementation of ImageJ plugin.
23   * 
24   * <p>Currently supports only 8bit images and stacks.
25   * 
26   * @author p.baniukiewicz
27   * @see LidReconstructor
28   */
29  public class DicLidReconstruction_ implements IQuimpPluginFilter {
30  
31    /**
32     * The Constant LOGGER.
33     */
34    static final Logger LOGGER = LoggerFactory.getLogger(DicLidReconstruction_.class.getName());
35    private LidReconstructor dic;
36    private ImagePlus imp;
37    /**
38     * Filled by {@link #showUi(boolean)}.
39     */
40    private double angle;
41    /**
42     * Filled by {@link #showUi(boolean)}.
43     */
44    private double decay;
45    /**
46     * Filled by {@link #showUi(boolean)}.
47     */
48    private int masksize;
49    /**
50     * Angle of filtering.
51     * 
52     * <p>Filled by {@link #showUi(boolean)} as {@link #angle}+90.
53     */
54    private String prefilterangle;
55    /**
56     * Output image inversion.
57     * 
58     * <p>Filled by {@link #showUi(boolean)}
59     */
60    private boolean invertOutput;
61  
62    /*
63     * (non-Javadoc)
64     * 
65     * @see ij.plugin.filter.PlugInFilter#setup(java.lang.String, ij.ImagePlus)
66     */
67    @Override
68    public int setup(String arg, ImagePlus imp) {
69      this.imp = imp;
70      return DOES_8G + NO_CHANGES;
71    }
72  
73    /*
74     * (non-Javadoc)
75     * 
76     * @see com.github.celldynamics.quimp.plugin.IQuimpCorePlugin#setup()
77     */
78    @Override
79    public int setup() {
80      return 0;
81    }
82  
83    /**
84     * This method is run when current image was accepted and input data were correct.
85     * 
86     * @param ip is the current slice
87     */
88    @Override
89    public void run(ImageProcessor ip) {
90      // validate registered user
91      new Registration(IJ.getInstance(), "QuimP Registration");
92      ImageProcessor ret;
93      if (showUi(true) == 0) {
94        return; // if user clicked Cancel or data were not valid
95      }
96      try {
97        // create result as separate 16bit image
98        ImagePlus result = new ImagePlus("DIC_" + imp.getTitle(),
99                new ShortProcessor(ip.getWidth(), ip.getHeight()));
100       dic = new LidReconstructor(imp.getProcessor(), decay, angle, prefilterangle, masksize);
101       if (imp.getStack().getSize() == 1) { // if there is no stack we can avoid additional rotation
102         // here (see DICReconstruction documentation)
103         IJ.showProgress(0.0);
104         ret = dic.reconstructionDicLid();
105         if (invertOutput) {
106           ret.invert();
107         }
108         result.getProcessor().setPixels(ret.getPixels());
109         IJ.showProgress(1.0);
110         result.show();
111       } else { // we have stack. Process slice by slice
112         // create result stack
113         ImageStack resultstack = new ImageStack(imp.getWidth(), imp.getHeight());
114         ImageStack stack = imp.getStack();
115         for (int s = 1; s <= stack.getSize(); s++) {
116           IJ.showProgress(s / ((double) stack.getSize() + 1));
117           dic.setIp(stack.getProcessor(s));
118           ret = dic.reconstructionDicLid();
119           if (invertOutput) {
120             ret.invert();
121           }
122           resultstack.addSlice(ret);
123         }
124         IJ.showProgress(1.0);
125         // pack in ImagePlus
126         new ImagePlus("DIC_" + imp.getTitle(), resultstack).show();
127       }
128     } catch (DicException e) { // exception can be thrown if input image is 16-bit and saturated
129       IJ.log(e.getMessage());
130       LOGGER.debug(e.getMessage(), e);
131       LOGGER.error("Problem with DIC reconstruction: " + e.getMessage());
132     } finally {
133       imp.updateAndDraw();
134     }
135   }
136 
137   /**
138    * Round given angle to closest full angle: 0, 45, 90, 135
139    * 
140    * @param angle input angle in range <0;360)
141    * @return full anlge as string. Can be 0, 45, 90, 135
142    */
143   String roundtofull(double angle) {
144     int anglei = (int) angle; // cut fractions
145     if (anglei >= 180) {
146       anglei -= 180;
147     }
148     // calculate distances between 0, 45, 90, 135, 180
149     Integer[] d = new Integer[5];
150     d[0] = Math.abs(0 - anglei);
151     d[1] = Math.abs(45 - anglei);
152     d[2] = Math.abs(90 - anglei);
153     d[3] = Math.abs(135 - anglei);
154     d[4] = Math.abs(180 - anglei);
155     // min distance
156     int i = QuimPArrayUtils.minListIndex(Arrays.asList(d));
157     i = i > 3 ? 0 : i; // deal with 180 that should be 0
158     return Integer.toString(i * 45);
159   }
160 
161   /*
162    * (non-Javadoc)
163    * 
164    * @see
165    * IQuimpCorePlugin#setPluginConfig(com.github.celldynamics.quimp.
166    * plugin.ParamList)
167    */
168   @Override
169   public void setPluginConfig(ParamList par) throws QuimpPluginException {
170   }
171 
172   /*
173    * (non-Javadoc)
174    * 
175    * @see com.github.celldynamics.quimp.plugin.IQuimpCorePlugin#getPluginConfig()
176    */
177   @Override
178   public ParamList getPluginConfig() {
179     return null;
180   }
181 
182   /**
183    * Shows user dialog and check conditions.
184    * 
185    * @return 1 if user clicked OK and input data are correct (they are numbers) or return 0
186    *         otherwise
187    */
188   @Override
189   public int showUi(boolean val) {
190     GenericDialog gd = new GenericDialog("DIC reconstruction");
191     gd.addMessage("Reconstruction of DIC image by Line Integrals.\n");
192     gd.addNumericField("Shear angle (measured counterclockwise)", 45.0, 0, 6, "[deg]");
193     gd.addNumericField("Decay factor (>0)", 0.0, 2, 6, "[-]");
194     // gd.addChoice("Angle perpendicular to shear", new String[] { "0", "45", "90", "135" },
195     // "45");
196     gd.addNumericField("Filter mask size (odd, 0 to switch filtering off)", 0, 0);
197     gd.addCheckbox("Invert output", false);
198 
199     gd.setResizable(false);
200     gd.showDialog();
201     if (gd.wasCanceled()) {
202       return 0;
203     }
204     // read GUI elements and store results in private fields order as these
205     // methods are called should match to GUI build order
206     angle = Math.abs(gd.getNextNumber());
207     if (angle >= 360) {
208       angle -= 360;
209     }
210     decay = gd.getNextNumber();
211     prefilterangle = roundtofull(angle + 90); // filtering angle
212     masksize = (int) gd.getNextNumber();
213     invertOutput = gd.getNextBoolean();
214 
215     if (gd.invalidNumber()) { // check if numbers in fields were correct
216       IJ.error("One of the numbers in dialog box is not valid");
217       return 0;
218     }
219     if (masksize != 0 && masksize % 2 == 0) {
220       IJ.error("Mask size must be uneven");
221       return 0;
222     }
223 
224     return 1;
225   }
226 
227   /*
228    * (non-Javadoc)
229    * 
230    * @see com.github.celldynamics.quimp.plugin.IQuimpCorePlugin#getVersion()
231    */
232   @Override
233   public String getVersion() {
234     return "See QuimP version";
235   }
236 
237   /*
238    * (non-Javadoc)
239    * 
240    * @see com.github.celldynamics.quimp.plugin.IQuimpCorePlugin#about()
241    */
242   @Override
243   public String about() {
244     return "DIC plugin.\n" + "Author: Piotr Baniukiewicz\n" + "mail: p.baniukiewicz@warwick.ac.uk\n"
245             + "This plugin supports macro parameters\n" + "Check macro recorder";
246   }
247 
248 }