use of uk.ac.sussex.gdsc.smlm.ij.SeriesImageSource in project GDSC-SMLM by aherbert.
the class PeakResultTableModelFrame method doSourceShowImage.
private void doSourceShowImage() {
final PeakResultTableModel model = getModel();
if (model == null) {
return;
}
final ImageSource source = model.getSource();
if (source == null) {
return;
}
// Check if already open
final ImagePlus imp = WindowManager.getImage(source.getName());
if (imp != null) {
imp.getWindow().toFront();
return;
}
// Check if an ImageJ image source
if (source instanceof IJImageSource) {
final IJImageSource imageSource = (IJImageSource) source;
final String path = imageSource.getPath();
if (path != null && new File(path).exists()) {
IJ.showStatus("Opening image ...");
IJ.open(path);
IJ.showStatus("");
} else {
IJ.log("Cannot find the image source: " + path);
}
return;
}
// Open a SeriesImageSource.
if (source instanceof SeriesImageSource) {
final SeriesImageSource imageSource = (SeriesImageSource) source;
// No memory buffer
imageSource.setBufferLimit(0);
imageSource.setReadHint(ReadHint.NONSEQUENTIAL);
if (!source.open()) {
IJ.log("Cannot open the series image source");
return;
}
new TiffSeriesVirtualStack(imageSource).show();
}
}
use of uk.ac.sussex.gdsc.smlm.ij.SeriesImageSource 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.smlm.ij.SeriesImageSource in project GDSC-SMLM by aherbert.
the class CmosAnalysis method runAnalysis.
private void runAnalysis() {
final long start = System.currentTimeMillis();
// Create thread pool and workers. The system is likely to be IO limited
// so reduce the computation threads to allow the reading thread in the
// SeriesImageSource to run.
// If the images are small enough to fit into memory then 3 threads are used,
// otherwise it is 1.
final int nThreads = Math.max(1, getThreads() - 3);
final ExecutorService executor = Executors.newFixedThreadPool(nThreads);
final LocalList<Future<?>> futures = new LocalList<>(nThreads);
final LocalList<ImageWorker> workers = new LocalList<>(nThreads);
final double[][] data = new double[subDirs.size() * 2][];
double[] pixelOffset = null;
double[] pixelVariance = null;
Statistics statsOffset = null;
Statistics statsVariance = null;
// For each sub-directory compute the mean and variance
final int nSubDirs = subDirs.size();
boolean error = false;
int width = 0;
int height = 0;
for (int n = 0; n < nSubDirs; n++) {
ImageJUtils.showSlowProgress(n, nSubDirs);
final SubDir sd = subDirs.unsafeGet(n);
ImageJUtils.showStatus(() -> "Analysing " + sd.name);
final StopWatch sw = StopWatch.createStarted();
// Option to reuse data
final File file = new File(settings.directory, "perPixel" + sd.name + ".tif");
boolean found = false;
if (settings.reuseProcessedData && file.exists()) {
final Opener opener = new Opener();
opener.setSilentMode(true);
final ImagePlus imp = opener.openImage(file.getPath());
if (imp != null && imp.getStackSize() == 2 && imp.getBitDepth() == 32) {
if (n == 0) {
width = imp.getWidth();
height = imp.getHeight();
} else if (width != imp.getWidth() || height != imp.getHeight()) {
error = true;
IJ.error(TITLE, "Image width/height mismatch in image series: " + file.getPath() + String.format("\n \nExpected %dx%d, Found %dx%d", width, height, imp.getWidth(), imp.getHeight()));
break;
}
final ImageStack stack = imp.getImageStack();
data[2 * n] = SimpleArrayUtils.toDouble((float[]) stack.getPixels(1));
data[2 * n + 1] = SimpleArrayUtils.toDouble((float[]) stack.getPixels(2));
found = true;
}
}
if (!found) {
// Open the series
final SeriesImageSource source = new SeriesImageSource(sd.name, sd.path.getPath());
if (!source.open()) {
error = true;
IJ.error(TITLE, "Failed to open image series: " + sd.path.getPath());
break;
}
if (n == 0) {
width = source.getWidth();
height = source.getHeight();
} else if (width != source.getWidth() || height != source.getHeight()) {
error = true;
IJ.error(TITLE, "Image width/height mismatch in image series: " + sd.path.getPath() + String.format("\n \nExpected %dx%d, Found %dx%d", width, height, source.getWidth(), source.getHeight()));
break;
}
// So the bar remains at 99% when workers have finished use frames + 1
final Ticker ticker = ImageJUtils.createTicker(source.getFrames() + 1L, nThreads);
// Open the first frame to get the bit depth.
// Assume the first pixels are not empty as the source is open.
Object pixels = source.nextRaw();
final int bitDepth = ImageJUtils.getBitDepth(pixels);
ArrayMoment moment;
if (settings.rollingAlgorithm) {
moment = new RollingArrayMoment();
// We assume 16-bit camera at the maximum
} else if (bitDepth <= 16 && IntegerArrayMoment.isValid(IntegerType.UNSIGNED_16, source.getFrames())) {
moment = new IntegerArrayMoment();
} else {
moment = new SimpleArrayMoment();
}
final BlockingQueue<Object> jobs = new ArrayBlockingQueue<>(nThreads * 2);
for (int i = 0; i < nThreads; i++) {
final ImageWorker worker = new ImageWorker(ticker, jobs, moment);
workers.add(worker);
futures.add(executor.submit(worker));
}
// Process the raw pixel data
long lastTime = 0;
while (pixels != null) {
final long time = System.currentTimeMillis();
if (time - lastTime > 150) {
if (ImageJUtils.isInterrupted()) {
error = true;
break;
}
lastTime = time;
IJ.showStatus("Analysing " + sd.name + " Frame " + source.getStartFrameNumber());
}
put(jobs, pixels);
pixels = source.nextRaw();
}
source.close();
if (error) {
// Kill the workers
workers.stream().forEach(worker -> worker.finished = true);
// Clear the queue
jobs.clear();
// Signal any waiting workers
workers.stream().forEach(worker -> jobs.add(ImageWorker.STOP_SIGNAL));
// Cancel by interruption. We set the finished flag so the ImageWorker should
// ignore the interrupt.
futures.stream().forEach(future -> future.cancel(true));
break;
}
// Finish all the worker threads cleanly
workers.stream().forEach(worker -> jobs.add(ImageWorker.STOP_SIGNAL));
// Wait for all to finish
ConcurrencyUtils.waitForCompletionUnchecked(futures);
// Create the final aggregate statistics
for (final ImageWorker w : workers) {
moment.add(w.moment);
}
data[2 * n] = moment.getMean();
data[2 * n + 1] = moment.getVariance();
// Get the processing speed.
sw.stop();
// ticker holds the number of number of frames processed
final double bits = (double) bitDepth * source.getFrames() * source.getWidth() * source.getHeight();
final double bps = bits / sw.getTime(TimeUnit.SECONDS);
final SiPrefix prefix = SiPrefix.getSiPrefix(bps);
ImageJUtils.log("Processed %d frames. Time = %s. Rate = %s %sbits/s", moment.getN(), sw.toString(), MathUtils.rounded(prefix.convert(bps)), prefix.getPrefix());
// Reset
futures.clear();
workers.clear();
final ImageStack stack = new ImageStack(width, height);
stack.addSlice("Mean", SimpleArrayUtils.toFloat(data[2 * n]));
stack.addSlice("Variance", SimpleArrayUtils.toFloat(data[2 * n + 1]));
IJ.save(new ImagePlus("PerPixel", stack), file.getPath());
}
final Statistics s = Statistics.create(data[2 * n]);
if (pixelOffset != null) {
// Compute mean ADU
final Statistics signal = new Statistics();
final double[] mean = data[2 * n];
for (int i = 0; i < pixelOffset.length; i++) {
signal.add(mean[i] - pixelOffset[i]);
}
ImageJUtils.log("%s Mean = %s +/- %s. Signal = %s +/- %s ADU", sd.name, MathUtils.rounded(s.getMean()), MathUtils.rounded(s.getStandardDeviation()), MathUtils.rounded(signal.getMean()), MathUtils.rounded(signal.getStandardDeviation()));
} else {
// Set the offset assuming the first sub-directory is the bias image
pixelOffset = data[0];
pixelVariance = data[1];
statsOffset = s;
statsVariance = Statistics.create(pixelVariance);
ImageJUtils.log("%s Offset = %s +/- %s. Variance = %s +/- %s", sd.name, MathUtils.rounded(s.getMean()), MathUtils.rounded(s.getStandardDeviation()), MathUtils.rounded(statsVariance.getMean()), MathUtils.rounded(statsVariance.getStandardDeviation()));
}
IJ.showProgress(1);
}
ImageJUtils.clearSlowProgress();
if (error) {
executor.shutdownNow();
IJ.showStatus(TITLE + " cancelled");
return;
}
executor.shutdown();
if (pixelOffset == null || pixelVariance == null) {
IJ.showStatus(TITLE + " error: no bias image");
return;
}
// Compute the gain
ImageJUtils.showStatus("Computing gain");
final double[] pixelGain = new double[pixelOffset.length];
final double[] bibiT = new double[pixelGain.length];
final double[] biaiT = new double[pixelGain.length];
// Ignore first as this is the 0 exposure image
for (int n = 1; n < nSubDirs; n++) {
// Use equation 2.5 from the Huang et al paper.
final double[] b = data[2 * n];
final double[] a = data[2 * n + 1];
for (int i = 0; i < pixelGain.length; i++) {
final double bi = b[i] - pixelOffset[i];
final double ai = a[i] - pixelVariance[i];
bibiT[i] += bi * bi;
biaiT[i] += bi * ai;
}
}
for (int i = 0; i < pixelGain.length; i++) {
pixelGain[i] = biaiT[i] / bibiT[i];
}
final Statistics statsGain = Statistics.create(pixelGain);
ImageJUtils.log("Gain Mean = %s +/- %s", MathUtils.rounded(statsGain.getMean()), MathUtils.rounded(statsGain.getStandardDeviation()));
// Histogram of offset, variance and gain
final int bins = 2 * HistogramPlot.getBinsSturgesRule(pixelGain.length);
final WindowOrganiser wo = new WindowOrganiser();
showHistogram("Offset (ADU)", pixelOffset, bins, statsOffset, wo);
showHistogram("Variance (ADU^2)", pixelVariance, bins, statsVariance, wo);
showHistogram("Gain (ADU/e)", pixelGain, bins, statsGain, wo);
wo.tile();
// Save
final float[] bias = SimpleArrayUtils.toFloat(pixelOffset);
final float[] variance = SimpleArrayUtils.toFloat(pixelVariance);
final float[] gain = SimpleArrayUtils.toFloat(pixelGain);
measuredStack = new ImageStack(width, height);
measuredStack.addSlice("Offset", bias);
measuredStack.addSlice("Variance", variance);
measuredStack.addSlice("Gain", gain);
final ExtendedGenericDialog egd = new ExtendedGenericDialog(TITLE);
egd.addMessage("Save the sCMOS camera model?");
if (settings.modelDirectory == null) {
settings.modelDirectory = settings.directory;
settings.modelName = "sCMOS Camera";
}
egd.addStringField("Model_name", settings.modelName, 30);
egd.addDirectoryField("Model_directory", settings.modelDirectory);
egd.showDialog();
if (!egd.wasCanceled()) {
settings.modelName = egd.getNextString();
settings.modelDirectory = egd.getNextString();
saveCameraModel(width, height, bias, gain, variance);
}
// Remove the status from the ij.io.ImageWriter class
IJ.showStatus("");
ImageJUtils.log("Analysis time = " + TextUtils.millisToString(System.currentTimeMillis() - start));
}
use of uk.ac.sussex.gdsc.smlm.ij.SeriesImageSource 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();
}
}
}
Aggregations