Search in sources :

Example 1 with CustomTricubicFunction

use of uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction in project GDSC-SMLM by aherbert.

the class CubicSplineCalculatorTest method canComputeCoefficientsForGaussianFunction.

@Test
void canComputeCoefficientsForGaussianFunction() {
    final int x = 4;
    final int y = 4;
    final int z = 4;
    final double xscale = 1;
    final double yscale = 0.5;
    final double zscale = 2.0;
    final double[] xval = SimpleArrayUtils.newArray(x, 0, xscale);
    final double[] yval = SimpleArrayUtils.newArray(y, 0, yscale);
    final double[] zval = SimpleArrayUtils.newArray(z, 0, zscale);
    final double[][][] fval = createData(x, y, z, null);
    final CustomTricubicInterpolatingFunction f1 = new CustomTricubicInterpolator().interpolate(xval, yval, zval, fval);
    final double[] exp = new double[64];
    f1.getSplineNode(1, 1, 1).getCoefficients(exp);
    final CustomTricubicFunction f = CustomTricubicFunctionUtils.create(exp);
    final CubicSplinePosition[] s = new CubicSplinePosition[4];
    for (int i = 0; i < 4; i++) {
        s[i] = new CubicSplinePosition((double) i / 3);
    }
    final double[][][] value = new double[4][4][4];
    final double[] b = new double[64];
    int count = 0;
    for (int k = 0; k < 4; k++) {
        for (int j = 0; j < 4; j++) {
            for (int i = 0; i < 4; i++) {
                value[i][j][k] = f.value(s[i], s[j], s[k]);
                b[count++] = value[i][j][k];
            }
        }
    }
    final CubicSplineCalculator calc = new CubicSplineCalculator();
    double[] obs = calc.compute(value);
    Assertions.assertArrayEquals(exp, obs, 1e-6);
    obs = calc.compute(b);
    Assertions.assertArrayEquals(exp, obs, 1e-6);
}
Also used : CustomTricubicInterpolator(uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicInterpolator) CustomTricubicInterpolatingFunction(uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicInterpolatingFunction) CubicSplinePosition(uk.ac.sussex.gdsc.core.math.interpolation.CubicSplinePosition) CustomTricubicFunction(uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction) Test(org.junit.jupiter.api.Test)

Example 2 with CustomTricubicFunction

use of uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction in project GDSC-SMLM by aherbert.

the class CubicSplineDataTest method canExternaliseFunction.

private static void canExternaliseFunction(RandomSeed seed, boolean singlePrecision) throws IOException {
    final UniformRandomProvider r = RngUtils.create(seed.getSeed());
    final int x = 6;
    final int y = 5;
    final int z = 4;
    final int size = x * y;
    final CustomTricubicFunction[][] splines = new CustomTricubicFunction[z][x * y];
    final double[] a = new double[64];
    for (int zz = 0; zz < z; zz++) {
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < 64; j++) {
                a[j] = r.nextDouble();
            }
            splines[zz][i] = CustomTricubicFunctionUtils.create(a);
            if (singlePrecision) {
                splines[zz][i] = splines[zz][i].toSinglePrecision();
            }
        }
    }
    final CubicSplineData f1 = new CubicSplineData(x, y, splines);
    final ByteArrayOutputStream b = new ByteArrayOutputStream();
    f1.write(b);
    final byte[] bytes = b.toByteArray();
    final CubicSplineData f2 = CubicSplineData.read(new ByteArrayInputStream(bytes));
    final double[] exp = new double[64];
    final double[] obs = new double[64];
    for (int zz = 0; zz < z; zz++) {
        for (int i = 0; i < size; i++) {
            f1.splines[zz][i].getCoefficients(exp);
            f2.splines[zz][i].getCoefficients(obs);
            Assertions.assertArrayEquals(exp, obs);
        }
    }
}
Also used : ByteArrayInputStream(java.io.ByteArrayInputStream) UniformRandomProvider(org.apache.commons.rng.UniformRandomProvider) ByteArrayOutputStream(java.io.ByteArrayOutputStream) CustomTricubicFunction(uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction)

Example 3 with CustomTricubicFunction

use of uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction in project GDSC-SMLM by aherbert.

the class CubicSplineManager method createCubicSpline.

/**
 * Creates the cubic spline.
 *
 * @param imagePsf the image PSF details
 * @param image the image
 * @param singlePrecision Set to true to use single precision (float values) to store the cubic
 *        spline coefficients
 * @return the cubic spline PSF
 */
public static CubicSplinePsf createCubicSpline(ImagePSFOrBuilder imagePsf, ImageStack image, final boolean singlePrecision) {
    final int maxx = image.getWidth();
    final int maxy = image.getHeight();
    final int maxz = image.getSize();
    final float[][] psf = new float[maxz][];
    for (int z = 0; z < maxz; z++) {
        psf[z] = ImageJImageConverter.getData(image.getPixels(z + 1), null);
    }
    // We reduce by a factor of 3
    final int maxi = (maxx - 1) / 3;
    final int maxj = (maxy - 1) / 3;
    final int maxk = (maxz - 1) / 3;
    final int size = maxi * maxj;
    final CustomTricubicFunction[][] splines = new CustomTricubicFunction[maxk][size];
    final int threadCount = Prefs.getThreads();
    final Ticker ticker = ImageJUtils.createTicker((long) maxi * maxj * maxk, threadCount);
    final ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
    final LocalList<Future<?>> futures = new LocalList<>(maxk);
    // spline node along each dimension, i.e. dimension length = n*3 + 1 with n the number of nodes.
    for (int k = 0; k < maxk; k++) {
        final int kk = k;
        futures.add(threadPool.submit(() -> {
            final CubicSplineCalculator calc = new CubicSplineCalculator();
            final double[] value = new double[64];
            final int zz = 3 * kk;
            for (int j = 0, index = 0; j < maxj; j++) {
                // 4x4 block origin in the XY data
                int index0 = 3 * j * maxx;
                for (int i = 0; i < maxi; i++, index++) {
                    ticker.tick();
                    int count = 0;
                    for (int z = 0; z < 4; z++) {
                        final float[] data = psf[zz + z];
                        for (int y = 0; y < 4; y++) {
                            for (int x = 0, ii = index0 + y * maxx; x < 4; x++) {
                                value[count++] = data[ii++];
                            }
                        }
                    }
                    splines[kk][index] = CustomTricubicFunctionUtils.create(calc.compute(value));
                    if (singlePrecision) {
                        splines[kk][index] = splines[kk][index].toSinglePrecision();
                    }
                    index0 += 3;
                }
            }
        }));
    }
    ticker.stop();
    threadPool.shutdown();
    ConcurrencyUtils.waitForCompletionUnchecked(futures);
    // Normalise
    double maxSum = 0;
    for (int k = 0; k < maxk; k++) {
        double sum = 0;
        for (int i = 0; i < size; i++) {
            sum += splines[k][i].value000();
        }
        if (maxSum < sum) {
            maxSum = sum;
        }
    }
    if (maxSum == 0) {
        throw new IllegalStateException("The cubic spline has no maximum signal");
    }
    final double scale = 1.0 / maxSum;
    for (int k = 0; k < maxk; k++) {
        for (int i = 0; i < size; i++) {
            splines[k][i] = splines[k][i].scale(scale);
        }
    }
    // Create on an integer scale
    final CubicSplineData f = new CubicSplineData(maxi, maxj, splines);
    // Create a new info with the PSF details
    final ImagePSF.Builder b = ImagePSF.newBuilder();
    b.setImageCount(imagePsf.getImageCount());
    // Reducing the image has the effect of enlarging the pixel size
    b.setPixelSize(imagePsf.getPixelSize() * 3.0);
    b.setPixelDepth(imagePsf.getPixelDepth() * 3.0);
    // The centre has to be moved as we reduced the image size by 3.
    // In the ImagePSF the XY centre puts 0.5 at the centre of the pixel.
    // The spline puts 0,0 at the centre of each pixel for convenience.
    double cx = maxi / 2.0;
    if (imagePsf.getXCentre() != 0) {
        cx = (imagePsf.getXCentre() - 0.5) / 3;
    }
    double cy = maxj / 2.0;
    if (imagePsf.getYCentre() != 0) {
        cy = (imagePsf.getYCentre() - 0.5) / 3;
    }
    double cz = maxk / 2.0;
    if (imagePsf.getZCentre() != 0) {
        cz = imagePsf.getZCentre() / 3;
    } else if (imagePsf.getCentreImage() != 0) {
        cz = (imagePsf.getCentreImage() - 1) / 3.0;
    }
    b.setXCentre(cx);
    b.setYCentre(cy);
    b.setZCentre(cz);
    return new CubicSplinePsf(b.build(), f);
}
Also used : CubicSplineCalculator(uk.ac.sussex.gdsc.smlm.function.cspline.CubicSplineCalculator) Ticker(uk.ac.sussex.gdsc.core.logging.Ticker) LocalList(uk.ac.sussex.gdsc.core.utils.LocalList) ImagePSF(uk.ac.sussex.gdsc.smlm.data.config.PSFProtos.ImagePSF) CubicSplineData(uk.ac.sussex.gdsc.smlm.function.cspline.CubicSplineData) ExecutorService(java.util.concurrent.ExecutorService) Future(java.util.concurrent.Future) CustomTricubicFunction(uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction)

Example 4 with CustomTricubicFunction

use of uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction in project GDSC-SMLM by aherbert.

the class CubicSplineData method sample.

/**
 * Sample the function.
 *
 * <p>n samples will be taken per node in each dimension. A final sample is taken at the end of
 * the sample range thus the final range for each axis will be the current axis range.
 *
 * <p>The procedure setValue(int,int,int,double) method will be executed in ZYX order.
 *
 * @param nx the number of samples per spline node in the x dimension
 * @param ny the number of samples per spline node in the y dimension
 * @param nz the number of samples per spline node in the z dimension
 * @param procedure the procedure
 * @param progress the progress
 * @throws IllegalArgumentException If the number of sample is not positive
 */
public void sample(int nx, int ny, int nz, TrivalueProcedure procedure, TrackProgress progress) {
    if (nx < 1 || ny < 1 || nz < 1) {
        throw new IllegalArgumentException("Samples must be positive");
    }
    // We can interpolate all nodes n-times plus a final point at the last node
    final int maxx = (getMaxX() - 1) * nx;
    final int maxy = (getMaxY() - 1) * ny;
    final int maxz = (getMaxZ() - 1) * nz;
    if (!procedure.setDimensions(maxx + 1, maxy + 1, maxz + 1)) {
        return;
    }
    final Ticker ticker = Ticker.createStarted(progress, (long) (maxx + 1) * (maxy + 1) * (maxz + 1), false);
    // Pre-compute interpolation tables
    final CubicSplinePosition[] sx = createCubicSplinePosition(nx);
    final CubicSplinePosition[] sy = createCubicSplinePosition(ny);
    final CubicSplinePosition[] sz = createCubicSplinePosition(nz);
    // Write axis values
    // Cache the table and the spline position to use for each interpolation point
    final int[] xt = new int[maxx + 1];
    final int[] xp = new int[maxx + 1];
    for (int x = 0; x <= maxx; x++) {
        int xposition = x / nx;
        int xtable = x % nx;
        if (x == maxx) {
            // Final interpolation point
            xposition--;
            xtable = nx;
        }
        xt[x] = xtable;
        xp[x] = xposition;
        procedure.setX(x, xposition + (double) xtable / nx);
    }
    final int[] yt = new int[maxy + 1];
    final int[] yp = new int[maxy + 1];
    for (int y = 0; y <= maxy; y++) {
        int yposition = y / ny;
        int ytable = y % ny;
        if (y == maxy) {
            // Final interpolation point
            yposition--;
            ytable = ny;
        }
        yt[y] = ytable;
        yp[y] = yposition;
        procedure.setY(y, yposition + (double) ytable / ny);
    }
    final int[] zt = new int[maxz + 1];
    final int[] zp = new int[maxz + 1];
    for (int z = 0; z <= maxz; z++) {
        int zposition = z / nz;
        int ztable = z % nz;
        if (z == maxz) {
            // Final interpolation point
            zposition--;
            ztable = nz;
        }
        zt[z] = ztable;
        zp[z] = zposition;
        procedure.setZ(z, zposition + (double) ztable / nz);
    }
    // Write interpolated values
    for (int z = 0; z <= maxz; z++) {
        final CustomTricubicFunction[] xySplines = splines[zp[z]];
        for (int y = 0; y <= maxy; y++) {
            final int index = yp[y] * getMaxX();
            for (int x = 0; x <= maxx; x++) {
                procedure.setValue(x, y, z, xySplines[index + xp[x]].value(sx[xt[x]], sy[yt[y]], sz[zt[z]]));
                ticker.tick();
            }
        }
    }
    ticker.stop();
}
Also used : Ticker(uk.ac.sussex.gdsc.core.logging.Ticker) CubicSplinePosition(uk.ac.sussex.gdsc.core.math.interpolation.CubicSplinePosition) FloatCustomTricubicFunction(uk.ac.sussex.gdsc.core.math.interpolation.FloatCustomTricubicFunction) CustomTricubicFunction(uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction)

Example 5 with CustomTricubicFunction

use of uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction in project GDSC-SMLM by aherbert.

the class CubicSplineData method read.

/**
 * Read a tricubic spline from the input stream.
 *
 * <p>Note: For best performance a buffered input stream should be used.
 *
 * @param inputStream the input stream
 * @param progress the progress
 * @return the tricubic spline data
 * @throws IOException Signals that an I/O exception has occurred.
 */
public static CubicSplineData read(InputStream inputStream, TrackProgress progress) throws IOException {
    // Read dimensions
    final DataInput in = new DataInputStream(inputStream);
    final int maxx = in.readInt();
    final int maxy = in.readInt();
    final int maxz = in.readInt();
    final Ticker ticker = Ticker.createStarted(progress, (long) maxx * maxy * maxz, false);
    // Read precision
    final boolean singlePrecision = in.readBoolean();
    final SplineReader reader = (singlePrecision) ? new FloatSplineReader() : new DoubleSplineReader();
    final int size = maxx * maxy;
    final CustomTricubicFunction[][] splines = new CustomTricubicFunction[maxz][maxx * maxy];
    for (int z = 0; z < maxz; z++) {
        for (int i = 0; i < size; i++) {
            splines[z][i] = reader.read(in);
            ticker.tick();
        }
    }
    ticker.stop();
    // Skip validation
    return new CubicSplineData(maxx, maxy, splines, false);
}
Also used : DataInput(java.io.DataInput) Ticker(uk.ac.sussex.gdsc.core.logging.Ticker) DataInputStream(java.io.DataInputStream) FloatCustomTricubicFunction(uk.ac.sussex.gdsc.core.math.interpolation.FloatCustomTricubicFunction) CustomTricubicFunction(uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction)

Aggregations

CustomTricubicFunction (uk.ac.sussex.gdsc.core.math.interpolation.CustomTricubicFunction)7 CubicSplinePosition (uk.ac.sussex.gdsc.core.math.interpolation.CubicSplinePosition)4 Ticker (uk.ac.sussex.gdsc.core.logging.Ticker)3 Test (org.junit.jupiter.api.Test)2 FloatCustomTricubicFunction (uk.ac.sussex.gdsc.core.math.interpolation.FloatCustomTricubicFunction)2 CubicSplineCalculator (uk.ac.sussex.gdsc.smlm.function.cspline.CubicSplineCalculator)2 ImageStack (ij.ImageStack)1 ImageProcessor (ij.process.ImageProcessor)1 ByteArrayInputStream (java.io.ByteArrayInputStream)1 ByteArrayOutputStream (java.io.ByteArrayOutputStream)1 DataInput (java.io.DataInput)1 DataInputStream (java.io.DataInputStream)1 Arrays (java.util.Arrays)1 ExecutorService (java.util.concurrent.ExecutorService)1 Future (java.util.concurrent.Future)1 Logger (java.util.logging.Logger)1 InitialGuess (org.apache.commons.math3.optim.InitialGuess)1 MaxEval (org.apache.commons.math3.optim.MaxEval)1 PointValuePair (org.apache.commons.math3.optim.PointValuePair)1 SimpleBounds (org.apache.commons.math3.optim.SimpleBounds)1