From b29922692bc122cbe18c75684b4db225d5e573d5 Mon Sep 17 00:00:00 2001 From: Trevor Manz Date: Tue, 10 Aug 2021 18:28:48 -0400 Subject: [PATCH] Add `attrs` util. Remove generic axes util. --- src/io.ts | 15 ++++++++------- src/ome.ts | 26 +++++++++++++++----------- src/utils.ts | 6 ------ types/ome.ts | 2 +- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/io.ts b/src/io.ts index ca5b8237..ea9011d5 100644 --- a/src/io.ts +++ b/src/io.ts @@ -1,7 +1,7 @@ import { DTYPE_VALUES, ImageLayer, MultiscaleImageLayer, ZarrPixelSource } from '@hms-dbmi/viv'; import { Group as ZarrGroup, HTTPStore, openGroup, ZarrArray } from 'zarr'; import GridLayer from './gridLayer'; -import { loadOmeroMultiscales, loadPlate, loadWell } from './ome'; +import { loadOmeroMultiscales, loadPlate, loadWell, omeAttrs } from './ome'; import type { ImageLayerConfig, LayerState, @@ -14,7 +14,6 @@ import { COLORS, CYMRGB, getAxisLabels, - getAxisLabelsFromMultiscales, guessTileSize, hexToRGB, loadMultiscales, @@ -108,10 +107,9 @@ function loadMultiChannel(config: MultichannelConfig, data: ZarrPixelSource { const node = await open(config.source); let data: ZarrArray[]; - let axis_labels; if (node instanceof ZarrGroup) { - const attrs = (await node.attrs.asObject()) as Ome.Attrs; + const attrs = await omeAttrs(node); if ('plate' in attrs) { return loadPlate(config, node, attrs.plate); @@ -130,7 +128,7 @@ export async function createSourceData(config: ImageLayerConfig): Promise new ZarrPixelSource(d, labels, tileSize)); const [base] = loader; diff --git a/src/ome.ts b/src/ome.ts index 84775ed3..0a72457a 100644 --- a/src/ome.ts +++ b/src/ome.ts @@ -2,7 +2,12 @@ import { ZarrPixelSource } from '@hms-dbmi/viv'; import pMap from 'p-map'; import { Group as ZarrGroup, HTTPStore, openGroup, ZarrArray } from 'zarr'; import type { ImageLayerConfig, SourceData } from './state'; -import { join, loadMultiscales, getAxisLabelsFromMultiscales, guessTileSize, range, parseMatrix } from './utils'; +import { join, loadMultiscales, guessTileSize, range, parseMatrix } from './utils'; + +export async function omeAttrs(grp: ZarrGroup, path?: string) { + const node = path ? await grp.getItem(path) : grp; + return node.attrs.asObject() as Promise; +} export async function loadWell(config: ImageLayerConfig, grp: ZarrGroup, wellAttrs: Ome.Well): Promise { // Can filter Well fields by URL query ?acquisition=ID @@ -28,7 +33,7 @@ export async function loadWell(config: ImageLayerConfig, grp: ZarrGroup, wellAtt // Need to get acquisitions metadata from parent Plate const plateUrl = grp.store.url.replace(`${row}/${col}`, ''); const plate = await openGroup(new HTTPStore(plateUrl)); - const plateAttrs = (await plate.attrs.asObject()) as { plate: Ome.Plate }; + const plateAttrs = await omeAttrs<{ plate: Ome.Plate }>(plate); acquisitions = plateAttrs?.plate?.acquisitions ?? []; // filter imagePaths by acquisition @@ -42,7 +47,7 @@ export async function loadWell(config: ImageLayerConfig, grp: ZarrGroup, wellAtt const rows = Math.ceil(imgPaths.length / cols); // Use first image for rendering settings, resolutions etc. - const imgAttrs = (await grp.getItem(imgPaths[0]).then((g) => g.attrs.asObject())) as Ome.Attrs; + const imgAttrs = await omeAttrs(grp, imgPaths[0]); if (!('omero' in imgAttrs)) { throw Error('Path for image is not valid.'); } @@ -51,7 +56,7 @@ export async function loadWell(config: ImageLayerConfig, grp: ZarrGroup, wellAtt // Create loader for every Image. const promises = imgPaths.map((p) => grp.getItem(join(p, resolution))); const data = (await Promise.all(promises)) as ZarrArray[]; - const axis_labels = getOmeAxisLabels(imgAttrs); + const axis_labels = getOmeAxisLabels(imgAttrs.multiscales); const meta = parseOmeroMeta(imgAttrs.omero, axis_labels); const tileSize = guessTileSize(data[0]); @@ -119,13 +124,13 @@ export async function loadPlate(config: ImageLayerConfig, grp: ZarrGroup, plateA const wellPaths = plateAttrs.wells.map((well) => well.path); // Use first image as proxy for others. - const wellAttrs = (await grp.getItem(wellPaths[0]).then((g) => g.attrs.asObject())) as Ome.Attrs; + const wellAttrs = await omeAttrs(grp, wellPaths[0]); if (!('well' in wellAttrs)) { throw Error('Path for image is not valid, not a well.'); } const imgPath = wellAttrs.well.images[0].path; - const imgAttrs = (await grp.getItem(join(wellPaths[0], imgPath)).then((g) => g.attrs.asObject())) as Ome.Attrs; + const imgAttrs = await omeAttrs(grp, join(wellPaths[0], imgPath)); if (!('omero' in imgAttrs)) { throw Error('Path for image is not valid.'); } @@ -141,7 +146,7 @@ export async function loadPlate(config: ImageLayerConfig, grp: ZarrGroup, plateA { concurrency: 10 } ); const data = await Promise.all(promises); - const axis_labels = getOmeAxisLabels(imgAttrs); + const axis_labels = getOmeAxisLabels(imgAttrs.multiscales); const meta = parseOmeroMeta(imgAttrs.omero, axis_labels); const tileSize = guessTileSize(data[0][1]); const loaders = data.map((d) => { @@ -200,7 +205,7 @@ export async function loadOmeroMultiscales( ): Promise { const { name, opacity = 1, colormap = '' } = config; const data = await loadMultiscales(grp, attrs.multiscales); - const axis_labels = getOmeAxisLabels(attrs); + const axis_labels = getOmeAxisLabels(attrs.multiscales); const meta = parseOmeroMeta(attrs.omero, axis_labels); const tileSize = guessTileSize(data[0]); @@ -252,8 +257,7 @@ function parseOmeroMeta({ rdefs, channels, name }: Ome.Omero, axis_labels: strin }; } -function getOmeAxisLabels(attrs: Ome.Attrs): [...string[], 'y', 'x'] { - let axis_labels = getAxisLabelsFromMultiscales(attrs); +function getOmeAxisLabels(multiscales: Ome.Multiscale[]): [...string[], 'y', 'x'] { const default_axes = ['t', 'c', 'z', 'y', 'x']; // v0.1 & v0.2 - return (axis_labels || default_axes) as [...string[], 'y', 'x']; + return (multiscales[0].axes || default_axes) as [...string[], 'y', 'x']; } diff --git a/src/utils.ts b/src/utils.ts index 39536a69..7c824ac7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -100,12 +100,6 @@ export function getAxisLabels(arr: ZarrArray, axis_labels?: string[]): [...strin return axis_labels as [...string[], 'y', 'x']; } -export function getAxisLabelsFromMultiscales(attrs) { - if ('multiscales' in attrs && attrs.multiscales?.[0]?.axes) { - return attrs.multiscales[0].axes; - } -} - export function isInterleaved(shape: number[]) { const lastDimSize = shape[shape.length - 1]; return lastDimSize === 3 || lastDimSize === 4; diff --git a/types/ome.ts b/types/ome.ts index c4c52ce3..01c72c20 100644 --- a/types/ome.ts +++ b/types/ome.ts @@ -1,5 +1,5 @@ declare module Ome { - type Version = '0.1'; + type Version = '0.1' | '0.2' | '0.3'; interface Channel { active: boolean;