Skip to content

Instantly share code, notes, and snippets.

@will-moore
Created April 9, 2020 22:09
Show Gist options
  • Save will-moore/d0e64b4c0b043f8a71b295c0bdf51653 to your computer and use it in GitHub Desktop.
Save will-moore/d0e64b4c0b043f8a71b295c0bdf51653 to your computer and use it in GitHub Desktop.

Test Zarr.js

Using http://guido.io/zarr.js/ to load data from a zarr file. with NO grouping (can't use the xarray zarr file used by xpublish).

Using code added to https://gitlab.com/openmicroscopy/incubator/omero-napari/-/merge_requests/1 to generate a zarr file (works with 3-channel z-stack).

$ omero napari to_zarr Image:52

Put that zarr file in the same directory as these files, then serve:

$ python -m http.server

Then http://0.0.0.0:8000/ will load 52.zarr (currently hard-coded to work with https://downloads.openmicroscopy.org/images/DV/elena/P-TRE/P-TRE_21_R3D_D3D.dv )

Running everything locally and looking at the time taken to load 3 planes of data (512 x 512) when we load the image or Z-index changes is approx 500 millisecs.

This compares with about 220 millisecs to load a single rendered 3-channel plane in OMERO.iviewer.

<html>
<head>
</head>
<body>
<canvas style="width:512; height: 512; border: solid black 1px" id="canvas"></canvas>
<div>
Z: <input id='zslider' type='range' min='0' max='49' value='25'/>
</div>
<div>
Channel 1:
<label>Red
<input type='radio' name='ch0color' value='red'/>
</label>
<label>Green
<input type='radio' name='ch0color' value='green'/>
</label>
<label>Blue
<input type='radio' name='ch0color' checked value='blue'/>
</label>
<input id='ch0start' type='range' min='0' max='2555' value='0'>
<input id='ch0end' type='range' min='0' max='2555' value='2555'>
</div>
<div>
Channel 2:
<label>Red
<input type='radio' name='ch1color' value='red'/>
</label>
<label>Green
<input type='radio' name='ch1color' checked value='green'/>
</label>
<label>Blue
<input type='radio' name='ch1color' value='blue'/>
</label>
<input id='ch1start' type='range' min='0' max='3676' value='0'>
<input id='ch1end' type='range' min='0' max='3676' value='3676'>
</div>
<div>
Channel 3:
<label>Red
<input type='radio' name='ch2color' checked value='red'/>
</label>
<label>Green
<input type='radio' name='ch2color' value='green'/>
</label>
<label>Blue
<input type='radio' name='ch2color' value='blue'/>
</label>
<input id='ch2start' type='range' min='0' max='10567' value='0'>
<input id='ch2end' type='range' min='0' max='10567' value='10567'>
</div>
</body>
<script type="module" src="index.js"></script>
</html>
import { loadPlane } from './zarr.js';
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
let width;
let height;
let sizeC = 3;
let theZ = 2;
let channelColors = ['blue', 'green', 'red'];
let settings = [[5, 1430], [69, 2058], [55, 6259]];
let pixelData = [];
const colors = {'red': 0, 'green': 1, 'blue': 2}
function toRGBA(value, start, end, color) {
let rgba = [0, 0, 0, 256];
let val = 256 * (value - start) / (end - start);
rgba[colors[color]] = val;
return rgba;
}
async function loadImage() {
// load data...
pixelData = [];
let n = Date.now()
for (let c=0; c< sizeC; c++) {
let chunk = await loadPlane('52_plain.zarr', c, theZ);
height = chunk.shape[0];
width = chunk.shape[1];
pixelData.push(chunk.data);
console.log('loadImage time', Date.now() - n);
}
canvas.width = width;
canvas.height = height;
renderImage();
}
function renderImage() {
console.log('renderImage...');
updateChannelColors();
updateChannelSettings();
// render data...
let n = Date.now();
let renderedData = new Uint8ClampedArray(height * width * 4);
for (let i=0; i<width*height; i++) {
let pixel = [0, 0, 0, 256];
for (let c=0; c < sizeC; c++) {
const [start,end] = settings[c];
const color = channelColors[c];
let rgba = toRGBA(pixelData[c][i], start, end, color);
pixel[0] += rgba[0];
pixel[1] += rgba[1];
pixel[2] += rgba[2];
}
renderedData[i*4] = pixel[0];
renderedData[i*4 + 1] += pixel[1];
renderedData[i*4 + 2] += pixel[2];
renderedData[i*4 + 3] += pixel[3];
}
console.log('renderData time', Date.now() - n);
const myImageData = new ImageData(renderedData, width, height);
ctx.putImageData(myImageData, 0, 0);
console.log('putImageData time', Date.now() - n);
}
function updateChannelColors() {
for (let c=0; c<sizeC; c++) {
let color = 'red';
document.querySelectorAll(`input[name="ch${ c }color"]`).forEach(el => {
if (el.checked) {
color = el.value;
}
});
channelColors[c] = color;
}
}
function updateChannelSettings() {
for (let c=0; c<sizeC; c++) {
let start = document.getElementById(`ch${ c }start`).value;
let end = document.getElementById(`ch${ c }end`).value;
settings[c] = [parseInt(start), parseInt(end)];
}
}
document.querySelectorAll('input').forEach(el => {
if (el.id === 'zslider') {
el.addEventListener('change', (e) => {
theZ = parseInt(e.target.value);
loadImage();
});
} else {
el.addEventListener('input', (e) => renderImage());
}
});
loadImage();
import * as zarr from "https://unpkg.com/zarr/dist/zarr.es5.js"
async function test() {
const store = new zarr.ObjectStore();
const myZarrArray = await zarr.ones([3, 4], { store, chunks: [1, 2] });
console.log(myZarrArray.chunks); // [1, 2]
const arr = await myZarrArray.get([zarr.slice(0, 2)]);
console.log(arr instanceof zarr.NestedArray); // true
console.log(arr.shape); // [2, 4]
}
const options = {
concurrencyLimit: 10, // max number of concurrent requests (default 10)
progressCallback: ({ progress, queueSize }) => {
console.log(`${progress / queueSize * 100}% complete.`)
} // callback executed after each request
};
// await z.get([null], options);
async function loadPlane(imageName, theC, theZ) {
// http://guido.io/zarr.js/#/getting-started/raw-arrays?id=example
const z = await zarr.openArray({
store: "http://0.0.0.0:8000/",
path: imageName,
mode: "r",
});
// console.log("Index 0:", await z.get([0, 0, 256, zarr.slice(null, 512)], options));
// console.log("Index 0:", await z.get([0, zarr.slice(null, 50), 256, 256], options));
// null means 'get all of this dimension'
// console.log("Index 0:", await z.get([0, null, 256, 256], options));
// const { data, strides, shape } = await z.getRaw([0, null, null, 256]);
const chunk = await z.getRaw([theC, theZ, null, null]);
return chunk;
}
export { test, loadPlane };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment