@fideus-labs/ngff-zarr 0.2.0 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fideus-labs/ngff-zarr",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "TypeScript implementation of ngff-zarr for reading and writing OME-Zarr files",
5
5
  "keywords": [
6
6
  "ome-zarr",
@@ -26,11 +26,35 @@
26
26
  "types": "./esm/mod.d.ts",
27
27
  "exports": {
28
28
  ".": {
29
+ "types": "./esm/mod.d.ts",
30
+ "browser": "./esm/browser-mod.js",
29
31
  "import": "./esm/mod.js",
30
32
  "require": "./script/mod.js",
31
- "types": "./esm/mod.d.ts"
33
+ "default": "./esm/mod.js"
34
+ },
35
+ "./browser": {
36
+ "types": "./types/browser-mod.d.ts",
37
+ "import": "./esm/browser-mod.js",
38
+ "require": "./script/browser-mod.js",
39
+ "default": "./esm/browser-mod.js"
40
+ },
41
+ "./methods/itkwasm-browser": {
42
+ "types": "./types/methods/itkwasm-browser.d.ts",
43
+ "import": "./esm/methods/itkwasm-browser.js",
44
+ "require": "./script/methods/itkwasm-browser.js",
45
+ "default": "./esm/methods/itkwasm-browser.js"
46
+ },
47
+ "./methods/itkwasm-node": {
48
+ "types": "./types/methods/itkwasm-node.d.ts",
49
+ "import": "./esm/methods/itkwasm-node.js",
50
+ "require": "./script/methods/itkwasm-node.js",
51
+ "default": "./esm/methods/itkwasm-node.js"
32
52
  }
33
53
  },
54
+ "browser": {
55
+ "./esm/methods/itkwasm-node.js": "./esm/methods/itkwasm-browser.js",
56
+ "./script/methods/itkwasm-node.js": "./script/methods/itkwasm-browser.js"
57
+ },
34
58
  "files": [
35
59
  "esm/",
36
60
  "script/",
@@ -40,7 +64,7 @@
40
64
  ],
41
65
  "dependencies": {
42
66
  "@itk-wasm/downsample": "^1.8.1",
43
- "itk-wasm": "^1.0.0-b.195",
67
+ "itk-wasm": "^1.0.0-b.196",
44
68
  "p-queue": "^8.1.0",
45
69
  "@zarrita/storage": "^0.1.1",
46
70
  "zod": "^4.0.2",
@@ -0,0 +1,14 @@
1
+ export * from "./types/units.js";
2
+ export * from "./types/methods.js";
3
+ export * from "./types/array_interface.js";
4
+ export * from "./types/zarr_metadata.js";
5
+ export * from "./types/ngff_image.js";
6
+ export * from "./types/multiscales.js";
7
+ export * from "./schemas/units.js";
8
+ export * from "./schemas/methods.js";
9
+ export * from "./schemas/zarr_metadata.js";
10
+ export * from "./schemas/ngff_image.js";
11
+ export * from "./schemas/multiscales.js";
12
+ export { isValidDimension, isValidUnit, validateMetadata, } from "./utils/validation.js";
13
+ export { createAxis, createDataset, createMetadata, createMultiscales, createNgffImage, } from "./utils/factory.js";
14
+ //# sourceMappingURL=browser-mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-mod.d.ts","sourceRoot":"","sources":["../src/browser-mod.ts"],"names":[],"mappings":"AASA,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AAEvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AAEzC,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,UAAU,EACV,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createNgffImage = exports.createMultiscales = exports.createMetadata = exports.createDataset = exports.createAxis = exports.validateMetadata = exports.isValidUnit = exports.isValidDimension = void 0;
18
+ // SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC
19
+ // SPDX-License-Identifier: MIT
20
+ // Browser-compatible module exports
21
+ // This module excludes I/O functionality (from_ngff_zarr, to_ngff_zarr)
22
+ // because those modules depend on Node.js/Deno-specific filesystem APIs
23
+ // that are not available in browser environments.
24
+ //
25
+ // For browser use cases, the schemas, types, and validation utilities
26
+ // are the most commonly needed functionality.
27
+ __exportStar(require("./types/units.js"), exports);
28
+ __exportStar(require("./types/methods.js"), exports);
29
+ __exportStar(require("./types/array_interface.js"), exports);
30
+ __exportStar(require("./types/zarr_metadata.js"), exports);
31
+ __exportStar(require("./types/ngff_image.js"), exports);
32
+ __exportStar(require("./types/multiscales.js"), exports);
33
+ __exportStar(require("./schemas/units.js"), exports);
34
+ __exportStar(require("./schemas/methods.js"), exports);
35
+ __exportStar(require("./schemas/zarr_metadata.js"), exports);
36
+ __exportStar(require("./schemas/ngff_image.js"), exports);
37
+ __exportStar(require("./schemas/multiscales.js"), exports);
38
+ var validation_js_1 = require("./utils/validation.js");
39
+ Object.defineProperty(exports, "isValidDimension", { enumerable: true, get: function () { return validation_js_1.isValidDimension; } });
40
+ Object.defineProperty(exports, "isValidUnit", { enumerable: true, get: function () { return validation_js_1.isValidUnit; } });
41
+ Object.defineProperty(exports, "validateMetadata", { enumerable: true, get: function () { return validation_js_1.validateMetadata; } });
42
+ var factory_js_1 = require("./utils/factory.js");
43
+ Object.defineProperty(exports, "createAxis", { enumerable: true, get: function () { return factory_js_1.createAxis; } });
44
+ Object.defineProperty(exports, "createDataset", { enumerable: true, get: function () { return factory_js_1.createDataset; } });
45
+ Object.defineProperty(exports, "createMetadata", { enumerable: true, get: function () { return factory_js_1.createMetadata; } });
46
+ Object.defineProperty(exports, "createMultiscales", { enumerable: true, get: function () { return factory_js_1.createMultiscales; } });
47
+ Object.defineProperty(exports, "createNgffImage", { enumerable: true, get: function () { return factory_js_1.createNgffImage; } });
48
+ // Note: Excluding I/O modules for browser compatibility
@@ -0,0 +1,6 @@
1
+ import { NgffImage } from "../types/ngff_image.js";
2
+ /**
3
+ * Main downsampling function for ITK-Wasm (browser version)
4
+ */
5
+ export declare function downsampleItkWasm(ngffImage: NgffImage, scaleFactors: (Record<string, number> | number)[], smoothing: "gaussian" | "bin_shrink" | "label_image"): Promise<NgffImage[]>;
6
+ //# sourceMappingURL=itkwasm-browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"itkwasm-browser.d.ts","sourceRoot":"","sources":["../../src/methods/itkwasm-browser.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AA2fnD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE,EACjD,SAAS,EAAE,UAAU,GAAG,YAAY,GAAG,aAAa,GACnD,OAAO,CAAC,SAAS,EAAE,CAAC,CAsHtB"}
@@ -0,0 +1,488 @@
1
+ "use strict";
2
+ // SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC
3
+ // SPDX-License-Identifier: MIT
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || function (mod) {
21
+ if (mod && mod.__esModule) return mod;
22
+ var result = {};
23
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24
+ __setModuleDefault(result, mod);
25
+ return result;
26
+ };
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.downsampleItkWasm = downsampleItkWasm;
29
+ /**
30
+ * Browser-compatible ITK-Wasm downsampling support
31
+ * Uses WebWorker-based implementations from @itk-wasm/downsample
32
+ */
33
+ const downsample_1 = require("@itk-wasm/downsample");
34
+ const zarr = __importStar(require("zarrita"));
35
+ const ngff_image_js_1 = require("../types/ngff_image.js");
36
+ const itkwasm_shared_js_1 = require("./itkwasm-shared.js");
37
+ /**
38
+ * Perform Gaussian downsampling using ITK-Wasm (browser version)
39
+ */
40
+ async function downsampleGaussian(image, dimFactors, spatialDims) {
41
+ // Handle time dimension by processing each time slice independently
42
+ if (image.dims.includes("t")) {
43
+ const tDimIndex = image.dims.indexOf("t");
44
+ const tSize = image.data.shape[tDimIndex];
45
+ const newDims = image.dims.filter((dim) => dim !== "t");
46
+ // Downsample each time slice
47
+ const downsampledSlices = [];
48
+ for (let t = 0; t < tSize; t++) {
49
+ // Extract time slice
50
+ const selection = new Array(image.data.shape.length).fill(null);
51
+ selection[tDimIndex] = t;
52
+ const sliceData = await zarr.get(image.data, selection);
53
+ // Create temporary zarr array for this slice
54
+ const sliceStore = new Map();
55
+ const sliceRoot = zarr.root(sliceStore);
56
+ const sliceShape = image.data.shape.filter((_, i) => i !== tDimIndex);
57
+ const sliceChunkShape = sliceShape.map((s) => Math.min(s, 256));
58
+ const sliceArray = await zarr.create(sliceRoot.resolve("slice"), {
59
+ shape: sliceShape,
60
+ chunk_shape: sliceChunkShape,
61
+ data_type: image.data.dtype,
62
+ fill_value: 0,
63
+ });
64
+ const fullSelection = new Array(sliceShape.length).fill(null);
65
+ await zarr.set(sliceArray, fullSelection, sliceData);
66
+ // Create NgffImage for this slice (without 't' dimension)
67
+ const sliceImage = new ngff_image_js_1.NgffImage({
68
+ data: sliceArray,
69
+ dims: newDims,
70
+ scale: Object.fromEntries(Object.entries(image.scale).filter(([dim]) => dim !== "t")),
71
+ translation: Object.fromEntries(Object.entries(image.translation).filter(([dim]) => dim !== "t")),
72
+ name: image.name,
73
+ axesUnits: image.axesUnits
74
+ ? Object.fromEntries(Object.entries(image.axesUnits).filter(([dim]) => dim !== "t"))
75
+ : undefined,
76
+ computedCallbacks: image.computedCallbacks,
77
+ });
78
+ // Recursively downsample this slice (without 't', so no infinite loop)
79
+ const downsampledSlice = await downsampleGaussian(sliceImage, dimFactors, spatialDims);
80
+ downsampledSlices.push(downsampledSlice.data);
81
+ }
82
+ // Combine downsampled slices back into a single array with 't' dimension
83
+ const firstSlice = downsampledSlices[0];
84
+ const combinedShape = [...image.data.shape];
85
+ combinedShape[tDimIndex] = tSize;
86
+ // Update spatial dimensions based on downsampled size
87
+ for (let i = 0; i < image.dims.length; i++) {
88
+ if (i !== tDimIndex) {
89
+ const sliceIndex = i < tDimIndex ? i : i - 1;
90
+ combinedShape[i] = firstSlice.shape[sliceIndex];
91
+ }
92
+ }
93
+ // Create combined array
94
+ const combinedStore = new Map();
95
+ const combinedRoot = zarr.root(combinedStore);
96
+ const combinedArray = await zarr.create(combinedRoot.resolve("combined"), {
97
+ shape: combinedShape,
98
+ chunk_shape: combinedShape.map((s) => Math.min(s, 256)),
99
+ data_type: image.data.dtype,
100
+ fill_value: 0,
101
+ });
102
+ // Copy each downsampled slice into the combined array
103
+ for (let t = 0; t < tSize; t++) {
104
+ const sliceData = await zarr.get(downsampledSlices[t]);
105
+ const targetSelection = new Array(combinedShape.length).fill(null);
106
+ targetSelection[tDimIndex] = t;
107
+ await zarr.set(combinedArray, targetSelection, sliceData);
108
+ }
109
+ // Compute new metadata (time dimension unchanged, spatial dimensions downsampled)
110
+ const [translation, scale] = (0, itkwasm_shared_js_1.nextScaleMetadata)(image, dimFactors, spatialDims);
111
+ return new ngff_image_js_1.NgffImage({
112
+ data: combinedArray,
113
+ dims: image.dims,
114
+ scale: { ...image.scale, ...scale },
115
+ translation: { ...image.translation, ...translation },
116
+ name: image.name,
117
+ axesUnits: image.axesUnits,
118
+ computedCallbacks: image.computedCallbacks,
119
+ });
120
+ }
121
+ const isVector = image.dims.includes("c");
122
+ // Convert to ITK-Wasm format
123
+ const itkImage = await (0, itkwasm_shared_js_1.zarrToItkImage)(image.data, image.dims, isVector);
124
+ // Prepare shrink factors - need to be for ALL dimensions in ITK order (reversed)
125
+ const shrinkFactors = [];
126
+ for (let i = image.dims.length - 1; i >= 0; i--) {
127
+ const dim = image.dims[i];
128
+ if (itkwasm_shared_js_1.SPATIAL_DIMS.includes(dim)) {
129
+ shrinkFactors.push(dimFactors[dim] || 1);
130
+ }
131
+ }
132
+ // Use all zeros for cropRadius
133
+ const cropRadius = new Array(shrinkFactors.length).fill(0);
134
+ // Perform downsampling using browser-compatible function
135
+ const { downsampled } = await (0, downsample_1.downsample)(itkImage, {
136
+ shrinkFactors,
137
+ cropRadius: cropRadius,
138
+ });
139
+ // Compute new metadata
140
+ const [translation, scale] = (0, itkwasm_shared_js_1.nextScaleMetadata)(image, dimFactors, spatialDims);
141
+ // Convert back to zarr array in a new in-memory store
142
+ const store = new Map();
143
+ const chunkShape = downsampled.size.map((s) => Math.min(s, 256)).reverse();
144
+ const array = await (0, itkwasm_shared_js_1.itkImageToZarr)(downsampled, store, "image", chunkShape, image.dims);
145
+ return new ngff_image_js_1.NgffImage({
146
+ data: array,
147
+ dims: image.dims,
148
+ scale,
149
+ translation,
150
+ name: image.name,
151
+ axesUnits: image.axesUnits,
152
+ computedCallbacks: image.computedCallbacks,
153
+ });
154
+ }
155
+ /**
156
+ * Perform bin shrink downsampling using ITK-Wasm (browser version)
157
+ */
158
+ async function downsampleBinShrinkImpl(image, dimFactors, spatialDims) {
159
+ // Handle time dimension by processing each time slice independently
160
+ if (image.dims.includes("t")) {
161
+ const tDimIndex = image.dims.indexOf("t");
162
+ const tSize = image.data.shape[tDimIndex];
163
+ const newDims = image.dims.filter((dim) => dim !== "t");
164
+ // Downsample each time slice
165
+ const downsampledSlices = [];
166
+ for (let t = 0; t < tSize; t++) {
167
+ // Extract time slice
168
+ const selection = new Array(image.data.shape.length).fill(null);
169
+ selection[tDimIndex] = t;
170
+ const sliceData = await zarr.get(image.data, selection);
171
+ // Create temporary zarr array for this slice
172
+ const sliceStore = new Map();
173
+ const sliceRoot = zarr.root(sliceStore);
174
+ const sliceShape = image.data.shape.filter((_, i) => i !== tDimIndex);
175
+ const sliceChunkShape = sliceShape.map((s) => Math.min(s, 256));
176
+ const sliceArray = await zarr.create(sliceRoot.resolve("slice"), {
177
+ shape: sliceShape,
178
+ chunk_shape: sliceChunkShape,
179
+ data_type: image.data.dtype,
180
+ fill_value: 0,
181
+ });
182
+ const fullSelection = new Array(sliceShape.length).fill(null);
183
+ await zarr.set(sliceArray, fullSelection, sliceData);
184
+ // Create NgffImage for this slice (without 't' dimension)
185
+ const sliceImage = new ngff_image_js_1.NgffImage({
186
+ data: sliceArray,
187
+ dims: newDims,
188
+ scale: Object.fromEntries(Object.entries(image.scale).filter(([dim]) => dim !== "t")),
189
+ translation: Object.fromEntries(Object.entries(image.translation).filter(([dim]) => dim !== "t")),
190
+ name: image.name,
191
+ axesUnits: image.axesUnits
192
+ ? Object.fromEntries(Object.entries(image.axesUnits).filter(([dim]) => dim !== "t"))
193
+ : undefined,
194
+ computedCallbacks: image.computedCallbacks,
195
+ });
196
+ // Recursively downsample this slice
197
+ const downsampledSlice = await downsampleBinShrinkImpl(sliceImage, dimFactors, spatialDims);
198
+ downsampledSlices.push(downsampledSlice.data);
199
+ }
200
+ // Combine downsampled slices back into a single array with 't' dimension
201
+ const firstSlice = downsampledSlices[0];
202
+ const combinedShape = [...image.data.shape];
203
+ combinedShape[tDimIndex] = tSize;
204
+ // Update spatial dimensions based on downsampled size
205
+ for (let i = 0; i < image.dims.length; i++) {
206
+ if (i !== tDimIndex) {
207
+ const sliceIndex = i < tDimIndex ? i : i - 1;
208
+ combinedShape[i] = firstSlice.shape[sliceIndex];
209
+ }
210
+ }
211
+ // Create combined array
212
+ const combinedStore = new Map();
213
+ const combinedRoot = zarr.root(combinedStore);
214
+ const combinedArray = await zarr.create(combinedRoot.resolve("combined"), {
215
+ shape: combinedShape,
216
+ chunk_shape: combinedShape.map((s) => Math.min(s, 256)),
217
+ data_type: image.data.dtype,
218
+ fill_value: 0,
219
+ });
220
+ // Copy each downsampled slice into the combined array
221
+ for (let t = 0; t < tSize; t++) {
222
+ const sliceData = await zarr.get(downsampledSlices[t]);
223
+ const targetSelection = new Array(combinedShape.length).fill(null);
224
+ targetSelection[tDimIndex] = t;
225
+ await zarr.set(combinedArray, targetSelection, sliceData);
226
+ }
227
+ // Compute new metadata
228
+ const [translation, scale] = (0, itkwasm_shared_js_1.nextScaleMetadata)(image, dimFactors, spatialDims);
229
+ return new ngff_image_js_1.NgffImage({
230
+ data: combinedArray,
231
+ dims: image.dims,
232
+ scale: { ...image.scale, ...scale },
233
+ translation: { ...image.translation, ...translation },
234
+ name: image.name,
235
+ axesUnits: image.axesUnits,
236
+ computedCallbacks: image.computedCallbacks,
237
+ });
238
+ }
239
+ const isVector = image.dims.includes("c");
240
+ // Convert to ITK-Wasm format
241
+ const itkImage = await (0, itkwasm_shared_js_1.zarrToItkImage)(image.data, image.dims, isVector);
242
+ // Prepare shrink factors - only for spatial dimensions in ITK order (reversed)
243
+ const shrinkFactors = [];
244
+ for (let i = image.dims.length - 1; i >= 0; i--) {
245
+ const dim = image.dims[i];
246
+ if (itkwasm_shared_js_1.SPATIAL_DIMS.includes(dim)) {
247
+ shrinkFactors.push(dimFactors[dim] || 1);
248
+ }
249
+ }
250
+ // Perform downsampling using browser-compatible function
251
+ const { downsampled } = await (0, downsample_1.downsampleBinShrink)(itkImage, {
252
+ shrinkFactors,
253
+ });
254
+ // Compute new metadata
255
+ const [translation, scale] = (0, itkwasm_shared_js_1.nextScaleMetadata)(image, dimFactors, spatialDims);
256
+ // Convert back to zarr array in a new in-memory store
257
+ const store = new Map();
258
+ const chunkShape = downsampled.size.map((s) => Math.min(s, 256)).reverse();
259
+ const array = await (0, itkwasm_shared_js_1.itkImageToZarr)(downsampled, store, "image", chunkShape, image.dims);
260
+ return new ngff_image_js_1.NgffImage({
261
+ data: array,
262
+ dims: image.dims,
263
+ scale,
264
+ translation,
265
+ name: image.name,
266
+ axesUnits: image.axesUnits,
267
+ computedCallbacks: image.computedCallbacks,
268
+ });
269
+ }
270
+ /**
271
+ * Perform label image downsampling using ITK-Wasm (browser version)
272
+ */
273
+ async function downsampleLabelImageImpl(image, dimFactors, spatialDims) {
274
+ // Handle time dimension by processing each time slice independently
275
+ if (image.dims.includes("t")) {
276
+ const tDimIndex = image.dims.indexOf("t");
277
+ const tSize = image.data.shape[tDimIndex];
278
+ const newDims = image.dims.filter((dim) => dim !== "t");
279
+ // Downsample each time slice
280
+ const downsampledSlices = [];
281
+ for (let t = 0; t < tSize; t++) {
282
+ // Extract time slice
283
+ const selection = new Array(image.data.shape.length).fill(null);
284
+ selection[tDimIndex] = t;
285
+ const sliceData = await zarr.get(image.data, selection);
286
+ // Create temporary zarr array for this slice
287
+ const sliceStore = new Map();
288
+ const sliceRoot = zarr.root(sliceStore);
289
+ const sliceShape = image.data.shape.filter((_, i) => i !== tDimIndex);
290
+ const sliceChunkShape = sliceShape.map((s) => Math.min(s, 256));
291
+ const sliceArray = await zarr.create(sliceRoot.resolve("slice"), {
292
+ shape: sliceShape,
293
+ chunk_shape: sliceChunkShape,
294
+ data_type: image.data.dtype,
295
+ fill_value: 0,
296
+ });
297
+ const fullSelection = new Array(sliceShape.length).fill(null);
298
+ await zarr.set(sliceArray, fullSelection, sliceData);
299
+ // Create NgffImage for this slice (without 't' dimension)
300
+ const sliceImage = new ngff_image_js_1.NgffImage({
301
+ data: sliceArray,
302
+ dims: newDims,
303
+ scale: Object.fromEntries(Object.entries(image.scale).filter(([dim]) => dim !== "t")),
304
+ translation: Object.fromEntries(Object.entries(image.translation).filter(([dim]) => dim !== "t")),
305
+ name: image.name,
306
+ axesUnits: image.axesUnits
307
+ ? Object.fromEntries(Object.entries(image.axesUnits).filter(([dim]) => dim !== "t"))
308
+ : undefined,
309
+ computedCallbacks: image.computedCallbacks,
310
+ });
311
+ // Recursively downsample this slice
312
+ const downsampledSlice = await downsampleLabelImageImpl(sliceImage, dimFactors, spatialDims);
313
+ downsampledSlices.push(downsampledSlice.data);
314
+ }
315
+ // Combine downsampled slices back into a single array with 't' dimension
316
+ const firstSlice = downsampledSlices[0];
317
+ const combinedShape = [...image.data.shape];
318
+ combinedShape[tDimIndex] = tSize;
319
+ // Update spatial dimensions based on downsampled size
320
+ for (let i = 0; i < image.dims.length; i++) {
321
+ if (i !== tDimIndex) {
322
+ const sliceIndex = i < tDimIndex ? i : i - 1;
323
+ combinedShape[i] = firstSlice.shape[sliceIndex];
324
+ }
325
+ }
326
+ // Create combined array
327
+ const combinedStore = new Map();
328
+ const combinedRoot = zarr.root(combinedStore);
329
+ const combinedArray = await zarr.create(combinedRoot.resolve("combined"), {
330
+ shape: combinedShape,
331
+ chunk_shape: combinedShape.map((s) => Math.min(s, 256)),
332
+ data_type: image.data.dtype,
333
+ fill_value: 0,
334
+ });
335
+ // Copy each downsampled slice into the combined array
336
+ for (let t = 0; t < tSize; t++) {
337
+ const sliceData = await zarr.get(downsampledSlices[t]);
338
+ const targetSelection = new Array(combinedShape.length).fill(null);
339
+ targetSelection[tDimIndex] = t;
340
+ await zarr.set(combinedArray, targetSelection, sliceData);
341
+ }
342
+ // Compute new metadata
343
+ const [translation, scale] = (0, itkwasm_shared_js_1.nextScaleMetadata)(image, dimFactors, spatialDims);
344
+ return new ngff_image_js_1.NgffImage({
345
+ data: combinedArray,
346
+ dims: image.dims,
347
+ scale: { ...image.scale, ...scale },
348
+ translation: { ...image.translation, ...translation },
349
+ name: image.name,
350
+ axesUnits: image.axesUnits,
351
+ computedCallbacks: image.computedCallbacks,
352
+ });
353
+ }
354
+ const isVector = image.dims.includes("c");
355
+ // Convert to ITK-Wasm format
356
+ const itkImage = await (0, itkwasm_shared_js_1.zarrToItkImage)(image.data, image.dims, isVector);
357
+ // Prepare shrink factors - need to be for ALL dimensions in ITK order (reversed)
358
+ const shrinkFactors = [];
359
+ for (let i = image.dims.length - 1; i >= 0; i--) {
360
+ const dim = image.dims[i];
361
+ if (itkwasm_shared_js_1.SPATIAL_DIMS.includes(dim)) {
362
+ shrinkFactors.push(dimFactors[dim] || 1);
363
+ }
364
+ else {
365
+ shrinkFactors.push(1); // Non-spatial dimensions don't shrink
366
+ }
367
+ }
368
+ // Use all zeros for cropRadius
369
+ const cropRadius = new Array(shrinkFactors.length).fill(0);
370
+ // Perform downsampling using browser-compatible function
371
+ const { downsampled } = await (0, downsample_1.downsampleLabelImage)(itkImage, {
372
+ shrinkFactors,
373
+ cropRadius: cropRadius,
374
+ });
375
+ // Compute new metadata
376
+ const [translation, scale] = (0, itkwasm_shared_js_1.nextScaleMetadata)(image, dimFactors, spatialDims);
377
+ // Convert back to zarr array in a new in-memory store
378
+ const store = new Map();
379
+ const chunkShape = downsampled.size.map((s) => Math.min(s, 256)).reverse();
380
+ const array = await (0, itkwasm_shared_js_1.itkImageToZarr)(downsampled, store, "image", chunkShape, image.dims);
381
+ return new ngff_image_js_1.NgffImage({
382
+ data: array,
383
+ dims: image.dims,
384
+ scale,
385
+ translation,
386
+ name: image.name,
387
+ axesUnits: image.axesUnits,
388
+ computedCallbacks: image.computedCallbacks,
389
+ });
390
+ }
391
+ /**
392
+ * Main downsampling function for ITK-Wasm (browser version)
393
+ */
394
+ async function downsampleItkWasm(ngffImage, scaleFactors, smoothing) {
395
+ const multiscales = [ngffImage];
396
+ const dims = ngffImage.dims;
397
+ const spatialDims = dims.filter((dim) => itkwasm_shared_js_1.SPATIAL_DIMS.includes(dim));
398
+ let previousImage = ngffImage;
399
+ let previousDimFactors = {};
400
+ for (const dim of dims)
401
+ previousDimFactors[dim] = 1;
402
+ for (let i = 0; i < scaleFactors.length; i++) {
403
+ const scaleFactor = scaleFactors[i];
404
+ let sourceImage;
405
+ let sourceDimFactors;
406
+ if (smoothing === "bin_shrink") {
407
+ // Purely incremental: scaleFactor is the shrink for this step
408
+ sourceImage = previousImage;
409
+ sourceDimFactors = {};
410
+ if (typeof scaleFactor === "number") {
411
+ for (const dim of spatialDims)
412
+ sourceDimFactors[dim] = scaleFactor;
413
+ }
414
+ else {
415
+ for (const dim of spatialDims) {
416
+ sourceDimFactors[dim] = scaleFactor[dim] || 1;
417
+ }
418
+ }
419
+ for (const dim of dims) {
420
+ if (!(dim in sourceDimFactors))
421
+ sourceDimFactors[dim] = 1;
422
+ }
423
+ }
424
+ else {
425
+ // Hybrid absolute strategy
426
+ const dimFactors = (0, itkwasm_shared_js_1.dimScaleFactors)(dims, scaleFactor, previousDimFactors, ngffImage, previousImage);
427
+ let canDownsampleIncrementally = true;
428
+ for (const dim of Object.keys(dimFactors)) {
429
+ const dimIndex = ngffImage.dims.indexOf(dim);
430
+ if (dimIndex >= 0) {
431
+ const originalSize = ngffImage.data.shape[dimIndex];
432
+ const targetSize = Math.floor(originalSize /
433
+ (typeof scaleFactor === "number"
434
+ ? scaleFactor
435
+ : scaleFactor[dim]));
436
+ const prevDimIndex = previousImage.dims.indexOf(dim);
437
+ const previousSize = previousImage.data.shape[prevDimIndex];
438
+ if (Math.floor(previousSize / dimFactors[dim]) !== targetSize) {
439
+ canDownsampleIncrementally = false;
440
+ break;
441
+ }
442
+ }
443
+ }
444
+ if (canDownsampleIncrementally) {
445
+ sourceImage = previousImage;
446
+ sourceDimFactors = dimFactors;
447
+ }
448
+ else {
449
+ sourceImage = ngffImage;
450
+ const originalDimFactors = {};
451
+ for (const dim of dims)
452
+ originalDimFactors[dim] = 1;
453
+ sourceDimFactors = (0, itkwasm_shared_js_1.dimScaleFactors)(dims, scaleFactor, originalDimFactors);
454
+ }
455
+ }
456
+ let downsampled;
457
+ if (smoothing === "gaussian") {
458
+ downsampled = await downsampleGaussian(sourceImage, sourceDimFactors, spatialDims);
459
+ }
460
+ else if (smoothing === "bin_shrink") {
461
+ downsampled = await downsampleBinShrinkImpl(sourceImage, sourceDimFactors, spatialDims);
462
+ }
463
+ else if (smoothing === "label_image") {
464
+ downsampled = await downsampleLabelImageImpl(sourceImage, sourceDimFactors, spatialDims);
465
+ }
466
+ else {
467
+ throw new Error(`Unknown smoothing method: ${smoothing}`);
468
+ }
469
+ multiscales.push(downsampled);
470
+ previousImage = downsampled;
471
+ if (smoothing === "bin_shrink") {
472
+ if (typeof scaleFactor === "number") {
473
+ for (const dim of spatialDims) {
474
+ previousDimFactors[dim] *= scaleFactor;
475
+ }
476
+ }
477
+ else {
478
+ for (const dim of spatialDims) {
479
+ previousDimFactors[dim] *= scaleFactor[dim] || 1;
480
+ }
481
+ }
482
+ }
483
+ else {
484
+ previousDimFactors = (0, itkwasm_shared_js_1.updatePreviousDimFactors)(scaleFactor, spatialDims, previousDimFactors);
485
+ }
486
+ }
487
+ return multiscales;
488
+ }
@@ -0,0 +1,6 @@
1
+ import { NgffImage } from "../types/ngff_image.js";
2
+ /**
3
+ * Main downsampling function for ITK-Wasm (browser version)
4
+ */
5
+ export declare function downsampleItkWasm(ngffImage: NgffImage, scaleFactors: (Record<string, number> | number)[], smoothing: "gaussian" | "bin_shrink" | "label_image"): Promise<NgffImage[]>;
6
+ //# sourceMappingURL=itkwasm-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"itkwasm-node.d.ts","sourceRoot":"","sources":["../../src/methods/itkwasm-node.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AA2fnD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE,EACjD,SAAS,EAAE,UAAU,GAAG,YAAY,GAAG,aAAa,GACnD,OAAO,CAAC,SAAS,EAAE,CAAC,CAsHtB"}