Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/loaders/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"geotiff": "^2.0.5",
"lzw-tiff-decoder": "^0.1.1",
"quickselect": "^2.0.0",
"zarr": "^0.6.2",
"zarrita": "^0.5.2",
"zod": "^3.22.4"
},
"unbuild": {
Expand Down
8 changes: 4 additions & 4 deletions packages/loaders/src/tiff/pixel-source.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { GeoTIFFImage } from 'geotiff';
import type { TypedArray } from 'zarr';
import type { TypedArray, NumberDataType, BigintDataType } from 'zarrita';
import { SIGNAL_ABORTED, getImageSize, isInterleaved } from '../utils';

import type {
Expand Down Expand Up @@ -64,11 +64,11 @@ class TiffPixelSource<S extends string[]> implements PixelSource<S> {
* geotiff.js returns objects with different structure
* depending on `interleave`. It's weird, but this seems to work.
*/
const data = (interleave ? raster : raster[0]) as TypedArray;
const data = (interleave ? raster : raster[0]) as TypedArray<NumberDataType | BigintDataType>;
return {
data,
width: (raster as TypedArray & { width: number }).width,
height: (raster as TypedArray & { height: number }).height
width: (raster as TypedArray<NumberDataType | BigintDataType> & { width: number }).width,
height: (raster as TypedArray<NumberDataType | BigintDataType> & { height: number }).height
} as PixelData;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/loaders/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Labels, PixelSource } from '@vivjs/types';
import quickselect from 'quickselect';
import type { TypedArray } from 'zarr';
import type { TypedArray, Array as ZarrArray, NumberDataType, BigintDataType } from 'zarrita';
import type { OmeXml } from './omexml';

export const DTYPE_LOOKUP = {
Expand All @@ -24,7 +24,7 @@ export const DTYPE_LOOKUP = {
* @param {TypedArray} arr
* @return {{ mean: number, sd: number, q1: number, q3: number, median: number, domain: number[], contrastLimits: number[] }}
*/
export function getChannelStats(arr: TypedArray) {
export function getChannelStats(arr: TypedArray<NumberDataType>) {
let len = arr.length;
let min = Number.POSITIVE_INFINITY;
let max = Number.NEGATIVE_INFINITY;
Expand Down
6 changes: 3 additions & 3 deletions packages/loaders/src/zarr/bioformats-zarr.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ZarrArray } from 'zarr';
import type { Array as ZarrArray, NumberDataType, BigintDataType, Readable } from 'zarrita';

import { fromString } from '../omexml';
import {
Expand All @@ -9,7 +9,7 @@ import {
import ZarrPixelSource from './pixel-source';

export async function load(
root: ZarrArray['store'],
root: Readable,
xmlSource: string | File | Response
) {
let xmlSourceText: string;
Expand All @@ -26,7 +26,7 @@ export async function load(

const labels = guessBioformatsLabels(data[0], imgMeta);
const tileSize = guessTileSize(data[0]);
const pyramid = data.map(arr => new ZarrPixelSource(arr, labels, tileSize));
const pyramid = data.map(arr => new ZarrPixelSource(arr, { labels, tileSize }));

return {
data: pyramid,
Expand Down
6 changes: 3 additions & 3 deletions packages/loaders/src/zarr/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HTTPStore } from 'zarr';
import { FetchStore } from 'zarrita';
import { FileStore } from './lib/storage';
import { getRootPrefix } from './lib/utils';

Expand Down Expand Up @@ -26,7 +26,7 @@ export async function loadBioformatsZarr(

if (typeof source === 'string') {
const url = source.endsWith('/') ? source.slice(0, -1) : source;
const store = new HTTPStore(`${url}/${ZARR_DIR}`, options);
const store = new FetchStore(`${url}/${ZARR_DIR}`, { overrides: options.fetchOptions });
const xmlSource = await fetch(`${url}/${METADATA}`, options.fetchOptions);
if (!xmlSource.ok) {
throw Error('No OME-XML metadata found for store.');
Expand Down Expand Up @@ -72,7 +72,7 @@ export async function loadOmeZarr(
source: string,
options: Partial<ZarrOptions & { type: 'multiscales' }> = {}
) {
const store = new HTTPStore(source, options);
const store = new FetchStore(source, { overrides: options.fetchOptions });

if (options?.type !== 'multiscales') {
throw Error('Only multiscale OME-Zarr is supported.');
Expand Down
33 changes: 6 additions & 27 deletions packages/loaders/src/zarr/lib/storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { KeyError } from 'zarr';
import type { AsyncStore } from 'zarr/types/storage/types';
import { KeyError } from 'zarrita';
import type { AsyncReadable } from 'zarrita';

/**
* Preserves (double) slashes earlier in the path, so this works better
Expand All @@ -16,30 +16,13 @@ function joinUrlParts(...args: string[]) {
.join('/');
}

class ReadOnlyStore {
async keys() {
return [];
}

async deleteItem() {
return false;
}

async setItem() {
console.warn('Cannot write to read-only store.');
return false;
}
}

export class FileStore
extends ReadOnlyStore
implements AsyncStore<ArrayBuffer>
implements AsyncReadable
{
private _map: Map<string, File>;
private _rootPrefix: string;

constructor(fileMap: Map<string, File>, rootPrefix = '') {
super();
this._map = fileMap;
this._rootPrefix = rootPrefix;
}
Expand All @@ -48,17 +31,13 @@ export class FileStore
return joinUrlParts(this._rootPrefix, key);
}

async getItem(key: string) {
async get(key: string) {
const file = this._map.get(this._key(key));
if (!file) {
throw new KeyError(key);
}
// Reference: https://github.com/manzt/zarrita.js/pull/161
const buffer = await file.arrayBuffer();
return buffer;
}

async containsItem(key: string) {
const path = this._key(key);
return this._map.has(path);
return new Uint8Array(buffer);
}
}
19 changes: 10 additions & 9 deletions packages/loaders/src/zarr/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { openGroup } from 'zarr';
import type { ZarrArray } from 'zarr';
import { open as zarrOpen, root as zarrRoot, get as zarrGet } from 'zarrita';
import type { Array as ZarrArray, Readable, AsyncReadable, NumberDataType, BigintDataType } from 'zarrita';
import type { OmeXml } from '../../omexml';
import { getLabels, isInterleaved, prevPowerOf2 } from '../../utils';

Expand All @@ -26,7 +26,7 @@ function isOmeZarr(dataShape: number[], Pixels: OmeXml[0]['Pixels']) {
* tries to specify different dimension orders.
*/
export function guessBioformatsLabels(
{ shape }: ZarrArray,
{ shape }: ZarrArray<NumberDataType | BigintDataType, Readable>,
{ Pixels }: OmeXml[0]
) {
if (isOmeZarr(shape, Pixels)) {
Expand Down Expand Up @@ -79,9 +79,10 @@ function castLabels(dimnames: string[]) {
return dimnames as Labels<string[]>;
}

export async function loadMultiscales(store: ZarrArray['store'], path = '') {
const grp = await openGroup(store, path);
const rootAttrs = (await grp.attrs.asObject()) as RootAttrs;
export async function loadMultiscales(store: Readable, initialPath = '') {
const storeRoot = zarrRoot(store).resolve(initialPath);
const grp = await zarrOpen(storeRoot, { kind: 'group' });
const rootAttrs = (grp.attrs) as unknown as RootAttrs;

let paths = ['0'];
// Default axes used for v0.1 and v0.2.
Expand All @@ -98,15 +99,15 @@ export async function loadMultiscales(store: ZarrArray['store'], path = '') {
}
}

const data = paths.map(path => grp.getItem(path));
const data = paths.map(async path => await zarrOpen(storeRoot.resolve(path), { kind: 'array' }));
return {
data: (await Promise.all(data)) as ZarrArray[],
data: (await Promise.all(data)) as ZarrArray<NumberDataType | BigintDataType, Readable>[],
rootAttrs,
labels
};
}

export function guessTileSize(arr: ZarrArray) {
export function guessTileSize(arr: ZarrArray<NumberDataType | BigintDataType, Readable>) {
const interleaved = isInterleaved(arr.shape);
const [yChunk, xChunk] = arr.chunks.slice(interleaved ? -3 : -2);
const size = Math.min(yChunk, xChunk);
Expand Down
6 changes: 3 additions & 3 deletions packages/loaders/src/zarr/ome-zarr.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ZarrArray } from 'zarr';
import type { Array as ZarrArray, Readable } from 'zarrita';
import { guessTileSize, loadMultiscales } from './lib/utils';
import ZarrPixelSource from './pixel-source';

Expand Down Expand Up @@ -42,10 +42,10 @@ export interface RootAttrs {
multiscales: Multiscale[];
}

export async function load(store: ZarrArray['store']) {
export async function load(store: Readable) {
const { data, rootAttrs, labels } = await loadMultiscales(store);
const tileSize = guessTileSize(data[0]);
const pyramid = data.map(arr => new ZarrPixelSource(arr, labels, tileSize));
const pyramid = data.map(arr => new ZarrPixelSource(arr, { labels, tileSize }));
return {
data: pyramid,
metadata: rootAttrs
Expand Down
Loading
Loading