Skip to content

N5Utils.saveRegion errors when trying to append to an existing image. #62

Description

@odinsbane

I have a test that checks modifying an existing ngff zarr image. It uses N5Utiles.saveRegion while using the alpha versions to be able to create v0.5 ngff zarr files I get the following error.

Exception in thread "main" java.lang.NegativeArraySizeException: -98304
at org.janelia.saalfeldlab.n5.DataType.lambda$static$1(DataType.java:56)
at org.janelia.saalfeldlab.n5.DataType.createDataBlock(DataType.java:174)
at org.janelia.saalfeldlab.n5.imglib2.N5Utils$MergeChunkSupplier.get(N5Utils.java:1860)
at org.janelia.saalfeldlab.n5.shard.DefaultDatasetAccess.writeRegionRecursive(DefaultDatasetAccess.java:379)
at org.janelia.saalfeldlab.n5.shard.DefaultDatasetAccess.writeRegion(DefaultDatasetAccess.java:313)
at org.janelia.saalfeldlab.n5.GsonKeyValueN5Writer.writeRegion(GsonKeyValueN5Writer.java:233)
at org.janelia.saalfeldlab.n5.imglib2.N5Utils.saveRegion(N5Utils.java:1384)
at org.janelia.saalfeldlab.n5.imglib2.N5Utils.saveRegion(N5Utils.java:1314)

I'm using the latest alpha versions available on maven, so maybe master has already solved this issue.

I made an example to demonstrate the problem. It's a bit long to make it compete.

import ij.ImagePlus;
import ij.ImageStack;
import ij.measure.Calibration;
import ij.process.ImageProcessor;
import ij.process.ShortProcessor;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.img.VirtualStackAdapter;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.NumericType;
import net.imglib2.view.Views;
import org.janelia.saalfeldlab.n5.N5Writer;
import org.janelia.saalfeldlab.n5.blosc.BloscCompression;
import org.janelia.saalfeldlab.n5.imglib2.N5Utils;
import org.janelia.saalfeldlab.n5.universe.N5Factory;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;

public class BreakingAppending {
    final static double oz = -10;
    final static double oy = -5;
    final static double ox = -15;
    final static double px = 0.25;
    final static double py = 0.25;
    final static double pz = 2.0;
    final static double fi = 60;
    final static String unit = "µm";
    final static String timeUnit = "sec";

    static Set<Path> createdFolders = new HashSet<>();

    /**
     * Creates an image plus with the desired array dimensions. It will be
     * calibrated to the global calibration values.
     *
     * Each slice has a pixel value corresponding to its location in the
     * image stack.
     *
     * @param w width
     * @param h height
     * @param z slices
     * @param t frames
     * @param c channels
     * @return A stack of ShortProcessors
     */
    static ImagePlus generic(int w, int h, int z, int t, int c){
        Calibration cb = new Calibration();
        cb.zOrigin = oz;
        cb.yOrigin = oy;
        cb.xOrigin = ox;

        cb.pixelDepth = pz;
        cb.pixelHeight = py;
        cb.pixelWidth = px;

        cb.frameInterval = fi;
        cb.setTimeUnit(timeUnit);
        cb.setUnit(unit);

        ImagePlus plus = new ImagePlus();
        plus.setCalibration(cb);

        ImageStack stack = new ImageStack(w, h);
        for(int i = 0; i<z*c*t; i++){
            ImageProcessor proc = new ShortProcessor(w, h);
            short[] px =(short[])proc.getPixels();
            for(int j = 0; j<px.length; j++){
                px[j] = (short)i;
            }
            stack.addSlice(proc);
        }

        plus.setStack(stack, c, z, t);
        return plus;

    }
    public static <T extends NativeType<T> & NumericType<T>> RandomAccessibleInterval<T>  getXYZCTRandomAccessIntervale(ImagePlus plus){
        RandomAccessibleInterval<T> img = (RandomAccessibleInterval<T>) VirtualStackAdapter.wrap(plus);
        if(plus.getNChannels() > 1){
            //switches channesl with z.
            img = Views.moveAxis(img, 2, 3);
        } else{
            //add a channel.
            img = Views.addDimension(img, 0L, 0L);
            if( plus.getNFrames() > 1 ){
                //switch time to last position.
                img = Views.moveAxis(img, 3, 4);
            }
        }
        if(plus.getNFrames() == 1){
            img = Views.addDimension(img, 0L, 0L);
        }
        return img;
    }

    public static <T extends NativeType<T> & NumericType<T>> void append(){
        int w = 96;
        int h = 64;
        int z = 16;
        int t = 1;
        int c = 2;
        ImagePlus plus = generic(w, h, z, t, c);
        N5Factory factory = new N5Factory();
        factory.zarrDimensionSeparator("/");
        Path op = Paths.get("modify-test.zarr");
        try (N5Writer writer = factory.openWriter(op.toString())) {
            RandomAccessibleInterval<T> img = getXYZCTRandomAccessIntervale(plus);

            String datasetPath = "";
            String arrayDatasetPath = "/s0";

            String shapeKey = "shape";

            int[] blocks = {plus.getWidth(), plus.getHeight(), plus.getNSlices(), 1, 1};
            N5Utils.save(
                    img,
                    writer,
                    datasetPath + arrayDatasetPath,
                    blocks,
                    new BloscCompression()
            );
            long[] shape = new long[5];
            int n = shape.length - 1;


            long[] translation = new long[shape.length];
            translation[n] = 2;

            N5Utils.saveRegion(Views.translate(img, translation), writer, datasetPath + arrayDatasetPath);
        }
    }
    public static void main(String[] args){
        append();
    }

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions