use of uk.ac.sussex.gdsc.smlm.data.config.PSFProtos.PSF in project GDSC-SMLM by aherbert.
the class CreateData method showSummary.
private double showSummary(List<? extends FluorophoreSequenceModel> fluorophores, List<LocalisationModel> localisations) {
IJ.showStatus("Calculating statistics ...");
final Statistics[] stats = new Statistics[NAMES.length];
for (int i = 0; i < stats.length; i++) {
stats[i] = (settings.getShowHistograms() || alwaysRemoveOutliers[i]) ? new StoredDataStatistics() : new Statistics();
}
// Find the largest timepoint
final ImagePlus outputImp = WindowManager.getImage(benchmarkImageId);
int frameCount;
if (outputImp == null) {
sortLocalisationsByTime(localisations);
frameCount = localisations.get(localisations.size() - 1).getTime();
} else {
frameCount = outputImp.getStackSize();
}
final int[] countHistogram = new int[frameCount + 1];
// Use the localisations that were drawn to create the sampled on/off times
rebuildNeighbours(localisations);
// Assume that there is at least one localisation
final LocalisationModel first = localisations.get(0);
// The current localisation
int currentId = first.getId();
// The last time this localisation was on
int lastT = first.getTime();
// Number of blinks
int blinks = 0;
// On-time of current pulse
int currentT = 0;
double signal = 0;
final double centreOffset = settings.getSize() * 0.5;
// Used to convert the sampled times in frames into seconds
final double framesPerSecond = 1000.0 / settings.getExposureTime();
// final double gain = new CreateDataSettingsHelper(settings).getTotalGainSafe();
for (final LocalisationModel l : localisations) {
final double[] data = l.getData();
if (data == null) {
throw new IllegalStateException("No localisation data. This should not happen!");
}
final double noise = data[1];
final double sx = data[2];
final double sy = data[3];
final double intensityInPhotons = data[4];
// Q. What if the noise is zero, i.e. no background photon / read noise?
// Just ignore it at current. This is only an approximation to the SNR estimate
// if this is not a Gaussian spot.
final double snr = Gaussian2DPeakResultHelper.getMeanSignalUsingP05(intensityInPhotons, sx, sy) / noise;
stats[SIGNAL].add(intensityInPhotons);
stats[NOISE].add(noise);
if (noise != 0) {
stats[SNR].add(snr);
}
// if (l.isContinuous())
if (l.getNext() != null && l.getPrevious() != null) {
stats[SIGNAL_CONTINUOUS].add(intensityInPhotons);
if (noise != 0) {
stats[SNR_CONTINUOUS].add(snr);
}
}
final int id = l.getId();
// Check if this a new fluorophore
if (currentId != id) {
// Add previous fluorophore
stats[SAMPLED_BLINKS].add(blinks);
stats[SAMPLED_T_ON].add(currentT / framesPerSecond);
stats[TOTAL_SIGNAL].add(signal);
// Reset
blinks = 0;
currentT = 1;
currentId = id;
signal = intensityInPhotons;
} else {
signal += intensityInPhotons;
// Check if the current fluorophore pulse is broken (i.e. a blink)
if (l.getTime() - 1 > lastT) {
blinks++;
stats[SAMPLED_T_ON].add(currentT / framesPerSecond);
currentT = 1;
stats[SAMPLED_T_OFF].add(((l.getTime() - 1) - lastT) / framesPerSecond);
} else {
// Continuous on-time
currentT++;
}
}
lastT = l.getTime();
countHistogram[lastT]++;
stats[X].add((l.getX() - centreOffset) * settings.getPixelPitch());
stats[Y].add((l.getY() - centreOffset) * settings.getPixelPitch());
stats[Z].add(l.getZ() * settings.getPixelPitch());
}
// Final fluorophore
stats[SAMPLED_BLINKS].add(blinks);
stats[SAMPLED_T_ON].add(currentT / framesPerSecond);
stats[TOTAL_SIGNAL].add(signal);
// Samples per frame
for (int t = 1; t < countHistogram.length; t++) {
stats[SAMPLES].add(countHistogram[t]);
}
if (fluorophores != null) {
for (final FluorophoreSequenceModel f : fluorophores) {
stats[BLINKS].add(f.getNumberOfBlinks());
// On-time
for (final double t : f.getOnTimes()) {
stats[T_ON].add(t);
}
// Off-time
for (final double t : f.getOffTimes()) {
stats[T_OFF].add(t);
}
}
} else {
// show no blinks
stats[BLINKS].add(0);
stats[T_ON].add(1);
}
if (results != null) {
// Convert depth-of-field to pixels
final double depth = settings.getDepthOfField() / settings.getPixelPitch();
try {
// Get widths
final WidthResultProcedure wp = new WidthResultProcedure(results, DistanceUnit.PIXEL);
wp.getW();
stats[WIDTH].add(wp.wx);
} catch (final DataException ex) {
ImageJUtils.log("Unable to compute width: " + ex.getMessage());
}
try {
// Get z depth
final StandardResultProcedure sp = new StandardResultProcedure(results, DistanceUnit.PIXEL);
sp.getXyz();
// Get precision
final PrecisionResultProcedure pp = new PrecisionResultProcedure(results);
pp.getPrecision();
stats[PRECISION].add(pp.precisions);
for (int i = 0; i < pp.size(); i++) {
if (Math.abs(sp.z[i]) < depth) {
stats[PRECISION_IN_FOCUS].add(pp.precisions[i]);
}
}
} catch (final DataException ex) {
ImageJUtils.log("Unable to compute LSE precision: " + ex.getMessage());
}
// Compute density per frame. Multi-thread for speed
if (settings.getDensityRadius() > 0) {
final int threadCount = Prefs.getThreads();
final Ticker ticker = ImageJUtils.createTicker(results.getLastFrame(), threadCount, "Calculating density ...");
final ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
final List<Future<?>> futures = new LinkedList<>();
final TFloatArrayList coordsX = new TFloatArrayList();
final TFloatArrayList coordsY = new TFloatArrayList();
final Statistics densityStats = stats[DENSITY];
final float radius = (float) (settings.getDensityRadius() * getHwhm());
final Rectangle bounds = results.getBounds();
final double area = (double) bounds.width * bounds.height;
// Store the density for each result.
final int[] allDensity = new int[results.size()];
final FrameCounter counter = results.newFrameCounter();
results.forEach((PeakResultProcedure) result -> {
if (counter.advance(result.getFrame())) {
counter.increment(runDensityCalculation(threadPool, futures, coordsX, coordsY, densityStats, radius, area, allDensity, counter.getCount(), ticker));
}
coordsX.add(result.getXPosition());
coordsY.add(result.getYPosition());
});
runDensityCalculation(threadPool, futures, coordsX, coordsY, densityStats, radius, area, allDensity, counter.getCount(), ticker);
ConcurrencyUtils.waitForCompletionUnchecked(futures);
threadPool.shutdown();
ImageJUtils.finished();
// Split results into singles (density = 0) and clustered (density > 0)
final MemoryPeakResults singles = copyMemoryPeakResults("No Density");
final MemoryPeakResults clustered = copyMemoryPeakResults("Density");
counter.reset();
results.forEach((PeakResultProcedure) result -> {
final int density = allDensity[counter.getAndIncrement()];
result.setOrigValue(density);
if (density == 0) {
singles.add(result);
} else {
clustered.add(result);
}
});
}
}
final StringBuilder sb = new StringBuilder();
sb.append(datasetNumber).append('\t');
if (settings.getCameraType() == CameraType.SCMOS) {
sb.append("sCMOS (").append(settings.getCameraModelName()).append(") ");
final Rectangle bounds = cameraModel.getBounds();
sb.append(" ").append(bounds.x).append(",").append(bounds.y);
final int size = settings.getSize();
sb.append(" ").append(size).append("x").append(size);
} else if (CalibrationProtosHelper.isCcdCameraType(settings.getCameraType())) {
sb.append(CalibrationProtosHelper.getName(settings.getCameraType()));
final int size = settings.getSize();
sb.append(" ").append(size).append("x").append(size);
if (settings.getCameraType() == CameraType.EMCCD) {
sb.append(" EM=").append(settings.getEmGain());
}
sb.append(" CG=").append(settings.getCameraGain());
sb.append(" RN=").append(settings.getReadNoise());
sb.append(" B=").append(settings.getBias());
} else {
throw new IllegalStateException();
}
sb.append(" QE=").append(settings.getQuantumEfficiency()).append('\t');
sb.append(settings.getPsfModel());
if (psfModelType == PSF_MODEL_IMAGE) {
sb.append(" Image").append(settings.getPsfImageName());
} else if (psfModelType == PSF_MODEL_ASTIGMATISM) {
sb.append(" model=").append(settings.getAstigmatismModel());
} else {
sb.append(" DoF=").append(MathUtils.rounded(settings.getDepthOfFocus()));
if (settings.getEnterWidth()) {
sb.append(" SD=").append(MathUtils.rounded(settings.getPsfSd()));
} else {
sb.append(" λ=").append(MathUtils.rounded(settings.getWavelength()));
sb.append(" NA=").append(MathUtils.rounded(settings.getNumericalAperture()));
}
}
sb.append('\t');
sb.append((fluorophores == null) ? localisations.size() : fluorophores.size()).append('\t');
sb.append(stats[SAMPLED_BLINKS].getN() + (int) stats[SAMPLED_BLINKS].getSum()).append('\t');
sb.append(localisations.size()).append('\t');
sb.append(frameCount).append('\t');
sb.append(MathUtils.rounded(areaInUm)).append('\t');
sb.append(MathUtils.rounded(localisations.size() / (areaInUm * frameCount), 4)).append('\t');
sb.append(MathUtils.rounded(getHwhm(), 4)).append('\t');
double sd = getPsfSd();
sb.append(MathUtils.rounded(sd, 4)).append('\t');
sd *= settings.getPixelPitch();
final double sa = PsfCalculator.squarePixelAdjustment(sd, settings.getPixelPitch()) / settings.getPixelPitch();
sb.append(MathUtils.rounded(sa, 4)).append('\t');
// Width not valid for the Image PSF.
// Q. Is this true? We can approximate the FHWM for a spot-like image PSF.
final int nStats = (psfModelType == PSF_MODEL_IMAGE) ? stats.length - 1 : stats.length;
for (int i = 0; i < nStats; i++) {
final double centre = (alwaysRemoveOutliers[i]) ? ((StoredDataStatistics) stats[i]).getStatistics().getPercentile(50) : stats[i].getMean();
sb.append(MathUtils.rounded(centre, 4)).append('\t');
}
createSummaryTable().accept(sb.toString());
// Show histograms
if (settings.getShowHistograms() && !java.awt.GraphicsEnvironment.isHeadless()) {
IJ.showStatus("Calculating histograms ...");
final boolean[] chosenHistograms = getChoosenHistograms();
final WindowOrganiser wo = new WindowOrganiser();
final HistogramPlotBuilder builder = new HistogramPlotBuilder(TITLE);
for (int i = 0; i < NAMES.length; i++) {
if (chosenHistograms[i]) {
builder.setData((StoredDataStatistics) stats[i]).setName(NAMES[i]).setIntegerBins(integerDisplay[i]).setRemoveOutliersOption((settings.getRemoveOutliers() || alwaysRemoveOutliers[i]) ? 2 : 0).setNumberOfBins(settings.getHistogramBins()).show(wo);
}
}
wo.tile();
}
IJ.showStatus("");
return stats[SIGNAL].getMean();
}
use of uk.ac.sussex.gdsc.smlm.data.config.PSFProtos.PSF in project GDSC-SMLM by aherbert.
the class BenchmarkSpotFilter method showDialog.
private boolean showDialog() {
final ExtendedGenericDialog gd = new ExtendedGenericDialog(TITLE);
settings = Settings.load();
config = settings.config;
final StringBuilder sb = new StringBuilder();
sb.append("Finds spots in the benchmark image created by CreateData plugin.\n");
final double s = simulationParameters.sd / simulationParameters.pixelPitch;
final double sa = getSa() / simulationParameters.pixelPitch;
sb.append("PSF width = ").append(MathUtils.rounded(s)).append(" px (sa = ").append(MathUtils.rounded(sa)).append(" px). HWHM = ").append(MathUtils.rounded(s * Gaussian2DFunction.SD_TO_HWHM_FACTOR)).append(" px\n");
sb.append("Simulation depth = ").append(MathUtils.rounded(simulationParameters.depth)).append(" nm");
if (simulationParameters.fixedDepth) {
sb.append(" (fixed)");
}
sb.append("\n \nConfigure the spot filter:");
gd.addMessage(sb.toString());
if (batchMode) {
// Support enumeration of single/difference spot filters
gd.addCheckbox("Mean", settings.batchMean);
gd.addCheckbox("Gaussian", settings.batchGaussian);
gd.addCheckbox("Circular", settings.batchCircular);
gd.addCheckbox("Median", settings.batchMedian);
// For difference filters we set the smoothing for the second filter
// using only one distance
gd.addMessage("Difference filter settings:");
gd.addCheckbox("Difference_filter", settings.differenceFilter);
gd.addSlider("Difference_smoothing", 1.5, 5, settings.differenceSmooth);
gd.addMessage("Local maxima search settings:");
gd.addSlider("Min_search_width", 1, 4, settings.minSearch);
gd.addSlider("Max_search_width", 1, 4, settings.maxSearch);
gd.addCheckbox("Filter_relative_distances (to HWHM)", settings.filterRelativeDistances);
} else {
gd.addChoice("Spot_filter_type", SettingsManager.getDataFilterTypeNames(), config.getDataFilterType().ordinal());
gd.addChoice("Spot_filter", SettingsManager.getDataFilterMethodNames(), config.getDataFilterMethod(0).ordinal());
gd.addCheckbox("Filter_relative_distances (to HWHM)", !config.getDataFilterParameterAbsolute(0));
gd.addSlider("Smoothing", 0, 2.5, config.getDataFilterParameterValue(0));
gd.addSlider("Search_width", 1, 4, settings.search);
}
gd.addSlider("Border", 0, 5, settings.border);
gd.addMessage("Scoring options:");
gd.addCheckbox("Score_relative_distances (to HWHM)", settings.scoreRelativeDistances);
gd.addSlider("Analysis_border", 0, 5, settings.analysisBorder);
gd.addCheckbox("Hard_border", settings.hardBorder);
gd.addChoice("Matching_method", Settings.MATCHING_METHOD, settings.matchingMethod);
gd.addSlider("Match_distance", 0.5, 3.5, settings.upperDistance);
gd.addSlider("Lower_distance", 0, 3.5, settings.lowerDistance);
gd.addSlider("Signal_factor", 0, 3.5, settings.upperSignalFactor);
gd.addSlider("Lower_factor", 0, 3.5, settings.lowerSignalFactor);
gd.addSlider("Recall_fraction", 50, 100, settings.recallFraction);
if (!batchMode) {
gd.addCheckbox("Show_plots", settings.showPlot);
gd.addCheckbox("Plot_rank_by_intensity", settings.rankByIntensity);
gd.addCheckbox("Show_failures_plots", settings.showFailuresPlot);
gd.addCheckbox("Show_TP", settings.showTP);
gd.addCheckbox("Show_FP", settings.showFP);
gd.addCheckbox("Show_FN", settings.showFN);
}
if (extraOptions) {
gd.addCheckbox("Debug", settings.debug);
}
if (batchMode) {
gd.addHelp(HelpUrls.getUrl("filter-spot-data-batch"));
} else {
gd.addHelp(HelpUrls.getUrl("filter-spot-data"));
}
gd.showDialog();
if (gd.wasCanceled()) {
return false;
}
// Here we use PSF stored in the results if supported (i.e. a Gaussian).
// The results are likely to come from the CreateData simulation.
final PSF psf = results.getPsf();
if (PsfHelper.isGaussian2D(psf)) {
config.getFitConfiguration().setPsf(results.getPsf());
} else {
config.getFitConfiguration().setInitialPeakStdDev(s);
}
if (batchMode) {
settings.batchMean = gd.getNextBoolean();
settings.batchGaussian = gd.getNextBoolean();
settings.batchCircular = gd.getNextBoolean();
settings.batchMedian = gd.getNextBoolean();
if (!(settings.batchMean || settings.batchGaussian || settings.batchCircular || settings.batchMedian)) {
return false;
}
settings.differenceFilter = gd.getNextBoolean();
settings.differenceSmooth = gd.getNextNumber();
settings.minSearch = (int) gd.getNextNumber();
settings.maxSearch = (int) gd.getNextNumber();
settings.filterRelativeDistances = gd.getNextBoolean();
} else {
config.setDataFilterType(SettingsManager.getDataFilterTypeValues()[gd.getNextChoiceIndex()]);
final int filterIndex = gd.getNextChoiceIndex();
settings.filterRelativeDistances = gd.getNextBoolean();
final double smoothing = Math.abs(gd.getNextNumber());
config.setDataFilter(SettingsManager.getDataFilterMethodValues()[filterIndex], MathUtils.roundUsingDecimalPlaces(smoothing, 3), !settings.filterRelativeDistances, 0);
settings.search = gd.getNextNumber();
}
settings.border = gd.getNextNumber();
settings.scoreRelativeDistances = gd.getNextBoolean();
settings.analysisBorder = Math.abs(gd.getNextNumber());
settings.hardBorder = gd.getNextBoolean();
settings.matchingMethod = gd.getNextChoiceIndex();
settings.upperDistance = Math.abs(gd.getNextNumber());
settings.lowerDistance = Math.abs(gd.getNextNumber());
settings.upperSignalFactor = Math.abs(gd.getNextNumber());
settings.lowerSignalFactor = Math.abs(gd.getNextNumber());
settings.recallFraction = Math.abs(gd.getNextNumber());
if (!batchMode) {
settings.showPlot = gd.getNextBoolean();
settings.rankByIntensity = gd.getNextBoolean();
settings.showFailuresPlot = gd.getNextBoolean();
settings.showTP = gd.getNextBoolean();
settings.showFP = gd.getNextBoolean();
settings.showFN = gd.getNextBoolean();
}
if (extraOptions) {
settings.debug = gd.getNextBoolean();
}
settings.save();
if (gd.invalidNumber()) {
return false;
}
if (settings.lowerDistance > settings.upperDistance) {
settings.lowerDistance = settings.upperDistance;
}
if (settings.lowerSignalFactor > settings.upperSignalFactor) {
settings.lowerSignalFactor = settings.upperSignalFactor;
}
// Set border here so that the results are consistent with single-filter mode.
config.setBorder(MathUtils.roundUsingDecimalPlaces(settings.border, 3), !settings.filterRelativeDistances);
if (!batchMode) {
// Single filter ...
config.setSearch(MathUtils.roundUsingDecimalPlaces(settings.search, 3), !settings.filterRelativeDistances);
// Allow more complicated filters to be configured
if (!PeakFit.configureDataFilter(config, PeakFit.FLAG_NO_SAVE)) {
return false;
}
}
int analysisBorder;
if (settings.scoreRelativeDistances) {
// Convert distance to PSF standard deviation units
final double hwhmMax = config.getHwhmMax();
matchDistance = settings.upperDistance * hwhmMax;
lowerMatchDistance = settings.lowerDistance * hwhmMax;
analysisBorder = (int) (settings.analysisBorder * hwhmMax);
} else {
matchDistance = settings.upperDistance;
lowerMatchDistance = settings.lowerDistance;
analysisBorder = (int) settings.analysisBorder;
}
if (analysisBorder > 0) {
border = new Rectangle(analysisBorder, analysisBorder, imp.getWidth() - 2 * analysisBorder, imp.getHeight() - 2 * analysisBorder);
} else {
border = new Rectangle(imp.getWidth(), imp.getHeight());
}
return true;
}
use of uk.ac.sussex.gdsc.smlm.data.config.PSFProtos.PSF in project GDSC-SMLM by aherbert.
the class CreateData method createPsf.
private PSF createPsf(double psfSd) {
if (psf == null) {
if (psfModelType == PSF_MODEL_ASTIGMATISM) {
// Note: the astigmatismModel may not yet be created so create if necessary.
// This is used to store the best PSF to use to fit the data.
AstigmatismModel astigmatismModel = this.astigmatismModel;
if (astigmatismModel == null) {
astigmatismModel = AstigmatismModelManager.getModel(settings.getAstigmatismModel());
}
if (astigmatismModel == null) {
throw new IllegalArgumentException("Failed to load model: " + settings.getAstigmatismModel());
}
// Assume conversion for simulation
try {
if (DoubleEquality.relativeError(astigmatismModel.getNmPerPixel(), settings.getPixelPitch()) > 1e-6) {
// Convert to nm
astigmatismModel = AstigmatismModelManager.convert(astigmatismModel, DistanceUnit.NM, DistanceUnit.NM);
// Reset pixel pitch. This will draw the spot using the correct size on the different
// size pixels.
astigmatismModel = astigmatismModel.toBuilder().setNmPerPixel(settings.getPixelPitch()).build();
}
// Convert for simulation in pixels
astigmatismModel = AstigmatismModelManager.convert(astigmatismModel, DistanceUnit.PIXEL, DistanceUnit.PIXEL);
} catch (final ConversionException ex) {
// Wrap so this can be caught as the same type
throw new IllegalArgumentException(ex);
}
psf = PsfProtosHelper.createPsf(astigmatismModel, DistanceUnit.PIXEL, DistanceUnit.PIXEL);
psf = psf.toBuilder().setModelName(settings.getAstigmatismModel()).build();
} else {
PSF.Builder psfBuilder;
// Set the PSF as a Gaussian using the width at z=0.
// In future this could be improved for other PSFs.
psfBuilder = PsfProtosHelper.defaultOneAxisGaussian2DPSF.toBuilder();
psfBuilder.getParametersBuilder(PsfHelper.INDEX_SX).setValue(psfSd);
psf = psfBuilder.build();
}
}
return psf;
}
use of uk.ac.sussex.gdsc.smlm.data.config.PSFProtos.PSF in project GDSC-SMLM by aherbert.
the class SettingsManager method readFitEngineConfiguration.
/**
* Read the FitEngineConfiguration from the settings file in the settings directory. This loads
* the current Calibration, PSF and FitEngineSettings.
*
* @param flags the flags
* @return the FitEngineConfiguration
*/
public static FitEngineConfiguration readFitEngineConfiguration(int flags) {
final FitEngineSettings fitEngineSettings = readFitEngineSettings(flags);
final Calibration calibration = readCalibration(flags);
final PSF psf = readPsf(flags);
return new FitEngineConfiguration(fitEngineSettings, calibration, psf);
}
use of uk.ac.sussex.gdsc.smlm.data.config.PSFProtos.PSF in project GDSC-SMLM by aherbert.
the class ImageJImagePeakResultsTest method checkCanAddUsingDifferentMethods.
private void checkCanAddUsingDifferentMethods(RandomSeed seed, int displayFlags) {
final UniformRandomProvider rand = RngUtils.create(seed.getSeed());
displayFlags |= ImageJImagePeakResults.DISPLAY_SIGNAL;
final ImageJImagePeakResults[] r = new ImageJImagePeakResults[8];
final PSF psf = PsfHelper.create(PSFType.ONE_AXIS_GAUSSIAN_2D);
for (int i = 0; i < r.length; i++) {
r[i] = new ImageJImagePeakResults(title + i, bounds, 1);
r[i].setDisplayFlags(displayFlags);
r[i].setPsf(psf);
begin(r[i]);
}
final int size = 20;
final int[] t = new int[size];
final float[] x = new float[size];
final float[] y = new float[size];
final float[] v = new float[size];
for (int i = 0; i < size; i++) {
t[i] = i;
x[i] = (rand.nextFloat() * bounds.width);
y[i] = (rand.nextFloat() * bounds.height);
v[i] = (rand.nextFloat());
addPeakResult(r[0], x[i], y[i], v[i]);
addPeakResult(r[1], t[i], x[i], y[i], v[i]);
addValue(r[2], x[i], y[i], v[i]);
addValue(r[3], t[i], x[i], y[i], v[i]);
}
addPeakResults(r[4], x, y, v);
addPeakResults(r[5], t, x, y, v);
addValues(r[6], x, y, v);
addValues(r[7], t, x, y, v);
final float[][] image = new float[r.length][];
for (int i = 0; i < r.length; i++) {
r[i].end();
image[i] = getImage(r[i]);
logger.log(TestLogUtils.getRecord(Level.FINE, "[%d] = %s", i, Arrays.toString(image[i])));
}
// Test single value adds
float[] expecteds = image[0];
IntArrayFormatSupplier msg = new IntArrayFormatSupplier("Single add image %d", 1);
for (int i = 1; i < 4; i++) {
final float[] actuals = image[i];
Assertions.assertArrayEquals(expecteds, actuals, msg.set(0, i));
}
// Test multi value adds
expecteds = image[4];
msg = new IntArrayFormatSupplier("Multi add image %d", 1);
for (int i = 5; i < image.length; i++) {
final float[] actuals = image[i];
Assertions.assertArrayEquals(expecteds, actuals, msg.set(0, i));
}
// Test they are roughly the same (differences occur due to floating point summation
Assertions.assertArrayEquals(expecteds, image[0], 1e-5f, "Single != Multi");
}
Aggregations