Skip to content

Instantly share code, notes, and snippets.

@oeway
Last active May 18, 2021 18:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oeway/786f65e8994742ba9cbbd781cc9377cb to your computer and use it in GitHub Desktop.
Save oeway/786f65e8994742ba9cbbd781cc9377cb to your computer and use it in GitHub Desktop.
<docs>
# NGFF image file Loader for ImageJ.JS
[Next-generation file formats](https://ngff.openmicroscopy.org/latest/) is a specification for storing bioimaging data in the cloud.
This plugin enalbes loading NGFF image into ImageJ.JS.
The implementation is adapted from the [OMERO ImJoy](https://github.com/will-moore/omero-imjoy) made by @will-moore
</docs>
<config lang="json">
{
"name": "NGFF-Loader",
"type": "web-worker",
"tags": [],
"ui": "",
"version": "0.1.1",
"cover": "",
"description": "Loading zarr images into ImageJ.JS",
"icon": "extension",
"inputs": null,
"outputs": null,
"api_version": "0.1.8",
"env": "",
"permissions": [],
"requirements": [],
"dependencies": []
}
</config>
<script lang="javascript">
let zarrLib;
// This plugin wrap the code made by @will-moore
// https://github.com/will-moore/omero-imjoy
function range(length) {
return Array.from({ length }, (_, i) => i);
}
function mergeArrays(arrays) {
// Get the total length of all arrays.
let length = 0;
arrays.forEach(item => {
length += item.length;
});
// Create a new array with total length and merge all source arrays.
// Use first Array.constructor - output Array will be same type
// e.g. Int16Array -> Int16Array
let mergedArray = new arrays[0].constructor(length);
let offset = 0;
arrays.forEach(item => {
mergedArray.set(item, offset);
offset += item.length;
});
return mergedArray;
}
class ImJoyPlugin {
async setup() {
api.log('initialized')
let ij = await api.getWindow("ImageJ.JS");
if(!ij) ij = await api.createWindow({src: "https://ij.imjoy.io", name: "ImageJ.JS"});
await ij.addMenuItem({"_rintf": true, "label": "Load NGFF (experimental)", "callback": this.run.bind(this) })
}
async loadZarrImage(zarr_url) {
if (!zarrLib) zarrLib = await import("https://cdn.skypack.dev/@manzt/zarr-lite");
async function loadPyramid() {
const store = new zarrLib.HTTPStore(zarr_url);
const rootAttrs = await zarrLib.getJson(store, '.zattrs');
console.log('rootAttrs', rootAttrs);
let paths = [];
if ('multiscales' in rootAttrs) {
const {
datasets
} = rootAttrs.multiscales[0];
paths = datasets.map(d => d.path);
}
const p = paths.map(path => zarrLib.openArray({
store,
path
}));
return Promise.all(p);
}
const pyramid = await loadPyramid();
const zarr_arr = pyramid[0]
console.log('zarr_arr', zarr_arr);
let [sizeT, sizeC, sizeZ, sizeY, sizeX] = zarr_arr.shape;
const dtypes = {
"|i1": "int8",
"|u1": "uint8",
"<i2": "int16",
"<u2": "uint16",
"<i4": "int32",
"<u4": "uint32",
"<f4": "float32",
"<f8": "float64"
}
const dtype = dtypes[zarr_arr.dtype] || "uint16";
console.log('dtype', zarr_arr.dtype, dtype);
const totalPlanes = sizeC * sizeZ * sizeT;
let loadedPlanes = 0;
function notifyPlaneLoaded() {
loadedPlanes += 1;
api.showProgress(loadedPlanes/totalPlanes)
api.showMessage(`Loading Image planes: ${loadedPlanes} / ${totalPlanes}`)
}
async function getRaw(zarrArray, chunk) {
const data = await zarrArray.getRaw(chunk)
notifyPlaneLoaded()
return data;
}
const zarrData = await Promise.all(
range(sizeT).flatMap(t => {
return (range(sizeC).flatMap(c => {
return (range(sizeZ).map(z => {
return getRaw(zarr_arr, [t, c, z, null, null])
}))
}))
})
);
// hide the progress bar
await api.showProgress(0)
await api.showMessage(`Image planes loaded successfully.`)
console.log('zarrData', zarrData)
// merge planes into one array
const zarrPlanes = mergeArrays(zarrData.map(d => d.data));
console.log('zarrPlanes', zarrPlanes);
// now make sure ImageJ viewer is loaded
const ij = await api.getWindow("ImageJ.JS");
const filename = zarr_url.split('/').pop().split('#')[0].split('?')[0];
// encode the image data as an ndarray
await ij.viewImage({ _rtype: 'ndarray', _rdtype: dtype, _rshape: [sizeZ * sizeC * sizeT, sizeY, sizeX, 1], _rvalue: zarrPlanes.buffer }, {name: filename});
if(sizeT > 1 || sizeZ > 1)
await ij.runMacro(`run("Stack to Hyperstack...", "order=xyzct channels=${sizeC} slices=${sizeZ} frames=${sizeT} display=Grayscale");`)
}
async run(ctx) {
const url = await api.prompt("Type a URL to your NGFF image file", "https://s3.embassy.ebi.ac.uk/idr/zarr/v0.1/6001240.zarr")
if(url)
await this.loadZarrImage(url)
}
}
api.export(new ImJoyPlugin())
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment