/** Set display range for each channel to min-max. */
 private static void displayMinToMax(ImagePlus imp) {
   if (imp.isComposite()) {
     for (int c = 1; c <= imp.getNChannels(); c++) {
       double min = I1l.getStatsForChannel(imp, c).min;
       double max = I1l.getStatsForChannel(imp, c).max;
       imp.setC(c);
       IJ.setMinAndMax(imp, min, max);
     }
     imp.setC(1);
   } else {
     double min = imp.getStatistics().min;
     double max = imp.getStatistics().max;
     IJ.setMinAndMax(imp, min, max);
   }
 }
 /**
  * Execute plugin functionality: stack FFT with window function, max projection over all slices
  * (phase, Z angle), blank out central 1/8 circle (set to min value), display min-max.
  */
 public ResultSet exec(ImagePlus... imps) {
   ImagePlus imp = imps[0];
   Util_StackFFT2D stackFFT2D = new Util_StackFFT2D();
   stackFFT2D.resultTypeChoice = Util_StackFFT2D.resultType[1];
   ImagePlus impF = stackFFT2D.exec(imp);
   IJ.run(impF, "Z Project...", "projection=[Max Intensity]");
   ImagePlus impProjF = ij.WindowManager.getCurrentImage();
   maskCentralRegion(impProjF);
   if (impProjF.isComposite()) {
     // display grayscale, not colored composite
     CompositeImage ci = (CompositeImage) impProjF;
     ci.setMode(IJ.GRAYSCALE);
     impProjF.updateAndDraw();
   }
   displayMinToMax(impProjF);
   impProjF.setTitle(I1l.makeTitle(imps[0], TLA));
   String shortInfo =
       "Maximum intensity projection of log"
           + " (amplitude^2) 2D FFT stack, central region masked,"
           + " rescaled (min-max) to improve contrast of the relevant"
           + " frequency range.";
   results.addImp(shortInfo, impProjF);
   results.addInfo(
       "How to interpret",
       "look for clean 1st & 2nd"
           + " order spots, similar across angles. Note that spot"
           + " intensity depends on image content.");
   return results;
 }
 /** Mask central offset / low freq spike to better use display range. */
 private void maskCentralRegion(ImagePlus imp) {
   int w = imp.getWidth();
   double d = this.maskDiameter * w;
   if (w % 2 == 0) {
     d += 1; // tweak to centre the mask about zero freq stripes
   }
   // N.B. we assume the image is square! (FFT result)
   OvalRoi mask = new OvalRoi(w / 2 - d / 2 + 1, w / 2 - d / 2 + 1, d, d);
   imp.setRoi(mask);
   for (int c = 1; c <= imp.getNChannels(); c++) {
     imp.setC(c);
     double min = I1l.getStatsForChannel(imp, c).min;
     IJ.run(imp, "Set...", "value=" + min + " slice");
   }
   imp.setC(1);
   imp.deleteRoi();
 }
 @Override
 public void run(String arg) {
   ImagePlus imp = IJ.getImage();
   GenericDialog gd = new GenericDialog(name);
   gd.addMessage("Requires SI raw data in OMX (CPZAT) order.");
   gd.addNumericField("angles", angles, 0);
   gd.addNumericField("phases", phases, 0);
   gd.showDialog();
   if (gd.wasCanceled()) return;
   if (gd.wasOKed()) {
     angles = (int) gd.getNextNumber();
     phases = (int) gd.getNextNumber();
   }
   if (!I1l.stackDivisibleBy(imp, phases * angles)) {
     IJ.showMessage(name, "Error: stack size not consistent with phases/angles.");
     return;
   }
   results = exec(imp);
   results.report();
 }