From 199073c39ae83bf78c84113b724e5c2830dc79c8 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Thu, 25 Jan 2024 18:01:09 -0500 Subject: [PATCH] ENH: Implemennt ITKWASM_LABEL_IMAGE method --- ngff_zarr/methods/_itkwasm.py | 27 ++++++-- ngff_zarr/to_multiscales.py | 10 ++- pyproject.toml | 2 +- test/_data.py | 21 +----- test/test_to_ngff_zarr_itkwasm.py | 102 ++++++++++++------------------ 5 files changed, 73 insertions(+), 89 deletions(-) diff --git a/ngff_zarr/methods/_itkwasm.py b/ngff_zarr/methods/_itkwasm.py index e8038a8..7e1b433 100644 --- a/ngff_zarr/methods/_itkwasm.py +++ b/ngff_zarr/methods/_itkwasm.py @@ -19,10 +19,10 @@ def _itkwasm_blur_and_downsample( image_data, shrink_factors, kernel_radius, + smoothing, ): """Blur and then downsample a given image chunk""" import itkwasm - from itkwasm_downsample import downsample # chunk does not have metadata attached, values are ITK defaults image = itkwasm.image_from_array(image_data) @@ -32,9 +32,22 @@ def _itkwasm_blur_and_downsample( if any(block_len == 0 for block_len in block_size): return None - downsampled = downsample( - image, shrink_factors=shrink_factors, crop_radius=kernel_radius - ) + if smoothing == "gaussian": + from itkwasm_downsample import downsample + + downsampled = downsample( + image, shrink_factors=shrink_factors, crop_radius=kernel_radius + ) + elif smoothing == "label_image": + from itkwasm_downsample import downsample_label_image + + downsampled = downsample_label_image( + image, shrink_factors=shrink_factors, crop_radius=kernel_radius + ) + else: + msg = f"Unknown smoothing method: {smoothing}" + raise ValueError(msg) + return downsampled.data @@ -134,8 +147,8 @@ def _downsample_itkwasm_bin_shrink( return multiscales -def _downsample_itkwasm_gaussian( - ngff_image: NgffImage, default_chunks, out_chunks, scale_factors +def _downsample_itkwasm( + ngff_image: NgffImage, default_chunks, out_chunks, scale_factors, smoothing ): import itkwasm from itkwasm_downsample import downsample_bin_shrink, gaussian_kernel_radius @@ -232,6 +245,7 @@ def _downsample_itkwasm_gaussian( data, shrink_factors=shrink_factors, kernel_radius=kernel_radius, + smoothing=smoothing, dtype=dtype, depth=dict(enumerate(np.flip(kernel_radius))), # overlap is in tzyx boundary="nearest", @@ -248,6 +262,7 @@ def _downsample_itkwasm_gaussian( data, shrink_factors=shrink_factors, kernel_radius=kernel_radius, + smoothing=smoothing, dtype=dtype, depth=dict(enumerate(np.flip(kernel_radius))), # overlap is in tzyx boundary="nearest", diff --git a/ngff_zarr/to_multiscales.py b/ngff_zarr/to_multiscales.py index 49f6ecf..afaa721 100644 --- a/ngff_zarr/to_multiscales.py +++ b/ngff_zarr/to_multiscales.py @@ -22,8 +22,8 @@ _downsample_itk_gaussian, ) from .methods._itkwasm import ( + _downsample_itkwasm, _downsample_itkwasm_bin_shrink, - _downsample_itkwasm_gaussian, ) from .methods._support import _spatial_dims from .multiscales import Multiscales @@ -311,8 +311,12 @@ def to_multiscales( method = Methods.DASK_IMAGE_GAUSSIAN if method is Methods.ITKWASM_GAUSSIAN: - images = _downsample_itkwasm_gaussian( - ngff_image, default_chunks, out_chunks, scale_factors + images = _downsample_itkwasm( + ngff_image, default_chunks, out_chunks, scale_factors, "gaussian" + ) + elif method is Methods.ITKWASM_LABEL_IMAGE: + images = _downsample_itkwasm( + ngff_image, default_chunks, out_chunks, scale_factors, "label_image" ) elif method is Methods.ITKWASM_BIN_SHRINK: images = _downsample_itkwasm_bin_shrink( diff --git a/pyproject.toml b/pyproject.toml index 97c6231..78f1487 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ classifiers = [ ] dependencies = [ "dask[array]", - "itkwasm >= 1.0b166", + "itkwasm >= 1.0b167", "itkwasm-downsample >= 1.1.0", "numpy", "platformdirs", diff --git a/test/_data.py b/test/_data.py index c8d975b..aade0b7 100644 --- a/test/_data.py +++ b/test/_data.py @@ -27,11 +27,6 @@ def input_images(): ) result = {} - # store = DirectoryStore( - # test_data_dir / "input" / "cthead1.zarr", dimension_separator="/" - # ) - # image_ds = xr.open_zarr(store) - # image_da = image_ds.cthead1 image = itk.imread(test_data_dir / "input" / "cthead1.png") image_ngff = itk_image_to_ngff_image(image) result["cthead1"] = image_ngff @@ -45,19 +40,9 @@ def input_images(): image_ngff = itk_image_to_ngff_image(image) result["brain_two_components"] = image_ngff - # store = DirectoryStore( - # test_data_dir / "input" / "small_head.zarr", dimension_separator="/" - # ) - # image_ds = xr.open_zarr(store) - # image_da = image_ds.small_head - # result["small_head"] = image_da - - # store = DirectoryStore( - # test_data_dir / "input" / "2th_cthead1.zarr", - # ) - # image_ds = xr.open_zarr(store) - # image_da = image_ds['2th_cthead1'] - # result["2th_cthead1"] = image_da + image = itk.imread(test_data_dir / "input" / "2th_cthead1.png") + image_ngff = itk_image_to_ngff_image(image) + result["2th_cthead1"] = image_ngff return result diff --git a/test/test_to_ngff_zarr_itkwasm.py b/test/test_to_ngff_zarr_itkwasm.py index 586f2f4..20bf174 100644 --- a/test/test_to_ngff_zarr_itkwasm.py +++ b/test/test_to_ngff_zarr_itkwasm.py @@ -44,70 +44,50 @@ def test_gaussian_isotropic_scale_factors(input_images): # verify_against_baseline(dataset_name, baseline_name, multiscale) -# def test_gaussian_anisotropic_scale_factors(input_images): -# dataset_name = "cthead1" -# image = input_images[dataset_name] -# scale_factors = [{"x": 2, "y": 4}, {"x": 1, "y": 2}] -# multiscale = to_multiscale(image, scale_factors, method=Methods.DASK_IMAGE_GAUSSIAN) -# baseline_name = "x2y4_x1y2/DASK_IMAGE_GAUSSIAN" -# verify_against_baseline(dataset_name, baseline_name, multiscale) - -# dataset_name = "small_head" -# image = input_images[dataset_name] -# scale_factors = [ -# {"x": 3, "y": 2, "z": 4}, -# {"x": 2, "y": 2, "z": 2}, -# {"x": 1, "y": 2, "z": 1}, -# ] -# multiscale = to_multiscale(image, scale_factors, method=Methods.DASK_IMAGE_GAUSSIAN) -# baseline_name = "x3y2z4_x2y2z2_x1y2z1/DASK_IMAGE_GAUSSIAN" -# verify_against_baseline(dataset_name, baseline_name, multiscale) - - -# def test_label_nearest_isotropic_scale_factors(input_images): -# dataset_name = "2th_cthead1" -# image = input_images[dataset_name] -# baseline_name = "2_4/DASK_IMAGE_NEAREST" -# multiscale = to_multiscale(image, [2, 4], method=Methods.DASK_IMAGE_NEAREST) -# store_new_image(dataset_name, baseline_name, multiscale) -# verify_against_baseline(dataset_name, baseline_name, multiscale) - -# dataset_name = "2th_cthead1" -# image = input_images[dataset_name] -# baseline_name = "2_3/DASK_IMAGE_NEAREST" -# multiscale = to_multiscale(image, [2, 3], method=Methods.DASK_IMAGE_NEAREST) -# store_new_image(dataset_name, baseline_name, multiscale) -# verify_against_baseline(dataset_name, baseline_name, multiscale) - +def test_gaussian_anisotropic_scale_factors(input_images): + dataset_name = "cthead1" + image = input_images[dataset_name] + scale_factors = [{"x": 2, "y": 2}, {"x": 2, "y": 4}] + multiscales = to_multiscales(image, scale_factors, method=Methods.ITKWASM_GAUSSIAN) + baseline_name = "x2y2_x2y4/ITKWASM_GAUSSIAN.zarr" + store_new_multiscales(dataset_name, baseline_name, multiscales) + verify_against_baseline(dataset_name, baseline_name, multiscales) -# def test_label_nearest_anisotropic_scale_factors(input_images): -# dataset_name = "2th_cthead1" -# image = input_images[dataset_name] -# scale_factors = [{"x": 2, "y": 4}, {"x": 1, "y": 2}] -# multiscale = to_multiscale(image, scale_factors, method=Methods.DASK_IMAGE_NEAREST) -# baseline_name = "x2y4_x1y2/DASK_IMAGE_NEAREST" -# store_new_image(dataset_name, baseline_name, multiscale) -# verify_against_baseline(dataset_name, baseline_name, multiscale) + # dataset_name = "small_head" + # image = input_images[dataset_name] + # scale_factors = [ + # {"x": 3, "y": 2, "z": 4}, + # {"x": 2, "y": 2, "z": 2}, + # {"x": 1, "y": 2, "z": 1}, + # ] + # multiscale = to_multiscale(image, scale_factors, method=Methods.DASK_IMAGE_GAUSSIAN) + # baseline_name = "x3y2z4_x2y2z2_x1y2z1/DASK_IMAGE_GAUSSIAN" + # verify_against_baseline(dataset_name, baseline_name, multiscale) -# def test_label_mode_isotropic_scale_factors(input_images): -# dataset_name = "2th_cthead1" -# image = input_images[dataset_name] -# baseline_name = "2_4/DASK_IMAGE_MODE" -# multiscale = to_multiscale(image, [2, 4], method=Methods.DASK_IMAGE_MODE) -# verify_against_baseline(dataset_name, baseline_name, multiscale) +def test_label_image_isotropic_scale_factors(input_images): + dataset_name = "2th_cthead1" + image = input_images[dataset_name] + baseline_name = "2_4/ITKWASM_LABEL_IMAGE.zarr" + multiscales = to_multiscales(image, [2, 4], method=Methods.ITKWASM_LABEL_IMAGE) + store_new_multiscales(dataset_name, baseline_name, multiscales) + verify_against_baseline(dataset_name, baseline_name, multiscales) -# dataset_name = "2th_cthead1" -# image = input_images[dataset_name] -# baseline_name = "2_3/DASK_IMAGE_MODE" -# multiscale = to_multiscale(image, [2, 3], method=Methods.DASK_IMAGE_MODE) -# verify_against_baseline(dataset_name, baseline_name, multiscale) + dataset_name = "2th_cthead1" + image = input_images[dataset_name] + baseline_name = "2_3/ITKWASM_LABEL_IMAGE.zarr" + multiscales = to_multiscales(image, [2, 3], method=Methods.ITKWASM_LABEL_IMAGE) + store_new_multiscales(dataset_name, baseline_name, multiscales) + verify_against_baseline(dataset_name, baseline_name, multiscales) -# def test_label_mode_anisotropic_scale_factors(input_images): -# dataset_name = "2th_cthead1" -# image = input_images[dataset_name] -# scale_factors = [{"x": 2, "y": 4}, {"x": 1, "y": 2}] -# multiscale = to_multiscale(image, scale_factors, method=Methods.DASK_IMAGE_MODE) -# baseline_name = "x2y4_x1y2/DASK_IMAGE_MODE" -# verify_against_baseline(dataset_name, baseline_name, multiscale) +def test_label_image_anisotropic_scale_factors(input_images): + dataset_name = "2th_cthead1" + image = input_images[dataset_name] + scale_factors = [{"x": 2, "y": 2}, {"x": 2, "y": 4}] + multiscales = to_multiscales( + image, scale_factors, method=Methods.ITKWASM_LABEL_IMAGE + ) + baseline_name = "x2y4_x2y8/ITKWASM_LABEL_IMAGE.zarr" + store_new_multiscales(dataset_name, baseline_name, multiscales) + verify_against_baseline(dataset_name, baseline_name, multiscales)