From 362ab631c30a038ce6f9d8a1e118d650cd2ec067 Mon Sep 17 00:00:00 2001 From: rhoadesScholar Date: Mon, 8 Jul 2024 11:28:51 -0400 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=E2=9A=A1=EF=B8=8F=20Add=20Rescaled?= =?UTF-8?q?Array=20to=20handle=20non-integer=20resampling.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit May be able to replace ResampledArray in the future, consider deprecation of ResampledArray. --- .../datasets/arrays/rescaled_array.py | 345 ++++++++++++++++++ .../datasets/arrays/rescaled_array_config.py | 34 ++ 2 files changed, 379 insertions(+) create mode 100644 dacapo/experiments/datasplits/datasets/arrays/rescaled_array.py create mode 100644 dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py diff --git a/dacapo/experiments/datasplits/datasets/arrays/rescaled_array.py b/dacapo/experiments/datasplits/datasets/arrays/rescaled_array.py new file mode 100644 index 000000000..b8c322822 --- /dev/null +++ b/dacapo/experiments/datasplits/datasets/arrays/rescaled_array.py @@ -0,0 +1,345 @@ +from .array import Array + +import funlib.persistence +from funlib.geometry import Coordinate, Roi + +import numpy as np +from skimage.transform import rescale + + +class RescaledArray(Array): + """ + This is a zarr array that is a rescaled version of another array. + + Rescaling is done by rescaling the source array to the given + voxel size. + + Attributes: + name: str + The name of the array + source_array: Array + The source array + target_voxel_size: tuple[int | float] + The target voxel size + interp_order: int + The order of the interpolation used for resampling + Methods: + attrs: Dict + Returns the attributes of the source array + axes: str + Returns the axes of the source array + dims: int + Returns the number of dimensions of the source array + voxel_size: Coordinate + Returns the voxel size of the rescaled array + roi: Roi + Returns the region of interest of the rescaled array + writable: bool + Returns whether the rescaled array is writable + dtype: np.dtype + Returns the data type of the rescaled array + num_channels: int + Returns the number of channels of the rescaled array + data: np.ndarray + Returns the data of the rescaled array + scale: Tuple[float] + Returns the scale of the rescaled array + __getitem__(roi: Roi) -> np.ndarray + Returns the data of the rescaled array within the given region of interest + _can_neuroglance() -> bool + Returns whether the source array can be visualized with neuroglance + _neuroglancer_layer() -> Dict + Returns the neuroglancer layer of the source array + _neuroglancer_source() -> Dict + Returns the neuroglancer source of the source array + _source_name() -> str + Returns the name of the source array + Note: + This class is a subclass of Array. + + + """ + + def __init__(self, array_config): + """ + Constructor of the RescaledArray class. + + Args: + array_config: ArrayConfig + The configuration of the array + Examples: + >>> rescaled_array = RescaledArray(array_config) + Note: + This constructor resamples the source array to the given voxel size. + """ + self.name = array_config.name + self._source_array = array_config.source_array_config.array_type( + array_config.source_array_config + ) + + self.target_voxel_size = array_config.target_voxel_size + self.interp_order = array_config.interp_order + + @property + def attrs(self): + """ + Returns the attributes of the source array. + + Returns: + Dict: The attributes of the source array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array.attrs + Note: + This method returns the attributes of the source array. + + """ + return self._source_array.attrs + + @property + def axes(self): + """ + Returns the axes of the source array. + + Returns: + str: The axes of the source array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array.axes + Note: + This method returns the axes of the source array. + """ + return self._source_array.axes + + @property + def dims(self) -> int: + """ + Returns the number of dimensions of the source array. + + Returns: + int: The number of dimensions of the source array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array.dims + Note: + This method returns the number of dimensions of the source array. + """ + return self._source_array.dims + + @property + def voxel_size(self) -> Coordinate: + """ + Returns the voxel size of the rescaled array. + + Returns: + Coordinate: The voxel size of the rescaled array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array.voxel_size + Note: + This method returns the voxel size of the rescaled array. + """ + return self.target_voxel_size + + @property + def roi(self) -> Roi: + """ + Returns the region of interest of the rescaled array. + + Returns: + Roi: The region of interest of the rescaled array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array.roi + Note: + This method returns the region of interest of the rescaled array. + + """ + return self._source_array.roi.snap_to_grid(self.voxel_size, mode="shrink") + + @property + def writable(self) -> bool: + """ + Returns whether the rescaled array is writable. + + Returns: + bool: True if the rescaled array is writable, False otherwise + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array.writable + Note: + This method returns whether the rescaled array is writable. + + """ + return False + + @property + def dtype(self): + """ + Returns the data type of the rescaled array. + + Returns: + np.dtype: The data type of the rescaled array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array.dtype + Note: + This method returns the data type of the rescaled array. + """ + return self._source_array.dtype + + @property + def num_channels(self) -> int: + """ + Returns the number of channels of the rescaled array. + + Returns: + int: The number of channels of the rescaled array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array.num_channels + Note: + This method returns the number of channels of the rescaled array. + """ + return self._source_array.num_channels + + @property + def data(self): + """ + Returns the data of the rescaled array. + + Returns: + np.ndarray: The data of the rescaled array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array.data + Note: + This method returns the data of the rescaled array. + """ + raise ValueError( + "Cannot get a writable view of this array because it is a virtual " + "array created by modifying another array on demand." + ) + + @property + def scale(self): + """ + Returns the scaling factor of the rescaled array. + + Returns: + Tuple[float]: The scaling factor of the rescaled array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array.scale + Note: + This method returns the scaling factor of the rescaled array. + + """ + spatial_scales = tuple( + o / r for r, o in zip(self.target_voxel_size, self._source_array.voxel_size) + ) + if "c" in self.axes: + scales = list(spatial_scales) + scales.insert(self.axes.index("c"), 1.0) + return tuple(scales) + else: + return spatial_scales + + def __getitem__(self, roi: Roi) -> np.ndarray: + """ + Returns the data of the rescaled array within the given region of interest. + + Args: + roi: Roi + The region of interest + Returns: + np.ndarray: The data of the rescaled array within the given region of interest + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array[roi] + Note: + This method returns the data of the rescaled array within the given region of interest. + """ + snapped_roi = roi.snap_to_grid(self._source_array.voxel_size, mode="grow") + rescaled_array = funlib.persistence.Array( + rescale( + self._source_array[snapped_roi].astype(np.float32), + self.scale, + order=self.interp_order, + anti_aliasing=self.interp_order != 0, + ).astype(self.dtype), + roi=snapped_roi, + voxel_size=self.voxel_size, + ) + return rescaled_array.to_ndarray(roi) + + def _can_neuroglance(self): + """ + Returns whether the source array can be visualized with neuroglance. + + Returns: + bool: True if the source array can be visualized with neuroglance, False otherwise + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array._can_neuroglance() + Note: + This method returns whether the source array can be visualized with neuroglance. + """ + return self._source_array._can_neuroglance() + + def _neuroglancer_layer(self): + """ + Returns the neuroglancer layer of the source array. + + Returns: + Dict: The neuroglancer layer of the source array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array._neuroglancer_layer() + Note: + This method returns the neuroglancer layer of the source array. + """ + return self._source_array._neuroglancer_layer() + + def _neuroglancer_source(self): + """ + Returns the neuroglancer source of the source array. + + Returns: + Dict: The neuroglancer source of the source array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array._neuroglancer_source() + Note: + This method returns the neuroglancer source of the source array. + """ + return self._source_array._neuroglancer_source() + + def _source_name(self): + """ + Returns the name of the source array. + + Returns: + str: The name of the source array + Raises: + ValueError: If the rescaled array is not writable + Examples: + >>> rescaled_array._source_name() + Note: + This method returns the name of the source array. + """ + return self._source_array._source_name() diff --git a/dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py b/dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py new file mode 100644 index 000000000..8feba3c59 --- /dev/null +++ b/dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py @@ -0,0 +1,34 @@ +import attr + +from .array_config import ArrayConfig +from .rescaled_array import RescaledArray + + +@attr.s +class RescaledArrayConfig(ArrayConfig): + """ + A configuration for a RescaledArray. This array will up or down sample an array into the desired voxel size. + + Attributes: + source_array_config (ArrayConfig): The Array that you want to upsample or downsample. + target_voxel_size (tuple[int | float]): The target voxel size. + interp_order (bool): The order of the interpolation. + Methods: + create_array: Creates a RescaledArray from the configuration. + Note: + This class is meant to be used with the ArrayDataset class. + + """ + + array_type = RescaledArray + + source_array_config: ArrayConfig = attr.ib( + metadata={"help_text": "The Array that you want to upsample or downsample."} + ) + + target_voxel_size: tuple[int | float] = attr.ib( + metadata={"help_text": "The target voxel size."} + ) + interp_order: bool = attr.ib( + metadata={"help_text": "The order of the interpolation."} + ) From 9f54ce1f87a67f674ea6dea3d83167b15cb40b61 Mon Sep 17 00:00:00 2001 From: rhoadesScholar Date: Mon, 8 Jul 2024 15:29:28 -0400 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Try=20to=20fix=20seri?= =?UTF-8?q?alization=20issues=20with=20RescaledArray?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dacapo/experiments/datasplits/datasets/arrays/__init__.py | 1 + .../datasplits/datasets/arrays/rescaled_array_config.py | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/dacapo/experiments/datasplits/datasets/arrays/__init__.py b/dacapo/experiments/datasplits/datasets/arrays/__init__.py index 63d6d6e21..c647817cd 100644 --- a/dacapo/experiments/datasplits/datasets/arrays/__init__.py +++ b/dacapo/experiments/datasplits/datasets/arrays/__init__.py @@ -19,6 +19,7 @@ ) # noqa from .dvid_array_config import DVIDArray, DVIDArrayConfig from .sum_array_config import SumArray, SumArrayConfig +from .rescaled_array_config import RescaledArray, RescaledArrayConfig # nonconfigurable arrays (helpers) from .numpy_array import NumpyArray # noqa diff --git a/dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py b/dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py index 8feba3c59..964605a29 100644 --- a/dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py +++ b/dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py @@ -11,7 +11,7 @@ class RescaledArrayConfig(ArrayConfig): Attributes: source_array_config (ArrayConfig): The Array that you want to upsample or downsample. - target_voxel_size (tuple[int | float]): The target voxel size. + target_voxel_size (tuple): The target voxel size. interp_order (bool): The order of the interpolation. Methods: create_array: Creates a RescaledArray from the configuration. @@ -26,9 +26,7 @@ class RescaledArrayConfig(ArrayConfig): metadata={"help_text": "The Array that you want to upsample or downsample."} ) - target_voxel_size: tuple[int | float] = attr.ib( - metadata={"help_text": "The target voxel size."} - ) + target_voxel_size: tuple = attr.ib(metadata={"help_text": "The target voxel size."}) interp_order: bool = attr.ib( metadata={"help_text": "The order of the interpolation."} ) From 0069462e6be1dedca7687d372d2ad9620676f7e3 Mon Sep 17 00:00:00 2001 From: rhoadesScholar Date: Wed, 10 Jul 2024 10:49:32 -0400 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=F0=9F=93=9D=20Add=20example=20of?= =?UTF-8?q?=20simple=20blockwise=20segmentation=20script=20and=20function.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dacapo/blockwise/empanada_function.py | 2 +- examples/blockwise/segment_function.py | 36 ++++++++++++++++++++++++++ examples/blockwise/segment_script.py | 34 ++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 examples/blockwise/segment_function.py create mode 100644 examples/blockwise/segment_script.py diff --git a/dacapo/blockwise/empanada_function.py b/dacapo/blockwise/empanada_function.py index 301a282e8..09871de88 100644 --- a/dacapo/blockwise/empanada_function.py +++ b/dacapo/blockwise/empanada_function.py @@ -50,7 +50,7 @@ def segment_function(input_array, block, **parameters): Args: input_array (np.ndarray): The 3D array to segment. - block (dask.array.core.Block): The block object. + block (daisy.Block): The block object. **parameters: Parameters for the empanada-napari segmenter. Returns: np.ndarray: The segmented 3D array. diff --git a/examples/blockwise/segment_function.py b/examples/blockwise/segment_function.py new file mode 100644 index 000000000..d47c9346a --- /dev/null +++ b/examples/blockwise/segment_function.py @@ -0,0 +1,36 @@ +import logging + +import scipy.ndimage + +logger = logging.getLogger(__file__) + + +def segment_function(input_array, block, **steps): + """ + Segment a 3D block using a small numpy-based post-processing script. + + Args: + input_array (np.ndarray): The 3D array to segment. + block (daisy.Block): The block object. + **steps (dict): The steps to apply to the segmentation. + Returns: + np.ndarray: The segmented 3D array. + """ + data = input_array.to_ndarray(block.read_roi) + + # Apply the segmentation function here + for step, params in steps.items(): + if step == "gaussian_smooth": + sigma = params.get("sigma", 1.0) + logger.info(f"Applying Gaussian smoothing with sigma={sigma}") + data = scipy.ndimage.gaussian_filter(data, sigma=sigma) + elif step == "threshold": + threshold = params.get("threshold", 0.5) + logger.info(f"Applying thresholding with threshold={threshold}") + data = data > threshold + elif step == "label": + structuring_element = params.get("structuring_element", None) + logger.info("Applying labeling") + data, _ = scipy.ndimage.label(data, structuring_element) # type: ignore + + return data diff --git a/examples/blockwise/segment_script.py b/examples/blockwise/segment_script.py new file mode 100644 index 000000000..8ef7e790f --- /dev/null +++ b/examples/blockwise/segment_script.py @@ -0,0 +1,34 @@ +from dacapo.blockwise.scheduler import segment_blockwise +from funlib.geometry import Roi, Coordinate + +# Make the ROIs +path_to_function = "segment_function.py" +context = Coordinate((0, 0, 0)) +total_roi = Roi(offset=(0, 0, 0), shape=(100, 100, 100)) +read_roi = Roi(offset=(0, 0, 0), shape=(10, 10, 10)) +write_roi = Roi(offset=(0, 0, 0), shape=(1, 1, 1)) +num_workers = 16 + +# Run the script blockwise +success = segment_blockwise( + segment_function_file=path_to_function, + context=context, + total_roi=total_roi, + read_roi=read_roi, + write_roi=write_roi, + num_workers=num_workers, + steps={ + "gaussian_smooth": {"sigma": 1.0}, + "threshold": {"threshold": 0.5}, + "label": {}, + }, +) + +# Print the success +if success: + print("Success") +else: + print("Failure") + +# example run command: +# bsub -n 4 python dummy_script.py From 2aab8ad3f99a37608adae125d8bf24c3db303e46 Mon Sep 17 00:00:00 2001 From: rhoadesScholar Date: Wed, 10 Jul 2024 11:08:25 -0400 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=F0=9F=93=9D=20Add=20example=20of?= =?UTF-8?q?=20simple=20blockwise=20segmentation=20script=20and=20function.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- examples/blockwise/segment_config.yaml | 7 ++++++ examples/blockwise/segment_function.py | 6 +++-- examples/blockwise/segment_script.py | 34 -------------------------- examples/blockwise/segment_script.sh | 25 +++++++++++++++++++ 5 files changed, 38 insertions(+), 37 deletions(-) create mode 100644 examples/blockwise/segment_config.yaml delete mode 100644 examples/blockwise/segment_script.py create mode 100644 examples/blockwise/segment_script.sh diff --git a/.gitignore b/.gitignore index d14f3fb84..dff704b9a 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,5 @@ tmp/ # daisy logs daisy_logs/ -*.csv \ No newline at end of file +*.csv +*.private \ No newline at end of file diff --git a/examples/blockwise/segment_config.yaml b/examples/blockwise/segment_config.yaml new file mode 100644 index 000000000..80e0ff128 --- /dev/null +++ b/examples/blockwise/segment_config.yaml @@ -0,0 +1,7 @@ + +steps: + gaussian_smooth: + sigma: 2.0 + threshold: + threshold: 200 + label: {} diff --git a/examples/blockwise/segment_function.py b/examples/blockwise/segment_function.py index d47c9346a..c49239c4e 100644 --- a/examples/blockwise/segment_function.py +++ b/examples/blockwise/segment_function.py @@ -1,22 +1,24 @@ import logging import scipy.ndimage +import yaml logger = logging.getLogger(__file__) -def segment_function(input_array, block, **steps): +def segment_function(input_array, block, config_path): """ Segment a 3D block using a small numpy-based post-processing script. Args: input_array (np.ndarray): The 3D array to segment. block (daisy.Block): The block object. - **steps (dict): The steps to apply to the segmentation. + config_path (str): The path to the configuration yaml file. Returns: np.ndarray: The segmented 3D array. """ data = input_array.to_ndarray(block.read_roi) + steps = yaml.load(config_path, Loader=yaml.FullLoader) # Apply the segmentation function here for step, params in steps.items(): diff --git a/examples/blockwise/segment_script.py b/examples/blockwise/segment_script.py deleted file mode 100644 index 8ef7e790f..000000000 --- a/examples/blockwise/segment_script.py +++ /dev/null @@ -1,34 +0,0 @@ -from dacapo.blockwise.scheduler import segment_blockwise -from funlib.geometry import Roi, Coordinate - -# Make the ROIs -path_to_function = "segment_function.py" -context = Coordinate((0, 0, 0)) -total_roi = Roi(offset=(0, 0, 0), shape=(100, 100, 100)) -read_roi = Roi(offset=(0, 0, 0), shape=(10, 10, 10)) -write_roi = Roi(offset=(0, 0, 0), shape=(1, 1, 1)) -num_workers = 16 - -# Run the script blockwise -success = segment_blockwise( - segment_function_file=path_to_function, - context=context, - total_roi=total_roi, - read_roi=read_roi, - write_roi=write_roi, - num_workers=num_workers, - steps={ - "gaussian_smooth": {"sigma": 1.0}, - "threshold": {"threshold": 0.5}, - "label": {}, - }, -) - -# Print the success -if success: - print("Success") -else: - print("Failure") - -# example run command: -# bsub -n 4 python dummy_script.py diff --git a/examples/blockwise/segment_script.sh b/examples/blockwise/segment_script.sh new file mode 100644 index 000000000..ec4f191fe --- /dev/null +++ b/examples/blockwise/segment_script.sh @@ -0,0 +1,25 @@ +# This file contains the commands used to run an example of the segment-blockwise command. +# The arguments to the segment-blockwise command are: +# -sf: path to the python file with the segmentation_function (in this case the empanada_function.py file) +# -tr: Total ROI to process. It is a list of 3 elements, each one is a list with the start and end of the ROI in the x, y and z axis respectively. +# e.g. -tr "[320000:330000, 100000:110000, 10000:20000]" \ +# -rr: ROI to read. It is a list of 3 elements, each one is a list with the start and end of the ROI in the x, y and z axis respectively. +# -wr: ROI to write. It is a list of 3 elements, each one is a list with the start and end of the ROI in the x, y and z axis respectively. +# -nw: Number of workers to use. +# -ic: Input container. It is the path to the input zarr file. +# -id: Input dataset. It is the path to the input dataset inside the input zarr file. +# -oc: Output container. It is the path to the output zarr file. +# -od: Output dataset. It is the path to the output dataset inside the output zarr file. +# --config_path: Path to the config yaml file. + + +dacapo segment-blockwise \ +-sf segment_function.py \ +-rr "[256,256,256]" \ +-wr "[256,256,256]" \ +-nw 32 \ +-ic predictions/c-elegen/bw/c_elegen_bw_op50_ld_scratch_0_300000.zarr \ +-id ld/ld \ +-oc predictions/c-elegen/bw/jrc_c-elegans-bw-1_postprocessed.zarr \ +-od ld \ +--config_path segment_config.yaml From b339972bdb8ee6a9f7ff0d664506c283d28c3bc0 Mon Sep 17 00:00:00 2001 From: rhoadesScholar Date: Wed, 10 Jul 2024 14:54:01 -0400 Subject: [PATCH 5/5] =?UTF-8?q?revert:=20=E2=8F=AA=EF=B8=8F=20Remove=20res?= =?UTF-8?q?caled=20array=20(for=20other=20branch=20to=20merge).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datasplits/datasets/arrays/__init__.py | 1 - .../datasets/arrays/rescaled_array.py | 345 ------------------ .../datasets/arrays/rescaled_array_config.py | 32 -- 3 files changed, 378 deletions(-) delete mode 100644 dacapo/experiments/datasplits/datasets/arrays/rescaled_array.py delete mode 100644 dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py diff --git a/dacapo/experiments/datasplits/datasets/arrays/__init__.py b/dacapo/experiments/datasplits/datasets/arrays/__init__.py index c647817cd..63d6d6e21 100644 --- a/dacapo/experiments/datasplits/datasets/arrays/__init__.py +++ b/dacapo/experiments/datasplits/datasets/arrays/__init__.py @@ -19,7 +19,6 @@ ) # noqa from .dvid_array_config import DVIDArray, DVIDArrayConfig from .sum_array_config import SumArray, SumArrayConfig -from .rescaled_array_config import RescaledArray, RescaledArrayConfig # nonconfigurable arrays (helpers) from .numpy_array import NumpyArray # noqa diff --git a/dacapo/experiments/datasplits/datasets/arrays/rescaled_array.py b/dacapo/experiments/datasplits/datasets/arrays/rescaled_array.py deleted file mode 100644 index b8c322822..000000000 --- a/dacapo/experiments/datasplits/datasets/arrays/rescaled_array.py +++ /dev/null @@ -1,345 +0,0 @@ -from .array import Array - -import funlib.persistence -from funlib.geometry import Coordinate, Roi - -import numpy as np -from skimage.transform import rescale - - -class RescaledArray(Array): - """ - This is a zarr array that is a rescaled version of another array. - - Rescaling is done by rescaling the source array to the given - voxel size. - - Attributes: - name: str - The name of the array - source_array: Array - The source array - target_voxel_size: tuple[int | float] - The target voxel size - interp_order: int - The order of the interpolation used for resampling - Methods: - attrs: Dict - Returns the attributes of the source array - axes: str - Returns the axes of the source array - dims: int - Returns the number of dimensions of the source array - voxel_size: Coordinate - Returns the voxel size of the rescaled array - roi: Roi - Returns the region of interest of the rescaled array - writable: bool - Returns whether the rescaled array is writable - dtype: np.dtype - Returns the data type of the rescaled array - num_channels: int - Returns the number of channels of the rescaled array - data: np.ndarray - Returns the data of the rescaled array - scale: Tuple[float] - Returns the scale of the rescaled array - __getitem__(roi: Roi) -> np.ndarray - Returns the data of the rescaled array within the given region of interest - _can_neuroglance() -> bool - Returns whether the source array can be visualized with neuroglance - _neuroglancer_layer() -> Dict - Returns the neuroglancer layer of the source array - _neuroglancer_source() -> Dict - Returns the neuroglancer source of the source array - _source_name() -> str - Returns the name of the source array - Note: - This class is a subclass of Array. - - - """ - - def __init__(self, array_config): - """ - Constructor of the RescaledArray class. - - Args: - array_config: ArrayConfig - The configuration of the array - Examples: - >>> rescaled_array = RescaledArray(array_config) - Note: - This constructor resamples the source array to the given voxel size. - """ - self.name = array_config.name - self._source_array = array_config.source_array_config.array_type( - array_config.source_array_config - ) - - self.target_voxel_size = array_config.target_voxel_size - self.interp_order = array_config.interp_order - - @property - def attrs(self): - """ - Returns the attributes of the source array. - - Returns: - Dict: The attributes of the source array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array.attrs - Note: - This method returns the attributes of the source array. - - """ - return self._source_array.attrs - - @property - def axes(self): - """ - Returns the axes of the source array. - - Returns: - str: The axes of the source array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array.axes - Note: - This method returns the axes of the source array. - """ - return self._source_array.axes - - @property - def dims(self) -> int: - """ - Returns the number of dimensions of the source array. - - Returns: - int: The number of dimensions of the source array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array.dims - Note: - This method returns the number of dimensions of the source array. - """ - return self._source_array.dims - - @property - def voxel_size(self) -> Coordinate: - """ - Returns the voxel size of the rescaled array. - - Returns: - Coordinate: The voxel size of the rescaled array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array.voxel_size - Note: - This method returns the voxel size of the rescaled array. - """ - return self.target_voxel_size - - @property - def roi(self) -> Roi: - """ - Returns the region of interest of the rescaled array. - - Returns: - Roi: The region of interest of the rescaled array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array.roi - Note: - This method returns the region of interest of the rescaled array. - - """ - return self._source_array.roi.snap_to_grid(self.voxel_size, mode="shrink") - - @property - def writable(self) -> bool: - """ - Returns whether the rescaled array is writable. - - Returns: - bool: True if the rescaled array is writable, False otherwise - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array.writable - Note: - This method returns whether the rescaled array is writable. - - """ - return False - - @property - def dtype(self): - """ - Returns the data type of the rescaled array. - - Returns: - np.dtype: The data type of the rescaled array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array.dtype - Note: - This method returns the data type of the rescaled array. - """ - return self._source_array.dtype - - @property - def num_channels(self) -> int: - """ - Returns the number of channels of the rescaled array. - - Returns: - int: The number of channels of the rescaled array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array.num_channels - Note: - This method returns the number of channels of the rescaled array. - """ - return self._source_array.num_channels - - @property - def data(self): - """ - Returns the data of the rescaled array. - - Returns: - np.ndarray: The data of the rescaled array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array.data - Note: - This method returns the data of the rescaled array. - """ - raise ValueError( - "Cannot get a writable view of this array because it is a virtual " - "array created by modifying another array on demand." - ) - - @property - def scale(self): - """ - Returns the scaling factor of the rescaled array. - - Returns: - Tuple[float]: The scaling factor of the rescaled array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array.scale - Note: - This method returns the scaling factor of the rescaled array. - - """ - spatial_scales = tuple( - o / r for r, o in zip(self.target_voxel_size, self._source_array.voxel_size) - ) - if "c" in self.axes: - scales = list(spatial_scales) - scales.insert(self.axes.index("c"), 1.0) - return tuple(scales) - else: - return spatial_scales - - def __getitem__(self, roi: Roi) -> np.ndarray: - """ - Returns the data of the rescaled array within the given region of interest. - - Args: - roi: Roi - The region of interest - Returns: - np.ndarray: The data of the rescaled array within the given region of interest - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array[roi] - Note: - This method returns the data of the rescaled array within the given region of interest. - """ - snapped_roi = roi.snap_to_grid(self._source_array.voxel_size, mode="grow") - rescaled_array = funlib.persistence.Array( - rescale( - self._source_array[snapped_roi].astype(np.float32), - self.scale, - order=self.interp_order, - anti_aliasing=self.interp_order != 0, - ).astype(self.dtype), - roi=snapped_roi, - voxel_size=self.voxel_size, - ) - return rescaled_array.to_ndarray(roi) - - def _can_neuroglance(self): - """ - Returns whether the source array can be visualized with neuroglance. - - Returns: - bool: True if the source array can be visualized with neuroglance, False otherwise - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array._can_neuroglance() - Note: - This method returns whether the source array can be visualized with neuroglance. - """ - return self._source_array._can_neuroglance() - - def _neuroglancer_layer(self): - """ - Returns the neuroglancer layer of the source array. - - Returns: - Dict: The neuroglancer layer of the source array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array._neuroglancer_layer() - Note: - This method returns the neuroglancer layer of the source array. - """ - return self._source_array._neuroglancer_layer() - - def _neuroglancer_source(self): - """ - Returns the neuroglancer source of the source array. - - Returns: - Dict: The neuroglancer source of the source array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array._neuroglancer_source() - Note: - This method returns the neuroglancer source of the source array. - """ - return self._source_array._neuroglancer_source() - - def _source_name(self): - """ - Returns the name of the source array. - - Returns: - str: The name of the source array - Raises: - ValueError: If the rescaled array is not writable - Examples: - >>> rescaled_array._source_name() - Note: - This method returns the name of the source array. - """ - return self._source_array._source_name() diff --git a/dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py b/dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py deleted file mode 100644 index 964605a29..000000000 --- a/dacapo/experiments/datasplits/datasets/arrays/rescaled_array_config.py +++ /dev/null @@ -1,32 +0,0 @@ -import attr - -from .array_config import ArrayConfig -from .rescaled_array import RescaledArray - - -@attr.s -class RescaledArrayConfig(ArrayConfig): - """ - A configuration for a RescaledArray. This array will up or down sample an array into the desired voxel size. - - Attributes: - source_array_config (ArrayConfig): The Array that you want to upsample or downsample. - target_voxel_size (tuple): The target voxel size. - interp_order (bool): The order of the interpolation. - Methods: - create_array: Creates a RescaledArray from the configuration. - Note: - This class is meant to be used with the ArrayDataset class. - - """ - - array_type = RescaledArray - - source_array_config: ArrayConfig = attr.ib( - metadata={"help_text": "The Array that you want to upsample or downsample."} - ) - - target_voxel_size: tuple = attr.ib(metadata={"help_text": "The target voxel size."}) - interp_order: bool = attr.ib( - metadata={"help_text": "The order of the interpolation."} - )