Last active
May 18, 2021 18:57
-
-
Save oeway/786f65e8994742ba9cbbd781cc9377cb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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