%mavenRepo scijava.public https://maven.scijava.org/content/groups/public
%maven org.scijava:scijava-common:2.97.0
%maven net.imglib2:imglib2:6.2.0
%maven org.janelia.saalfeldlab:n5:3.1.2
%maven org.janelia.saalfeldlab:n5-imglib2:7.0.0
%maven org.janelia.saalfeldlab:n5-universe:1.3.1
In [1]:
In [2]:
import java.nio.file.*;
import java.util.stream.*;
import java.util.concurrent.*;
import com.google.gson.*;
import net.imglib2.*;
import net.imglib2.img.array.*;
import net.imglib2.type.numeric.real.*;
import net.imglib2.view.*;
import net.imglib2.util.*;
import org.janelia.saalfeldlab.n5.*;
import org.janelia.saalfeldlab.n5.imglib2.*;
import org.janelia.saalfeldlab.n5.universe.*;
In [3]:
public static void pathInfo(Path p) {
try {
System.out.println(String.format("%s is %d bytes", p, Files.size(p)));
} catch(IOException e ){}
}
public static void printBlocks(String path) throws IOException {
try (Stream<Path> stream = Files.walk(Paths.get(path))) {
.filter(Files::isRegularFile)
stream.filter( p -> p.getFileName().toString().matches("[0-9]"))
.forEach( x -> { pathInfo(x); });
}
}
In [4]:
// N5Factory can make N5Readers and N5Writers
var factory = new N5Factory();
// trying to open a reader for a container that does not yet exist will throw an error
// var n5Reader = factory.openReader("my-container.n5");
// creating a writer creates a container at the given location
// if it does not already exist
var n5Writer = factory.openWriter("my-container.n5");
// now we can make a reader
var n5Reader = factory.openReader("my-container.n5");
// test if the container exists
.exists(""); // true
n5Reader
// "" and "/" both refer to the root of the container
.exists("/"); // true n5Reader
In [5]:
.openWriter("my-container.h5").getClass(); // HDF5 Format N5Writer
factory.openWriter("my-container.n5").getClass(); // N5 Format N5Writer
factory.openWriter("my-container.zarr").getClass(); // Zarr Format N5Writer factory
In [6]:
.createGroup("foo");
n5Writer.createGroup("foo/bar");
n5Writer.createGroup("lorum/ipsum/dolor/sit/amet");
n5Writer
.exists("lorum/ipsum"); // true
n5Writer.exists("not/a/real/group"); // false n5Writer
In [7]:
.list(""); // [lorum, foo]
n5Writer.list("foo"); // [bar] n5Writer
In [8]:
Arrays.toString(n5Writer.deepList(""));
[lorum, lorum/ipsum, lorum/ipsum/dolor, lorum/ipsum/dolor/sit, lorum/ipsum/dolor/sit/amet, foo, foo/bar]
In [9]:
public static RandomAccessibleInterval<FloatType> demoImage(long... size) {
final RandomAccessibleInterval<FloatType> img = ArrayImgs.floats(size);
float f = 0f;
Cursor<FloatType> c = Views.flatIterable(img).cursor();
while (c.hasNext())
.next().set(f++);
c
return img;
}
In [10]:
// the parameters
var img = demoImage(64,64); // the image to write- size 64 x 64
var groupPath = "data";
var blockSize = new int[]{32,32};
var compression = new GzipCompression();
// save the image
.save(img, n5Writer, groupPath, blockSize, compression); N5Utils
In [11]:
var exec = Executors.newFixedThreadPool(4); // with 4 parallel threads
.save(img, n5Writer, groupPath, blockSize, compression, exec); N5Utils
In [12]:
Code
printBlocks("my-container.n5/data");
my-container.n5/data/1/1 is 1762 bytes
my-container.n5/data/1/0 is 2012 bytes
my-container.n5/data/0/1 is 1763 bytes
my-container.n5/data/0/0 is 2020 bytes
In [13]:
// remove the old data
.remove(groupPath);
n5Writer
// rewrite with a different block size
var blockSize = new int[]{64,8};
.save(img, n5Writer, groupPath, blockSize, compression);
N5Utils
// how many blocks are there?
printBlocks("my-container.n5/data");
my-container.n5/data/0/1 is 837 bytes
my-container.n5/data/0/7 is 847 bytes
my-container.n5/data/0/3 is 839 bytes
my-container.n5/data/0/6 is 844 bytes
my-container.n5/data/0/0 is 968 bytes
my-container.n5/data/0/4 is 846 bytes
my-container.n5/data/0/2 is 840 bytes
my-container.n5/data/0/5 is 847 bytes
In [14]:
// rewrite without compression
var groupPath = "dataNoCompression";
var blockSize = new int[]{32,32};
var compression = new RawCompression();
.save(img, n5Writer, groupPath, blockSize, compression);
N5Utils
// what size are the blocks?
In [15]:
Code
printBlocks("my-container.n5/dataNoCompression");
my-container.n5/dataNoCompression/1/1 is 4108 bytes
my-container.n5/dataNoCompression/1/0 is 4108 bytes
my-container.n5/dataNoCompression/0/1 is 4108 bytes
my-container.n5/dataNoCompression/0/0 is 4108 bytes
In [16]:
var loadedImg = N5Utils.open(n5Writer, groupPath);
Util.getTypeFromInterval(loadedImg).getClass(); // FloatType
Arrays.toString(loadedImg.dimensionsAsLongArray()); // [64, 64]
In [17]:
// overwrite our previous data
var img = ArrayImgs.unsignedBytes(2,2);
.save(img, n5Writer, groupPath, blockSize, compression);
N5Utils
// load the new data, the old data are no longer accessible
var loadedImg = N5Utils.open(n5Writer, groupPath);
Arrays.toString(loadedImg.dimensionsAsLongArray()); // [2, 2]
In [18]:
// create a group inside the container (think: "folder")
var groupName = "put-data-in-me";
.createGroup(groupName);
n5Writer
// attributes have names and values
// make an attribute called "date" with a String value
var attributeName = "date";
.setAttribute(groupName, attributeName, "2024-Jan-01");
n5Writer
// Ask the N5 API to make a double array from the data attribute
// it will try and fail, so an exception will be thrown
try {
var nothing = n5Writer.getAttribute(groupName, attributeName, double[].class);
} catch( N5Exception e ) {
System.out.println("Error: could not get attribute as double[]");
}
// get the value of the "date" attribute as a String
String date = n5Writer.getAttribute(groupName, attributeName, String.class);
date
Error: could not get attribute as double[]
2024-Jan-01
In [19]:
.setAttribute(groupName, "a", 42);
n5Writervar num = n5Writer.getAttribute(groupName, "a", double.class); // 42.0
var str = n5Writer.getAttribute(groupName, "a", String.class); // "42"
In [20]:
Code
class FunWithMetadata {
String name;
int number;
double[] data;
public FunWithMetadata(String name, int number, double[] data) {
this.name = name;
this.number = number;
this.data = data;
}
public String toString(){
return String.format( "FunWithMetadata{%s(%d): %s}",
, number, Arrays.toString(data));
name}
};
In [21]:
var metadata = new FunWithMetadata("Dorothy", 2, new double[]{2.72, 3.14});
.setAttribute(groupName, "metadata", metadata);
n5Writer
// get attribute as an instance of FunWithMetdata
.getAttribute(groupName, "metadata", FunWithMetadata.class); n5Writer
FunWithMetadata{Dorothy(2): [2.72, 3.14]}
In [22]:
// get attribute as an instance of JsonElement
.getAttribute(groupName, "/", JsonElement.class); n5Writer
{"date":"2024-Jan-01","a":42,"metadata":{"name":"Dorothy","number":2,"data":[2.72,3.14]}}
In [23]:
// set attributes
.setAttribute(groupName, "sender", "Alice");
n5Writer.setAttribute(groupName, "receiver", "Bob");
n5Writer
// notice that they're set
.getAttribute(groupName, "sender", String.class); // Alice
n5Writer.getAttribute(groupName, "receiver", String.class); // Bob
n5Writer
// remove "sender"
.removeAttribute(groupName, "sender");
n5Writer
// remove "receiver" and store result in a variable
var receiver = n5Writer.removeAttribute(groupName, "receiver", String.class); // Bob
.getAttribute(groupName, "sender", String.class); // null
n5Writer.getAttribute(groupName, "receiver", String.class); // null n5Writer
In [24]:
Arrays.toString(n5Writer.getAttribute("data", "dimensions", long[].class));
[64, 64]
In [25]:
var arrayMetadata = n5Writer.getDatasetAttributes("data");
.getDimensions();
arrayMetadata.getBlockSize();
arrayMetadata.getDataType();
arrayMetadata.getCompression(); arrayMetadata