From 65230192a658de82020413a09351ddb1dd850c3a Mon Sep 17 00:00:00 2001 From: lorenzo Date: Wed, 13 Nov 2024 16:30:58 +0100 Subject: [PATCH 1/5] implement nice repr for ngio objects --- src/ngio/core/image_handler.py | 12 ++++ src/ngio/core/image_like_handler.py | 21 +++++++ src/ngio/core/label_handler.py | 23 ++++++++ src/ngio/core/ngff_image.py | 74 +++++++++++++++++------- src/ngio/tables/tables_group.py | 3 + src/ngio/tables/v1/_generic_table.py | 25 ++++++-- src/ngio/tables/v1/feature_tables.py | 32 +++++++++- src/ngio/tables/v1/masking_roi_tables.py | 30 +++++++++- src/ngio/tables/v1/roi_tables.py | 24 +++++++- 9 files changed, 214 insertions(+), 30 deletions(-) diff --git a/src/ngio/core/image_handler.py b/src/ngio/core/image_handler.py index 6ef15c9..a34c706 100644 --- a/src/ngio/core/image_handler.py +++ b/src/ngio/core/image_handler.py @@ -59,6 +59,18 @@ def __init__( _label_group=label_group, ) + def __repr__(self) -> str: + """Return the string representation of the class.""" + name = "Image(" + len_name = len(name) + return ( + f"{name}" + f"path={self.path},\n" + f"{' ':>{len_name}}{self.pixel_size},\n" + f"{' ':>{len_name}}{self.dimensions},\n" + ")" + ) + @property def metadata(self) -> ImageMeta: """Return the metadata of the image.""" diff --git a/src/ngio/core/image_like_handler.py b/src/ngio/core/image_like_handler.py index aeeac44..5b22ff0 100644 --- a/src/ngio/core/image_like_handler.py +++ b/src/ngio/core/image_like_handler.py @@ -132,6 +132,27 @@ def group(self) -> zarr.Group: """Return the Zarr group containing the image data.""" return self._group + @property + def root_path(self) -> str: + """Return the path to the root group.""" + return str(self._group.store.path) + + @property + def group_path(self) -> str: + """Return the path to the group.""" + root = self.root_path + if root.endswith("/"): + root = root[:-1] + return f"{root}/{self._group.path}" + + @property + def array_path(self) -> str: + """Return the path to the array.""" + group = self.group_path + if group.endswith("/"): + group = group[:-1] + return f"{group}/{self.path}" + @property def metadata(self) -> ImageLabelMeta: """Return the metadata of the image.""" diff --git a/src/ngio/core/label_handler.py b/src/ngio/core/label_handler.py index 1a3845c..aaeee38 100644 --- a/src/ngio/core/label_handler.py +++ b/src/ngio/core/label_handler.py @@ -25,6 +25,7 @@ def __init__( self, store: StoreOrGroup, *, + name: str, path: str | None = None, idx: int | None = None, pixel_size: PixelSize | None = None, @@ -41,6 +42,7 @@ def __init__( Args: store (StoreOrGroup): The Zarr store or group containing the image data. + name (str): The name of the label. path (str | None): The path to the level. idx (int | None): The index of the level. pixel_size (PixelSize | None): The pixel size of the level. @@ -64,6 +66,26 @@ def __init__( _label_group=label_group, ) + self._name = name + + def __repr__(self) -> str: + """Return the string representation of the label.""" + name = "Label(" + len_name = len(name) + return ( + f"{name}" + f"path={self.path},\n" + f"{' ':>{len_name}}name={self.name},\n" + f"{' ':>{len_name}}{self.pixel_size},\n" + f"{' ':>{len_name}}{self.dimensions},\n" + ")" + ) + + @property + def name(self) -> str: + """Return the name of the label.""" + return self._name + @property def metadata(self) -> LabelMeta: """Return the metadata of the image.""" @@ -277,6 +299,7 @@ def get_label( return Label( store=self._label_group[name], + name=name, path=path, pixel_size=pixel_size, highest_resolution=highest_resolution, diff --git a/src/ngio/core/ngff_image.py b/src/ngio/core/ngff_image.py index 3dd645d..7d584f9 100644 --- a/src/ngio/core/ngff_image.py +++ b/src/ngio/core/ngff_image.py @@ -1,5 +1,7 @@ """Abstract class for handling OME-NGFF images.""" +from typing import Any + import dask.array as da import numpy as np @@ -37,6 +39,19 @@ def __init__( ngio_logger.info(f"Opened image located in store: {store}") ngio_logger.info(f"- Image number of levels: {self.num_levels}") + def __repr__(self) -> str: + """Get the string representation of the image.""" + name = "NGFFImage(" + len_name = len(name) + return ( + f"{name}" + f"store={self.store}, \n" + f"{' ':>{len_name}}paths={self.levels_paths}, \n" + f"{' ':>{len_name}}labels={self.label.list()}, \n" + f"{' ':>{len_name}}tables={self.table.list()}, \n" + ")" + ) + @property def image_meta(self) -> ImageMeta: """Get the image metadata.""" @@ -138,48 +153,63 @@ def _compute_percentiles( return starts, ends - def set_omero( + def lazy_init_omero( self, - labels: list[str], - wavelengths: list[str] | None = None, + labels: list[str] | int | None = None, + wavelength_ids: list[str] | None = None, colors: list[str] | None = None, - adjust_window: bool = True, - start_percentile: int = 5, - end_percentile: int = 95, active: list[bool] | None = None, - write: bool = True, + start_percentile: float | None = 1, + end_percentile: float | None = 99, + data_type: Any = np.uint16, + consolidate: bool = True, ) -> None: """Set the OMERO metadata for the image. Args: - labels (list[str]): The labels of the channels. - wavelengths (list[str], optional): The wavelengths of the channels. - colors (list[str], optional): The colors of the channels. - adjust_window (bool, optional): Whether to adjust the window. - start_percentile (int, optional): The start percentile. - end_percentile (int, optional): The end percentile. - active (list[bool], optional): Whether the channel is active. - write (bool, optional): Whether to write the metadata to the image. + labels (list[str] | int | None): The labels of the channels. + wavelength_ids (list[str] | None): The wavelengths of the channels. + colors (list[str] | None): The colors of the channels. + active (list[bool] | None): Whether the channels are active. + start_percentile (float | None): The start percentile for computing the data + range. If None, the start is the same as the min value of the data type. + end_percentile (float | None): The end percentile for for computing the data + range. If None, the start is the same as the max value of the data type. + data_type (Any): The data type of the image. + consolidate (bool): Whether to consolidate the metadata. """ - image_ref = self.get_image() + if labels is None: + ref = self.get_image() + labels = ref.num_channels - if adjust_window: + if start_percentile is not None and end_percentile is not None: start, end = self._compute_percentiles( start_percentile=start_percentile, end_percentile=end_percentile ) + elif start_percentile is None and end_percentile is None: + raise ValueError("Both start and end percentiles cannot be None.") + elif end_percentile is None and start_percentile is not None: + raise ValueError( + "End percentile cannot be None if start percentile is not." + ) + else: + start, end = None, None - self.image_meta.omero_lazy_init( + self.image_meta.lazy_init_omero( labels=labels, - wavelength_ids=wavelengths, + wavelength_ids=wavelength_ids, colors=colors, - active=active, start=start, end=end, - data_type=image_ref.on_disk_array.dtype, + active=active, + data_type=data_type, ) + if consolidate: + self._image_meta.write_meta(self.image_meta) + def update_omero_window( - self, start_percentile: int = 5, end_percentile: int = 95 + self, start_percentile: int = 1, end_percentile: int = 99 ) -> None: """Update the OMERO window. diff --git a/src/ngio/tables/tables_group.py b/src/ngio/tables/tables_group.py index 2eaf9d5..17f76d2 100644 --- a/src/ngio/tables/tables_group.py +++ b/src/ngio/tables/tables_group.py @@ -59,6 +59,7 @@ def _find_table_impl( def _get_table_impl( group: zarr.Group, + name: str, validate_metadata: bool = True, table_type: TableType | None = None, validate_table: bool = True, @@ -77,6 +78,7 @@ def _get_table_impl( version = common_meta.fractal_table_version return _find_table_impl(table_type=table_type, version=version)( group=group, + name=name, validate_metadata=validate_metadata, validate_table=validate_table, index_key=index_key, @@ -207,6 +209,7 @@ def get_table( return _get_table_impl( group=self._table_group[name], + name=name, validate_metadata=validate_metadata, table_type=table_type, validate_table=validate_table, diff --git a/src/ngio/tables/v1/_generic_table.py b/src/ngio/tables/v1/_generic_table.py index d32d79a..57ca60b 100644 --- a/src/ngio/tables/v1/_generic_table.py +++ b/src/ngio/tables/v1/_generic_table.py @@ -87,6 +87,26 @@ def __init__( ) self.state = State.CONSOLIDATED + @property + def root_path(self) -> str: + """Return the path of the root group. + + This is in general the path of the NGFFImage. + """ + return str(self._table_group.store.path) + + @property + def group_path(self) -> str: + """Return the path of the group. + + This is the path of the group containing the table. + """ + root = self.root_path + if root.endswith("/"): + root = root[:-1] + + return f"{root}/{self._table_group.path}" + @property def table(self) -> pd.DataFrame: """Return the ROI table as a DataFrame.""" @@ -135,11 +155,6 @@ def group(self) -> zarr.Group: """Return the group of the table.""" return self._table_group - @property - def group_path(self) -> Path: - """Return the path of the group.""" - return Path(self._table_group.store.path) / self._table_group.path - @property def index_key(self) -> str: """Return the index key of the table.""" diff --git a/src/ngio/tables/v1/feature_tables.py b/src/ngio/tables/v1/feature_tables.py index 6af8a5a..0a74d44 100644 --- a/src/ngio/tables/v1/feature_tables.py +++ b/src/ngio/tables/v1/feature_tables.py @@ -32,6 +32,7 @@ class FeatureTableV1: def __init__( self, group: zarr.Group, + name: str, validate_metadata: bool = True, validate_table: bool = True, index_key: str | None = None, @@ -41,6 +42,7 @@ def __init__( Args: group (zarr.Group): The group containing the ROI table. + name (str): The name of the ROI table. validate_metadata (bool): If True, the metadata is validated. validate_table (bool): If True, the table is validated. index_key (str): The column name to use as the index of the DataFrame. @@ -62,6 +64,19 @@ def __init__( validators=validators, ) + self._name = name + + def __repr__(self) -> str: + """Return the string representation of the class.""" + region_path = self.meta.region["path"] + label_name = region_path.split("/")[-1] + return ( + f"FeatureTable(name={self.name}, " + f"source_label={label_name}, " + f"features={self.table.columns.to_list()}, " + f"num_labels={len(self.table.index)})" + ) + @classmethod def _new( cls, @@ -98,7 +113,22 @@ def _new( index_type="int", meta=meta, ) - return cls(group=group) + return cls(group=group, name=name) + + @property + def name(self) -> str: + """Return the name of the feature table.""" + return self._name + + @property + def root_path(self) -> str: + """Return the path of the root group.""" + return self._table_handler.root_path + + @property + def group_path(self) -> str: + """Return the path of the group.""" + return self._table_handler.group_path @property def meta(self) -> FeatureTableV1Meta: diff --git a/src/ngio/tables/v1/masking_roi_tables.py b/src/ngio/tables/v1/masking_roi_tables.py index 475b282..2497c4c 100644 --- a/src/ngio/tables/v1/masking_roi_tables.py +++ b/src/ngio/tables/v1/masking_roi_tables.py @@ -46,6 +46,7 @@ class MaskingROITableV1: def __init__( self, group: zarr.Group, + name: str, validate_metadata: bool = True, validate_table: bool = True, index_key: str | None = None, @@ -55,6 +56,7 @@ def __init__( Args: group (zarr.Group): The group containing the ROI table. + name (str): The name of the ROI table. validate_metadata (bool): If True, the metadata is validated. validate_table (bool): If True, the table is validated. index_key (str): The column name to use as the index of the DataFrame. @@ -75,6 +77,17 @@ def __init__( index_type="int", validators=validators, ) + self._name = name + + def __repr__(self) -> str: + """Return the string representation of the class.""" + region_path = self.meta.region["path"] + label_name = region_path.split("/")[-1] + return ( + f"MaskingROITable(name={self.name}, " + f"source_label={label_name}, " + f"num_labels={len(self.table.index)})" + ) @classmethod def _new( @@ -115,7 +128,22 @@ def _new( index_type="int", meta=meta, ) - return cls(group=group) + return cls(group=group, name=name) + + @property + def name(self) -> str: + """Return the name of the ROI table.""" + return self._name + + @property + def root_path(self) -> str: + """Return the path of the root group.""" + return self._table_handler.root_path + + @property + def group_path(self) -> str: + """Return the path of the group.""" + return self._table_handler.group_path @property def meta(self) -> MaskingROITableV1Meta: diff --git a/src/ngio/tables/v1/roi_tables.py b/src/ngio/tables/v1/roi_tables.py index 09bca7d..8c415a0 100644 --- a/src/ngio/tables/v1/roi_tables.py +++ b/src/ngio/tables/v1/roi_tables.py @@ -62,6 +62,7 @@ class ROITableV1: def __init__( self, group: zarr.Group, + name: str, validate_metadata: bool = True, validate_table: bool = True, index_key: str | None = None, @@ -71,6 +72,7 @@ def __init__( Args: group (zarr.Group): The group containing the ROI table. + name (str): The name of the ROI table. validate_metadata (bool): If True, the metadata is validated. validate_table (bool): If True, the table is validated. index_key (str): The column name to use as the index of the DataFrame. @@ -95,6 +97,7 @@ def __init__( self._table_handler = BaseTable( group=group, index_key=index_key, index_type="str", validators=validators ) + self._name = name @classmethod def _new( @@ -134,7 +137,26 @@ def _new( index_type="str", meta=meta, ) - return cls(group=group) + return cls(group=group, name=name) + + def __repr__(self) -> str: + """Return the string representation of the ROI table.""" + return f"ROITable(name={self.name}, num_rois={len(self.table.index)})" + + @property + def name(self) -> str: + """Return the name of the ROI table.""" + return self._name + + @property + def root_path(self) -> str: + """Return the path of the root group.""" + return self._table_handler.root_path + + @property + def group_path(self) -> str: + """Return the path of the group.""" + return self._table_handler.group_path @property def meta(self) -> ROITableV1Meta: From 853f40f285f91770cf06c8302ec714f5e8e2db21 Mon Sep 17 00:00:00 2001 From: lorenzo Date: Wed, 13 Nov 2024 16:39:07 +0100 Subject: [PATCH 2/5] fix minor bugs --- docs/notebooks/processing.ipynb | 2 +- src/ngio/core/roi.py | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/notebooks/processing.ipynb b/docs/notebooks/processing.ipynb index 36d08b2..b32cbbb 100644 --- a/docs/notebooks/processing.ipynb +++ b/docs/notebooks/processing.ipynb @@ -56,7 +56,7 @@ "\n", "mip_ngff = ngff_image.derive_new_image(\"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0_mip\",\n", " name=\"MIP\",\n", - " shape=(1, 1, 2160, 5120))" + " on_disk_shape=(1, 1, 2160, 5120))" ] }, { diff --git a/src/ngio/core/roi.py b/src/ngio/core/roi.py index f49b027..8f02a35 100644 --- a/src/ngio/core/roi.py +++ b/src/ngio/core/roi.py @@ -13,7 +13,7 @@ from ngio.ngff_meta.fractal_image_meta import PixelSize, SpaceUnits -def _to_raster(value: float, pixel_size: PixelSize, max_shape: int) -> int: +def _to_raster(value: float, pixel_size: float, max_shape: int) -> int: """Convert to raster coordinates.""" round_value = int(np.round(value / pixel_size)) return min(round_value, max_shape) @@ -40,13 +40,17 @@ def to_raster_coo( self, pixel_size: PixelSize, dimensions: Dimensions ) -> "RasterCooROI": """Convert to raster coordinates.""" + dim_x = dimensions.get("x") + dim_y = dimensions.get("y") + dim_z = dimensions.get("z", 1) + return RasterCooROI( - x=_to_raster(self.x, pixel_size.x, dimensions.x), - y=_to_raster(self.y, pixel_size.y, dimensions.y), - z=_to_raster(self.z, pixel_size.z, dimensions.z), - x_length=_to_raster(self.x_length, pixel_size.x, dimensions.x), - y_length=_to_raster(self.y_length, pixel_size.y, dimensions.y), - z_length=_to_raster(self.z_length, pixel_size.z, dimensions.z), + x=_to_raster(self.x, pixel_size.x, dim_x), + y=_to_raster(self.y, pixel_size.y, dim_y), + z=_to_raster(self.z, pixel_size.z, dim_z), + x_length=_to_raster(self.x_length, pixel_size.x, dim_x), + y_length=_to_raster(self.y_length, pixel_size.y, dim_y), + z_length=_to_raster(self.z_length, pixel_size.z, dim_z), original_roi=self, ) @@ -64,10 +68,6 @@ class RasterCooROI(BaseModel): def to_world_coo_roi(self, pixel_size: PixelSize) -> WorldCooROI: """Convert to world coordinates.""" - if self.field_index is None: - raise ValueError( - "Field index must be provided to convert to world coordinates roi." - ) return WorldCooROI( x=_to_world(self.x, pixel_size.x), y=_to_world(self.y, pixel_size.y), From 247996177cc6c6feece3de34dec553f285c9caa6 Mon Sep 17 00:00:00 2001 From: lorenzo Date: Wed, 13 Nov 2024 17:27:37 +0100 Subject: [PATCH 3/5] expand testing --- docs/notebooks/basic_usage.ipynb | 7 +++++ src/ngio/core/ngff_image.py | 35 ++++++++++++++++++------ src/ngio/ngff_meta/fractal_image_meta.py | 22 +++++++++++---- tests/core/test_ngff_image.py | 13 ++++++++- tests/core/test_roi.py | 20 ++++++++++++++ 5 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 tests/core/test_roi.py diff --git a/docs/notebooks/basic_usage.ipynb b/docs/notebooks/basic_usage.ipynb index 8734acc..64983d1 100644 --- a/docs/notebooks/basic_usage.ipynb +++ b/docs/notebooks/basic_usage.ipynb @@ -181,6 +181,13 @@ "print(f\"{new_ngff_image.levels_paths=}\")\n", "print(f\"{new_ngff_image.num_levels=}\")" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/src/ngio/core/ngff_image.py b/src/ngio/core/ngff_image.py index 7d584f9..07e5bc0 100644 --- a/src/ngio/core/ngff_image.py +++ b/src/ngio/core/ngff_image.py @@ -141,11 +141,8 @@ def _compute_percentiles( starts, ends = [], [] for c in range(num_c): data = lowest_res_image.get_array(c=c, mode="dask").ravel() - _start_percentile = da.percentile( - data, start_percentile, method="nearest" - ).compute() - _end_percentile = da.percentile( - data, end_percentile, method="nearest" + _start_percentile, _end_percentile = da.percentile( + data, [start_percentile, end_percentile], method="nearest" ).compute() starts.append(_start_percentile) @@ -209,7 +206,11 @@ def lazy_init_omero( self._image_meta.write_meta(self.image_meta) def update_omero_window( - self, start_percentile: int = 1, end_percentile: int = 99 + self, + start_percentile: int = 1, + end_percentile: int = 99, + min_value: int | float | None = None, + max_value: int | float | None = None, ) -> None: """Update the OMERO window. @@ -218,6 +219,8 @@ def update_omero_window( Args: start_percentile (int): The start percentile. end_percentile (int): The end percentile + min_value (int | float | None): The minimum value of the window. + max_value (int | float | None): The maximum value of the window. """ start, ends = self._compute_percentiles( @@ -226,7 +229,21 @@ def update_omero_window( meta = self.image_meta ref_image = self.get_image() - max_dtype = np.iinfo(ref_image.on_disk_array.dtype).max + for func in [np.iinfo, np.finfo]: + try: + type_max = func(ref_image.on_disk_array.dtype).max + type_min = func(ref_image.on_disk_array.dtype).min + break + except ValueError: + continue + else: + raise ValueError("Data type not recognized.") + + if min_value is None: + min_value = type_min + if max_value is None: + max_value = type_max + num_c = ref_image.dimensions.get("c", 1) if meta.omero is None: @@ -246,8 +263,8 @@ def update_omero_window( ): channel.channel_visualisation.start = s channel.channel_visualisation.end = e - channel.channel_visualisation.min = 0 - channel.channel_visualisation.max = max_dtype + channel.channel_visualisation.min = min_value + channel.channel_visualisation.max = max_value ngio_logger.info( f"Updated window for channel {channel.label}. " diff --git a/src/ngio/ngff_meta/fractal_image_meta.py b/src/ngio/ngff_meta/fractal_image_meta.py index a858b06..89f85ab 100644 --- a/src/ngio/ngff_meta/fractal_image_meta.py +++ b/src/ngio/ngff_meta/fractal_image_meta.py @@ -175,12 +175,22 @@ def lazy_init( data_type(Any): The data type of the channel. active(bool): Whether the channel should be shown by default. """ - start = start if start is not None else np.iinfo(data_type).min - end = end if end is not None else np.iinfo(data_type).max + for func in [np.iinfo, np.finfo]: + try: + min_value = func(data_type).min + max_value = func(data_type).max + break + except ValueError: + continue + else: + raise ValueError(f"Invalid data type {data_type}.") + + start = start if start is not None else min_value + end = end if end is not None else max_value return ChannelVisualisation( color=color, - min=np.iinfo(data_type).min, - max=np.iinfo(data_type).max, + min=min_value, + max=max_value, start=start, end=end, active=active, @@ -246,7 +256,9 @@ def _check_elements(elements: Collection[T], expected_type: Any) -> Collection[T for element in elements: if not isinstance(element, expected_type): - raise ValueError(f"All elements must be of the same type {expected_type}.") + raise ValueError( + f"All elements must be of the same type {expected_type}. Got {element}." + ) return elements diff --git a/tests/core/test_ngff_image.py b/tests/core/test_ngff_image.py index 591b1ea..7c8efb3 100644 --- a/tests/core/test_ngff_image.py +++ b/tests/core/test_ngff_image.py @@ -1,9 +1,13 @@ +from pathlib import Path + + class TestNgffImage: - def test_ngff_image(self, ome_zarr_image_v04_path): + def test_ngff_image(self, ome_zarr_image_v04_path: Path) -> None: from ngio.core.ngff_image import NgffImage ngff_image = NgffImage(ome_zarr_image_v04_path) image_handler = ngff_image.get_image(path="0") + ngff_image.__repr__() assert ngff_image.num_levels == 5 assert ngff_image.levels_paths == ["0", "1", "2", "3", "4"] @@ -27,3 +31,10 @@ def test_ngff_image(self, ome_zarr_image_v04_path): assert ( new_image_handler.on_disk_array.chunks == image_handler.on_disk_array.chunks ) + new_ngff_image.lazy_init_omero( + labels=3, + wavelength_ids=["A01_C01", "A02_C02", "A03_C03"], + consolidate=True, + ) + + new_ngff_image.update_omero_window(start_percentile=1.1, end_percentile=98.9) diff --git a/tests/core/test_roi.py b/tests/core/test_roi.py new file mode 100644 index 0000000..6ff0847 --- /dev/null +++ b/tests/core/test_roi.py @@ -0,0 +1,20 @@ +class TestRoi: + def test_roi_conversion(self) -> None: + """Test the conversion between world and raster coordinates.""" + from ngio.core.roi import Dimensions, PixelSize, RasterCooROI, WorldCooROI + + w_roi = WorldCooROI(x=0, y=0, z=0, x_length=10, y_length=10, z_length=10) + + dim = Dimensions( + on_disk_shape=(10, 10, 10), axes_names=["z", "y", "x"], axes_order=[0, 1, 2] + ) + + r_roi = w_roi.to_raster_coo(PixelSize(x=0.1, y=0.1, z=0.1), dim) + + assert (r_roi.x, r_roi.y, r_roi.z) == (0, 0, 0) + + r_roi_2 = RasterCooROI( + x=0, y=0, z=0, x_length=10, y_length=10, z_length=10, original_roi=w_roi + ) + + assert r_roi_2.model_dump() == r_roi.model_dump() From e65c5fa8c8381fbce3d9090ef14fafc74135e667 Mon Sep 17 00:00:00 2001 From: lorenzo Date: Wed, 13 Nov 2024 17:33:48 +0100 Subject: [PATCH 4/5] expand tests --- tests/core/test_image_handler.py | 1 + tests/core/test_image_like_handler.py | 1 + tests/core/test_label_handler.py | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/core/test_image_handler.py b/tests/core/test_image_handler.py index 3262162..d729a67 100644 --- a/tests/core/test_image_handler.py +++ b/tests/core/test_image_handler.py @@ -6,6 +6,7 @@ def test_ngff_image(self, ome_zarr_image_v04_path): image_handler = Image(store=ome_zarr_image_v04_path, path="0") + assert image_handler.array_path == f"{ome_zarr_image_v04_path}/0" assert image_handler.channel_labels == ["DAPI", "nanog", "Lamin B1"] assert image_handler.get_channel_idx(label="DAPI") == 0 assert image_handler.get_channel_idx(wavelength_id="A01_C01") == 0 diff --git a/tests/core/test_image_like_handler.py b/tests/core/test_image_like_handler.py index c478360..b9ab7f5 100644 --- a/tests/core/test_image_like_handler.py +++ b/tests/core/test_image_like_handler.py @@ -12,6 +12,7 @@ def test_ngff_image(self, ome_zarr_image_v04_path: Path) -> None: store = ome_zarr_image_v04_path image_handler = ImageLike(store=store, path="0") + assert image_handler.array_path == f"{ome_zarr_image_v04_path}/0" assert image_handler.path == "0" assert image_handler.pixel_size.zyx == (1.0, 0.1625, 0.1625) assert image_handler.axes_names == ["c", "z", "y", "x"] diff --git a/tests/core/test_label_handler.py b/tests/core/test_label_handler.py index 6f40660..d71ec36 100644 --- a/tests/core/test_label_handler.py +++ b/tests/core/test_label_handler.py @@ -1,5 +1,8 @@ +from pathlib import Path + + class TestLabel: - def test_label_image(self, ome_zarr_image_v04_path): + def test_label_image(self, ome_zarr_image_v04_path: Path) -> None: import dask.array as da import numpy as np @@ -9,6 +12,7 @@ def test_label_image(self, ome_zarr_image_v04_path): image_handler = ngff_image.get_image(path="0") label_handler = ngff_image.label.derive(name="label") + assert label_handler.array_path == f"{ome_zarr_image_v04_path}/labels/label/0" assert ngff_image.label.list() == ["label"] assert "c" not in label_handler.axes_names assert label_handler.shape == image_handler.shape[1:] From 75b9eeca5e1b55548e64787802a68fec7302ee3a Mon Sep 17 00:00:00 2001 From: lorenzo Date: Wed, 13 Nov 2024 17:49:03 +0100 Subject: [PATCH 5/5] expand testing --- tests/core/test_image_handler.py | 6 +++- tests/core/test_label_handler.py | 1 + tests/tables/test_table_group.py | 5 ++- tests/tables/test_v1_tables.py | 55 ++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 tests/tables/test_v1_tables.py diff --git a/tests/core/test_image_handler.py b/tests/core/test_image_handler.py index d729a67..a90a985 100644 --- a/tests/core/test_image_handler.py +++ b/tests/core/test_image_handler.py @@ -1,10 +1,14 @@ +from pathlib import Path + + class TestImageHandler: - def test_ngff_image(self, ome_zarr_image_v04_path): + def test_ngff_image(self, ome_zarr_image_v04_path: Path) -> None: import numpy as np from ngio.core.image_handler import Image image_handler = Image(store=ome_zarr_image_v04_path, path="0") + image_handler.__repr__() assert image_handler.array_path == f"{ome_zarr_image_v04_path}/0" assert image_handler.channel_labels == ["DAPI", "nanog", "Lamin B1"] diff --git a/tests/core/test_label_handler.py b/tests/core/test_label_handler.py index d71ec36..8e5805e 100644 --- a/tests/core/test_label_handler.py +++ b/tests/core/test_label_handler.py @@ -11,6 +11,7 @@ def test_label_image(self, ome_zarr_image_v04_path: Path) -> None: ngff_image = NgffImage(ome_zarr_image_v04_path) image_handler = ngff_image.get_image(path="0") label_handler = ngff_image.label.derive(name="label") + label_handler.__repr__() assert label_handler.array_path == f"{ome_zarr_image_v04_path}/labels/label/0" assert ngff_image.label.list() == ["label"] diff --git a/tests/tables/test_table_group.py b/tests/tables/test_table_group.py index 704e40b..7775079 100644 --- a/tests/tables/test_table_group.py +++ b/tests/tables/test_table_group.py @@ -1,5 +1,8 @@ +from pathlib import Path + + class TestTableGroup: - def test_table_group(self, ome_zarr_image_v04_path): + def test_table_group(self, ome_zarr_image_v04_path: Path) -> None: from ngio.core.ngff_image import NgffImage ngff_image = NgffImage(ome_zarr_image_v04_path) diff --git a/tests/tables/test_v1_tables.py b/tests/tables/test_v1_tables.py new file mode 100644 index 0000000..9513ea5 --- /dev/null +++ b/tests/tables/test_v1_tables.py @@ -0,0 +1,55 @@ +from pathlib import Path + + +class TestTables: + def test_roi_table(self, ome_zarr_image_v04_path: Path) -> None: + from ngio.core.ngff_image import NgffImage + from ngio.core.roi import WorldCooROI + + ngff_image = NgffImage(ome_zarr_image_v04_path) + + roi_table = ngff_image.table.new( + name="roi_table", + table_type="roi_table", + overwrite=False, + ) + roi_table.__repr__() + roi_table.set_rois( + [ + WorldCooROI( + x=0, + y=0, + z=0, + x_length=10, + y_length=10, + z_length=10, + infos={"FieldIndex": "FOV1"}, + ) + ] + ) + + def test_masking_roi_table(self, ome_zarr_image_v04_path: Path) -> None: + from ngio.core.ngff_image import NgffImage + + ngff_image = NgffImage(ome_zarr_image_v04_path) + + masking_roi_table = ngff_image.table.new( + name="masking_roi_table", + table_type="masking_roi_table", + label_image="region", + overwrite=False, + ) + masking_roi_table.__repr__() + + def test_feature_table(self, ome_zarr_image_v04_path: Path) -> None: + from ngio.core.ngff_image import NgffImage + + ngff_image = NgffImage(ome_zarr_image_v04_path) + + feature_table = ngff_image.table.new( + name="feat_table", + table_type="feature_table", + label_image="region", + overwrite=True, + ) + feature_table.__repr__()