Skip to content

Commit c67626e

Browse files
committed
WIP: feat(ts): add downsampleItkWasm method
1 parent 4e91c0c commit c67626e

File tree

4 files changed

+851
-28
lines changed

4 files changed

+851
-28
lines changed

ts/src/io/to_multiscales.ts

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ import {
1010
createMultiscales,
1111
} from "../utils/factory.ts";
1212
import { getMethodMetadata } from "../utils/method_metadata.ts";
13+
import { downsampleItkwasm } from "../methods/itkwasm.ts";
1314

1415
export interface ToNgffImageOptions {
1516
dims?: string[];
1617
scale?: Record<string, number>;
1718
translation?: Record<string, number>;
1819
name?: string;
20+
shape?: number[]; // Explicit shape for typed arrays
1921
}
2022

2123
/**
@@ -34,10 +36,11 @@ export async function toNgffImage(
3436
scale = {},
3537
translation = {},
3638
name = "image",
39+
shape: explicitShape,
3740
} = options;
3841

3942
// Determine data shape and create typed array
40-
let typedData: Float32Array;
43+
let typedData: Float32Array | Uint8Array | Uint16Array;
4144
let shape: number[];
4245

4346
if (Array.isArray(data)) {
@@ -78,18 +81,34 @@ export async function toNgffImage(
7881
}
7982
} else {
8083
// ArrayLike (already a typed array)
81-
typedData = new Float32Array(data as ArrayLike<number>);
82-
shape = [typedData.length];
84+
// Use explicit shape if provided, otherwise infer from data length and dims
85+
if (explicitShape) {
86+
shape = [...explicitShape];
87+
} else {
88+
// Try to infer shape - this is a best guess
89+
shape = [data.length];
90+
}
91+
92+
// Preserve the original typed array type
93+
if (data instanceof Uint8Array) {
94+
typedData = data;
95+
} else if (data instanceof Uint16Array) {
96+
typedData = data;
97+
} else {
98+
typedData = new Float32Array(data as ArrayLike<number>);
99+
}
83100
}
84101

85-
// Adjust shape to match dims length
86-
while (shape.length < dims.length) {
87-
shape.unshift(1);
102+
// Adjust shape to match dims length if not explicitly provided
103+
if (!explicitShape) {
104+
while (shape.length < dims.length) {
105+
shape.unshift(1);
106+
}
88107
}
89108

90-
if (shape.length > dims.length) {
109+
if (shape.length !== dims.length) {
91110
throw new Error(
92-
`Data dimensionality (${shape.length}) exceeds dims length (${dims.length})`,
111+
`Shape dimensionality (${shape.length}) must match dims length (${dims.length})`,
93112
);
94113
}
95114

@@ -109,7 +128,7 @@ export async function toNgffImage(
109128

110129
// Write data to zarr array
111130
await zarr.set(zarrArray, [], {
112-
data: typedData,
131+
data: typedData as Float32Array,
113132
shape,
114133
stride: calculateStride(shape),
115134
});
@@ -147,19 +166,40 @@ export interface ToMultiscalesOptions {
147166
* @param options - Configuration options
148167
* @returns Multiscales object
149168
*/
150-
export function toMultiscales(
169+
export async function toMultiscales(
151170
image: NgffImage,
152171
options: ToMultiscalesOptions = {},
153-
): Multiscales {
172+
): Promise<Multiscales> {
154173
const {
155174
scaleFactors = [2, 4],
156175
method = Methods.ITKWASM_GAUSSIAN,
157176
chunks: _chunks,
158177
} = options;
159178

160-
// For now, create only the base image (no actual downsampling)
161-
// This is a simplified implementation for testing metadata functionality
162-
const images = [image];
179+
let images: NgffImage[];
180+
181+
// Check if we should perform actual downsampling
182+
if (
183+
method === Methods.ITKWASM_GAUSSIAN ||
184+
method === Methods.ITKWASM_BIN_SHRINK ||
185+
method === Methods.ITKWASM_LABEL_IMAGE
186+
) {
187+
// Perform actual downsampling using ITK-Wasm
188+
const smoothing = method === Methods.ITKWASM_GAUSSIAN
189+
? "gaussian"
190+
: method === Methods.ITKWASM_BIN_SHRINK
191+
? "bin_shrink"
192+
: "label_image";
193+
194+
images = await downsampleItkwasm(
195+
image,
196+
scaleFactors as (Record<string, number> | number)[],
197+
smoothing,
198+
);
199+
} else {
200+
// Fallback: create only the base image (no actual downsampling)
201+
images = [image];
202+
}
163203

164204
// Create axes from image dimensions
165205
const axes = image.dims.map((dim) => {
@@ -178,14 +218,14 @@ export function toMultiscales(
178218
}
179219
});
180220

181-
// Create datasets
182-
const datasets = [
183-
createDataset(
184-
"0",
185-
image.dims.map((dim) => image.scale[dim]),
186-
image.dims.map((dim) => image.translation[dim]),
187-
),
188-
];
221+
// Create datasets for all images
222+
const datasets = images.map((img, index) => {
223+
return createDataset(
224+
`${index}`,
225+
img.dims.map((dim) => img.scale[dim]),
226+
img.dims.map((dim) => img.translation[dim]),
227+
);
228+
});
189229

190230
// Create metadata with method information
191231
const methodMetadata = getMethodMetadata(method);

0 commit comments

Comments
 (0)