use of uk.ac.sussex.gdsc.core.ij.SeriesOpener in project GDSC-SMLM by aherbert.
the class PeakFit method setup.
@Override
public int setup(String arg, ImagePlus imp) {
SmlmUsageTracker.recordPlugin(this.getClass(), arg);
pluginFlags = FLAGS;
extraOptions = ImageJUtils.isExtraOptions();
maximaIdentification = StringUtils.contains(arg, "spot");
fitMaxima = StringUtils.contains(arg, "maxima");
simpleFit = StringUtils.contains(arg, "simple");
final boolean runSeries = StringUtils.contains(arg, "series");
ImageSource imageSource = null;
if (fitMaxima) {
// The image source will be found from the peak results.
if (!showMaximaDialog()) {
return DONE;
}
final MemoryPeakResults localResults = ResultsManager.loadInputResults(settings.inputOption, false, DistanceUnit.PIXEL);
if (localResults == null || localResults.size() == 0) {
IJ.error(TITLE, "No results could be loaded");
return DONE;
}
if (settings.fitAcrossAllFrames) {
// Allow the user to select a different image. The source will be set as per the
// main fit routine from the image (imp).
singleFrame = 0;
} else {
// Check for single frame
singleFrame = getSingleFrame(localResults);
// Forces the maxima to be used with their original source.
imp = null;
imageSource = localResults.getSource();
pluginFlags |= NO_IMAGE_REQUIRED;
}
} else if (runSeries) {
imp = null;
// Select input folder
final String inputDirectory = IJ.getDirectory("Select image series ...");
if (inputDirectory == null) {
return DONE;
}
// Load input series ...
SeriesOpener series;
if (extraOptions) {
final String helpKey = maximaIdentification ? "spot-finder-series" : "peak-fit-series";
series = SeriesOpener.create(inputDirectory, true, HelpUrls.getUrl(helpKey));
} else {
series = new SeriesOpener(inputDirectory);
}
if (series.getNumberOfImages() == 0) {
IJ.error(TITLE, "No images in the selected directory:\n" + inputDirectory);
return DONE;
}
final SeriesImageSource seriesImageSource = new SeriesImageSource(getName(series.getImageList()), series);
// TrackProgress logging is very verbose if the series has many images
// Status is used only when reading TIFF info.
// seriesImageSource.setTrackProgress(SimpleImageJTrackProgress.getInstance());
seriesImageSource.setTrackProgress(new TrackProgressAdaptor() {
@Override
public void status(String format, Object... args) {
ImageJUtils.showStatus(() -> String.format(format, args));
}
});
imageSource = seriesImageSource;
pluginFlags |= NO_IMAGE_REQUIRED;
}
// If the image source has not been set then use the input image
if (imageSource == null) {
if (imp == null) {
IJ.noImage();
return DONE;
}
// Check it is not a previous result
if (imp.getTitle().endsWith(ImageJImagePeakResults.IMAGE_SUFFIX)) {
IJImageSource ijImageSource = null;
// Check the image to see if it has an image source XML structure in the info property
final Object o = imp.getProperty("Info");
final Pattern pattern = Pattern.compile("Source: (<.*IJImageSource>.*<.*IJImageSource>)", Pattern.DOTALL);
final Matcher match = pattern.matcher((o == null) ? "" : o.toString());
if (match.find()) {
final ImageSource tmpSource = ImageSource.fromXml(match.group(1));
if (tmpSource instanceof IJImageSource) {
ijImageSource = (IJImageSource) tmpSource;
if (!ijImageSource.open()) {
ijImageSource = null;
} else {
imp = WindowManager.getImage(ijImageSource.getName());
}
}
}
if (ijImageSource == null) {
// Look for a parent using the title
final String parentTitle = imp.getTitle().substring(0, imp.getTitle().length() - ImageJImagePeakResults.IMAGE_SUFFIX.length() - 1);
final ImagePlus parentImp = WindowManager.getImage(parentTitle);
if (parentImp != null) {
ijImageSource = new IJImageSource(parentImp);
imp = parentImp;
}
}
String message = "The selected image may be a previous fit result";
if (ijImageSource != null) {
if (!TextUtils.isNullOrEmpty(ijImageSource.getName())) {
message += " of: \n \n" + ijImageSource.getName();
}
message += " \n \nFit the parent?";
} else {
message += " \n \nDo you want to continue?";
}
final YesNoCancelDialog d = new YesNoCancelDialog(null, TITLE, message);
if (ijImageSource == null) {
if (!d.yesPressed()) {
return DONE;
}
} else {
if (d.yesPressed()) {
imageSource = ijImageSource;
}
if (d.cancelPressed()) {
return DONE;
}
}
}
if (imageSource == null) {
try {
imageSource = new IJImageSource(imp);
} catch (final IllegalArgumentException ex) {
// This can happen if the image has an origin not in integer pixels
// e.g. the plugin is run on a plot
IJ.error(TITLE, "Error using image: " + imp.getTitle() + "\n \n" + ex.getMessage());
return DONE;
}
}
}
time = -1;
if (!initialiseImage(imageSource, getBounds(imp), false)) {
IJ.error(TITLE, "Failed to initialise the source image: " + imageSource.getName());
return DONE;
}
final int flags = showDialog(imp);
if ((flags & DONE) == 0) {
initialiseFitting();
}
return flags;
}
use of uk.ac.sussex.gdsc.core.ij.SeriesOpener in project GDSC-SMLM by aherbert.
the class TiffSeriesViewer method run.
@Override
public void run(String arg) {
SmlmUsageTracker.recordPlugin(this.getClass(), arg);
settings = Settings.load();
final ExtendedGenericDialog gd = new ExtendedGenericDialog(TITLE);
gd.addChoice("Mode", Settings.MODE, settings.inputMode, new OptionListener<Integer>() {
@Override
public boolean collectOptions(Integer value) {
settings.inputMode = value;
return collectOptions(false);
}
@Override
public boolean collectOptions() {
return collectOptions(true);
}
private boolean collectOptions(boolean silent) {
// This has limited silent support to fake running in a macro
if (settings.inputMode == 0) {
String dir = null;
final String title = "Select image series ...";
if (silent) {
final String macroOptions = Macro.getOptions();
if (macroOptions != null) {
dir = Macro.getValue(macroOptions, title, null);
}
} else {
dir = ImageJUtils.getDirectory(title, settings.inputDirectory);
}
if (TextUtils.isNullOrEmpty(dir)) {
return false;
}
settings.inputDirectory = dir;
} else {
String file = null;
final String title = "Select image ...";
if (silent) {
final String macroOptions = Macro.getOptions();
if (macroOptions != null) {
file = Macro.getValue(macroOptions, title, null);
}
} else {
file = ImageJUtils.getFilename(title, settings.inputFile);
}
if (TextUtils.isNullOrEmpty(file)) {
return false;
}
settings.inputFile = file;
}
updateLabel();
return true;
}
});
gd.addMessage("");
label = gd.getLastLabel();
if (ImageJUtils.isShowGenericDialog()) {
final Choice choice = gd.getLastChoice();
choice.addItemListener(event -> {
settings.inputMode = choice.getSelectedIndex();
updateLabel();
});
updateLabel();
}
gd.addCheckbox("Log_progress", settings.logProgress);
gd.addChoice("Output_mode", Settings.OUTPUT_MODE, settings.outputMode, new OptionListener<Integer>() {
@Override
public boolean collectOptions(Integer value) {
settings.outputMode = value;
return collectOptions(false);
}
@Override
public boolean collectOptions() {
return collectOptions(true);
}
private boolean collectOptions(boolean silent) {
if (settings.outputMode == 0) {
// Nothing to do
return false;
}
final ExtendedGenericDialog egd = new ExtendedGenericDialog("Output Options");
egd.addNumericField("Slices_per_image", settings.imageCount, 0);
egd.addDirectoryField("Output_directory", settings.outputDirectory);
egd.setSilent(silent);
egd.showDialog(true, gd);
if (egd.wasCanceled()) {
return false;
}
settings.imageCount = (int) egd.getNextNumber();
settings.outputDirectory = egd.getNextString();
updateLabel2();
return true;
}
});
gd.addMessage("");
label2 = gd.getLastLabel();
if (ImageJUtils.isShowGenericDialog()) {
final Choice choice = gd.getLastChoice();
choice.addItemListener(event -> {
settings.outputMode = choice.getSelectedIndex();
updateLabel2();
});
updateLabel2();
}
gd.addHelp(HelpUrls.getUrl("tiff-series-viewer"));
gd.showDialog();
if (gd.wasCanceled()) {
return;
}
settings.inputMode = gd.getNextChoiceIndex();
settings.logProgress = gd.getNextBoolean();
settings.outputMode = gd.getNextChoiceIndex();
settings.save();
SeriesImageSource source;
if (settings.inputMode == 0) {
final SeriesOpener series = new SeriesOpener(settings.inputDirectory);
if (series.getNumberOfImages() == 0) {
IJ.error(TITLE, "No images in the selected directory:\n" + settings.inputDirectory);
return;
}
source = new SeriesImageSource(PeakFit.getName(series.getImageList()), series);
} else {
source = new SeriesImageSource(FileUtils.getName(settings.inputFile), new String[] { settings.inputFile });
}
// No memory buffer
source.setBufferLimit(0);
source.setReadHint(ReadHint.NONSEQUENTIAL);
if (!source.isTiffSeries) {
IJ.error(TITLE, "Not a TIFF image");
return;
}
ImageJUtils.showStatus("Opening TIFF ...");
final TrackProgressAdaptor progress = new TrackProgressAdaptor() {
@Override
public void progress(double fraction) {
IJ.showProgress(fraction);
}
@Override
public void progress(long position, long total) {
IJ.showProgress((double) position / total);
}
@Override
public void log(String format, Object... args) {
if (settings.logProgress) {
ImageJUtils.log(format, args);
}
}
@Override
public void status(String format, Object... args) {
ImageJUtils.showStatus(() -> String.format(format, args));
}
@Override
public boolean isLog() {
return settings.logProgress;
}
};
source.setTrackProgress(progress);
if (!source.open()) {
IJ.error(TITLE, "Cannot open the image");
return;
}
ImageJUtils.showStatus("");
// Create a virtual stack
final TiffSeriesVirtualStack stack = new TiffSeriesVirtualStack(source);
if (settings.outputMode == 0) {
stack.show();
} else {
final int nImages = Math.max(1, settings.imageCount);
final ImagePlus imp = stack.createImp();
// The calibration only has the offset so ignore for speed.
// Calibration cal = imp.getCalibration();
final int size = stack.getSize();
// Create the format string
final int digits = String.format("%d", size).length();
final String format = new File(settings.outputDirectory, imp.getShortTitle() + "%0" + digits + "d.tif").getPath();
IJ.showStatus("Saving image ...");
try {
for (int i = 1; i <= size; i += nImages) {
if (ImageJUtils.isInterrupted()) {
break;
}
ImageJUtils.showSlowProgress(i, size);
final String path = String.format(format, i);
final ImageStack out = new ImageStack(source.getWidth(), source.getHeight());
for (int j = 0, k = i; j < nImages && k <= size; j++, k++) {
out.addSlice(null, stack.getPixels(k));
}
final ImagePlus outImp = new ImagePlus(path, out);
// outImp.setCalibration(cal);
saveAsTiff(outImp, path);
}
IJ.showStatus("Saved image");
} catch (final IOException ex) {
IJ.log(ExceptionUtils.getStackTrace(ex));
IJ.error(TITLE, "Failed to save image: " + ex.getMessage());
IJ.showStatus("Failed to save image");
} finally {
ImageJUtils.clearSlowProgress();
}
}
}
use of uk.ac.sussex.gdsc.core.ij.SeriesOpener in project GDSC-SMLM by aherbert.
the class MeanVarianceTest method run.
@Override
public void run(String arg) {
SmlmUsageTracker.recordPlugin(this.getClass(), arg);
settings = Settings.load();
settings.save();
String helpKey = "mean-variance-test";
if (ImageJUtils.isExtraOptions()) {
final ImagePlus imp = WindowManager.getCurrentImage();
if (imp.getStackSize() > 1) {
final GenericDialog gd = new GenericDialog(TITLE);
gd.addMessage("Perform single image analysis on the current image?");
gd.addNumericField("Bias", settings.bias, 0);
gd.addHelp(HelpUrls.getUrl(helpKey));
gd.showDialog();
if (gd.wasCanceled()) {
return;
}
singleImage = true;
settings.bias = Math.abs(gd.getNextNumber());
} else {
IJ.error(TITLE, "Single-image mode requires a stack");
return;
}
}
List<ImageSample> images;
String inputDirectory = "";
if (singleImage) {
IJ.showStatus("Loading images...");
images = getImages();
if (images.size() == 0) {
IJ.error(TITLE, "Not enough images for analysis");
return;
}
} else {
inputDirectory = IJ.getDirectory("Select image series ...");
if (inputDirectory == null) {
return;
}
final SeriesOpener series = new SeriesOpener(inputDirectory);
series.setVariableSize(true);
if (series.getNumberOfImages() < 3) {
IJ.error(TITLE, "Not enough images in the selected directory");
return;
}
if (!IJ.showMessageWithCancel(TITLE, String.format("Analyse %d images, first image:\n%s", series.getNumberOfImages(), series.getImageList()[0]))) {
return;
}
IJ.showStatus("Loading images");
images = getImages(series);
if (images.size() < 3) {
IJ.error(TITLE, "Not enough images for analysis");
return;
}
if (images.get(0).exposure != 0) {
IJ.error(TITLE, "First image in series must have exposure 0 (Bias image)");
return;
}
}
final boolean emMode = (arg != null && arg.contains("em"));
GenericDialog gd = new GenericDialog(TITLE);
gd.addMessage("Set the output options:");
gd.addCheckbox("Show_table", settings.showTable);
gd.addCheckbox("Show_charts", settings.showCharts);
if (emMode) {
// Ask the user for the camera gain ...
gd.addMessage("Estimating the EM-gain requires the camera gain without EM readout enabled");
gd.addNumericField("Camera_gain (Count/e-)", settings.cameraGain, 4);
}
if (emMode) {
helpKey += "-em-ccd";
}
gd.addHelp(HelpUrls.getUrl(helpKey));
gd.showDialog();
if (gd.wasCanceled()) {
return;
}
settings.showTable = gd.getNextBoolean();
settings.showCharts = gd.getNextBoolean();
if (emMode) {
settings.cameraGain = gd.getNextNumber();
}
IJ.showStatus("Computing mean & variance");
final double nImages = images.size();
for (int i = 0; i < images.size(); i++) {
IJ.showStatus(String.format("Computing mean & variance %d/%d", i + 1, images.size()));
images.get(i).compute(singleImage, i / nImages, (i + 1) / nImages);
}
IJ.showProgress(1);
IJ.showStatus("Computing results");
// Allow user to input multiple bias images
int start = 0;
final Statistics biasStats = new Statistics();
final Statistics noiseStats = new Statistics();
final double bias;
if (singleImage) {
bias = settings.bias;
} else {
while (start < images.size()) {
final ImageSample sample = images.get(start);
if (sample.exposure == 0) {
biasStats.add(sample.means);
for (final PairSample pair : sample.samples) {
noiseStats.add(pair.variance);
}
start++;
} else {
break;
}
}
bias = biasStats.getMean();
}
// Get the mean-variance data
int total = 0;
for (int i = start; i < images.size(); i++) {
total += images.get(i).samples.size();
}
if (settings.showTable && total > 2000) {
gd = new GenericDialog(TITLE);
gd.addMessage("Table output requires " + total + " entries.\n \nYou may want to disable the table.");
gd.addCheckbox("Show_table", settings.showTable);
gd.showDialog();
if (gd.wasCanceled()) {
return;
}
settings.showTable = gd.getNextBoolean();
}
final TextWindow results = (settings.showTable) ? createResultsWindow() : null;
double[] mean = new double[total];
double[] variance = new double[mean.length];
final Statistics gainStats = (singleImage) ? new StoredDataStatistics(total) : new Statistics();
final WeightedObservedPoints obs = new WeightedObservedPoints();
for (int i = (singleImage) ? 0 : start, j = 0; i < images.size(); i++) {
final StringBuilder sb = (settings.showTable) ? new StringBuilder() : null;
final ImageSample sample = images.get(i);
for (final PairSample pair : sample.samples) {
if (j % 16 == 0) {
IJ.showProgress(j, total);
}
mean[j] = pair.getMean();
variance[j] = pair.variance;
// Gain is in Count / e
double gain = variance[j] / (mean[j] - bias);
gainStats.add(gain);
obs.add(mean[j], variance[j]);
if (emMode) {
gain /= (2 * settings.cameraGain);
}
if (sb != null) {
sb.append(sample.title).append('\t');
sb.append(sample.exposure).append('\t');
sb.append(pair.slice1).append('\t');
sb.append(pair.slice2).append('\t');
sb.append(IJ.d2s(pair.mean1, 2)).append('\t');
sb.append(IJ.d2s(pair.mean2, 2)).append('\t');
sb.append(IJ.d2s(mean[j], 2)).append('\t');
sb.append(IJ.d2s(variance[j], 2)).append('\t');
sb.append(MathUtils.rounded(gain, 4)).append("\n");
}
j++;
}
if (results != null && sb != null) {
results.append(sb.toString());
}
}
IJ.showProgress(1);
if (singleImage) {
StoredDataStatistics stats = (StoredDataStatistics) gainStats;
ImageJUtils.log(TITLE);
if (emMode) {
final double[] values = stats.getValues();
MathArrays.scaleInPlace(0.5, values);
stats = StoredDataStatistics.create(values);
}
if (settings.showCharts) {
// Plot the gain over time
final String title = TITLE + " Gain vs Frame";
final Plot plot = new Plot(title, "Slice", "Gain");
plot.addPoints(SimpleArrayUtils.newArray(gainStats.getN(), 1, 1.0), stats.getValues(), Plot.LINE);
final PlotWindow pw = ImageJUtils.display(title, plot);
// Show a histogram
final String label = String.format("Mean = %s, Median = %s", MathUtils.rounded(stats.getMean()), MathUtils.rounded(stats.getMedian()));
final WindowOrganiser wo = new WindowOrganiser();
final PlotWindow pw2 = new HistogramPlotBuilder(TITLE, stats, "Gain").setRemoveOutliersOption(1).setPlotLabel(label).show(wo);
if (wo.isNotEmpty()) {
final Point point = pw.getLocation();
point.y += pw.getHeight();
pw2.setLocation(point);
}
}
ImageJUtils.log("Single-image mode: %s camera", (emMode) ? "EM-CCD" : "Standard");
final double gain = stats.getMedian();
if (emMode) {
final double totalGain = gain;
final double emGain = totalGain / settings.cameraGain;
ImageJUtils.log(" Gain = 1 / %s (Count/e-)", MathUtils.rounded(settings.cameraGain, 4));
ImageJUtils.log(" EM-Gain = %s", MathUtils.rounded(emGain, 4));
ImageJUtils.log(" Total Gain = %s (Count/e-)", MathUtils.rounded(totalGain, 4));
} else {
settings.cameraGain = gain;
ImageJUtils.log(" Gain = 1 / %s (Count/e-)", MathUtils.rounded(settings.cameraGain, 4));
}
} else {
IJ.showStatus("Computing fit");
// Sort
final int[] indices = rank(mean);
mean = reorder(mean, indices);
variance = reorder(variance, indices);
// Compute optimal coefficients.
// a - b x
final double[] init = { 0, 1 / gainStats.getMean() };
final PolynomialCurveFitter fitter = PolynomialCurveFitter.create(2).withStartPoint(init);
final double[] best = fitter.fit(obs.toList());
// Construct the polynomial that best fits the data.
final PolynomialFunction fitted = new PolynomialFunction(best);
if (settings.showCharts) {
// Plot mean verses variance. Gradient is gain in Count/e.
final String title = TITLE + " results";
final Plot plot = new Plot(title, "Mean", "Variance");
final double[] xlimits = MathUtils.limits(mean);
final double[] ylimits = MathUtils.limits(variance);
double xrange = (xlimits[1] - xlimits[0]) * 0.05;
if (xrange == 0) {
xrange = 0.05;
}
double yrange = (ylimits[1] - ylimits[0]) * 0.05;
if (yrange == 0) {
yrange = 0.05;
}
plot.setLimits(xlimits[0] - xrange, xlimits[1] + xrange, ylimits[0] - yrange, ylimits[1] + yrange);
plot.setColor(Color.blue);
plot.addPoints(mean, variance, Plot.CROSS);
plot.setColor(Color.red);
plot.addPoints(new double[] { mean[0], mean[mean.length - 1] }, new double[] { fitted.value(mean[0]), fitted.value(mean[mean.length - 1]) }, Plot.LINE);
ImageJUtils.display(title, plot);
}
final double avBiasNoise = Math.sqrt(noiseStats.getMean());
ImageJUtils.log(TITLE);
ImageJUtils.log(" Directory = %s", inputDirectory);
ImageJUtils.log(" Bias = %s +/- %s (Count)", MathUtils.rounded(bias, 4), MathUtils.rounded(avBiasNoise, 4));
ImageJUtils.log(" Variance = %s + %s * mean", MathUtils.rounded(best[0], 4), MathUtils.rounded(best[1], 4));
if (emMode) {
// The gradient is the observed gain of the noise.
// In an EM-CCD there is a noise factor of 2.
// Q. Is this true for a correct noise factor calibration:
// double noiseFactor = (Read Noise EM-CCD) / (Read Noise CCD)
// Em-gain is the observed gain divided by the noise factor multiplied by camera gain
final double emGain = best[1] / (2 * settings.cameraGain);
// Compute total gain
final double totalGain = emGain * settings.cameraGain;
final double readNoise = avBiasNoise / settings.cameraGain;
// Effective noise is standard deviation of the bias image divided by the total gain (in
// Count/e-)
final double readNoiseE = avBiasNoise / totalGain;
ImageJUtils.log(" Read Noise = %s (e-) [%s (Count)]", MathUtils.rounded(readNoise, 4), MathUtils.rounded(avBiasNoise, 4));
ImageJUtils.log(" Gain = 1 / %s (Count/e-)", MathUtils.rounded(1 / settings.cameraGain, 4));
ImageJUtils.log(" EM-Gain = %s", MathUtils.rounded(emGain, 4));
ImageJUtils.log(" Total Gain = %s (Count/e-)", MathUtils.rounded(totalGain, 4));
ImageJUtils.log(" Effective Read Noise = %s (e-) (Read Noise/Total Gain)", MathUtils.rounded(readNoiseE, 4));
} else {
// The gradient is the observed gain of the noise.
settings.cameraGain = best[1];
// Noise is standard deviation of the bias image divided by the gain (in Count/e-)
final double readNoise = avBiasNoise / settings.cameraGain;
ImageJUtils.log(" Read Noise = %s (e-) [%s (Count)]", MathUtils.rounded(readNoise, 4), MathUtils.rounded(avBiasNoise, 4));
ImageJUtils.log(" Gain = 1 / %s (Count/e-)", MathUtils.rounded(1 / settings.cameraGain, 4));
}
}
IJ.showStatus("");
}
Aggregations