@fideus-labs/ngff-zarr 0.1.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.
Files changed (70) hide show
  1. package/README.md +1 -0
  2. package/esm/browser-mod.d.ts +14 -0
  3. package/esm/browser-mod.d.ts.map +1 -0
  4. package/esm/browser-mod.js +23 -0
  5. package/esm/io/itk_image_to_ngff_image.d.ts +5 -0
  6. package/esm/io/itk_image_to_ngff_image.d.ts.map +1 -1
  7. package/esm/io/itk_image_to_ngff_image.js +20 -20
  8. package/esm/io/ngff_image_to_itk_image.d.ts.map +1 -1
  9. package/esm/io/ngff_image_to_itk_image.js +2 -0
  10. package/esm/io/to_multiscales.js +1 -1
  11. package/esm/io/to_ngff_zarr.js +16 -0
  12. package/esm/methods/itkwasm-browser.d.ts +6 -0
  13. package/esm/methods/itkwasm-browser.d.ts.map +1 -0
  14. package/esm/methods/itkwasm-browser.js +462 -0
  15. package/esm/methods/itkwasm-node.d.ts +6 -0
  16. package/esm/methods/itkwasm-node.d.ts.map +1 -0
  17. package/esm/methods/itkwasm-node.js +462 -0
  18. package/esm/methods/itkwasm-shared.d.ts +68 -0
  19. package/esm/methods/itkwasm-shared.d.ts.map +1 -0
  20. package/esm/methods/itkwasm-shared.js +489 -0
  21. package/esm/methods/itkwasm.d.ts +11 -3
  22. package/esm/methods/itkwasm.d.ts.map +1 -1
  23. package/esm/methods/itkwasm.js +11 -810
  24. package/esm/schemas/coordinate_systems.d.ts +159 -552
  25. package/esm/schemas/coordinate_systems.d.ts.map +1 -1
  26. package/esm/schemas/coordinate_systems.js +0 -1
  27. package/esm/schemas/ome_zarr.d.ts +105 -69
  28. package/esm/schemas/ome_zarr.d.ts.map +1 -1
  29. package/esm/schemas/rfc4.d.ts +26 -131
  30. package/esm/schemas/rfc4.d.ts.map +1 -1
  31. package/esm/schemas/units.d.ts +70 -5
  32. package/esm/schemas/units.d.ts.map +1 -1
  33. package/esm/schemas/units.js +2 -15
  34. package/esm/schemas/zarr_metadata.d.ts +13 -300
  35. package/esm/schemas/zarr_metadata.d.ts.map +1 -1
  36. package/package.json +27 -3
  37. package/script/browser-mod.d.ts +14 -0
  38. package/script/browser-mod.d.ts.map +1 -0
  39. package/script/browser-mod.js +48 -0
  40. package/script/io/itk_image_to_ngff_image.d.ts +5 -0
  41. package/script/io/itk_image_to_ngff_image.d.ts.map +1 -1
  42. package/script/io/itk_image_to_ngff_image.js +20 -20
  43. package/script/io/ngff_image_to_itk_image.d.ts.map +1 -1
  44. package/script/io/ngff_image_to_itk_image.js +2 -0
  45. package/script/io/to_multiscales.js +1 -1
  46. package/script/io/to_ngff_zarr.js +16 -0
  47. package/script/methods/itkwasm-browser.d.ts +6 -0
  48. package/script/methods/itkwasm-browser.d.ts.map +1 -0
  49. package/script/methods/itkwasm-browser.js +488 -0
  50. package/script/methods/itkwasm-node.d.ts +6 -0
  51. package/script/methods/itkwasm-node.d.ts.map +1 -0
  52. package/script/methods/itkwasm-node.js +488 -0
  53. package/script/methods/itkwasm-shared.d.ts +68 -0
  54. package/script/methods/itkwasm-shared.d.ts.map +1 -0
  55. package/script/methods/itkwasm-shared.js +524 -0
  56. package/script/methods/itkwasm.d.ts +11 -3
  57. package/script/methods/itkwasm.d.ts.map +1 -1
  58. package/script/methods/itkwasm.js +14 -835
  59. package/script/schemas/coordinate_systems.d.ts +159 -552
  60. package/script/schemas/coordinate_systems.d.ts.map +1 -1
  61. package/script/schemas/coordinate_systems.js +0 -1
  62. package/script/schemas/ome_zarr.d.ts +105 -69
  63. package/script/schemas/ome_zarr.d.ts.map +1 -1
  64. package/script/schemas/rfc4.d.ts +26 -131
  65. package/script/schemas/rfc4.d.ts.map +1 -1
  66. package/script/schemas/units.d.ts +70 -5
  67. package/script/schemas/units.d.ts.map +1 -1
  68. package/script/schemas/units.js +2 -15
  69. package/script/schemas/zarr_metadata.d.ts +13 -300
  70. package/script/schemas/zarr_metadata.d.ts.map +1 -1
@@ -0,0 +1,524 @@
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.SPATIAL_DIMS = void 0;
29
+ exports.calculateIncrementalFactor = calculateIncrementalFactor;
30
+ exports.dimScaleFactors = dimScaleFactors;
31
+ exports.updatePreviousDimFactors = updatePreviousDimFactors;
32
+ exports.nextScaleMetadata = nextScaleMetadata;
33
+ exports.getItkComponentType = getItkComponentType;
34
+ exports.createIdentityMatrix = createIdentityMatrix;
35
+ exports.transposeArray = transposeArray;
36
+ exports.zarrToItkImage = zarrToItkImage;
37
+ exports.itkImageToZarr = itkImageToZarr;
38
+ const zarr = __importStar(require("zarrita"));
39
+ exports.SPATIAL_DIMS = ["x", "y", "z"];
40
+ /**
41
+ * Calculate the incremental factor needed to reach the target size from the previous size.
42
+ * This ensures exact target sizes when downsampling incrementally.
43
+ */
44
+ function calculateIncrementalFactor(previousSize, targetSize) {
45
+ if (targetSize <= 0) {
46
+ return 1;
47
+ }
48
+ // Start with the theoretical factor
49
+ let factor = Math.floor(Math.ceil(previousSize / (targetSize + 0.5)));
50
+ // Verify this gives us the right size
51
+ let actualSize = Math.floor(previousSize / factor);
52
+ if (actualSize !== targetSize) {
53
+ // Adjust factor to get exact target
54
+ factor = Math.max(1, Math.floor(previousSize / targetSize));
55
+ actualSize = Math.floor(previousSize / factor);
56
+ // If still not exact, try ceil
57
+ if (actualSize !== targetSize) {
58
+ factor = Math.max(1, Math.ceil(previousSize / targetSize));
59
+ }
60
+ }
61
+ return Math.max(1, factor);
62
+ }
63
+ /**
64
+ * Convert dimension scale factors to ITK-Wasm format
65
+ * This computes the incremental scale factor relative to the previous scale,
66
+ * not the absolute scale factor from the original image.
67
+ *
68
+ * When originalImage and previousImage are provided, calculates the exact
69
+ * incremental factor needed to reach the target size from the previous size.
70
+ * This ensures we get exact 1x, 2x, 3x, 4x sizes even with incremental downsampling.
71
+ */
72
+ function dimScaleFactors(dims, scaleFactor, previousDimFactors, originalImage, previousImage) {
73
+ const dimFactors = {};
74
+ if (typeof scaleFactor === "number") {
75
+ if (originalImage !== undefined && previousImage !== undefined) {
76
+ // Calculate target size: floor(original_size / scale_factor)
77
+ // Then calculate incremental factor from previous size to target size
78
+ for (const dim of dims) {
79
+ if (exports.SPATIAL_DIMS.includes(dim)) {
80
+ const dimIndex = originalImage.dims.indexOf(dim);
81
+ const originalSize = originalImage.data.shape[dimIndex];
82
+ const targetSize = Math.floor(originalSize / scaleFactor);
83
+ const prevDimIndex = previousImage.dims.indexOf(dim);
84
+ const previousSize = previousImage.data.shape[prevDimIndex];
85
+ dimFactors[dim] = calculateIncrementalFactor(previousSize, targetSize);
86
+ }
87
+ else {
88
+ dimFactors[dim] = 1;
89
+ }
90
+ }
91
+ }
92
+ else {
93
+ // Fallback to old behavior when images not provided
94
+ for (const dim of dims) {
95
+ if (exports.SPATIAL_DIMS.includes(dim)) {
96
+ // Divide by previous factor to get incremental scaling
97
+ // Use Math.floor to truncate (matching Python's int() behavior)
98
+ const incrementalFactor = scaleFactor /
99
+ (previousDimFactors[dim] || 1);
100
+ dimFactors[dim] = Math.max(1, Math.floor(incrementalFactor));
101
+ }
102
+ else {
103
+ dimFactors[dim] = previousDimFactors[dim] || 1;
104
+ }
105
+ }
106
+ }
107
+ }
108
+ else {
109
+ if (originalImage !== undefined && previousImage !== undefined) {
110
+ for (const dim in scaleFactor) {
111
+ const dimIndex = originalImage.dims.indexOf(dim);
112
+ const originalSize = originalImage.data.shape[dimIndex];
113
+ const targetSize = Math.floor(originalSize / scaleFactor[dim]);
114
+ const prevDimIndex = previousImage.dims.indexOf(dim);
115
+ const previousSize = previousImage.data.shape[prevDimIndex];
116
+ dimFactors[dim] = calculateIncrementalFactor(previousSize, targetSize);
117
+ }
118
+ }
119
+ else {
120
+ // Fallback to old behavior when images not provided
121
+ for (const dim in scaleFactor) {
122
+ // Divide by previous factor to get incremental scaling
123
+ // Use Math.floor to truncate (matching Python's int() behavior)
124
+ const incrementalFactor = scaleFactor[dim] /
125
+ (previousDimFactors[dim] || 1);
126
+ dimFactors[dim] = Math.max(1, Math.floor(incrementalFactor));
127
+ }
128
+ }
129
+ // Add dims not in scale_factor with factor of 1
130
+ for (const dim of dims) {
131
+ if (!(dim in dimFactors)) {
132
+ dimFactors[dim] = 1;
133
+ }
134
+ }
135
+ }
136
+ return dimFactors;
137
+ }
138
+ /**
139
+ * Update previous dimension factors
140
+ */
141
+ function updatePreviousDimFactors(scaleFactor, spatialDims, previousDimFactors) {
142
+ const updated = { ...previousDimFactors };
143
+ if (typeof scaleFactor === "number") {
144
+ for (const dim of spatialDims) {
145
+ updated[dim] = scaleFactor;
146
+ }
147
+ }
148
+ else {
149
+ for (const dim in scaleFactor) {
150
+ updated[dim] = scaleFactor[dim];
151
+ }
152
+ }
153
+ return updated;
154
+ }
155
+ /**
156
+ * Compute next scale metadata
157
+ */
158
+ function nextScaleMetadata(image, dimFactors, spatialDims) {
159
+ const translation = {};
160
+ const scale = {};
161
+ for (const dim of image.dims) {
162
+ if (spatialDims.includes(dim)) {
163
+ const factor = dimFactors[dim];
164
+ scale[dim] = image.scale[dim] * factor;
165
+ // Add offset to account for pixel center shift when downsampling
166
+ translation[dim] = image.translation[dim] +
167
+ 0.5 * (factor - 1) * image.scale[dim];
168
+ }
169
+ else {
170
+ scale[dim] = image.scale[dim];
171
+ translation[dim] = image.translation[dim];
172
+ }
173
+ }
174
+ return [translation, scale];
175
+ }
176
+ /**
177
+ * Copy typed array to appropriate type
178
+ */
179
+ function copyTypedArray(data) {
180
+ if (data instanceof Float32Array) {
181
+ return new Float32Array(data);
182
+ }
183
+ else if (data instanceof Float64Array) {
184
+ return new Float64Array(data);
185
+ }
186
+ else if (data instanceof Uint8Array) {
187
+ return new Uint8Array(data);
188
+ }
189
+ else if (data instanceof Int8Array) {
190
+ return new Int8Array(data);
191
+ }
192
+ else if (data instanceof Uint16Array) {
193
+ return new Uint16Array(data);
194
+ }
195
+ else if (data instanceof Int16Array) {
196
+ return new Int16Array(data);
197
+ }
198
+ else if (data instanceof Uint32Array) {
199
+ return new Uint32Array(data);
200
+ }
201
+ else if (data instanceof Int32Array) {
202
+ return new Int32Array(data);
203
+ }
204
+ else {
205
+ // Convert to Float32Array as fallback
206
+ return new Float32Array(data);
207
+ }
208
+ }
209
+ /**
210
+ * Get ITK component type from typed array
211
+ */
212
+ function getItkComponentType(data) {
213
+ if (data instanceof Uint8Array)
214
+ return "uint8";
215
+ if (data instanceof Int8Array)
216
+ return "int8";
217
+ if (data instanceof Uint16Array)
218
+ return "uint16";
219
+ if (data instanceof Int16Array)
220
+ return "int16";
221
+ if (data instanceof Uint32Array)
222
+ return "uint32";
223
+ if (data instanceof Int32Array)
224
+ return "int32";
225
+ if (data instanceof Float64Array)
226
+ return "float64";
227
+ return "float32";
228
+ }
229
+ /**
230
+ * Create identity matrix for ITK direction
231
+ */
232
+ function createIdentityMatrix(dimension) {
233
+ const matrix = new Float64Array(dimension * dimension);
234
+ for (let i = 0; i < dimension * dimension; i++) {
235
+ matrix[i] = i % (dimension + 1) === 0 ? 1 : 0;
236
+ }
237
+ return matrix;
238
+ }
239
+ /**
240
+ * Calculate stride for array
241
+ */
242
+ function calculateStride(shape) {
243
+ const stride = new Array(shape.length);
244
+ stride[shape.length - 1] = 1;
245
+ for (let i = shape.length - 2; i >= 0; i--) {
246
+ stride[i] = stride[i + 1] * shape[i + 1];
247
+ }
248
+ return stride;
249
+ }
250
+ /**
251
+ * Transpose array data according to permutation
252
+ */
253
+ function transposeArray(data, shape, permutation, componentType) {
254
+ const typedData = data;
255
+ // Create output array of same type
256
+ let output;
257
+ const totalSize = typedData.length;
258
+ switch (componentType) {
259
+ case "uint8":
260
+ output = new Uint8Array(totalSize);
261
+ break;
262
+ case "int8":
263
+ output = new Int8Array(totalSize);
264
+ break;
265
+ case "uint16":
266
+ output = new Uint16Array(totalSize);
267
+ break;
268
+ case "int16":
269
+ output = new Int16Array(totalSize);
270
+ break;
271
+ case "uint32":
272
+ output = new Uint32Array(totalSize);
273
+ break;
274
+ case "int32":
275
+ output = new Int32Array(totalSize);
276
+ break;
277
+ case "float64":
278
+ output = new Float64Array(totalSize);
279
+ break;
280
+ case "float32":
281
+ default:
282
+ output = new Float32Array(totalSize);
283
+ break;
284
+ }
285
+ // Calculate strides for source
286
+ const sourceStride = calculateStride(shape);
287
+ // Calculate new shape after permutation
288
+ const newShape = permutation.map((i) => shape[i]);
289
+ const targetStride = calculateStride(newShape);
290
+ // Perform transpose
291
+ const indices = new Array(shape.length).fill(0);
292
+ for (let i = 0; i < totalSize; i++) {
293
+ // Calculate source index from multi-dimensional indices
294
+ let sourceIdx = 0;
295
+ for (let j = 0; j < shape.length; j++) {
296
+ sourceIdx += indices[j] * sourceStride[j];
297
+ }
298
+ // Calculate target index with permuted dimensions
299
+ let targetIdx = 0;
300
+ for (let j = 0; j < permutation.length; j++) {
301
+ targetIdx += indices[permutation[j]] * targetStride[j];
302
+ }
303
+ output[targetIdx] = typedData[sourceIdx];
304
+ // Increment indices
305
+ for (let j = shape.length - 1; j >= 0; j--) {
306
+ indices[j]++;
307
+ if (indices[j] < shape[j])
308
+ break;
309
+ indices[j] = 0;
310
+ }
311
+ }
312
+ return output;
313
+ }
314
+ /**
315
+ * Convert zarr array to ITK-Wasm Image format
316
+ * If isVector is true, ensures "c" dimension is last by transposing if needed
317
+ */
318
+ async function zarrToItkImage(array, dims, isVector = false) {
319
+ // Read the full array data
320
+ const result = await zarr.get(array);
321
+ // Ensure we have the data
322
+ if (!result.data || result.data.length === 0) {
323
+ throw new Error("Zarr array data is empty");
324
+ }
325
+ let data;
326
+ let shape = result.shape;
327
+ let _finalDims = dims;
328
+ // If vector image, ensure "c" is last dimension
329
+ if (isVector) {
330
+ const cIndex = dims.indexOf("c");
331
+ if (cIndex !== -1 && cIndex !== dims.length - 1) {
332
+ // Need to transpose to move "c" to the end
333
+ const permutation = dims.map((_, i) => i).filter((i) => i !== cIndex);
334
+ permutation.push(cIndex);
335
+ // Reorder dims
336
+ _finalDims = permutation.map((i) => dims[i]);
337
+ // Reorder shape
338
+ shape = permutation.map((i) => result.shape[i]);
339
+ // Transpose the data
340
+ data = transposeArray(result.data, result.shape, permutation, getItkComponentType(result.data));
341
+ }
342
+ else {
343
+ // "c" already at end or not present, just copy data
344
+ data = copyTypedArray(result.data);
345
+ }
346
+ }
347
+ else {
348
+ // Not a vector image, just copy data
349
+ data = copyTypedArray(result.data);
350
+ }
351
+ // For vector images, the last dimension is the component count, not a spatial dimension
352
+ const spatialShape = isVector ? shape.slice(0, -1) : shape;
353
+ const components = isVector ? shape[shape.length - 1] : 1;
354
+ // ITK expects size in physical space order [x, y, z], but spatialShape is in array order [z, y, x]
355
+ // So we need to reverse it
356
+ const itkSize = [...spatialShape].reverse();
357
+ // Create ITK-Wasm image
358
+ const itkImage = {
359
+ imageType: {
360
+ dimension: spatialShape.length,
361
+ componentType: getItkComponentType(data),
362
+ pixelType: isVector ? "VariableLengthVector" : "Scalar",
363
+ components,
364
+ },
365
+ name: "image",
366
+ origin: spatialShape.map(() => 0),
367
+ spacing: spatialShape.map(() => 1),
368
+ direction: createIdentityMatrix(spatialShape.length),
369
+ size: itkSize,
370
+ data: data,
371
+ metadata: new Map(),
372
+ };
373
+ return itkImage;
374
+ }
375
+ /**
376
+ * Convert ITK-Wasm Image back to zarr array
377
+ * Uses the provided store instead of creating a new one
378
+ *
379
+ * Important: ITK-Wasm stores size in physical space order [x, y, z], but data in
380
+ * column-major order (x contiguous). This column-major layout with size [x, y, z]
381
+ * is equivalent to C-order (row-major) with shape [z, y, x]. We reverse the size
382
+ * to get the zarr shape and use C-order strides for that reversed shape.
383
+ *
384
+ * @param itkImage - The ITK-Wasm image to convert
385
+ * @param store - The zarr store to write to
386
+ * @param path - The path within the store
387
+ * @param chunkShape - The chunk shape (in spatial dimension order, will be adjusted for components)
388
+ * @param targetDims - The target dimension order (e.g., ["c", "z", "y", "x"])
389
+ */
390
+ async function itkImageToZarr(itkImage, store, path, chunkShape, targetDims) {
391
+ const root = zarr.root(store);
392
+ if (!itkImage.data) {
393
+ throw new Error("ITK image data is null or undefined");
394
+ }
395
+ // Determine data type - support all ITK TypedArray types
396
+ let dataType;
397
+ if (itkImage.data instanceof Uint8Array) {
398
+ dataType = "uint8";
399
+ }
400
+ else if (itkImage.data instanceof Int8Array) {
401
+ dataType = "int8";
402
+ }
403
+ else if (itkImage.data instanceof Uint16Array) {
404
+ dataType = "uint16";
405
+ }
406
+ else if (itkImage.data instanceof Int16Array) {
407
+ dataType = "int16";
408
+ }
409
+ else if (itkImage.data instanceof Uint32Array) {
410
+ dataType = "uint32";
411
+ }
412
+ else if (itkImage.data instanceof Int32Array) {
413
+ dataType = "int32";
414
+ }
415
+ else if (itkImage.data instanceof Float32Array) {
416
+ dataType = "float32";
417
+ }
418
+ else if (itkImage.data instanceof Float64Array) {
419
+ dataType = "float64";
420
+ }
421
+ else {
422
+ throw new Error(`Unsupported data type: ${itkImage.data.constructor.name}`);
423
+ }
424
+ // ITK stores size/spacing/origin in physical space order [x, y, z],
425
+ // but the data buffer is in C-order (row-major) which means [z, y, x] indexing.
426
+ // We need to reverse the size to match the data layout, just like we do for spacing/origin.
427
+ const shape = [...itkImage.size].reverse();
428
+ // For vector images, the components are stored in the data but not in the size
429
+ // The actual data length includes components
430
+ const components = itkImage.imageType.components || 1;
431
+ const isVector = components > 1;
432
+ // Validate data length matches expected shape (including components for vector images)
433
+ const spatialElements = shape.reduce((a, b) => a * b, 1);
434
+ const expectedLength = spatialElements * components;
435
+ if (itkImage.data.length !== expectedLength) {
436
+ console.error(`[ERROR] Data length mismatch in itkImageToZarr:`);
437
+ console.error(` ITK image size (physical order):`, itkImage.size);
438
+ console.error(` Shape (reversed):`, shape);
439
+ console.error(` Components:`, components);
440
+ console.error(` Expected data length:`, expectedLength);
441
+ console.error(` Actual data length:`, itkImage.data.length);
442
+ throw new Error(`Data length (${itkImage.data.length}) doesn't match expected shape ${shape} with ${components} components (${expectedLength} elements)`);
443
+ }
444
+ // Determine the final shape and whether we need to transpose
445
+ // ITK image data has shape [...spatialDimsReversed, components] (with c at end)
446
+ // If targetDims is provided, we need to match that order
447
+ let zarrShape;
448
+ let zarrChunkShape;
449
+ let finalData = itkImage.data;
450
+ if (isVector && targetDims) {
451
+ // Find where "c" should be in targetDims
452
+ const cIndex = targetDims.indexOf("c");
453
+ if (cIndex === -1) {
454
+ throw new Error("Vector image but 'c' not found in targetDims");
455
+ }
456
+ // Current shape is [z, y, x, c] (spatial reversed + c at end)
457
+ // Target shape should match targetDims order
458
+ const currentShape = [...shape, components];
459
+ // Build target shape based on targetDims
460
+ zarrShape = new Array(targetDims.length);
461
+ const spatialDims = shape.slice(); // [z, y, x]
462
+ let spatialIdx = 0;
463
+ for (let i = 0; i < targetDims.length; i++) {
464
+ if (targetDims[i] === "c") {
465
+ zarrShape[i] = components;
466
+ }
467
+ else {
468
+ zarrShape[i] = spatialDims[spatialIdx++];
469
+ }
470
+ }
471
+ // If c is not at the end, we need to transpose
472
+ if (cIndex !== targetDims.length - 1) {
473
+ // Build permutation: where does each target dim come from in current shape?
474
+ const permutation = [];
475
+ spatialIdx = 0;
476
+ for (let i = 0; i < targetDims.length; i++) {
477
+ if (targetDims[i] === "c") {
478
+ permutation.push(currentShape.length - 1); // c is at end of current
479
+ }
480
+ else {
481
+ permutation.push(spatialIdx++);
482
+ }
483
+ }
484
+ // Transpose the data
485
+ finalData = transposeArray(itkImage.data, currentShape, permutation, getItkComponentType(itkImage.data));
486
+ }
487
+ // Chunk shape should match zarrShape
488
+ zarrChunkShape = new Array(zarrShape.length);
489
+ spatialIdx = 0;
490
+ for (let i = 0; i < targetDims.length; i++) {
491
+ if (targetDims[i] === "c") {
492
+ zarrChunkShape[i] = components;
493
+ }
494
+ else {
495
+ zarrChunkShape[i] = chunkShape[spatialIdx++];
496
+ }
497
+ }
498
+ }
499
+ else {
500
+ // No targetDims or not a vector - use default behavior
501
+ zarrShape = isVector ? [...shape, components] : shape;
502
+ zarrChunkShape = isVector ? [...chunkShape, components] : chunkShape;
503
+ }
504
+ // Chunk shape should match the dimensionality of zarrShape
505
+ if (zarrChunkShape.length !== zarrShape.length) {
506
+ throw new Error(`chunkShape length (${zarrChunkShape.length}) must match shape length (${zarrShape.length})`);
507
+ }
508
+ const array = await zarr.create(root.resolve(path), {
509
+ shape: zarrShape,
510
+ chunk_shape: zarrChunkShape,
511
+ data_type: dataType,
512
+ fill_value: 0,
513
+ });
514
+ // Write data - preserve the actual data type, don't cast to Float32Array
515
+ // Shape and stride should match the ITK image size order
516
+ // Use null for each dimension to select the entire array
517
+ const selection = zarrShape.map(() => null);
518
+ await zarr.set(array, selection, {
519
+ data: finalData,
520
+ shape: zarrShape,
521
+ stride: calculateStride(zarrShape),
522
+ });
523
+ return array;
524
+ }
@@ -1,6 +1,14 @@
1
- import { NgffImage } from "../types/ngff_image.js";
2
1
  /**
3
- * Main downsampling function for ITK-Wasm
2
+ * ITK-Wasm downsampling support for multiscale generation
3
+ *
4
+ * This module provides conditional exports for browser and Node environments.
5
+ * The actual implementation is delegated to environment-specific modules:
6
+ * - itkwasm-browser.ts: Uses WebWorker-based functions for browser environments
7
+ * - itkwasm-node.ts: Uses native WASM functions for Node/Deno environments
8
+ *
9
+ * For Deno runtime, we default to the node implementation.
10
+ * For browser bundlers, they should use conditional exports in package.json
11
+ * to resolve to the browser implementation.
4
12
  */
5
- export declare function downsampleItkWasm(ngffImage: NgffImage, scaleFactors: (Record<string, number> | number)[], smoothing: "gaussian" | "bin_shrink" | "label_image"): Promise<NgffImage[]>;
13
+ export { downsampleItkWasm } from "./itkwasm-node.js";
6
14
  //# sourceMappingURL=itkwasm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"itkwasm.d.ts","sourceRoot":"","sources":["../../src/methods/itkwasm.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAohCnD;;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,CA+CtB"}
1
+ {"version":3,"file":"itkwasm.d.ts","sourceRoot":"","sources":["../../src/methods/itkwasm.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}