@jax-data-science/sci-image-visualizer 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/index.mjs +4 -1
- package/esm2022/lib/contracts/image.contract.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-spatial-3d-scene.mjs +70 -2
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +2 -2
- package/esm2022/lib/implementations/napari-js/napari-volume-region-meshes.mjs +11 -1
- package/esm2022/lib/region-editor/region-editor.component.mjs +46 -41
- package/esm2022/lib/region-import/label-volume.mjs +35 -4
- package/esm2022/lib/region-import/ome-zarr-labels.mjs +253 -0
- package/esm2022/lib/region-import/open-annotations.mjs +289 -0
- package/esm2022/lib/region-import/zarr-codecs.mjs +167 -0
- package/esm2022/lib/region-import/zip.mjs +157 -0
- package/esm2022/lib/routing-visualizer.service.mjs +20 -7
- package/esm2022/lib/visualizer/region-load.mjs +16 -1
- package/fesm2022/{jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-BELcx0jv.mjs → jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-a68Hw5rw.mjs} +1050 -50
- package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-a68Hw5rw.mjs.map +1 -0
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-DEDSm_NL.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-CvadfzjS.mjs} +4 -2
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-DEDSm_NL.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-CvadfzjS.mjs.map} +1 -1
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +3 -1
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/index.d.ts +6 -0
- package/lib/contracts/image.contract.d.ts +7 -0
- package/lib/implementations/napari-js/napari-spatial-3d-scene.d.ts +40 -1
- package/lib/implementations/napari-js/napari-volume-region-meshes.d.ts +5 -0
- package/lib/region-editor/region-editor.component.d.ts +5 -4
- package/lib/region-import/label-volume.d.ts +4 -0
- package/lib/region-import/ome-zarr-labels.d.ts +36 -0
- package/lib/region-import/open-annotations.d.ts +73 -0
- package/lib/region-import/zarr-codecs.d.ts +14 -0
- package/lib/region-import/zip.d.ts +31 -0
- package/lib/routing-visualizer.service.d.ts +9 -1
- package/package.json +2 -1
- package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-BELcx0jv.mjs.map +0 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { LruCache } from 'napari-js';
|
|
2
2
|
import * as ort from 'onnxruntime-web';
|
|
3
|
-
import { g as getOrtWasmBase, f as fetchModel, r as runEncoder, a as runDecoder } from './jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-
|
|
3
|
+
import { g as getOrtWasmBase, f as fetchModel, r as runEncoder, a as runDecoder } from './jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-a68Hw5rw.mjs';
|
|
4
4
|
import '@angular/core';
|
|
5
5
|
import '@angular/common/http';
|
|
6
6
|
import 'rxjs';
|
|
7
7
|
import 'rxjs/operators';
|
|
8
8
|
import 'napari-js/geometry';
|
|
9
|
+
import 'fast-png';
|
|
10
|
+
import 'fzstd';
|
|
9
11
|
import '@angular/core/rxjs-interop';
|
|
10
12
|
import '@angular/common';
|
|
11
13
|
import '@angular/forms';
|
|
@@ -235,4 +237,4 @@ class OnnxSamSession {
|
|
|
235
237
|
}
|
|
236
238
|
|
|
237
239
|
export { OnnxSamSession };
|
|
238
|
-
//# sourceMappingURL=jax-data-science-sci-image-visualizer-onnx-sam-session-
|
|
240
|
+
//# sourceMappingURL=jax-data-science-sci-image-visualizer-onnx-sam-session-CvadfzjS.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jax-data-science-sci-image-visualizer-onnx-sam-session-DEDSm_NL.mjs","sources":["../../src/lib/toolbar/segmentation/pending-calls.ts","../../src/lib/toolbar/segmentation/onnx-sam-session.ts"],"sourcesContent":["/** A reply from the SAM worker: progress for, or the result of, call `id`. */\nexport interface WorkerReply {\n id: number;\n type: string;\n /** Present on `type: 'progress'` replies. */\n fraction?: number;\n /** Present on `type: 'error'` replies. */\n error?: string;\n [key: string]: unknown;\n}\n\ninterface Pending {\n resolve: (reply: WorkerReply) => void;\n reject: (err: Error) => void;\n onProgress?: (fraction: number) => void;\n}\n\n/**\n * Request/response bookkeeping for a worker RPC: each call gets an id and a\n * promise that its reply settles. Every exit path (dispose, a worker crash)\n * must {@link rejectAll}, or awaiting callers never reach their `finally` and a\n * tool's busy flag sticks forever (RT-8).\n */\nexport class PendingCalls {\n private seq = 0;\n private readonly pending = new Map<number, Pending>();\n\n /** Calls still awaiting a reply. */\n get size(): number {\n return this.pending.size;\n }\n\n /** Register a new call; post the message with the returned id. */\n open(onProgress?: (fraction: number) => void): { id: number; promise: Promise<WorkerReply> } {\n const id = ++this.seq;\n const promise = new Promise<WorkerReply>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, onProgress });\n });\n return { id, promise };\n }\n\n /** Route a worker reply to its call: progress, error or result. */\n handle(reply: WorkerReply): void {\n const p = this.pending.get(reply.id);\n if (!p) return;\n if (reply.type === 'progress') {\n p.onProgress?.(reply.fraction ?? 0);\n return;\n }\n this.pending.delete(reply.id);\n if (reply.type === 'error') p.reject(new Error(reply.error ?? 'SAM worker error.'));\n else p.resolve(reply);\n }\n\n /** Reject every pending call with `err` and forget them. */\n rejectAll(err: Error): void {\n const calls = Array.from(this.pending.values());\n this.pending.clear();\n for (const p of calls) p.reject(err);\n }\n}\n","import { LruCache } from 'napari-js';\nimport * as ort from 'onnxruntime-web';\n\nimport { ISamSession, SamEmbedding, SamMaskResult, SamModelDef, SamPrompt } from '../../contracts/sam.contract';\nimport { fetchModel, runEncoder, runDecoder, type CoreEmbedding } from './sam-onnx-core';\nimport { getOrtWasmBase } from './ort-runtime-config';\nimport { PendingCalls, WorkerReply } from './pending-calls';\n\n/**\n * onnxruntime-web implementation of {@link ISamSession} (jit-ui#90), with two\n * execution modes chosen per model:\n *\n * - **in-process** (main thread) — for small WASM models flagged `inProcess`\n * (e.g. micro-sam ViT-T). Fast load + inference with no Web Worker spawn /\n * second ORT runtime / message round-trips; the brief encode runs on the main\n * thread (fine for a tiny model).\n * - **worker** — for WebGPU models (e.g. ViT-B, patho-sam fp16) whose heavy\n * encode would otherwise freeze the tab. Runs in {@link ./onnx-sam.worker} so\n * the UI stays responsive (the spinner animates).\n *\n * Both modes share their inference math via {@link ./sam-onnx-core}. Lazy-imported\n * by the SAM tools so onnxruntime-web / the worker never load in unit tests or\n * the initial bundle.\n */\nexport class OnnxSamSession implements ISamSession {\n private mode: 'inproc' | 'worker' | null = null;\n private loaded = false;\n private inputSize = 1024;\n\n // ── worker mode ──\n private worker: Worker | null = null;\n private readonly calls = new PendingCalls();\n\n // ── in-process mode ──\n private encoder: ort.InferenceSession | null = null;\n private decoder: ort.InferenceSession | null = null;\n private nextToken = 1;\n /** The few most recently used embeddings, by token (one per recently viewed image). */\n private readonly embeddings = new LruCache<CoreEmbedding, number>(3);\n\n async loadModel(model: SamModelDef, onProgress?: (fraction: number) => void): Promise<void> {\n if (!model.encoderUrl || !model.decoderUrl) {\n throw new Error(`SAM model \"${model.id}\" has no ONNX URLs configured.`);\n }\n this.inputSize = model.inputSize;\n const hasGpu = typeof navigator !== 'undefined' && 'gpu' in navigator;\n const eps: string[] = model.encoderProviders ?? (hasGpu ? ['webgpu', 'wasm'] : ['wasm']);\n const usesWebGpu = eps.includes('webgpu');\n // WebGPU must run in the worker (else it freezes the main thread); small\n // WASM models opt into the faster in-process path via `inProcess`.\n this.mode = !usesWebGpu && model.inProcess ? 'inproc' : 'worker';\n\n if (this.mode === 'inproc') {\n ort.env.wasm.wasmPaths = getOrtWasmBase();\n const encBuf = await fetchModel(model.encoderUrl, onProgress, model.revision);\n const decBuf = await fetchModel(model.decoderUrl, undefined, model.revision);\n this.encoder = await ort.InferenceSession.create(encBuf, { executionProviders: eps });\n this.decoder = await ort.InferenceSession.create(decBuf, { executionProviders: ['wasm'] });\n } else {\n await this.call(\n {\n type: 'load',\n encoderUrl: model.encoderUrl,\n decoderUrl: model.decoderUrl,\n revision: model.revision,\n wasmPaths: getOrtWasmBase(),\n inputSize: model.inputSize,\n encoderProviders: model.encoderProviders,\n },\n [],\n onProgress,\n );\n }\n this.loaded = true;\n }\n\n isLoaded(): boolean {\n return this.loaded;\n }\n\n async embed(image: { data: Uint8ClampedArray; width: number; height: number }): Promise<SamEmbedding> {\n if (this.mode === 'inproc') {\n const e = await runEncoder(this.encoder!, image.data, image.width, image.height, this.inputSize);\n const token = this.nextToken++;\n this.embeddings.set(token, e);\n return {\n data: new Float32Array(0),\n dims: e.dims,\n scale: e.scale,\n imageWidth: e.imageWidth,\n imageHeight: e.imageHeight,\n token,\n };\n }\n // worker: transfer the RGBA buffer (callers pass a fresh frame buffer).\n const buffer = image.data.buffer;\n const res = await this.call({ type: 'embed', width: image.width, height: image.height, buffer }, [buffer]);\n return {\n data: new Float32Array(0),\n dims: res['dims'] as number[],\n scale: res['scale'] as number,\n imageWidth: res['imageWidth'] as number,\n imageHeight: res['imageHeight'] as number,\n token: res['token'] as number,\n };\n }\n\n async decode(embedding: SamEmbedding, prompt: SamPrompt): Promise<SamMaskResult> {\n if (this.mode === 'inproc') {\n const e = this.embeddings.get(embedding.token as number);\n if (!e) throw new Error('SAM embedding expired; re-encode the image.');\n return runDecoder(this.decoder!, e, prompt);\n }\n const res = await this.call({ type: 'decode', token: embedding.token, prompt });\n return {\n mask: new Uint8Array(res['buffer'] as ArrayBuffer),\n width: res['width'] as number,\n height: res['height'] as number,\n iou: res['iou'] as number,\n };\n }\n\n /** Release the worker / ORT sessions. Calls still in flight reject (RT-8), so\n * their callers' `finally` blocks run (e.g. a tool's busy flag clears). */\n dispose(): void {\n this.dropWorker(new Error('SAM session disposed'));\n this.encoder?.release?.();\n this.decoder?.release?.();\n this.encoder = this.decoder = null;\n this.embeddings.clear();\n this.loaded = false;\n this.mode = null;\n }\n\n // ── worker plumbing ──────────────────────────────────────────────────────\n private ensureWorker(): Worker {\n if (this.worker) return this.worker;\n const worker = new Worker(new URL('./onnx-sam.worker', import.meta.url), { type: 'module' });\n worker.onmessage = (ev: MessageEvent<WorkerReply>) => this.calls.handle(ev.data);\n worker.onerror = (ev: ErrorEvent) => {\n // A crashed worker can't serve the loaded model any more: drop it, so the\n // next run reloads instead of posting to a dead worker.\n this.dropWorker(new Error(ev.message || 'SAM worker crashed.'));\n this.loaded = false;\n this.mode = null;\n };\n this.worker = worker;\n return worker;\n }\n\n /** Terminate the worker (if any) and reject every call still waiting on it. */\n private dropWorker(err: Error): void {\n this.worker?.terminate();\n this.worker = null;\n this.calls.rejectAll(err);\n }\n\n private call(\n msg: Record<string, unknown>,\n transfer: Transferable[] = [],\n onProgress?: (f: number) => void,\n ): Promise<WorkerReply> {\n const worker = this.ensureWorker();\n const { id, promise } = this.calls.open(onProgress);\n worker.postMessage({ ...msg, id }, transfer);\n return promise;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"jax-data-science-sci-image-visualizer-onnx-sam-session-CvadfzjS.mjs","sources":["../../src/lib/toolbar/segmentation/pending-calls.ts","../../src/lib/toolbar/segmentation/onnx-sam-session.ts"],"sourcesContent":["/** A reply from the SAM worker: progress for, or the result of, call `id`. */\nexport interface WorkerReply {\n id: number;\n type: string;\n /** Present on `type: 'progress'` replies. */\n fraction?: number;\n /** Present on `type: 'error'` replies. */\n error?: string;\n [key: string]: unknown;\n}\n\ninterface Pending {\n resolve: (reply: WorkerReply) => void;\n reject: (err: Error) => void;\n onProgress?: (fraction: number) => void;\n}\n\n/**\n * Request/response bookkeeping for a worker RPC: each call gets an id and a\n * promise that its reply settles. Every exit path (dispose, a worker crash)\n * must {@link rejectAll}, or awaiting callers never reach their `finally` and a\n * tool's busy flag sticks forever (RT-8).\n */\nexport class PendingCalls {\n private seq = 0;\n private readonly pending = new Map<number, Pending>();\n\n /** Calls still awaiting a reply. */\n get size(): number {\n return this.pending.size;\n }\n\n /** Register a new call; post the message with the returned id. */\n open(onProgress?: (fraction: number) => void): { id: number; promise: Promise<WorkerReply> } {\n const id = ++this.seq;\n const promise = new Promise<WorkerReply>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, onProgress });\n });\n return { id, promise };\n }\n\n /** Route a worker reply to its call: progress, error or result. */\n handle(reply: WorkerReply): void {\n const p = this.pending.get(reply.id);\n if (!p) return;\n if (reply.type === 'progress') {\n p.onProgress?.(reply.fraction ?? 0);\n return;\n }\n this.pending.delete(reply.id);\n if (reply.type === 'error') p.reject(new Error(reply.error ?? 'SAM worker error.'));\n else p.resolve(reply);\n }\n\n /** Reject every pending call with `err` and forget them. */\n rejectAll(err: Error): void {\n const calls = Array.from(this.pending.values());\n this.pending.clear();\n for (const p of calls) p.reject(err);\n }\n}\n","import { LruCache } from 'napari-js';\nimport * as ort from 'onnxruntime-web';\n\nimport { ISamSession, SamEmbedding, SamMaskResult, SamModelDef, SamPrompt } from '../../contracts/sam.contract';\nimport { fetchModel, runEncoder, runDecoder, type CoreEmbedding } from './sam-onnx-core';\nimport { getOrtWasmBase } from './ort-runtime-config';\nimport { PendingCalls, WorkerReply } from './pending-calls';\n\n/**\n * onnxruntime-web implementation of {@link ISamSession} (jit-ui#90), with two\n * execution modes chosen per model:\n *\n * - **in-process** (main thread) — for small WASM models flagged `inProcess`\n * (e.g. micro-sam ViT-T). Fast load + inference with no Web Worker spawn /\n * second ORT runtime / message round-trips; the brief encode runs on the main\n * thread (fine for a tiny model).\n * - **worker** — for WebGPU models (e.g. ViT-B, patho-sam fp16) whose heavy\n * encode would otherwise freeze the tab. Runs in {@link ./onnx-sam.worker} so\n * the UI stays responsive (the spinner animates).\n *\n * Both modes share their inference math via {@link ./sam-onnx-core}. Lazy-imported\n * by the SAM tools so onnxruntime-web / the worker never load in unit tests or\n * the initial bundle.\n */\nexport class OnnxSamSession implements ISamSession {\n private mode: 'inproc' | 'worker' | null = null;\n private loaded = false;\n private inputSize = 1024;\n\n // ── worker mode ──\n private worker: Worker | null = null;\n private readonly calls = new PendingCalls();\n\n // ── in-process mode ──\n private encoder: ort.InferenceSession | null = null;\n private decoder: ort.InferenceSession | null = null;\n private nextToken = 1;\n /** The few most recently used embeddings, by token (one per recently viewed image). */\n private readonly embeddings = new LruCache<CoreEmbedding, number>(3);\n\n async loadModel(model: SamModelDef, onProgress?: (fraction: number) => void): Promise<void> {\n if (!model.encoderUrl || !model.decoderUrl) {\n throw new Error(`SAM model \"${model.id}\" has no ONNX URLs configured.`);\n }\n this.inputSize = model.inputSize;\n const hasGpu = typeof navigator !== 'undefined' && 'gpu' in navigator;\n const eps: string[] = model.encoderProviders ?? (hasGpu ? ['webgpu', 'wasm'] : ['wasm']);\n const usesWebGpu = eps.includes('webgpu');\n // WebGPU must run in the worker (else it freezes the main thread); small\n // WASM models opt into the faster in-process path via `inProcess`.\n this.mode = !usesWebGpu && model.inProcess ? 'inproc' : 'worker';\n\n if (this.mode === 'inproc') {\n ort.env.wasm.wasmPaths = getOrtWasmBase();\n const encBuf = await fetchModel(model.encoderUrl, onProgress, model.revision);\n const decBuf = await fetchModel(model.decoderUrl, undefined, model.revision);\n this.encoder = await ort.InferenceSession.create(encBuf, { executionProviders: eps });\n this.decoder = await ort.InferenceSession.create(decBuf, { executionProviders: ['wasm'] });\n } else {\n await this.call(\n {\n type: 'load',\n encoderUrl: model.encoderUrl,\n decoderUrl: model.decoderUrl,\n revision: model.revision,\n wasmPaths: getOrtWasmBase(),\n inputSize: model.inputSize,\n encoderProviders: model.encoderProviders,\n },\n [],\n onProgress,\n );\n }\n this.loaded = true;\n }\n\n isLoaded(): boolean {\n return this.loaded;\n }\n\n async embed(image: { data: Uint8ClampedArray; width: number; height: number }): Promise<SamEmbedding> {\n if (this.mode === 'inproc') {\n const e = await runEncoder(this.encoder!, image.data, image.width, image.height, this.inputSize);\n const token = this.nextToken++;\n this.embeddings.set(token, e);\n return {\n data: new Float32Array(0),\n dims: e.dims,\n scale: e.scale,\n imageWidth: e.imageWidth,\n imageHeight: e.imageHeight,\n token,\n };\n }\n // worker: transfer the RGBA buffer (callers pass a fresh frame buffer).\n const buffer = image.data.buffer;\n const res = await this.call({ type: 'embed', width: image.width, height: image.height, buffer }, [buffer]);\n return {\n data: new Float32Array(0),\n dims: res['dims'] as number[],\n scale: res['scale'] as number,\n imageWidth: res['imageWidth'] as number,\n imageHeight: res['imageHeight'] as number,\n token: res['token'] as number,\n };\n }\n\n async decode(embedding: SamEmbedding, prompt: SamPrompt): Promise<SamMaskResult> {\n if (this.mode === 'inproc') {\n const e = this.embeddings.get(embedding.token as number);\n if (!e) throw new Error('SAM embedding expired; re-encode the image.');\n return runDecoder(this.decoder!, e, prompt);\n }\n const res = await this.call({ type: 'decode', token: embedding.token, prompt });\n return {\n mask: new Uint8Array(res['buffer'] as ArrayBuffer),\n width: res['width'] as number,\n height: res['height'] as number,\n iou: res['iou'] as number,\n };\n }\n\n /** Release the worker / ORT sessions. Calls still in flight reject (RT-8), so\n * their callers' `finally` blocks run (e.g. a tool's busy flag clears). */\n dispose(): void {\n this.dropWorker(new Error('SAM session disposed'));\n this.encoder?.release?.();\n this.decoder?.release?.();\n this.encoder = this.decoder = null;\n this.embeddings.clear();\n this.loaded = false;\n this.mode = null;\n }\n\n // ── worker plumbing ──────────────────────────────────────────────────────\n private ensureWorker(): Worker {\n if (this.worker) return this.worker;\n const worker = new Worker(new URL('./onnx-sam.worker', import.meta.url), { type: 'module' });\n worker.onmessage = (ev: MessageEvent<WorkerReply>) => this.calls.handle(ev.data);\n worker.onerror = (ev: ErrorEvent) => {\n // A crashed worker can't serve the loaded model any more: drop it, so the\n // next run reloads instead of posting to a dead worker.\n this.dropWorker(new Error(ev.message || 'SAM worker crashed.'));\n this.loaded = false;\n this.mode = null;\n };\n this.worker = worker;\n return worker;\n }\n\n /** Terminate the worker (if any) and reject every call still waiting on it. */\n private dropWorker(err: Error): void {\n this.worker?.terminate();\n this.worker = null;\n this.calls.rejectAll(err);\n }\n\n private call(\n msg: Record<string, unknown>,\n transfer: Transferable[] = [],\n onProgress?: (f: number) => void,\n ): Promise<WorkerReply> {\n const worker = this.ensureWorker();\n const { id, promise } = this.calls.open(onProgress);\n worker.postMessage({ ...msg, id }, transfer);\n return promise;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ALL_GENES, b as ALL_TOOLBAR_TOOLS, C as CANVAS_TOOL_IDS,
|
|
1
|
+
export { A as ALL_GENES, b as ALL_TOOLBAR_TOOLS, c as ANNOTATION_IMAGE_MAX_DIM, C as CANVAS_TOOL_IDS, d as CELL_SEGMENTER, e as CHANNEL_HISTOGRAM_API, h as CellposeSegmenterService, i as ChannelHistogramComponent, D as DEFAULT_CATEGORICAL_PALETTE, j as DEFAULT_FALLBACK_PALETTE, k as DEFAULT_MUTED_OPACITY, l as DEFAULT_SAM_MODEL_ID, m as DEFAULT_SPATIAL_VIEW, H as HexColorPickerComponent, I as IMAGE_STATE_PORT, n as INSTANCE_SEGMENTER, o as ImageConverterService, L as LUT_COLORS, M as MISSING_COLOR, p as MultiPolygon, N as NAPARI_DECIMATE_OPTIONS, q as NAPARI_DEFAULT_DECIMATE, s as NO_CATEGORY, t as NO_OBSERVATION, P as PLOT_MODE_CONTEXT, u as PLOT_MODE_SESSION, v as PLOT_TYPE_CONTRIBUTIONS, w as PLOT_TYPE_DESCRIPTORS, x as PREFERENCES_PORT, y as PlotType, z as Polygon, B as ProcessingImage, R as REGION_EDITOR_API, E as REGION_IO_PORT, F as REGION_SOURCE_MIN_CHARS, G as Rectangle, J as Region, K as RegionEditorComponent, O as RoutingVisualizerService, S as SAM_MODELS, Q as SEMANTIC_SEGMENTER, T as SPATIAL_DATA_PORT, U as SPATIAL_WIRE_VERSION, V as ShapeSelection, W as SpatialChartsComponent, X as SpatialControlsComponent, Y as SpatialDataHttpService, Z as SpatialSelectionStore, _ as SupersededError, $ as TILE_ACCESS_PORT, a0 as TOOLBAR_TOOLS, a1 as VISUALIZER, a2 as VIZ_ALERT_TOAST_KEY, a3 as VIZ_CONFIG, a4 as VIZ_TOAST_KEY, a5 as ViewerFeature, a6 as VisualizationModule, a7 as VisualizerComponent, a8 as assertManifestVersion, a9 as benefitsFromGrouping, aa as buildOmicsTraces, ab as capabilitiesOf, ac as clearSamModelCache, ad as contrastWindow, ae as contributedPlotTypeOption, af as countMask, ag as cropImage, ah as datasetFromManifest, ai as decodeColumn, aj as decodeCoords, ak as decodeFeatureVector, al as decodePolygons, am as decodeRadius, an as defaultPresetSet, ao as dialogToolContributions, ap as emptySelection, aq as encodeCategorical, ar as encodeContinuous, as as expandZips, at as findColumnMeta, au as getDefaultSamModelId, g as getOrtWasmBase, av as getPlotTypeDescriptor, aw as getSamModel, ax as hasZarrMetadata, ay as hydrateBounds, az as importFile, aA as isBuiltinPlotType, aB as isCanvasToolId, aC as isCategoricalColumn, aD as isContinuousColumn, aE as isDialogToolContribution, aF as isEmptyPresetSet, aG as isLabelVolumeName, aH as isLittleEndian, aI as isNapari3d, aJ as isNapariIsosurface, aK as isNapariScatter, aL as isNapariScatter3d, aM as isNapariSurface, aN as isNapariVolume, aO as isSamModelReady, aP as isSpatialOmics, aQ as isSpatialOmics3d, aR as isThreeDimensional, aS as isZipName, aT as labelClasses, aU as labelSliceGeoJson, aV as lutFor, aW as markerDiameters, aX as maskToIndices, aY as mutedFromSelection, aZ as omicsLayout, a_ as openAnnotations, a$ as openGeoJsonRegionSource, b0 as openHttpRegionSource, b1 as parsePresetSet, b2 as planRegionImport, b3 as pointInRing, b4 as provideVisualization, b5 as readAnnotationSlices, b6 as readLabelVolume, b7 as readOmeZarrLabels, b8 as readZip, b9 as rendererOwnsWheel, ba as resolveCategoryColors, bb as selectByCategory, bc as selectInRegions, bd as setDefaultSamModel, be as setOrtWasmBase, bf as setSamModelUrls, bg as sortToolContributions, bh as visibleToolContributions } from './jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-a68Hw5rw.mjs';
|
|
2
2
|
import '@angular/core';
|
|
3
3
|
import '@angular/common/http';
|
|
4
4
|
import 'napari-js';
|
|
@@ -6,6 +6,8 @@ import 'rxjs';
|
|
|
6
6
|
import 'rxjs/operators';
|
|
7
7
|
import 'onnxruntime-web';
|
|
8
8
|
import 'napari-js/geometry';
|
|
9
|
+
import 'fast-png';
|
|
10
|
+
import 'fzstd';
|
|
9
11
|
import '@angular/core/rxjs-interop';
|
|
10
12
|
import '@angular/common';
|
|
11
13
|
import '@angular/forms';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jax-data-science-sci-image-visualizer.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"jax-data-science-sci-image-visualizer.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/index.d.ts
CHANGED
|
@@ -56,6 +56,12 @@ export { labelClasses, labelSliceGeoJson } from './lib/region-import/label-class
|
|
|
56
56
|
export type { LabelClasses } from './lib/region-import/label-classes';
|
|
57
57
|
export { importFile, planRegionImport } from './lib/region-import/region-import';
|
|
58
58
|
export type { RegionImportFile, RegionImportPlan } from './lib/region-import/region-import';
|
|
59
|
+
export { ANNOTATION_IMAGE_MAX_DIM, openAnnotations, readAnnotationSlices, } from './lib/region-import/open-annotations';
|
|
60
|
+
export type { AnnotationDecoders, AnnotationSlices, OpenedAnnotations, } from './lib/region-import/open-annotations';
|
|
61
|
+
export { hasZarrMetadata, readOmeZarrLabels } from './lib/region-import/ome-zarr-labels';
|
|
62
|
+
export type { LabelStack, OmeZarrLabels } from './lib/region-import/ome-zarr-labels';
|
|
63
|
+
export { expandZips, isZipName, readZip } from './lib/region-import/zip';
|
|
64
|
+
export type { ZipEntry } from './lib/region-import/zip';
|
|
59
65
|
export type { RegionSource, RegionSourceView, SourceFeature } from './lib/region-source/region-source';
|
|
60
66
|
export type { RegionIndexPage, RegionIndexQuery, RegionIndexRow, RegionSourceInfo, RegionTile, RegionTileKey, TileFeature, } from './lib/region-source/region-tiles';
|
|
61
67
|
export { VisualizationModule } from './lib/visualization.module';
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* `ImageInfo`/`ImageMetadata` classes are pure data holders that satisfy them
|
|
8
8
|
* structurally, so the host keeps passing its own instances unchanged.
|
|
9
9
|
*/
|
|
10
|
+
import type { RegionSource } from '../region-source/region-source';
|
|
10
11
|
/** Per-channel metadata from the server descriptor (issue #76). */
|
|
11
12
|
export interface IChannelInfo {
|
|
12
13
|
name: string;
|
|
@@ -65,6 +66,12 @@ export interface IImageInfo {
|
|
|
65
66
|
* its regions are drawn from the server's level-of-detail tiles instead of being loaded
|
|
66
67
|
* from `roiJsonStr`. On a stack, slice z shows the source's plane z. */
|
|
67
68
|
regionSourceUrl?: string;
|
|
69
|
+
/** The image's own regions, one region source per slice, opened the first time its slice is
|
|
70
|
+
* shown: what an image built from annotations alone carries (see `openAnnotations`: a label
|
|
71
|
+
* volume traced slice by slice, a folder of per-slice GeoJSON). Attached when the image lands,
|
|
72
|
+
* replacing its regions, as an imported label volume or folder is; takes precedence over
|
|
73
|
+
* `roiJsonStrs`, `roiJsonStr` and `regionSourceUrl`. Keyed by slice index. */
|
|
74
|
+
regionSlices?: ReadonlyMap<number, () => Promise<RegionSource>>;
|
|
68
75
|
/** Per-slice, per-channel plane URLs for a SERVERLESS multichannel image
|
|
69
76
|
* (`tiled: false` + `imageMeta[0].channelCount > 1`). `channelUrls[z][c]`
|
|
70
77
|
* is a complete single-band image of channel c at slice z. The OSD simple
|
|
@@ -2,6 +2,8 @@ import { IHistogram } from '../../contracts/channel-histogram-api.contract';
|
|
|
2
2
|
import { SpatialDataset, SpatialObservations } from '../../contracts/spatial-dataset.contract';
|
|
3
3
|
import { SpatialViewState } from '../../contracts/display-types';
|
|
4
4
|
import { SpatialSelectionMask } from '../../spatial/spatial-selection';
|
|
5
|
+
import type { IImageInfo } from '../../contracts/image.contract';
|
|
6
|
+
import { RegionMesh3D } from './napari-volume-region-meshes';
|
|
5
7
|
import { Spatial3dScalars } from './napari-spatial-encoding';
|
|
6
8
|
import { SpatialSceneBase, SpatialSession } from './napari-spatial-scene';
|
|
7
9
|
import type { NapariScene, SceneContext } from './napari-scene';
|
|
@@ -17,6 +19,7 @@ import type { NapariScene, SceneContext } from './napari-scene';
|
|
|
17
19
|
* shape while keeping the selection. The first 3D layer added frames the camera (`fit3d: 'once'`).
|
|
18
20
|
*/
|
|
19
21
|
export declare class Spatial3dScene extends SpatialSceneBase implements NapariScene {
|
|
22
|
+
private readonly info?;
|
|
20
23
|
/** The 3D point cloud. One layer: selection is a per-point alpha on it, not a second. */
|
|
21
24
|
private points;
|
|
22
25
|
private layerKey;
|
|
@@ -58,10 +61,46 @@ export declare class Spatial3dScene extends SpatialSceneBase implements NapariSc
|
|
|
58
61
|
* one; a rebuild that leaves the key alone lets it finish. */
|
|
59
62
|
private readonly densityLoads;
|
|
60
63
|
private readonly geneMapLoads;
|
|
64
|
+
/**
|
|
65
|
+
* The region reconstructions (an atlas's structures, imported onto the reference volume's image
|
|
66
|
+
* stack), drawn by the same {@link VolumeRegionMeshes} as the Volume view's, so a structure is the
|
|
67
|
+
* same surface in both. Placed in the reference volume's box, once it is there and the image the
|
|
68
|
+
* regions were drawn on is that volume's stack (see {@link placeRegionMeshes}).
|
|
69
|
+
*/
|
|
70
|
+
private readonly regionMeshes;
|
|
71
|
+
/** Whether the reference volume lets every region surface through (see setRegionsThroughVolume). */
|
|
72
|
+
private regionsThrough;
|
|
61
73
|
/** The store's invert setting, which the density volumes follow (no other subscription of
|
|
62
74
|
* this scene carries it). */
|
|
63
75
|
private invertSub;
|
|
64
|
-
|
|
76
|
+
/**
|
|
77
|
+
* @param info the image plotted with the cloud: the reference volume's stack (the 2D view's
|
|
78
|
+
* image, see showVolumeAsImage) when the dataset has a volume, which the regions are on
|
|
79
|
+
*/
|
|
80
|
+
constructor(ctx: SceneContext, session: SpatialSession, info?: IImageInfo | undefined);
|
|
81
|
+
/** Show these region reconstructions with the cloud (replacing the ones shown). */
|
|
82
|
+
setRegionMeshes(meshes: readonly RegionMesh3D[]): void;
|
|
83
|
+
setRegionHighlight(keys: readonly string[]): void;
|
|
84
|
+
setRegionHidden(keys: readonly string[]): void;
|
|
85
|
+
/** As the Volume view: the reference volume hides the surfaces behind what it shows, or none. */
|
|
86
|
+
setRegionsThroughVolume(through: boolean): void;
|
|
87
|
+
/**
|
|
88
|
+
* Place the region surfaces in the reference volume's box, as the Volume view places them in its
|
|
89
|
+
* own: image pixel (x, y) is voxel (x, y), slice k is voxel plane k, and the box is the volume's
|
|
90
|
+
* voxels times its voxel size, centred on the origin as napari centres the volume (and the cloud
|
|
91
|
+
* is offset to sit in it). Only when the plotted image IS that volume's stack: regions drawn on
|
|
92
|
+
* another image (a tissue image) have no place in it, and are not shown - nor are surfaces
|
|
93
|
+
* placed in a volume this one replaced.
|
|
94
|
+
*/
|
|
95
|
+
private placeRegionMeshes;
|
|
96
|
+
/**
|
|
97
|
+
* Image px per voxel of the volume the surfaces sit in: 1, the reference volume being the stack's
|
|
98
|
+
* own voxels, as the Volume view's is at full resolution, so both reconstruct a class alike (and
|
|
99
|
+
* share its cached surface). Null while there is no volume stack to place them in.
|
|
100
|
+
*/
|
|
101
|
+
voxelStep(): number | null;
|
|
102
|
+
/** Whether the plotted image is the reference volume's stack: its size and slice count. */
|
|
103
|
+
private isVolumeStack;
|
|
65
104
|
mount(): Promise<void>;
|
|
66
105
|
/** The observations' own z is on screen: there is no plane to step. */
|
|
67
106
|
setZ(): void;
|
|
@@ -54,6 +54,11 @@ export declare class VolumeRegionMeshes {
|
|
|
54
54
|
/** The meshes shown. */
|
|
55
55
|
get shown(): RegionMesh3D[];
|
|
56
56
|
clear(): void;
|
|
57
|
+
/**
|
|
58
|
+
* The volume the surfaces sat in is gone (or no longer theirs): take every surface down, keeping
|
|
59
|
+
* the meshes wanted, so they come back when a frame is set again.
|
|
60
|
+
*/
|
|
61
|
+
clearFrame(): void;
|
|
57
62
|
private sync;
|
|
58
63
|
private add;
|
|
59
64
|
private opacityOf;
|
|
@@ -360,10 +360,11 @@ export declare class RegionEditorComponent implements OnInit, OnDestroy {
|
|
|
360
360
|
/** "Import regions from server": the host picks files or a served region set; they import as chosen files do. */
|
|
361
361
|
protected importFromServer(): void;
|
|
362
362
|
/**
|
|
363
|
-
* Import `files
|
|
364
|
-
* region source per slice,
|
|
365
|
-
*
|
|
366
|
-
*
|
|
363
|
+
* Import `files`: one GeoJSON file as before (its regions, editable); anything else (see
|
|
364
|
+
* readAnnotationSlices) as one region source per slice, replacing the regions and sources of
|
|
365
|
+
* every slice: a label volume or an OME-Zarr label image traced the first time a slice is shown,
|
|
366
|
+
* its classes named (and treed) by its ontology; a folder as one source per slice file; a
|
|
367
|
+
* z-indexed GeoJSON one per plane; a zip of any of these.
|
|
367
368
|
*/
|
|
368
369
|
private importRegionFiles;
|
|
369
370
|
/** A region set the host's server serves (jit-service's /regions/{id}/), read through its HttpClient. */
|
|
@@ -20,6 +20,10 @@ export interface LabelVolume {
|
|
|
20
20
|
height: number;
|
|
21
21
|
/** Number of slices. */
|
|
22
22
|
slices: number;
|
|
23
|
+
/** Size of a voxel along the slice's x, its y and across slices, in micrometres, when the file
|
|
24
|
+
* says (NRRD `space directions`/`spacings`, micrometres unless `space units` says otherwise;
|
|
25
|
+
* NIfTI `pixdim`, in its `xyzt_units`, millimetres when unset). */
|
|
26
|
+
spacing?: [number, number, number];
|
|
23
27
|
/** Slice `z`'s labels, row-major (`height` rows of `width`). */
|
|
24
28
|
sliceLabels(z: number): Uint32Array;
|
|
25
29
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A label image of an OME-Zarr store (`labels/<name>/`, NGFF 0.4 on zarr v2 or 0.5 on zarr v3),
|
|
3
|
+
* read in the browser from the store's files (a folder chosen, or a zip's members): its full
|
|
4
|
+
* resolution level, sliced along z, with the classes its `image-label` metadata names and colours.
|
|
5
|
+
*
|
|
6
|
+
* Chunks are decoded when a slice that needs them is read, so a store is never decoded whole.
|
|
7
|
+
* Only what label images use: C-order chunks of integer (or integer-valued float) samples, through
|
|
8
|
+
* the codecs {@link decodeChunk} reads, a missing chunk being the fill value. Sharded or transposed
|
|
9
|
+
* arrays, F-order and filters throw rather than be read wrongly. Pure: usable from workers.
|
|
10
|
+
*/
|
|
11
|
+
import { LabelClasses } from './label-classes';
|
|
12
|
+
import type { RegionImportFile } from './region-import';
|
|
13
|
+
import { Inflate } from './zarr-codecs';
|
|
14
|
+
/** A stack of label slices read on demand. Label 0 is background. */
|
|
15
|
+
export interface LabelStack {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
slices: number;
|
|
19
|
+
/** Size of a voxel, x, y, z, in micrometres, when the source says. */
|
|
20
|
+
spacing?: [number, number, number];
|
|
21
|
+
/** Slice `z`'s labels, row-major (`height` rows of `width`). */
|
|
22
|
+
labels(z: number): Promise<Uint32Array>;
|
|
23
|
+
}
|
|
24
|
+
/** An OME-Zarr label image: its name, its slices, its classes. */
|
|
25
|
+
export interface OmeZarrLabels {
|
|
26
|
+
name: string;
|
|
27
|
+
stack: LabelStack;
|
|
28
|
+
classes: LabelClasses;
|
|
29
|
+
}
|
|
30
|
+
/** Whether `files` hold a zarr store (any zarr metadata among them). */
|
|
31
|
+
export declare function hasZarrMetadata(files: readonly RegionImportFile[]): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The first label image of the zarr store among `files` (the order `labels/`'s listing gives,
|
|
34
|
+
* else the first found), or null when the store has none.
|
|
35
|
+
*/
|
|
36
|
+
export declare function readOmeZarrLabels(files: readonly RegionImportFile[], inflate?: Inflate): Promise<OmeZarrLabels | null>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { IImageInfo } from '../contracts/image.contract';
|
|
2
|
+
import type { RegionSource } from '../region-source/region-source';
|
|
3
|
+
import { LabelClasses } from './label-classes';
|
|
4
|
+
import { Gunzip } from './label-volume';
|
|
5
|
+
import { LabelStack } from './ome-zarr-labels';
|
|
6
|
+
import { RegionImportFile } from './region-import';
|
|
7
|
+
import { Inflate } from './zarr-codecs';
|
|
8
|
+
import { InflateRaw } from './zip';
|
|
9
|
+
/** The longest side an image built for annotations may have: OpenSeadragon's non-tiled limit. */
|
|
10
|
+
export declare const ANNOTATION_IMAGE_MAX_DIM = 8192;
|
|
11
|
+
/** Opens a slice's region source (again, after the store closed it). */
|
|
12
|
+
type Opener = () => Promise<RegionSource>;
|
|
13
|
+
/** Decoders, injectable for tests and workers (defaults: the platform's streams). */
|
|
14
|
+
export interface AnnotationDecoders {
|
|
15
|
+
inflateRaw?: InflateRaw;
|
|
16
|
+
inflate?: Inflate;
|
|
17
|
+
gunzip?: Gunzip;
|
|
18
|
+
/** Open a GeoJSON text as a region source (default: in the shared region worker). */
|
|
19
|
+
openGeoJson?: (text: string) => Promise<RegionSource>;
|
|
20
|
+
/**
|
|
21
|
+
* Give a label image's regions the atlas look (dark outlines, labels) when its ontology names
|
|
22
|
+
* no style: drawn over the label image itself, outlines in their classes' own colours would not
|
|
23
|
+
* show. (Imported over a real image, the default look reads.)
|
|
24
|
+
*/
|
|
25
|
+
outlineLabels?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** The annotations among some files, as one region source per slice. */
|
|
28
|
+
export interface AnnotationSlices {
|
|
29
|
+
kind: 'labels' | 'folder' | 'single';
|
|
30
|
+
/** A name for what was opened (the label image's, the folder's, the file's). */
|
|
31
|
+
name: string;
|
|
32
|
+
slices: number;
|
|
33
|
+
/** One opener per slice index. */
|
|
34
|
+
openers: Map<number, Opener>;
|
|
35
|
+
/** A label image's slices and classes (to draw them, see openAnnotations). */
|
|
36
|
+
labels?: {
|
|
37
|
+
stack: LabelStack;
|
|
38
|
+
classes: LabelClasses;
|
|
39
|
+
};
|
|
40
|
+
/** Slice width and height, when known without opening the sources (a label image's). */
|
|
41
|
+
size?: [number, number];
|
|
42
|
+
/** Voxel size x, y, z in micrometres, when the source says. */
|
|
43
|
+
spacing?: [number, number, number];
|
|
44
|
+
/** What was read, for a message ("12 slices of 456 × 320"). */
|
|
45
|
+
summary: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The annotations among `files` (zips expanded), as one region source per slice; null when there
|
|
49
|
+
* are none to read. A label image's slices are traced, and a GeoJSON parsed, when the slice is
|
|
50
|
+
* first shown.
|
|
51
|
+
*/
|
|
52
|
+
export declare function readAnnotationSlices(chosen: readonly RegionImportFile[], decoders?: AnnotationDecoders): Promise<AnnotationSlices | null>;
|
|
53
|
+
/** What {@link openAnnotations} built: the image to publish, and how to release it. */
|
|
54
|
+
export interface OpenedAnnotations {
|
|
55
|
+
info: IImageInfo;
|
|
56
|
+
summary: string;
|
|
57
|
+
/** Revoke the image's blob URLs, and release the slices' sources the region store never took
|
|
58
|
+
* (once another image replaced it). */
|
|
59
|
+
dispose(): void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The image `files`' annotations are shown on by themselves (see the module comment). Throws when
|
|
63
|
+
* there are none, or when their regions span more than {@link ANNOTATION_IMAGE_MAX_DIM} pixels
|
|
64
|
+
* (they are then imported onto the image they were drawn on).
|
|
65
|
+
*/
|
|
66
|
+
export declare function openAnnotations(files: readonly RegionImportFile[], decoders?: AnnotationDecoders): Promise<OpenedAnnotations>;
|
|
67
|
+
/**
|
|
68
|
+
* One GeoJSON's slices: a collection whose features name planes (`geometry.plane.z`) split into
|
|
69
|
+
* one collection per plane, each keeping the set's class tree and style, its features moved to
|
|
70
|
+
* plane 0 (a slice's source shows plane 0); any other GeoJSON is one slice as it is.
|
|
71
|
+
*/
|
|
72
|
+
export declare function splitByPlane(text: string): string[];
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Inflate gzip or zlib data (default: the platform's `DecompressionStream`). */
|
|
2
|
+
export type Inflate = (bytes: Uint8Array, format: 'gzip' | 'deflate') => Promise<Uint8Array>;
|
|
3
|
+
/** Decode a Blosc frame. */
|
|
4
|
+
export declare function bloscDecompress(frame: Uint8Array, inflate?: Inflate): Promise<Uint8Array>;
|
|
5
|
+
/** Decode one LZ4 block (no frame) of `size` bytes. */
|
|
6
|
+
export declare function lz4BlockDecompress(src: Uint8Array, size: number): Uint8Array;
|
|
7
|
+
/**
|
|
8
|
+
* Decode a zarr chunk through `codecs`, outermost last as zarr lists them (v2: the one
|
|
9
|
+
* `compressor`; v3: the bytes-to-bytes codecs after `bytes`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function decodeChunk(bytes: Uint8Array, codecs: readonly {
|
|
12
|
+
id: string;
|
|
13
|
+
[k: string]: unknown;
|
|
14
|
+
}[], inflate?: Inflate): Promise<Uint8Array>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The members of a `.zip`, read in the browser: annotations handed over as one archive (a folder
|
|
3
|
+
* of per-slice GeoJSON, a label volume with its ontology, an OME-Zarr store's labels).
|
|
4
|
+
*
|
|
5
|
+
* Only what such archives use: stored or deflated members, ZIP64 sizes and offsets, UTF-8 or
|
|
6
|
+
* legacy names. An encrypted or otherwise compressed member throws when it is read, rather than be
|
|
7
|
+
* read wrongly; a member's CRC-32 is checked. Members are read on demand, so an archive of many
|
|
8
|
+
* chunks (a zarr store) is not inflated whole. Pure: usable from workers.
|
|
9
|
+
*/
|
|
10
|
+
import type { RegionImportFile } from './region-import';
|
|
11
|
+
/** One member of an archive. */
|
|
12
|
+
export interface ZipEntry {
|
|
13
|
+
/** Its path in the archive, '/'-separated. */
|
|
14
|
+
name: string;
|
|
15
|
+
/** Its uncompressed size, in bytes. */
|
|
16
|
+
size: number;
|
|
17
|
+
bytes(): Promise<Uint8Array>;
|
|
18
|
+
}
|
|
19
|
+
/** Inflate raw DEFLATE data (default: the platform's `DecompressionStream('deflate-raw')`). */
|
|
20
|
+
export type InflateRaw = (bytes: Uint8Array) => Promise<Uint8Array>;
|
|
21
|
+
/** Whether a file name is a zip archive. */
|
|
22
|
+
export declare function isZipName(name: string): boolean;
|
|
23
|
+
/** The members of the archive in `bytes` (directories and macOS metadata left out). */
|
|
24
|
+
export declare function readZip(bytes: Uint8Array, inflateRaw?: InflateRaw): ZipEntry[];
|
|
25
|
+
/**
|
|
26
|
+
* `files` with every `.zip` among them replaced by its members, named by their paths in it (a
|
|
27
|
+
* single top folder the archive wraps everything in is kept, as a folder pick would keep it).
|
|
28
|
+
*/
|
|
29
|
+
export declare function expandZips(files: readonly RegionImportFile[], inflateRaw?: InflateRaw): Promise<RegionImportFile[]>;
|
|
30
|
+
/** CRC-32 (IEEE), as zip records it. */
|
|
31
|
+
export declare function crc32(data: Uint8Array): number;
|
|
@@ -75,8 +75,14 @@ export declare class RoutingVisualizerService implements IVisualizer, IRegionEdi
|
|
|
75
75
|
private plottedFileName;
|
|
76
76
|
/** The div the backends render into (every backend shares it); '' before the first plot. */
|
|
77
77
|
private plotDiv;
|
|
78
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Whether regions can be shown in 3D on screen: a napari volume (or isosurface) view, or the 3D
|
|
80
|
+
* spatial cloud over a stack image (its reference volume's, see showVolumeAsImage). Both draw the
|
|
81
|
+
* region reconstructions the same way (VolumeRegionMeshes), in the volume's box.
|
|
82
|
+
*/
|
|
79
83
|
private readonly volumeView$;
|
|
84
|
+
/** Whether a napari volume (or isosurface) view is the current plot type: the slice pane's. */
|
|
85
|
+
private imageVolumeView;
|
|
80
86
|
/** Whether the volume view's slice pane is wanted (see {@link setSliceEditor}). */
|
|
81
87
|
private readonly sliceEditor$;
|
|
82
88
|
/** Whether the volume view draws the region reconstructions through the volume. */
|
|
@@ -301,6 +307,8 @@ export declare class RoutingVisualizerService implements IVisualizer, IRegionEdi
|
|
|
301
307
|
deleteSourceFeatures(ids: readonly string[]): void;
|
|
302
308
|
getRegionStyle$(): Observable<RegionStyle>;
|
|
303
309
|
setRegionStyle(patch: Partial<RegionStyle>): void;
|
|
310
|
+
/** Recompute which 3D view is on screen for `plotType` (see {@link volumeView$}). */
|
|
311
|
+
private updateVolumeView;
|
|
304
312
|
/** Whether a napari volume (or isosurface) view is on screen: regions can be shown in 3D. */
|
|
305
313
|
getVolumeView$(): Observable<boolean>;
|
|
306
314
|
/** Whether the volume view's slice pane is wanted (it shows while a volume view is on screen). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jax-data-science/sci-image-visualizer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20.9.0"
|
|
6
6
|
},
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"cellpose-js": "^0.6.0",
|
|
41
41
|
"fast-png": "^6.4.0",
|
|
42
|
+
"fzstd": "^0.1.1",
|
|
42
43
|
"napari-js": "^0.18.0",
|
|
43
44
|
"openseadragon": "^6.0.2",
|
|
44
45
|
"plotly.js-dist-min": "^3.0.1",
|