npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@janelia/web-h5j-loader

v1.0.1

Published

Lets browser apps load H5J data files

Downloads

6

Readme

web-h5j-loader

Summary

This module provides JavaScript functions to load data from files in H5J format. A H5J file is an HDF5 container with one or more channels of 3D volumetric data with 12-bit values compressed using H.265 (a.k.a. HEVC or High Efficiency Video Coding). H5J is a "visually lossless" format useful for fluorescence microscopy data.

The HDF5 container is read with the jsfive module. The H.265 data is decoded with the ffmpeg.wasm module, which uses WebAssembly (wasm). The ffmpeg.wasm module was built using Emscripten, to transpile the original FFmpeg C++ code into WebAssembly.

Usage

The following code loads an H5J file from a URL and decompresses one channel of data into 8-bit values in a Uint8Array:

import { openH5J, getH5JAttrs, readH5JChannelUint8 } from '@janelia/web-h5j-loader';
try {
  const fileH5J = await openH5J('http://example.org/example.h5j');
  const attrs = getH5JAttrs(fileH5J);
  const dataUint8 = await readH5JChannelUint8(attrs.channels.names[0], fileH5J);
  ...
} catch (e) {
    console.log(e.message);
}

Reading the original 12-bit values into an 8-bit array in this manner sacrifies some accuracy (see below), but is useful for some applications like high-performance direct volume rendering.

As an alternative, the original 12-bit values can be read into a 16-bit Uint16Array as shown in this next example. This example also shows loading the H5J from a file on the local host, in the onChange callback for an <input type="file" /> element:

import { openH5J, getH5JAttrs, readH5JChannelUint16 } from '@janelia/web-h5j-loader';
const onChange = async (event) => {
  try {
      const fileH5J = await openH5J(event.target.files[0]);
      const attrs = getH5JAttrs(fileH5J);
      const dataUint16 = await readH5JChannelUint16(attrs.channels.names[0], fileH5J);
      ...
    } catch (e) {
      console.log(e.message);
    }
}  

Cross-Origin Isolation

If run with a basic server, the example code above will produce an exception:

SharedArrayBuffer is not defined

The problem is that ffmpeg.wasm uses multiple threads to improve performance, and these threads require a SharedArrayBuffer to implement shared memory. Due to security risks, SharedArrayBuffer is disabled in most browsers unless it is used with cross-origin isolation. To enable cross-origin isolation, a site must be served with two additional headers:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

For a site created with create-react-app, a way to add these headers to the development server is to use the CRACO (Create React App Configuration Override) package.

  1. Install CRACO:
    npm install @craco/craco --save
    (With newer versions of NPM, it may be necessary to append the --legacy-peer-deps argument to the end of the previous installation line.)
  2. Add a craco.config.js file (as a sibling to the site's package.json file) with the following contents:
    module.exports = {
      devServer: {
        headers: {
          'Cross-Origin-Embedder-Policy': 'require-corp',
          'Cross-Origin-Opener-Policy': 'same-origin'
        },
      },
    };
  3. Then change the react-scripts to craco in most entries of the scripts section of the site's package.json file:
    ...
    "scripts": {
      "start": "craco start",
      "build": "craco build",
      "test": "craco test",
      "eject": "react-scripts eject"
    },
    ...

Testing

The tests for web-h5j-loader use the Jest framework, and can be run from the command line as follows:

npm run test

The final two tests in the web-h5j-loader.test.js file load a special data set, testData/h64w64d4096_uint16_0-4095.h5j, containing all possible 12-bit data values. These tests verify how this data is convereted to 8-bit values (Uint8Array) and 16-bit values (Uint16Array).

Accuracy

The special data set, testData/h64w64d4096_uint16_0-4095.h5j, consists of slices (of width 64 and height 64) with a constant 12-bit data value. There are $2^{12} = 4096$ such slices, with slice $d$ (from 0 to 4095) having data value $d$. Thus, this data set shows how each possible 12-bit data value is transformed by compression.

When this data set is loaded into a Uint8Array by readH5JChannelUint8(), the expected behavior would be that 16 12-bit values would be mapped to the same 8-bit value. For slice $d$, the expected 8-bit value would be the rounded value $v = \lfloor d / 16 + 0.5 \rfloor$. In practice, there is a little variation within each slice, with both $v$ and $v+1$ occurring. The mode (i.e., value occurring most frequently within the slice) is $v$, as expected, for most slices. The exceptions are slices with $d \mod 16 = 8$, where the mode is $v+1$, and $d \mod 16 = 9$, where the mode may be $v$ or $v+1$.

When testData/h64w64d4096_uint16_0-4095.h5j is loaded into a Uint16Array by readH5JChannelUint16(), each 12-bit value is preseved, as expected. For slice $d$, there is some variation in values, from $d-3$ to $d+2$. There is somewhat less variation in the mode, having values from $d-1$ to $d+1$.

Test data

The testData subdirectory contains various data sets for testing. One is the h64w64d4096_uint16_0-4095.h5j data set mentioned above, for accuracy testing.

Another has a sphere at the origin, a fat cone pointing along the positive $x$ axis, a thin cone pointing along the positive $y$ axis, and a cylinder along the $z$ axis. When loaded into a Uint8Array, the data value for the sphere is 64, for the fat cone is 96, for the thin cone is 128, and for the cylinder is 160. There are several copies of the data set at different resolutions:

  • Low resolution (64 x 64 x 64): sphere64cone96cone128cylinder160_w64h64d64th3.h5j
  • Medium resolution (256 x 256 x 256): sphere64cone96cone128cylinder160_w256h256d256th3.h5j
  • High resolution (512 x 512 x 512): sphere64cone96cone128cylinder160_w512h512d512th3.h5j
  • Non-cubical (256 x 128 x 64): sphere64cone96cone128cylinder160_w256h128d64th3.h5j

The Python script makeTestStack.py generates the TIFF stack that is the original data represented in these sphere64cone96cone128cylinder160...h5j files. See the next section for more about creating H5J files.

The final data set is and real fluorescence microscopy volume from the FlyLight Generation 1 MCFO collection (citation: https://dx.doi.org/10.1016/j.celrep.2012.09.011):

R10E08-20191011_61_I8-m-40x-central-GAL4-JRC2018_Unisex_20x_HR-aligned_stack.h5j

It has resolution 1210 x 566 x 174.

Making H5J files

The easiest way to make a H5J file is to convert a TIFF stack, a multi-frame (multi-page) TIFF image where each frame represents a step in depth. To convert the TIFF stack /tmp/example.tif, use Docker as follows:

docker run -v /tmp:/data janeliascicomp/flylight_tools:1.1.0 /app/scripts/cmd/convert.sh /data/example.tif /data/example.h5j /data/example.yml 0 1

docker run -v /tmp:/data janeliascicomp/flylight_tools:1.1.0 /app/scripts/cmd/h5jMetadata.sh /data/example.h5j /data/example.yml

The /tmp/example.yml file should have a format like the following (perhaps with more than just the one Channel_0):

attrs:
  image_size: [256.0, 128.0, 64.0]
  voxel_size: [1.0, 1.0, 1.0]
  channel_spec: r
groups:
  Channels:
    attrs:
      frames: [64]
      height: [128]
      pad_bottom: [0]
      pad_right: [0]
      width: [256]
    groups:
      Channel_0:
        attrs: {content_type: reference}