use of uk.ac.sussex.gdsc.smlm.data.config.CalibrationReader in project GDSC-SMLM by aherbert.
the class SummariseResults method createSummary.
private static String createSummary(StringBuilder sb, MemoryPeakResults result, int[] removeNullResults) {
sb.setLength(0);
final DescriptiveStatistics[] stats = new DescriptiveStatistics[2];
for (int i = 0; i < stats.length; i++) {
stats[i] = new DescriptiveStatistics();
}
if (result.hasNullResults()) {
IJ.log("Null results in dataset: " + result.getName());
if (removeNullResults[0] == UNKNOWN) {
final GenericDialog gd = new GenericDialog(TITLE);
gd.addMessage("There are invalid results in memory.\n \nClean these results?");
gd.enableYesNoCancel();
gd.hideCancelButton();
gd.showDialog();
removeNullResults[0] = (gd.wasOKed()) ? YES : NO;
}
if (removeNullResults[0] == NO) {
result = result.copy();
}
result.removeNullResults();
}
final CalibrationReader calibration = result.getCalibrationReader();
PrecisionMethod precisionMethod = PrecisionMethod.PRECISION_METHOD_NA;
boolean stored = false;
final int size = result.size();
if (size > 0) {
// Precision
try {
final PrecisionResultProcedure p = new PrecisionResultProcedure(result);
// Use stored precision if possible
stored = result.hasPrecision();
precisionMethod = p.getPrecision(stored);
for (final double v : p.precisions) {
stats[0].addValue(v);
}
} catch (final DataException ex) {
// Ignore
}
// SNR
try {
final SnrResultProcedure p = new SnrResultProcedure(result);
p.getSnr();
for (final double v : p.snr) {
stats[1].addValue(v);
}
} catch (final DataException ex) {
// Ignore
}
}
sb.append(result.getName());
int maxT = 0;
if (result.size() == 0) {
sb.append("\t0\t0");
} else {
sb.append('\t').append(result.size());
maxT = result.getMaxFrame();
sb.append('\t').append(maxT);
}
if (calibration != null && calibration.hasExposureTime()) {
sb.append('\t').append(TextUtils.millisToString((long) Math.ceil(maxT * calibration.getExposureTime())));
} else {
sb.append("\t-");
}
if (size > 0) {
final boolean includeDeviations = result.hasDeviations();
final long memorySize = MemoryPeakResults.estimateMemorySize(size, includeDeviations);
final String memory = TextUtils.bytesToString(memorySize);
sb.append('\t').append(memory);
} else {
sb.append("\t-");
}
final Rectangle bounds = result.getBounds(true);
TextUtils.formatTo(sb, "\t%d,%d,%d,%d", bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height);
if (calibration != null) {
sb.append('\t').append(calibration.hasNmPerPixel() ? MathUtils.rounded(calibration.getNmPerPixel()) : '-');
sb.append('\t').append(calibration.hasExposureTime() ? MathUtils.rounded(calibration.getExposureTime()) : '-');
if (calibration.hasCameraType()) {
sb.append('\t').append(CalibrationProtosHelper.getName(calibration.getCameraType()));
if (calibration.isCcdCamera()) {
sb.append(" bias=").append(calibration.getBias());
sb.append(" gain=").append(calibration.getCountPerPhoton());
}
} else {
sb.append("\t-");
}
sb.append('\t').append(calibration.hasDistanceUnit() ? UnitHelper.getShortName(calibration.getDistanceUnit()) : '-');
sb.append('\t').append(calibration.hasIntensityUnit() ? UnitHelper.getShortName(calibration.getIntensityUnit()) : '-');
} else {
sb.append("\t\t\t\t\t");
}
if (result.is3D()) {
sb.append("\tY");
} else {
sb.append("\tN");
}
sb.append("\t").append(FitProtosHelper.getName(precisionMethod));
if (stored) {
sb.append(" (Stored)");
}
for (int i = 0; i < stats.length; i++) {
if (Double.isNaN(stats[i].getMean())) {
sb.append("\t-\t-\t-\t-");
} else {
sb.append('\t').append(IJ.d2s(stats[i].getMean(), 3));
sb.append('\t').append(IJ.d2s(stats[i].getPercentile(50), 3));
sb.append('\t').append(IJ.d2s(stats[i].getMin(), 3));
sb.append('\t').append(IJ.d2s(stats[i].getMax(), 3));
}
}
return sb.toString();
}
use of uk.ac.sussex.gdsc.smlm.data.config.CalibrationReader in project GDSC-SMLM by aherbert.
the class TrackPopulationAnalysis method run.
@Override
public void run(String arg) {
SmlmUsageTracker.recordPlugin(this.getClass(), arg);
if (MemoryPeakResults.isMemoryEmpty()) {
IJ.error(TITLE, "No localisations in memory");
return;
}
settings = Settings.load();
// Saved by reference so just save now
settings.save();
// Read in multiple traced datasets
// All datasets must have the same pixel pitch and exposure time
// Get parameters
// Convert datasets to tracks
// For each track compute the 4 local track features using the configured window
//
// Optional:
// Fit a multi-variate Gaussian mixture model to the data
// (using the configured number of components/populations)
// Assign each point in the track using the model.
// Smooth the assignments.
//
// The alternative is to use the localisation category to assign populations.
//
// Plot histograms of each track parameter, coloured by component
final List<MemoryPeakResults> combinedResults = new LocalList<>();
if (!showInputDialog(combinedResults)) {
return;
}
final boolean hasCategory = showHasCategoryDialog(combinedResults);
if (!showDialog(hasCategory)) {
return;
}
ImageJUtils.log(TITLE + "...");
final List<Trace> tracks = getTracks(combinedResults, settings.window, settings.minTrackLength);
if (tracks.isEmpty()) {
IJ.error(TITLE, "No tracks. Please check the input data and min track length setting.");
return;
}
final Calibration cal = combinedResults.get(0).getCalibration();
final CalibrationReader cr = new CalibrationReader(cal);
// Use micrometer / second
final TypeConverter<DistanceUnit> distanceConverter = cr.getDistanceConverter(DistanceUnit.UM);
final double exposureTime = cr.getExposureTime() / 1000.0;
final Pair<int[], double[][]> trackData = extractTrackData(tracks, distanceConverter, exposureTime, hasCategory);
final double[][] data = trackData.getValue();
// Histogram the raw data.
final Array2DRowRealMatrix raw = new Array2DRowRealMatrix(data, false);
final WindowOrganiser wo = new WindowOrganiser();
// Store the histogram data for plotting the components
final double[][] columns = new double[FEATURE_NAMES.length][];
final double[][] limits = new double[FEATURE_NAMES.length][];
// Get column data
for (int i = 0; i < FEATURE_NAMES.length; i++) {
columns[i] = raw.getColumn(i);
if (i == FEATURE_D) {
// Plot using a logarithmic scale
SimpleArrayUtils.apply(columns[i], Math::log10);
}
limits[i] = MathUtils.limits(columns[i]);
}
// Compute histogram bins
final int[] bins = new int[FEATURE_NAMES.length];
if (settings.histogramBins > 0) {
Arrays.fill(bins, settings.histogramBins);
} else {
for (int i = 0; i < FEATURE_NAMES.length; i++) {
bins[i] = HistogramPlot.getBins(StoredData.create(columns[i]), BinMethod.FD);
}
// Use the maximum so all histograms look the same
Arrays.fill(bins, MathUtils.max(bins));
}
// Compute plots
final Plot[] plots = new Plot[FEATURE_NAMES.length];
for (int i = 0; i < FEATURE_NAMES.length; i++) {
final double[][] hist = HistogramPlot.calcHistogram(columns[i], limits[i][0], limits[i][1], bins[i]);
plots[i] = new Plot(TITLE + " " + FEATURE_NAMES[i], getFeatureLabel(i, i == FEATURE_D), "Frequency");
plots[i].addPoints(hist[0], hist[1], Plot.BAR);
ImageJUtils.display(plots[i].getTitle(), plots[i], ImageJUtils.NO_TO_FRONT, wo);
}
wo.tile();
// The component for each data point
int[] component;
// The number of components
int numComponents;
// Data used to fit the Gaussian mixture model
double[][] fitData;
// The fitted model
MixtureMultivariateGaussianDistribution model;
if (hasCategory) {
// Use the category as the component.
// No fit data and no output model
fitData = null;
model = null;
// The component is stored at the end of the raw track data.
final int end = data[0].length - 1;
component = Arrays.stream(data).mapToInt(d -> (int) d[end]).toArray();
numComponents = MathUtils.max(component) + 1;
// In the EM algorithm the probability of each data point is computed and normalised to
// sum to 1. The normalised probabilities are averaged to create the weights.
// Note the probability of each data point uses the previous weight and the algorithm
// iterates.
// This is not a fitted model but the input model so use
// zero weights to indicate no fitting was performed.
final double[] weights = new double[numComponents];
// Remove the trailing component to show the 'model' in a table.
createModelTable(Arrays.stream(data).map(d -> Arrays.copyOf(d, end)).toArray(double[][]::new), weights, component);
} else {
// Multivariate Gaussian mixture EM
// Provide option to not use the anomalous exponent in the population mix.
int sortDimension = SORT_DIMENSION;
if (settings.ignoreAlpha) {
// Remove index 0. This shifts the sort dimension.
sortDimension--;
fitData = Arrays.stream(data).map(d -> Arrays.copyOfRange(d, 1, d.length)).toArray(double[][]::new);
} else {
fitData = SimpleArrayUtils.deepCopy(data);
}
final MultivariateGaussianMixtureExpectationMaximization mixed = fitGaussianMixture(fitData, sortDimension);
if (mixed == null) {
IJ.error(TITLE, "Failed to fit a mixture model");
return;
}
model = sortComponents(mixed.getFittedModel(), sortDimension);
// For the best model, assign to the most likely population.
component = assignData(fitData, model);
// Table of the final model using the original data (i.e. not normalised)
final double[] weights = model.getWeights();
numComponents = weights.length;
createModelTable(data, weights, component);
}
// Output coloured histograms of the populations.
final LUT lut = LutHelper.createLut(settings.lutIndex);
IntFunction<Color> colourMap;
if (LutHelper.getColour(lut, 0).equals(Color.BLACK)) {
colourMap = i -> LutHelper.getNonZeroColour(lut, i, 0, numComponents - 1);
} else {
colourMap = i -> LutHelper.getColour(lut, i, 0, numComponents - 1);
}
for (int i = 0; i < FEATURE_NAMES.length; i++) {
// Extract the data for each component
final double[] col = columns[i];
final Plot plot = plots[i];
for (int n = 0; n < numComponents; n++) {
final StoredData feature = new StoredData();
for (int j = 0; j < component.length; j++) {
if (component[j] == n) {
feature.add(col[j]);
}
}
if (feature.size() == 0) {
continue;
}
final double[][] hist = HistogramPlot.calcHistogram(feature.values(), limits[i][0], limits[i][1], bins[i]);
// Colour the points
plot.setColor(colourMap.apply(n));
plot.addPoints(hist[0], hist[1], Plot.BAR);
}
plot.updateImage();
}
createTrackDataTable(tracks, trackData, fitData, model, component, cal, colourMap);
// Analysis.
// Assign the original localisations to their track component.
// Q. What about the start/end not covered by the window?
// Save tracks as a dataset labelled with the sub-track ID?
// Output for the bound component and free components track parameters.
// Compute dwell times.
// Other ...
// Track analysis plugin:
// Extract all continuous segments of the same component.
// Produce MSD plot with error bars.
// Fit using FBM model.
}
use of uk.ac.sussex.gdsc.smlm.data.config.CalibrationReader in project GDSC-SMLM by aherbert.
the class Configuration method refreshSettings.
private void refreshSettings(Calibration cal) {
if (cal == null) {
return;
}
// Do not use set() as we support merging a partial calibration
fitConfig.mergeCalibration(cal);
final CalibrationReader calibration = fitConfig.getCalibrationReader();
textCameraType.select(CalibrationProtosHelper.getName(calibration.getCameraType()));
if (calibration.hasNmPerPixel()) {
textNmPerPixel.setText("" + calibration.getNmPerPixel());
}
if (calibration.hasExposureTime()) {
textExposure.setText("" + calibration.getExposureTime());
}
}
use of uk.ac.sussex.gdsc.smlm.data.config.CalibrationReader in project GDSC-SMLM by aherbert.
the class Configuration method showDialog.
/**
* Show the current properties.
*
* @param fitEngineConfiguration the fit engine configuration
* @param save the save
* @return true, if successful
*/
public boolean showDialog(FitEngineConfiguration fitEngineConfiguration, boolean save) {
this.config = fitEngineConfiguration;
fitConfig = config.getFitConfiguration();
pluginSettings = Settings.load();
pluginSettings.save();
final CalibrationReader calibrationReader = fitConfig.getCalibrationReader();
ExtendedGenericDialog gd = new ExtendedGenericDialog(TITLE);
gd.addHelp(HelpUrls.getUrl("fit-configuration"));
gd.addMessage("Configuration settings for the single-molecule localisation microscopy plugins");
final String[] templates = ConfigurationTemplate.getTemplateNames(true);
gd.addChoice("Template", templates, templates[0]);
PeakFit.addCameraOptions(gd, fitConfig);
gd.addNumericField("Calibration (nm/px)", calibrationReader.getNmPerPixel(), 2);
gd.addNumericField("Exposure_time (ms)", calibrationReader.getExposureTime(), 2);
gd.addMessage("--- Gaussian parameters ---");
PeakFit.addPsfOptions(gd, fitConfig);
gd.addMessage("--- Maxima identification ---");
final FitEngineConfigurationProvider provider = this::getFitEngineConfiguration;
PeakFit.addDataFilterOptions(gd, provider);
PeakFit.addSearchOptions(gd, provider);
PeakFit.addBorderOptions(gd, provider);
PeakFit.addFittingOptions(gd, provider);
gd.addMessage("--- Gaussian fitting ---");
gd.addChoice("Fit_solver", SettingsManager.getFitSolverNames(), FitProtosHelper.getName(fitConfig.getFitSolver()));
// Parameters specific to each Fit solver are collected in a second dialog
gd.addNumericField("Fail_limit", config.getFailuresLimit(), 0);
gd.addNumericField("Pass_rate", config.getPassRate(), 2);
gd.addCheckbox("Include_neighbours", config.isIncludeNeighbours());
gd.addSlider("Neighbour_height", 0.01, 1, config.getNeighbourHeightThreshold());
gd.addSlider("Residuals_threshold", 0.01, 1, config.getResidualsThreshold());
PeakFit.addDuplicateDistanceOptions(gd, provider);
gd.addMessage("--- Peak filtering ---\nDiscard fits that shift; are too low; or expand/contract");
gd.addCheckbox("Smart_filter", fitConfig.isSmartFilter());
gd.addCheckbox("Disable_simple_filter", fitConfig.isDisableSimpleFilter());
gd.addSlider("Shift_factor", 0.01, 2, fitConfig.getCoordinateShiftFactor());
gd.addNumericField("Signal_strength", fitConfig.getSignalStrength(), 2);
gd.addNumericField("Min_photons", fitConfig.getMinPhotons(), 0);
gd.addSlider("Min_width_factor", 0, 0.99, fitConfig.getMinWidthFactor());
gd.addSlider("Width_factor", 1, 4.5, fitConfig.getMaxWidthFactor());
PeakFit.addPrecisionOptions(gd, this::getFitConfiguration);
// Add a mouse listener to the config file field
if (ImageJUtils.isShowGenericDialog()) {
final Vector<TextField> numerics = gd.getNumericFields();
final Vector<Checkbox> checkboxes = gd.getCheckboxes();
final Vector<Choice> choices = gd.getChoices();
final Iterator<TextField> nu = numerics.iterator();
final Iterator<Checkbox> cb = checkboxes.iterator();
final Iterator<Choice> ch = choices.iterator();
final Choice textTemplate = ch.next();
textTemplate.addItemListener(this::itemStateChanged);
textCameraType = ch.next();
textNmPerPixel = nu.next();
textExposure = nu.next();
textPsf = ch.next();
textDataFilterType = ch.next();
textDataFilterMethod = ch.next();
textSmooth = nu.next();
textSearch = nu.next();
textBorder = nu.next();
textFitting = nu.next();
textFitSolver = ch.next();
textFailuresLimit = nu.next();
textPassRate = nu.next();
textIncludeNeighbours = cb.next();
textNeighbourHeightThreshold = nu.next();
textResidualsThreshold = nu.next();
textDuplicateDistance = nu.next();
textSmartFilter = cb.next();
textDisableSimpleFilter = cb.next();
textCoordinateShiftFactor = nu.next();
textSignalStrength = nu.next();
textMinPhotons = nu.next();
textMinWidthFactor = nu.next();
textWidthFactor = nu.next();
textPrecisionThreshold = nu.next();
updateFilterInput();
textSmartFilter.addItemListener(this::itemStateChanged);
textDisableSimpleFilter.addItemListener(this::itemStateChanged);
}
if (save) {
gd.enableYesNoCancel("Save", "Save template");
}
gd.showDialog();
if (gd.wasCanceled()) {
return false;
}
// In case a template updated the calibration
final CalibrationWriter calibrationWriter = fitConfig.getCalibrationWriter();
// Ignore the template
gd.getNextChoice();
calibrationWriter.setCameraType(SettingsManager.getCameraTypeValues()[gd.getNextChoiceIndex()]);
calibrationWriter.setNmPerPixel(gd.getNextNumber());
calibrationWriter.setExposureTime(gd.getNextNumber());
fitConfig.setCalibration(calibrationWriter.getCalibration());
fitConfig.setPsfType(PeakFit.getPsfTypeValues()[gd.getNextChoiceIndex()]);
config.setDataFilterType(gd.getNextChoiceIndex());
config.setDataFilter(gd.getNextChoiceIndex(), Math.abs(gd.getNextNumber()), false, 0);
config.setSearch(gd.getNextNumber());
config.setBorder(gd.getNextNumber());
config.setFitting(gd.getNextNumber());
// Some enum values are not supported
fitConfig.setFitSolver(SettingsManager.getFitSolverValues()[gd.getNextChoiceIndex()]);
config.setFailuresLimit((int) gd.getNextNumber());
config.setPassRate(gd.getNextNumber());
config.setIncludeNeighbours(gd.getNextBoolean());
config.setNeighbourHeightThreshold(gd.getNextNumber());
config.setResidualsThreshold(gd.getNextNumber());
config.setDuplicateDistance(gd.getNextNumber());
fitConfig.setSmartFilter(gd.getNextBoolean());
fitConfig.setDisableSimpleFilter(gd.getNextBoolean());
fitConfig.setCoordinateShiftFactor(gd.getNextNumber());
fitConfig.setSignalStrength(gd.getNextNumber());
fitConfig.setMinPhotons(gd.getNextNumber());
fitConfig.setMinWidthFactor(gd.getNextNumber());
fitConfig.setMaxWidthFactor(gd.getNextNumber());
fitConfig.setPrecisionThreshold(gd.getNextNumber());
gd.collectOptions();
// Check arguments
try {
ParameterUtils.isAboveZero("nm per pixel", calibrationWriter.getNmPerPixel());
ParameterUtils.isAboveZero("Exposure time", calibrationWriter.getExposureTime());
if (fitConfig.getPsfTypeValue() != PSFType.ASTIGMATIC_GAUSSIAN_2D_VALUE) {
ParameterUtils.isAboveZero("Initial SD0", fitConfig.getInitialXSd());
if (fitConfig.getPsf().getParametersCount() > 1) {
ParameterUtils.isAboveZero("Initial SD1", fitConfig.getInitialYSd());
}
}
ParameterUtils.isAboveZero("Search_width", config.getSearch());
ParameterUtils.isAboveZero("Fitting_width", config.getFitting());
ParameterUtils.isPositive("Neighbour height threshold", config.getNeighbourHeightThreshold());
ParameterUtils.isPositive("Residuals threshold", config.getResidualsThreshold());
ParameterUtils.isPositive("Duplicate distance", config.getDuplicateDistance());
if (!fitConfig.isSmartFilter()) {
ParameterUtils.isPositive("Coordinate Shift factor", fitConfig.getCoordinateShiftFactor());
ParameterUtils.isPositive("Signal strength", fitConfig.getSignalStrength());
ParameterUtils.isPositive("Min photons", fitConfig.getMinPhotons());
ParameterUtils.isPositive("Min width factor", fitConfig.getMinWidthFactor());
ParameterUtils.isPositive("Width factor", fitConfig.getMaxWidthFactor());
ParameterUtils.isPositive("Precision threshold", fitConfig.getPrecisionThreshold());
}
} catch (final IllegalArgumentException ex) {
IJ.error(TITLE, ex.getMessage());
return false;
}
if (gd.invalidNumber()) {
return false;
}
final int flags = PeakFit.FLAG_NO_SAVE;
if (!PeakFit.configurePsfModel(config, flags)) {
return false;
}
if (!PeakFit.configureResultsFilter(config, flags)) {
return false;
}
if (!PeakFit.configureDataFilter(config, flags)) {
return false;
}
PeakFit.configureFitSolver(config, null, null, flags);
if (save) {
final boolean saveToFile = !gd.wasOKed();
if (saveToFile) {
gd = new ExtendedGenericDialog(TITLE);
gd.addFilenameField("Template_filename", pluginSettings.templateFilename);
gd.addMessage("Add notes to the template ...");
gd.addTextAreas(pluginSettings.notes, null, 10, 60);
gd.showDialog();
if (gd.wasCanceled()) {
return false;
}
pluginSettings.save();
final String filename = gd.getNextString();
pluginSettings.notes = gd.getNextText();
if (filename != null) {
pluginSettings.templateFilename = FileUtils.replaceExtension(filename, ".txt");
final File file = new File(pluginSettings.templateFilename);
final String name = FileUtils.removeExtension(file.getName());
final TemplateSettings.Builder settings = TemplateSettings.newBuilder();
settings.addNotes(pluginSettings.notes);
settings.setCalibration(fitConfig.getCalibration());
settings.setFitEngineSettings(config.getFitEngineSettings());
// Note: No results settings are currently supported
settings.setPsf(fitConfig.getPsf());
if (!ConfigurationTemplate.saveTemplate(name, settings.build(), file)) {
IJ.error(TITLE, "Failed to save to file: " + pluginSettings.templateFilename);
}
}
} else {
SettingsManager.writeSettings(config, 0);
}
}
return true;
}
use of uk.ac.sussex.gdsc.smlm.data.config.CalibrationReader in project GDSC-SMLM by aherbert.
the class ConvertResults method showDialog.
private static boolean showDialog(MemoryPeakResults results) {
final ExtendedGenericDialog gd = new ExtendedGenericDialog(TITLE);
gd.addMessage("Convert the current units for the results");
gd.addHelp(HelpUrls.getUrl("convert-results"));
final CalibrationReader cr = CalibrationWriter.create(results.getCalibration());
gd.addChoice("Distance_unit", SettingsManager.getDistanceUnitNames(), cr.getDistanceUnitValue());
gd.addNumericField("Calibration (nm/px)", cr.getNmPerPixel(), 2);
gd.addChoice("Intensity_unit", SettingsManager.getIntensityUnitNames(), cr.getIntensityUnitValue());
gd.addNumericField("Gain (Count/photon)", cr.getCountPerPhoton(), 2);
gd.addChoice("Angle_unit", SettingsManager.getAngleUnitNames(), cr.getAngleUnitValue());
gd.showDialog();
if (gd.wasCanceled()) {
return false;
}
final CalibrationWriter cw = results.getCalibrationWriterSafe();
final DistanceUnit distanceUnit = SettingsManager.getDistanceUnitValues()[gd.getNextChoiceIndex()];
cw.setNmPerPixel(Math.abs(gd.getNextNumber()));
final IntensityUnit intensityUnit = SettingsManager.getIntensityUnitValues()[gd.getNextChoiceIndex()];
cw.setCountPerPhoton(Math.abs(gd.getNextNumber()));
final AngleUnit angleUnit = SettingsManager.getAngleUnitValues()[gd.getNextChoiceIndex()];
// Don't set the calibration with bad values
if (distanceUnit.getNumber() > 0 && !(cw.getNmPerPixel() > 0)) {
IJ.error(TITLE, "Require positive nm/pixel for conversion");
return false;
}
if (intensityUnit.getNumber() > 0 && !(cw.getCountPerPhoton() > 0)) {
IJ.error(TITLE, "Require positive Count/photon for conversion");
return false;
}
final Calibration newCalibration = cw.getCalibration();
results.setCalibration(newCalibration);
if (!results.convertToUnits(distanceUnit, intensityUnit, angleUnit)) {
IJ.error(TITLE, "Conversion failed");
return false;
}
return true;
}
Aggregations