![JAR search and dependency download from the Maven repository](/logo.png)
SIMcheck.Rec_FourierPlots Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of SIMcheck Show documentation
Show all versions of SIMcheck Show documentation
ImageJ plugin suite for super-resolution structured illumination microscopy data quality control.
The newest version!
/*
* Copyright (c) 2015, Graeme Ball and Micron Oxford,
* University of Oxford, Department of Biochemistry.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/ .
*/
package SIMcheck;
import ij.*;
import ij.plugin.*;
import ij.process.*;
import ij.measure.*;
import ij.gui.*;
import java.awt.Color;
import java.awt.image.IndexColorModel;
/**
* This plugin plots FFTs of reconstructed stacks with resolution rings.
* @author Graeme Ball
* @see ij.plugin.FFT
* @see ij.process.FHT
*/
public class Rec_FourierPlots implements PlugIn, Executable {
public static final String name = "Reconstructed Fourier Plots";
public static final String TLA = "FTL"; // Fourier Transform Lateral
public static final String TLA2 = "FTR"; // FT Radial profile
public static final String TLA3 = "FTO"; // FT Orthogonal (XZ)
private ResultSet results = new ResultSet(name, TLA);
private static final IndexColorModel fourierLUT =
I1l.loadLut("SIMcheck/SIMcheckFourier.lut");
// parameter fields
public double[] resolutions = {0.10, 0.12, 0.15, 0.2, 0.3, 0.6};
public double blurRadius = 6.0d; // default for 512x512
public double winFraction = 0.06d; // window function size, 0-1
// TODO: refactor & remove default winFraction from here
// options
public boolean fft3d = true; // use 3D FFT? (requires JTransforms3)
public boolean autoCutoff = true; // no noise cut-off?
public boolean manualCutoff = false; // manual noise cut-off?
public boolean applyWinFunc = false; // apply window function?
public boolean gammaMinMax = false; // show 32-bit gamma 0.2, min-max?
public boolean logDisplay = false; // show 8-bit log(Amp^2)?
public boolean autoScale = false; // re-scale FFT to mode->max?
// N.B. autoScale now *always*/only happens for log(Amp^2): TODO, tidy!
public boolean blurAndLUT = false; // blur & apply false color LUT?
public boolean showAxial = false; // show axial FFT?
private double[] channelMinima = null;
@Override
public void run(String arg) {
ImagePlus imp = IJ.getImage();
imp.getWidth();
boolean doCheck = false;
if (FFT3D.ensureFftLibInstalled(false) == true) {
GenericDialog gd = new GenericDialog(name);
gd.addMessage("3D FFT (JTransforms), log-scaled power spectrum");
gd.addCheckbox("(1)_Cut-off:_auto (stack mode)", autoCutoff);
gd.addCheckbox(" Cut-off:_manual (default=0)", manualCutoff);
gd.addCheckbox("(2)_Window_function*", applyWinFunc);
gd.addCheckbox("(3)_Show_axial_FFT", showAxial);
gd.addMessage("* suppresses edge artifacts");
gd.enableYesNoCancel("OK", "2D-FFT options");
gd.showDialog();
if (gd.wasOKed()) {
// TODO: notCutoff, manualCutoff and autoScale radioButton group
this.autoCutoff = gd.getNextBoolean();
this.manualCutoff = gd.getNextBoolean();
this.applyWinFunc = gd.getNextBoolean();
this.showAxial = gd.getNextBoolean();
if (manualCutoff) {
// skip if noCutoff
this.channelMinima = new double[imp.getNChannels()];
SIMcheck_.specifyBackgrounds(
channelMinima, "Set noise cut-off:");
}
doCheck = true;
} else if (!gd.wasCanceled()) {
// i.e. "More" button clicked to skip 3D FFT
this.fft3d = false;
doCheck = dialog2D(imp);
}
// simplified options for 3d transform (if available)
} else {
doCheck = dialog2D(imp);
}
if (doCheck) {
results = exec(imp);
results.report();
}
}
/** Show dialog for check with 2D Fourier transforms and set options.
* Return false if check was canceled. */
private boolean dialog2D (ImagePlus imp) {
GenericDialog gd = new GenericDialog(name);
gd.addCheckbox("(1)_Cut-off:_auto (stack mode)", autoCutoff);
gd.addCheckbox(" Cut-off:_manual (default=0)", manualCutoff);
gd.addCheckbox("(2)_Window_function*", applyWinFunc);
gd.addCheckbox("(3)_32-bit_Amp, gamma 0.2, display min-max", gammaMinMax);
gd.addCheckbox(" 8-bit log(Amp^2), display mode-max", logDisplay);
gd.addCheckbox("(4)_Blur_&_false-color_LUT", blurAndLUT);
gd.addCheckbox("(5)_Show_axial_FFT", showAxial);
gd.addMessage("* suppresses edge artifacts");
gd.addMessage("** default: 32-bit Amplitude, gamma 0.2 (display 2-40)");
try {
Class.forName("org.jtransforms.fft.FloatFFT_3D");
} catch (ClassNotFoundException e ) {
gd.addMessage("! To be able run 3D FFTs, make sure JTransforms is installed");
}
gd.showDialog();
if (gd.wasOKed()) {
// TODO: notCutoff, manualCutoff and autoScale radioButton group
this.autoCutoff = gd.getNextBoolean();
this.manualCutoff = gd.getNextBoolean();
this.applyWinFunc = gd.getNextBoolean();
this.gammaMinMax = gd.getNextBoolean();
this.logDisplay = gd.getNextBoolean();
if (this.logDisplay) {
this.autoScale = true;
}
this.blurAndLUT = gd.getNextBoolean();
this.showAxial = gd.getNextBoolean();
if (manualCutoff) {
// skip if noCutoff
this.channelMinima = new double[imp.getNChannels()];
SIMcheck_.specifyBackgrounds(
channelMinima, "Set noise cut-off:");
}
return true;
} else{
return false;
}
}
/**
* Execute plugin functionality: for reconstructed and ortho-resliced,
* 2D FFT each slice and draw resolution rings (optional blur+LUT).
* @param imps reconstructed data ImagePlus should be first imp
* @return ResultSet containing FFT imp, ortho FFT imp, radial profile plot
*/
public ResultSet exec(ImagePlus... imps) {
try {
Class.forName("edu.emory.mathcs.parallelfftj.FloatTransformer");
// use parallel fftj for 3d transform if available
} catch(ClassNotFoundException e) {
this.fft3d = false; // can't find ImgLib2, so no 3D FFT
}
// TODO: check we have micron calibrations before continuing..
Calibration cal = imps[0].getCalibration();
ImagePlus imp2 = null;
if (manualCutoff) {
imp2 = Util_RescaleTo16bit.exec(imps[0].duplicate(), channelMinima);
} else if (autoCutoff) {
imp2 = Util_RescaleTo16bit.exec(imps[0].duplicate());
} else {
imp2 = imps[0].duplicate();
IJ.run("Conversions...", "scale");
IJ.run(imp2, "16-bit", "");
IJ.run("Conversions...", " ");
}
IJ.showStatus("Fourier transforming z-sections (lateral view)");
if (!applyWinFunc) {
winFraction = 0.0d;
}
ImagePlus impF = null;
if (fft3d) {
IJ.run(imp2, "32-bit", "");
impF = FFT3D.fftImp(imp2, winFraction);
displayModeToMax(impF);
} else {
if (logDisplay) {
impF = FFT2D.fftImp(imp2, winFraction);
blurRadius *= (double)impF.getWidth() / 512.0d;
IJ.showStatus("Blurring & rescaling z-sections (lateral view)");
autoscaleSlices(impF);
impF = gaussBlur(impF);
if (imps[0].isComposite()) {
impF = new CompositeImage(impF);
}
rescaleToStackMax(impF);
setLUT(impF, 0.0d, 255.0d);
} else {
impF = FFT2D.fftImp(imp2, true, winFraction, 0.2d);
impF = gaussBlur(impF);
if (imps[0].isComposite()) {
impF = new CompositeImage(impF);
}
if (gammaMinMax) {
if (blurAndLUT) {
displayModeToMax(impF);
} else {
displayMinToMax(impF);
}
setLUT(impF);
} else {
IJ.setMinAndMax(impF, 2, 40);
setLUT(impF, 2.0d, 40.0d);
}
}
}
impF = overlayResRings(impF, cal);
I1l.copyStackDims(imps[0], impF);
impF.setPosition(1, impF.getNSlices() / 2, 1);
impF.setTitle(I1l.makeTitle(imps[0], TLA));
results.addImp("Fourier Transform Lateral (XY; resolution rings" +
" in microns)", impF);
// radial & axial profiles only if we have calibration info
if (imp2.getCalibration().getUnit().equals("pixel")) {
IJ.log("Calibration info required for Radial & Axial FFT plots!");
} else {
ImagePlus radialProfiles = makeRadialProfiles(impF);
radialProfiles.setTitle(I1l.makeTitle(imps[0], TLA2));
results.addImp("Fourier Transform Radial profile "
+ "(lateral, central Z)", radialProfiles);
if (showAxial) {
Calibration calOrtho = null;
ImagePlus impOrtho = null;
ImagePlus impOrthoF = null;
if (fft3d) {
new StackConverter(impF).convertToGray32(); // for OrthoReslicer
OrthoReslicer orthoReslicer = new OrthoReslicer();
ImagePlus impF2 = impF.duplicate();
//impOrthoF = I1l.takeCentralZ(impOrthoF);
impOrthoF = orthoReslicer.exec(impF2, true);
impOrthoF = resizeAndPad3d(impOrthoF, cal);
IJ.setMinAndMax(impOrthoF, 20, 40);
setLUT(impOrthoF, 20.0d, 40.0d);
calOrtho = impOrthoF.getCalibration();
} else {
/// for orthogonal (axial) view, reslice first
new StackConverter(imp2).convertToGray32(); // for OrthoReslicer
OrthoReslicer orthoReslicer = new OrthoReslicer();
impOrtho = imp2.duplicate();
impOrtho = orthoReslicer.exec(impOrtho, false);
impOrtho = I1l.takeCentralZ(impOrtho);
calOrtho = impOrtho.getCalibration();
IJ.showStatus("FFT z-sections (orthogonal view)");
if (logDisplay) {
impOrthoF = FFT2D.fftImp(impOrtho, winFraction);
IJ.showStatus("Blur & rescale z-sections (orthogonal view)");
autoscaleSlices(impOrthoF);
impOrthoF = resizeAndPad(impOrthoF, cal);
impOrthoF = gaussBlur(impOrthoF);
// TODO, for multi-frame images, ensure impOrthoF is composite
rescaleToStackMax(impOrthoF);
setLUT(impOrthoF, 0.0d, 255.0d);
} else {
impOrthoF = FFT2D.fftImp(impOrtho, true, winFraction, 0.2d);
impOrthoF = resizeAndPad(impOrthoF, cal);
impOrthoF = gaussBlur(impOrthoF);
if (gammaMinMax) {
if (blurAndLUT) {
displayCentralModeToMax(impOrthoF);
} else {
displayMinToMax(impOrthoF);
}
setLUT(impOrthoF);
} else {
IJ.setMinAndMax(impOrthoF, 2, 40);
setLUT(impOrthoF, 2.0d, 40.0d);
}
}
}
calOrtho.pixelHeight = calOrtho.pixelWidth; // after resizeAndPad
impOrthoF = overlayResRings(impOrthoF, calOrtho);
I1l.copyStackDims(imps[0], impOrthoF);
impOrthoF.setPosition(1, impF.getNSlices() / 2, 1);
impOrthoF.setTitle(I1l.makeTitle(imps[0], TLA3));
results.addImp("Fourier Transform Orthogonal (XZ)", impOrthoF);
}
}
impF.setPosition(1, impF.getNSlices() / 2, 1);
if (fft3d) {
results.addInfo("About",
"by default the reconstructed data are (1) cropped to mode;"
+ " (2) 3D Fourier transformed (JTransforms)"
+ " and log-scaled power spectrum displayed.");
} else {
results.addInfo("About",
"by default the reconstructed data are (1) cropped to mode;"
+ " (2) Fourier transformed and scaled by a gamma function"
+ " (gamma=0.2).");
}
results.addInfo("How to interpret",
"Fourier plots highlight potential artifacts and indicate"
+ " effective resolution:"
+ " - Spots in Fourier spectrum indicate periodic patterns."
+ " - Flattened Fourier spectrum (plateau in radial profile)"
+ " indicates lack of high frequency signal and poor resolution."
+ " - Asymmetric FFT indicates angle-specific decrease in"
+ " resolution due to: angle-to-angle intensity variations,"
+ " angle-specific illumination pattern ('k0') fit error, or"
+ " angle-specific z-modulation issues. -- ");
return results;
}
/** Set display range for each channel to mode-max. */
private void displayModeToMax(ImagePlus imp) {
double mode, max;
for (int c = 1; c <= imp.getNChannels(); c++) {
mode = I1l.getStatsForChannel(imp, c).dmode;
max = I1l.getStatsForChannel(imp, c).max;
imp.setC(c);
IJ.setMinAndMax(imp, mode, max);
}
imp.setC(1);
}
/** Set display each channel mode-max of central 1/3 horizontal stripe. */
private void displayCentralModeToMax(ImagePlus imp) {
int yThird = imp.getHeight() / 3;
Roi centralRoi = new Roi(0, yThird, imp.getWidth(), yThird);
imp.setRoi(centralRoi);
ImagePlus impCentral = new Duplicator().run(imp, 2, 2, 1, 1, 1, 1);
imp.deleteRoi();
double mode, max;
for (int c = 1; c <= imp.getNChannels(); c++) {
mode = I1l.getStatsForChannel(impCentral, c).dmode;
max = I1l.getStatsForChannel(impCentral, c).max;
imp.setC(c);
IJ.setMinAndMax(imp, mode, max);
}
imp.setC(1);
}
/** Set display range for each channel to min-max. */
private void displayMinToMax(ImagePlus imp) {
double min, max;
for (int c = 1; c <= imp.getNChannels(); c++) {
min = I1l.getStatsForChannel(imp, c).min;
max = I1l.getStatsForChannel(imp, c).max;
imp.setC(c);
IJ.setMinAndMax(imp, min, max);
}
imp.setC(1);
}
/** Gaussian-blur result, or not, based on blurAndLUT option field. */
private ImagePlus gaussBlur(ImagePlus imp) {
if (blurAndLUT) {
imp = I1l.gaussBlur2D(imp, blurRadius);
}
return imp;
}
/** Rescale 8-bit imp 0-255 for input min or mode (autoscale) to max. */
private void autoscaleSlices(ImagePlus imp) {
int ns = imp.getStackSize();
for (int s = 1; s <= ns; s++) {
imp.setSlice(s);
ImageProcessor ip = imp.getProcessor();
ImageStatistics stats = imp.getProcessor().getStatistics();
int min = (int)stats.min;
if (autoScale) {
min = (int)stats.mode;
}
int max = (int)stats.max;
ByteProcessor bp = (ByteProcessor)imp.getProcessor();
ip = (ImageProcessor)I1l.setBPminMax(bp, min, max, 255);
imp.setProcessor(ip);
}
}
/** Rescale 8-bit image to fill up to max 255. */
private void rescaleToStackMax(ImagePlus imp) {
int nc = imp.getNChannels();
ImagePlus[] imps = new ImagePlus[nc];
for (int c = 0; c < nc; c++) {
imps[c] = I1l.copyChannel(imp, c + 1);
StackStatistics stats = new StackStatistics(imps[c]);
int ns = imps[c].getStackSize();
for (int s = 1; s <= ns; s++) {
imps[c].setSlice(s);
ByteProcessor bp = (ByteProcessor)imps[c].getProcessor();
bp = I1l.setBPminMax(bp, 0, (int)stats.max, 255);
imps[c].setProcessor((ImageProcessor)bp);
}
}
imp.setStack(I1l.mergeChannels("impsMerged", imps).getStack());
}
/** Use color LUT, or not, according to blurAndLUT option field. */
private void setLUT(ImagePlus imp) {
if (blurAndLUT) {
I1l.applyLUT(imp, fourierLUT);
} else {
if (imp.isComposite()) {
CompositeImage ci = (CompositeImage)imp;
ci.setMode(IJ.GRAYSCALE);
}
}
}
/** Use color LUT, or not; and update display range. */
private void setLUT(ImagePlus imp, double min, double max) {
if (blurAndLUT) {
double[] displayRange = {min, max};
I1l.applyLUT(imp, fourierLUT, displayRange);
} else {
if (imp.isComposite()) {
CompositeImage ci = (CompositeImage)imp;
ci.setMode(IJ.GRAYSCALE);
}
}
}
/** Resize imp for same y pixel size as in cal & pad square with 0s. */
private ImagePlus resizeAndPad(ImagePlus imp, Calibration cal) {
int width = imp.getHeight();
int height = imp.getHeight();
int depth = imp.getNSlices();
double rescaleFactor = cal.pixelHeight / cal.pixelDepth;
int rescaledHeight = (int)((double)height * rescaleFactor);
IJ.run(imp, "Scale...",
"x=1.0 y=" + rescaleFactor
+ " z=1.0 width=" + width
+ " height=" + rescaledHeight
+ " depth=" + depth + " interpolation=Bilinear"
+ " average process create title=impOrthoResized");
ImagePlus imp2 = IJ.getImage(); // TODO: refactor
imp2.hide();
int slices = imp2.getStackSize();
ImageStack stack = imp2.getStack();
ImageStack padStack = new ImageStack(width, height,
imp.getStackSize());
for (int s = 1; s <= slices; s++) {
ImageProcessor ip = stack.getProcessor(s);
int insertStart = width * (((height - rescaledHeight) / 2) + 1);
int insertEnd = insertStart + width * rescaledHeight;
if (ip instanceof ByteProcessor) {
ImageProcessor pip = new ByteProcessor(width, height); // to pad
byte[] pix = (byte[])((ByteProcessor)ip).getPixels();
byte[] padpix = new byte[width * height];
for (int i = insertStart; i < insertEnd; i++) {
padpix[i] = pix[i - insertStart];
}
pip.setPixels(padpix);
padStack.setProcessor(pip, s);
} else {
ImageProcessor pip = new FloatProcessor(width, height); // to pad
float[] pix = (float[])((FloatProcessor)ip).getPixels();
float[] padpix = new float[width * height];
for (int i = insertStart; i < insertEnd; i++) {
padpix[i] = pix[i - insertStart];
}
pip.setPixels(padpix);
padStack.setProcessor(pip, s);
}
}
imp2.setStack(padStack);
I1l.copyCal(imp, imp2);
imp2.setProperty("FHT", imp.getProperty("FHT"));
return imp2;
}
/** Resize and pad square resliced 3D FFT result. */
private ImagePlus resizeAndPad3d(ImagePlus imp, Calibration cal) {
int width = imp.getWidth();
int height = imp.getHeight();
int depth = imp.getNSlices();
double rescaleFactor = cal.pixelHeight / cal.pixelDepth;
int rescaledHeight = (int)((double)width * rescaleFactor);
double rescaleY = (double)height / rescaledHeight;
IJ.run(imp, "Scale...",
"x=1.0 y=" + (1.0d / rescaleY)
+ " z=1.0 width=" + width
+ " height=" + rescaledHeight
+ " depth=" + depth + " interpolation=Bilinear"
+ " average process create title=impOrthoResized");
ImagePlus imp2 = IJ.getImage(); // TODO: refactor
imp2.hide();
int slices = imp2.getStackSize();
ImageStack stack = imp2.getStack();
ImageStack padStack = new ImageStack(width, width,
imp.getStackSize());
for (int s = 1; s <= slices; s++) {
ImageProcessor ip = stack.getProcessor(s);
int insertStart = width * (((width - rescaledHeight) / 2) + 1);
int insertEnd = insertStart + width * rescaledHeight;
ImageProcessor pip = new FloatProcessor(width, width); // to pad
float[] pix = (float[])((FloatProcessor)ip).getPixels();
float[] padpix = new float[width * width];
for (int i = insertStart; i < insertEnd; i++) {
padpix[i] = pix[i - insertStart];
}
pip.setPixels(padpix);
padStack.setProcessor(pip, s);
}
imp2.setStack(padStack);
I1l.copyCal(imp, imp2); // imp2 cal needs to be updated later
return imp2;
}
/** Make radial profile plot for each channel at central Z. */
private ImagePlus makeRadialProfiles(ImagePlus imp) {
int nc = imp.getNChannels();
int nz = imp.getNSlices();
ImagePlus[] profiles = new ImagePlus[nc];
RadialProfile radialProfiler = new RadialProfile();
for (int c = 1; c <= nc; c++) {
imp.setPosition(c, nz / 2, 1);
ImageProcessor ip = imp.getProcessor();
ImagePlus impC = new ImagePlus("impC" + c, ip);
impC.setProperty("FHT", "F"); // tell radialProfiler it's Fourier
I1l.copyCal(imp, impC);
if (logDisplay) {
profiles[c - 1] = radialProfiler.exec(impC,
"Integrated Fourier amplitude (a.u.)");
} else {
profiles[c - 1] = radialProfiler.exec(impC);
}
}
ImagePlus impProfiles = I1l.mergeChannels(
"radial profiles", profiles);
return impProfiles;
}
/**
* Overlay resolution rings on each slice of a Fourier ImagePlus.
* NB. raw (non-FFT) imp is required for original calibrations.
*/
private ImagePlus overlayResRings(ImagePlus Fimp, Calibration cal) {
String unit = cal.getUnit();
if (!(unit.startsWith(""+IJ.micronSymbol) || unit.startsWith("u")
|| unit.startsWith("micro"))) {
IJ.log(" ! warning - non-micron calibration (" + unit
+ ") - cannot plot resolutions");
} else {
int width = Fimp.getWidth();
int height = Fimp.getHeight();
double pixWidth = cal.pixelWidth;
double pixHeight = cal.pixelHeight;
int fontSize = Fimp.getProcessor().getFont().getSize();
Overlay resOverlay = new Overlay();
for (int ring = 0; ring < resolutions.length; ring++) {
// res = pixel size * width/radius (i.e. 2cycles)
double dresX = ((double) width * pixWidth
/ resolutions[ring]);
int resX = (int) dresX;
double dresY = ((double) height * pixHeight
/ resolutions[ring]);
int resY = (int) dresY;
// NB. referring to *display* X & Y - could be e.g. true Z
int ovalW = resX * 2;
int ovalH = resY * 2;
int topLeftX = width / 2 + 1 - ovalW / 2;
int topLeftY = height / 2 + 1 - ovalH / 2;
OvalRoi currentOval = new OvalRoi(topLeftX, topLeftY,
ovalW, ovalH);
currentOval.setStrokeColor(Color.WHITE);
currentOval.setStrokeWidth(1.0);
resOverlay.add(currentOval);
TextRoi currentRes = new TextRoi(
(int) (width / 2 - fontSize),
(int) (height / 2 - 2 * (-0.5 + (ring + 1) % 2)
* (ovalH / 2) - fontSize),
Double.toString(resolutions[ring]));
currentRes.setStrokeColor(Color.WHITE);
resOverlay.add(currentRes);
}
Fimp.setOverlay(resOverlay);
}
return Fimp;
}
/** main method for manual testing */
public static void main(String[] args) {
new ImageJ();
ImagePlus impTest = TestData.recon;
impTest.show();
OrthoReslicer orthoReslicer = new OrthoReslicer();
ImagePlus impOrtho = orthoReslicer.exec(impTest, false);
impOrtho.show();
IJ.log("impOrtho height/width after reslice: " + impOrtho.getHeight()
+ "/" + impOrtho.getWidth());
ImagePlus impOrthoF = FFT2D.fftImp(impOrtho.duplicate());
impOrthoF.show();
int width = impOrtho.getWidth();
int height = impOrtho.getHeight();
ImageStack windowedStack = new ImageStack(width, height);
int paddedSize = FFT2D.calcPadSize(impOrtho);
if (paddedSize != width || paddedSize != height) {
width = paddedSize;
height = paddedSize;
}
ImageStack paddedStack = new ImageStack(width, height);
for (int s = 1; s <= impOrtho.getStackSize(); s++) {
ImageProcessor ip = impOrtho.getStack().getProcessor(s);
ImageProcessor wp = FFT2D.gaussWindow(ip.duplicate(), 0.125d);
windowedStack.addSlice(wp);
ImageProcessor pp = FFT2D.pad(wp.duplicate(), paddedSize);
paddedStack.addSlice(pp);
}
ImagePlus impWindowed = new ImagePlus();
impWindowed.setStack(windowedStack);
impWindowed.setTitle("windowed");
impWindowed.show();
ImagePlus impPadded = new ImagePlus();
impPadded.setStack(paddedStack);
impPadded.setTitle("padded");
impPadded.show();
IJ.selectWindow(impTest.getID());
IJ.runPlugIn(Rec_FourierPlots.class.getName(), "");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy