use of uk.ac.sussex.gdsc.core.utils.LocalList in project GDSC-SMLM by aherbert.
the class PeakResultConversionHelper method getNames.
/**
* Gets the names for the peak results parameters. This includes the standard parameters and any
* additional parameters defined in the PSF. If a parameter name is undefined then unknown is
* returned.
*
* @return the converters
*/
public String[] getNames() {
final LocalList<String> list = new LocalList<>(5);
list.add("Background");
list.add("Intensity");
list.add("X");
list.add("Y");
list.add("Z");
if (psf != null) {
try {
for (final PSFParameter p : PsfHelper.getParameters(psf)) {
final String name = p.getName();
list.add(TextUtils.isNullOrEmpty(name) ? "unknown" : name);
}
} catch (final ConfigurationException ex) {
// Ignore
}
}
return list.toArray(new String[0]);
}
use of uk.ac.sussex.gdsc.core.utils.LocalList in project GDSC-SMLM by aherbert.
the class PeakResultConversionHelper method getConverters.
/**
* Gets the converters for the peak results parameters. This includes the standard parameters and
* any additional parameters defined in the PSF. If a parameter unit type is undefined then an
* identity converter is created.
*
* @return the converters
*/
public Converter[] getConverters() {
final LocalList<Converter> list = new LocalList<>(5);
getIntensityConverter();
getDistanceConverter();
list.add(intensityConverter);
list.add(intensityConverter);
list.add(distanceConverter);
list.add(distanceConverter);
list.add(distanceConverter);
if (psf != null) {
try {
for (final PSFParameter p : PsfHelper.getParameters(psf)) {
switch(p.getUnit()) {
case DISTANCE:
list.add(distanceConverter);
break;
case INTENSITY:
list.add(intensityConverter);
break;
case ANGLE:
list.add(getAngleConverter());
break;
default:
list.add(new IdentityTypeConverter<>(p.getUnit()));
}
}
} catch (final ConfigurationException ex) {
// Ignore
}
}
return list.toArray(new Converter[0]);
}
use of uk.ac.sussex.gdsc.core.utils.LocalList in project GDSC-SMLM by aherbert.
the class PeakResultConversionHelper method getUnitNames.
/**
* Gets the unit names for the peak results parameters. This includes the standard parameters and
* any additional parameters defined in the PSF. If a parameter unit is undefined then an empty
* string is returned.
*
* @return the converters
*/
public String[] getUnitNames() {
final LocalList<String> list = new LocalList<>(5);
getIntensityConverter();
getDistanceConverter();
final String safeIntensityUnit = (intensityConverter.to() != null) ? UnitHelper.getShortName(intensityConverter.to()) : "";
final String safeDistanceUnit = (distanceConverter.to() != null) ? UnitHelper.getShortName(distanceConverter.to()) : "";
String safeAngleUnit = null;
list.add(safeIntensityUnit);
list.add(safeIntensityUnit);
list.add(safeDistanceUnit);
list.add(safeDistanceUnit);
list.add(safeDistanceUnit);
if (psf != null) {
try {
for (final PSFParameter p : PsfHelper.getParameters(psf)) {
switch(p.getUnit()) {
case DISTANCE:
list.add(safeDistanceUnit);
break;
case INTENSITY:
list.add(safeIntensityUnit);
break;
case ANGLE:
safeAngleUnit = getOrCreateAngleUnit(safeAngleUnit);
list.add(safeAngleUnit);
break;
default:
list.add("");
}
}
} catch (final ConfigurationException ex) {
// Ignore
}
}
return list.toArray(new String[0]);
}
use of uk.ac.sussex.gdsc.core.utils.LocalList in project GDSC-SMLM by aherbert.
the class PsfDrift method computeDrift.
private void computeDrift() {
// Create a grid of XY offset positions between 0-1 for PSF insert
final double[] grid = new double[settings.gridSize];
for (int i = 0; i < grid.length; i++) {
grid[i] = (double) i / settings.gridSize;
}
// Configure fitting region
final int w = 2 * settings.regionSize + 1;
centrePixel = w / 2;
// Check region size using the image PSF
final double newPsfWidth = imp.getWidth() / settings.scale;
if (Math.ceil(newPsfWidth) > w) {
ImageJUtils.log(TITLE + ": Fitted region size (%d) is smaller than the scaled PSF (%.1f)", w, newPsfWidth);
}
// Create robust PSF fitting settings
final double a = psfSettings.getPixelSize() * settings.scale;
final double sa = PsfCalculator.squarePixelAdjustment(psfSettings.getPixelSize() * (psfSettings.getFwhm() / Gaussian2DFunction.SD_TO_FWHM_FACTOR), a);
fitConfig.setInitialPeakStdDev(sa / a);
fitConfig.setBackgroundFitting(settings.backgroundFitting);
fitConfig.setNotSignalFitting(false);
fitConfig.setComputeDeviations(false);
fitConfig.setDisableSimpleFilter(true);
// Create the PSF over the desired z-depth
final int depth = (int) Math.round(settings.zDepth / psfSettings.getPixelDepth());
int startSlice = psfSettings.getCentreImage() - depth;
int endSlice = psfSettings.getCentreImage() + depth;
final int nSlices = imp.getStackSize();
startSlice = MathUtils.clip(1, nSlices, startSlice);
endSlice = MathUtils.clip(1, nSlices, endSlice);
final ImagePsfModel psf = createImagePsf(startSlice, endSlice, settings.scale);
final int minz = startSlice - psfSettings.getCentreImage();
final int maxz = endSlice - psfSettings.getCentreImage();
final int nZ = maxz - minz + 1;
final int gridSize2 = grid.length * grid.length;
total = nZ * gridSize2;
// Store all the fitting results
final int nStartPoints = getNumberOfStartPoints();
results = new double[total * nStartPoints][];
// TODO - Add ability to iterate this, adjusting the current offset in the PSF
// each iteration
// Create a pool of workers
final int threadCount = Prefs.getThreads();
final Ticker ticker = ImageJUtils.createTicker(total, threadCount, "Fitting...");
final BlockingQueue<Job> jobs = new ArrayBlockingQueue<>(threadCount * 2);
final List<Thread> threads = new LinkedList<>();
for (int i = 0; i < threadCount; i++) {
final Worker worker = new Worker(jobs, psf, w, fitConfig, ticker);
final Thread t = new Thread(worker);
threads.add(t);
t.start();
}
// Fit
outer: for (int z = minz, i = 0; z <= maxz; z++) {
for (int x = 0; x < grid.length; x++) {
for (int y = 0; y < grid.length; y++, i++) {
if (IJ.escapePressed()) {
break outer;
}
put(jobs, new Job(z, grid[x], grid[y], i));
}
}
}
// If escaped pressed then do not need to stop the workers, just return
if (ImageJUtils.isInterrupted()) {
ImageJUtils.finished();
return;
}
// Finish all the worker threads by passing in a null job
for (int i = 0; i < threads.size(); i++) {
put(jobs, new Job());
}
// Wait for all to finish
for (int i = 0; i < threads.size(); i++) {
try {
threads.get(i).join();
} catch (final InterruptedException ex) {
Thread.currentThread().interrupt();
throw new ConcurrentRuntimeException("Unexpected interrupt", ex);
}
}
threads.clear();
ImageJUtils.finished();
// Plot the average and SE for the drift curve
// Plot the recall
final double[] zPosition = new double[nZ];
final double[] avX = new double[nZ];
final double[] seX = new double[nZ];
final double[] avY = new double[nZ];
final double[] seY = new double[nZ];
final double[] recall = new double[nZ];
for (int z = minz, i = 0; z <= maxz; z++, i++) {
final Statistics statsX = new Statistics();
final Statistics statsY = new Statistics();
for (int s = 0; s < nStartPoints; s++) {
int resultPosition = i * gridSize2 + s * total;
final int endResultPosition = resultPosition + gridSize2;
while (resultPosition < endResultPosition) {
if (results[resultPosition] != null) {
statsX.add(results[resultPosition][0]);
statsY.add(results[resultPosition][1]);
}
resultPosition++;
}
}
zPosition[i] = z * psfSettings.getPixelDepth();
avX[i] = statsX.getMean();
seX[i] = statsX.getStandardError();
avY[i] = statsY.getMean();
seY[i] = statsY.getStandardError();
recall[i] = (double) statsX.getN() / (nStartPoints * gridSize2);
}
// Find the range from the z-centre above the recall limit
int centre = 0;
for (int slice = startSlice, i = 0; slice <= endSlice; slice++, i++) {
if (slice == psfSettings.getCentreImage()) {
centre = i;
break;
}
}
if (recall[centre] < settings.recallLimit) {
return;
}
int start = centre;
int end = centre;
for (int i = centre; i-- > 0; ) {
if (recall[i] < settings.recallLimit) {
break;
}
start = i;
}
for (int i = centre; ++i < recall.length; ) {
if (recall[i] < settings.recallLimit) {
break;
}
end = i;
}
final int iterations = 1;
LoessInterpolator loess = null;
if (settings.smoothing > 0) {
loess = new LoessInterpolator(settings.smoothing, iterations);
}
final double[][] smoothx = displayPlot("Drift X", "X (nm)", zPosition, avX, seX, loess, start, end);
final double[][] smoothy = displayPlot("Drift Y", "Y (nm)", zPosition, avY, seY, loess, start, end);
displayPlot("Recall", "Recall", zPosition, recall, null, null, start, end);
windowOrganiser.tile();
// Ask the user if they would like to store them in the image
final GenericDialog gd = new GenericDialog(TITLE);
gd.enableYesNoCancel();
gd.hideCancelButton();
startSlice = psfSettings.getCentreImage() - (centre - start);
endSlice = psfSettings.getCentreImage() + (end - centre);
ImageJUtils.addMessage(gd, "Save the drift to the PSF?\n \nSlices %d (%s nm) - %d (%s nm) above recall limit", startSlice, MathUtils.rounded(zPosition[start]), endSlice, MathUtils.rounded(zPosition[end]));
gd.addMessage("Optionally average the end points to set drift outside the limits.\n" + "(Select zero to ignore)");
gd.addSlider("Number_of_points", 0, 10, settings.positionsToAverage);
gd.showDialog();
if (gd.wasOKed()) {
settings.positionsToAverage = Math.abs((int) gd.getNextNumber());
final Map<Integer, Offset> oldOffset = psfSettings.getOffsetsMap();
final boolean useOldOffset = settings.useOffset && !oldOffset.isEmpty();
final LocalList<double[]> offset = new LocalList<>();
final double pitch = psfSettings.getPixelSize();
int index = 0;
for (int i = start, slice = startSlice; i <= end; slice++, i++) {
index = findCentre(zPosition[i], smoothx, index);
if (index == -1) {
ImageJUtils.log("Failed to find the offset for depth %.2f", zPosition[i]);
continue;
}
// The offset should store the difference to the centre in pixels so divide by the pixel
// pitch
double cx = smoothx[1][index] / pitch;
double cy = smoothy[1][index] / pitch;
if (useOldOffset) {
final Offset o = oldOffset.get(slice);
if (o != null) {
cx += o.getCx();
cy += o.getCy();
}
}
offset.add(new double[] { slice, cx, cy });
}
addMissingOffsets(startSlice, endSlice, nSlices, offset);
final Offset.Builder offsetBuilder = Offset.newBuilder();
final ImagePSF.Builder imagePsfBuilder = psfSettings.toBuilder();
for (final double[] o : offset) {
final int slice = (int) o[0];
offsetBuilder.setCx(o[1]);
offsetBuilder.setCy(o[2]);
imagePsfBuilder.putOffsets(slice, offsetBuilder.build());
}
imagePsfBuilder.putNotes(TITLE, String.format("Solver=%s, Region=%d", PeakFit.getSolverName(fitConfig), settings.regionSize));
imp.setProperty("Info", ImagePsfHelper.toString(imagePsfBuilder));
}
}
use of uk.ac.sussex.gdsc.core.utils.LocalList in project GDSC-SMLM by aherbert.
the class PsfCreator method align2D.
/**
* Align the PSFs with the combined PSF using the Image2DAligner class to align the 2D max
* intensity projections. The final alignment shift is the average of the shift from two
* projection alignments for each dimension.
*
* @param combined the combined
* @param psfs the psfs
* @return The XYZ translations for each PSF
*/
private float[][] align2D(ExtractedPsf combined, final ExtractedPsf[] psfs) {
// Note: For alignment we crop the X/Y projections around the current z-centre
// so the middle of the 2D image is the middle of the projection.
final int n = psfs.length * 3;
final List<Future<?>> futures = new LocalList<>(n);
final Image2DAligner[] align = new Image2DAligner[3];
for (int i = 0; i < 3; i++) {
align[i] = new Image2DAligner();
// No need to set the bounds as the PSF will be smaller
align[i].setReference(combined.getProjection(i, true).duplicate());
}
final float[][] results = new float[psfs.length][3];
for (int j = 0; j < psfs.length; j++) {
final int jj = j;
for (int i = 0; i < 3; i++) {
final int ii = i;
futures.add(threadPool.submit(() -> {
final ExtractedPsf psf = psfs[jj];
final double[] result = align[ii].copy().align(psf.getProjection(ii, true).duplicate(), 10);
// We just average the shift from each projection. There should be
// two shifts for each dimension
results[jj][Projection.getXDimension(ii)] -= result[0] / 2;
results[jj][Projection.getYDimension(ii)] -= result[1] / 2;
// psfs[index].show(TITLE + index);
}));
}
}
ConcurrencyUtils.waitForCompletionUnchecked(futures);
return results;
}
Aggregations