webknossos.dataset.properties
View Source
from os.path import isfile, join from pathlib import Path from typing import Any, Callable, Dict, List, Optional, Tuple, Union import attr import cattr import numpy as np import wkw from cattr.gen import make_dict_structure_fn, make_dict_unstructure_fn, override from webknossos.geometry import BoundingBox, Mag def _extract_num_channels( num_channels_in_properties: Optional[int], path: Path, layer: str, mag: Optional[Union[int, Mag]], ) -> int: # if a wk dataset is not created with this API, then it most likely doesn't have the attribute 'numChannels' in the # datasource-properties.json. In this case we need to extract the number of channels from the 'header.wkw'. if num_channels_in_properties is not None: return num_channels_in_properties if mag is None: # Unable to extract the 'num_channels' from the 'header.wkw' if the dataset has no magnifications. # This should never be the case because wkw-datasets that are created without this API always have a magnification. raise RuntimeError( "Cannot extract the number of channels of a dataset without a properties file and without any magnifications" ) mag = Mag(mag) wkw_ds_file_path = join(path, layer, mag.to_layer_name()) if not isfile(join(wkw_ds_file_path, "header.wkw")): raise Exception( f"The dataset you are trying to open does not have the attribute 'numChannels' for layer {layer}. " f"However, this attribute is necessary. To mitigate this problem, it was tried to locate " f"the file {wkw_ds_file_path} to extract the num_channels from there. " f"Since this file does not exist, the attempt to open the dataset failed. " f"Please add the attribute manually to solve the problem. " f"If the layer does not contain any data, you can also delete the layer and add it again." ) wkw_ds = wkw.Dataset.open(wkw_ds_file_path) return wkw_ds.header.num_channels _properties_floating_type_to_python_type: Dict[Union[str, type], np.dtype] = { "float": np.dtype("float32"), # np.float: np.dtype("float32"), # np.float is an alias for float float: np.dtype("float32"), "double": np.dtype("float64"), } _python_floating_type_to_properties_type = { "float32": "float", "float64": "double", } def _snake_to_camel_case(snake_case_name: str) -> str: parts = snake_case_name.split("_") return parts[0] + "".join(part.title() for part in parts[1:]) # --- View configuration -------------------- @attr.define class DatasetViewConfiguration: """ Stores information on how the dataset is shown in webknossos by default. """ four_bit: Optional[bool] = None interpolation: Optional[bool] = None render_missing_data_black: Optional[bool] = None loading_strategy: Optional[str] = None segmentation_pattern_opacity: Optional[int] = None zoom: Optional[float] = None position: Optional[Tuple[int, int, int]] = None rotation: Optional[Tuple[int, int, int]] = None @attr.define class LayerViewConfiguration: """ Stores information on how the dataset is shown in webknossos by default. """ color: Optional[Tuple[int, int, int]] = None alpha: Optional[float] = None intensity_range: Optional[Tuple[float, float]] = None min: Optional[float] = None # pylint: disable=redefined-builtin max: Optional[float] = None # pylint: disable=redefined-builtin is_disabled: Optional[bool] = None is_inverted: Optional[bool] = None is_in_edit_mode: Optional[bool] = None # --- Property -------------------- @attr.define class MagViewProperties: resolution: Union[int, Mag] cube_length: int @attr.define class LayerProperties: name: str category: str bounding_box: BoundingBox element_class: str wkw_resolutions: List[MagViewProperties] data_format: str num_channels: Optional[int] = None default_view_configuration: Optional[LayerViewConfiguration] = None @attr.define class SegmentationLayerProperties(LayerProperties): largest_segment_id: int = -1 mappings: List[str] = [] @attr.define class DatasetProperties: id: Dict[str, str] scale: Tuple[float, float, float] data_layers: List[Union[SegmentationLayerProperties, LayerProperties]] default_view_configuration: Optional[DatasetViewConfiguration] = None # --- Converter -------------------- dataset_converter = cattr.Converter() # register (un-)structure hooks for non-attr-classes bbox_to_wkw: Callable[[BoundingBox], dict] = lambda o: o.as_wkw() dataset_converter.register_unstructure_hook(BoundingBox, bbox_to_wkw) dataset_converter.register_structure_hook( BoundingBox, lambda d, _: BoundingBox.from_wkw(d) ) mag_to_array: Callable[[Mag], List[int]] = lambda o: o.to_array() dataset_converter.register_unstructure_hook(Mag, mag_to_array) dataset_converter.register_structure_hook(Mag, lambda d, _: Mag(d)) # Register (un-)structure hooks for attr-classes to bring the data into the expected format. # The properties on disk (in datasource-properties.json) use camel case for the names of the attributes. # However, we use snake case for the attribute names in python. # This requires that the names of the attributes are renamed during (un-)structuring. # Additionally we only want to unstructure attributes which don't have the default value # (e.g. Layer.default_view_configuration has many attributes which are all optionally). for cls in [ DatasetProperties, LayerProperties, SegmentationLayerProperties, MagViewProperties, DatasetViewConfiguration, LayerViewConfiguration, ]: dataset_converter.register_unstructure_hook( cls, make_dict_unstructure_fn( cls, dataset_converter, **{ a.name: override( omit_if_default=True, rename=_snake_to_camel_case(a.name) ) for a in attr.fields(cls) # pylint: disable=not-an-iterable }, ), ) dataset_converter.register_structure_hook( cls, make_dict_structure_fn( cls, dataset_converter, **{ a.name: override(rename=_snake_to_camel_case(a.name)) for a in attr.fields(cls) # pylint: disable=not-an-iterable }, ), ) # Disambiguation of Unions only work automatically if the two attrs-classes have at least 1 unique attribute # This is not the case here because SegmentationLayerProperties inherits LayerProperties def disambiguate_layer_properties(obj: dict, _: Any) -> LayerProperties: if obj["category"] == "color": return dataset_converter.structure(obj, LayerProperties) elif obj["category"] == "segmentation": return dataset_converter.structure(obj, SegmentationLayerProperties) else: raise RuntimeError( "Failed to read the properties of a layer: the category has to be 'color' or 'segmentation'." ) dataset_converter.register_structure_hook( Union[SegmentationLayerProperties, LayerProperties], disambiguate_layer_properties ) def disambiguate_mag(obj: dict, _: Any) -> Mag: # This function is necessary because cattrs does not support unions of non-attrs objects out of the box return Mag(obj) dataset_converter.register_structure_hook(Union[int, Mag], disambiguate_mag) # Separate converter to unstructure LayerProperties # This is used to initialize SegmentationLayerProperties from LayerProperties # The important difference to the dataset_converter is that the names of the attributes stay the same while defaults are also omitted. layer_properties_converter = cattr.Converter() layer_properties_converter.register_unstructure_hook( # use register_unstructure_hook_func LayerProperties, make_dict_unstructure_fn( LayerProperties, layer_properties_converter, omit_if_default=True ), )
View Source
class DatasetViewConfiguration: """ Stores information on how the dataset is shown in webknossos by default. """ four_bit: Optional[bool] = None interpolation: Optional[bool] = None render_missing_data_black: Optional[bool] = None loading_strategy: Optional[str] = None segmentation_pattern_opacity: Optional[int] = None zoom: Optional[float] = None position: Optional[Tuple[int, int, int]] = None rotation: Optional[Tuple[int, int, int]] = None
Stores information on how the dataset is shown in webknossos by default.
#  
DatasetViewConfiguration(
four_bit: Union[bool, NoneType] = None,
interpolation: Union[bool, NoneType] = None,
render_missing_data_black: Union[bool, NoneType] = None,
loading_strategy: Union[str, NoneType] = None,
segmentation_pattern_opacity: Union[int, NoneType] = None,
zoom: Union[float, NoneType] = None,
position: Union[Tuple[int, int, int], NoneType] = None,
rotation: Union[Tuple[int, int, int], NoneType] = None
)
View Source
def __init__(self, four_bit=attr_dict['four_bit'].default, interpolation=attr_dict['interpolation'].default, render_missing_data_black=attr_dict['render_missing_data_black'].default, loading_strategy=attr_dict['loading_strategy'].default, segmentation_pattern_opacity=attr_dict['segmentation_pattern_opacity'].default, zoom=attr_dict['zoom'].default, position=attr_dict['position'].default, rotation=attr_dict['rotation'].default): _setattr = _cached_setattr.__get__(self, self.__class__) _setattr('four_bit', four_bit) _setattr('interpolation', interpolation) _setattr('render_missing_data_black', render_missing_data_black) _setattr('loading_strategy', loading_strategy) _setattr('segmentation_pattern_opacity', segmentation_pattern_opacity) _setattr('zoom', zoom) _setattr('position', position) _setattr('rotation', rotation)
Method generated by attrs for class DatasetViewConfiguration.
#  
interpolation: Union[bool, NoneType] = <member 'interpolation' of 'DatasetViewConfiguration' objects>
#  
render_missing_data_black: Union[bool, NoneType] = <member 'render_missing_data_black' of 'DatasetViewConfiguration' objects>
#  
loading_strategy: Union[str, NoneType] = <member 'loading_strategy' of 'DatasetViewConfiguration' objects>
#  
segmentation_pattern_opacity: Union[int, NoneType] = <member 'segmentation_pattern_opacity' of 'DatasetViewConfiguration' objects>
#  
position: Union[Tuple[int, int, int], NoneType] = <member 'position' of 'DatasetViewConfiguration' objects>
#  
rotation: Union[Tuple[int, int, int], NoneType] = <member 'rotation' of 'DatasetViewConfiguration' objects>
View Source
class LayerViewConfiguration: """ Stores information on how the dataset is shown in webknossos by default. """ color: Optional[Tuple[int, int, int]] = None alpha: Optional[float] = None intensity_range: Optional[Tuple[float, float]] = None min: Optional[float] = None # pylint: disable=redefined-builtin max: Optional[float] = None # pylint: disable=redefined-builtin is_disabled: Optional[bool] = None is_inverted: Optional[bool] = None is_in_edit_mode: Optional[bool] = None
Stores information on how the dataset is shown in webknossos by default.
#  
LayerViewConfiguration(
color: Union[Tuple[int, int, int], NoneType] = None,
alpha: Union[float, NoneType] = None,
intensity_range: Union[Tuple[float, float], NoneType] = None,
min: Union[float, NoneType] = None,
max: Union[float, NoneType] = None,
is_disabled: Union[bool, NoneType] = None,
is_inverted: Union[bool, NoneType] = None,
is_in_edit_mode: Union[bool, NoneType] = None
)
View Source
def __init__(self, color=attr_dict['color'].default, alpha=attr_dict['alpha'].default, intensity_range=attr_dict['intensity_range'].default, min=attr_dict['min'].default, max=attr_dict['max'].default, is_disabled=attr_dict['is_disabled'].default, is_inverted=attr_dict['is_inverted'].default, is_in_edit_mode=attr_dict['is_in_edit_mode'].default): _setattr = _cached_setattr.__get__(self, self.__class__) _setattr('color', color) _setattr('alpha', alpha) _setattr('intensity_range', intensity_range) _setattr('min', min) _setattr('max', max) _setattr('is_disabled', is_disabled) _setattr('is_inverted', is_inverted) _setattr('is_in_edit_mode', is_in_edit_mode)
Method generated by attrs for class LayerViewConfiguration.
#  
color: Union[Tuple[int, int, int], NoneType] = <member 'color' of 'LayerViewConfiguration' objects>
#  
intensity_range: Union[Tuple[float, float], NoneType] = <member 'intensity_range' of 'LayerViewConfiguration' objects>
#  
is_disabled: Union[bool, NoneType] = <member 'is_disabled' of 'LayerViewConfiguration' objects>
#  
is_inverted: Union[bool, NoneType] = <member 'is_inverted' of 'LayerViewConfiguration' objects>
#  
is_in_edit_mode: Union[bool, NoneType] = <member 'is_in_edit_mode' of 'LayerViewConfiguration' objects>
View Source
class MagViewProperties: resolution: Union[int, Mag] cube_length: int
View Source
def __init__(self, resolution, cube_length): _setattr = _cached_setattr.__get__(self, self.__class__) _setattr('resolution', resolution) _setattr('cube_length', cube_length)
Method generated by attrs for class MagViewProperties.
#  
resolution: Union[int, webknossos.geometry.mag.Mag] = <member 'resolution' of 'MagViewProperties' objects>
View Source
class LayerProperties: name: str category: str bounding_box: BoundingBox element_class: str wkw_resolutions: List[MagViewProperties] data_format: str num_channels: Optional[int] = None default_view_configuration: Optional[LayerViewConfiguration] = None
#  
LayerProperties(
name: str,
category: str,
bounding_box: webknossos.geometry.bounding_box.BoundingBox,
element_class: str,
wkw_resolutions: List[webknossos.dataset.properties.MagViewProperties],
data_format: str,
num_channels: Union[int, NoneType] = None,
default_view_configuration: Union[webknossos.dataset.properties.LayerViewConfiguration, NoneType] = None
)
View Source
def __init__(self, name, category, bounding_box, element_class, wkw_resolutions, data_format, num_channels=attr_dict['num_channels'].default, default_view_configuration=attr_dict['default_view_configuration'].default): _setattr = _cached_setattr.__get__(self, self.__class__) _setattr('name', name) _setattr('category', category) _setattr('bounding_box', bounding_box) _setattr('element_class', element_class) _setattr('wkw_resolutions', wkw_resolutions) _setattr('data_format', data_format) _setattr('num_channels', num_channels) _setattr('default_view_configuration', default_view_configuration)
Method generated by attrs for class LayerProperties.
#  
bounding_box: webknossos.geometry.bounding_box.BoundingBox = <member 'bounding_box' of 'LayerProperties' objects>
#  
wkw_resolutions: List[webknossos.dataset.properties.MagViewProperties] = <member 'wkw_resolutions' of 'LayerProperties' objects>
#  
default_view_configuration: Union[webknossos.dataset.properties.LayerViewConfiguration, NoneType] = <member 'default_view_configuration' of 'LayerProperties' objects>
View Source
class SegmentationLayerProperties(LayerProperties): largest_segment_id: int = -1 mappings: List[str] = []
#  
SegmentationLayerProperties(
name: str,
category: str,
bounding_box: webknossos.geometry.bounding_box.BoundingBox,
element_class: str,
wkw_resolutions: List[webknossos.dataset.properties.MagViewProperties],
data_format: str,
num_channels: Union[int, NoneType] = None,
default_view_configuration: Union[webknossos.dataset.properties.LayerViewConfiguration, NoneType] = None,
largest_segment_id: int = -1,
mappings: List[str] = []
)
View Source
def __init__(self, name, category, bounding_box, element_class, wkw_resolutions, data_format, num_channels=attr_dict['num_channels'].default, default_view_configuration=attr_dict['default_view_configuration'].default, largest_segment_id=attr_dict['largest_segment_id'].default, mappings=attr_dict['mappings'].default): _setattr = _cached_setattr.__get__(self, self.__class__) _setattr('name', name) _setattr('category', category) _setattr('bounding_box', bounding_box) _setattr('element_class', element_class) _setattr('wkw_resolutions', wkw_resolutions) _setattr('data_format', data_format) _setattr('num_channels', num_channels) _setattr('default_view_configuration', default_view_configuration) _setattr('largest_segment_id', largest_segment_id) _setattr('mappings', mappings)
Method generated by attrs for class SegmentationLayerProperties.
#  
largest_segment_id: int = <member 'largest_segment_id' of 'SegmentationLayerProperties' objects>
View Source
class DatasetProperties: id: Dict[str, str] scale: Tuple[float, float, float] data_layers: List[Union[SegmentationLayerProperties, LayerProperties]] default_view_configuration: Optional[DatasetViewConfiguration] = None
#  
DatasetProperties(
id: Dict[str, str],
scale: Tuple[float, float, float],
data_layers: List[Union[webknossos.dataset.properties.SegmentationLayerProperties, webknossos.dataset.properties.LayerProperties]],
default_view_configuration: Union[webknossos.dataset.properties.DatasetViewConfiguration, NoneType] = None
)
View Source
def __init__(self, id, scale, data_layers, default_view_configuration=attr_dict['default_view_configuration'].default): _setattr = _cached_setattr.__get__(self, self.__class__) _setattr('id', id) _setattr('scale', scale) _setattr('data_layers', data_layers) _setattr('default_view_configuration', default_view_configuration)
Method generated by attrs for class DatasetProperties.
#  
data_layers: List[Union[webknossos.dataset.properties.SegmentationLayerProperties, webknossos.dataset.properties.LayerProperties]] = <member 'data_layers' of 'DatasetProperties' objects>
#  
default_view_configuration: Union[webknossos.dataset.properties.DatasetViewConfiguration, NoneType] = <member 'default_view_configuration' of 'DatasetProperties' objects>
View Source
bbox_to_wkw: Callable[[BoundingBox], dict] = lambda o: o.as_wkw()
View Source
mag_to_array: Callable[[Mag], List[int]] = lambda o: o.to_array()
#  
def
disambiguate_layer_properties(obj: dict, _: Any) -> webknossos.dataset.properties.LayerProperties:
View Source
def disambiguate_layer_properties(obj: dict, _: Any) -> LayerProperties: if obj["category"] == "color": return dataset_converter.structure(obj, LayerProperties) elif obj["category"] == "segmentation": return dataset_converter.structure(obj, SegmentationLayerProperties) else: raise RuntimeError( "Failed to read the properties of a layer: the category has to be 'color' or 'segmentation'." )
View Source
def disambiguate_mag(obj: dict, _: Any) -> Mag: # This function is necessary because cattrs does not support unions of non-attrs objects out of the box return Mag(obj)
View Source
class LayerViewConfiguration: """ Stores information on how the dataset is shown in webknossos by default. """ color: Optional[Tuple[int, int, int]] = None alpha: Optional[float] = None intensity_range: Optional[Tuple[float, float]] = None min: Optional[float] = None # pylint: disable=redefined-builtin max: Optional[float] = None # pylint: disable=redefined-builtin is_disabled: Optional[bool] = None is_inverted: Optional[bool] = None is_in_edit_mode: Optional[bool] = None
Stores information on how the dataset is shown in webknossos by default.
#  
cls(
color: Union[Tuple[int, int, int], NoneType] = None,
alpha: Union[float, NoneType] = None,
intensity_range: Union[Tuple[float, float], NoneType] = None,
min: Union[float, NoneType] = None,
max: Union[float, NoneType] = None,
is_disabled: Union[bool, NoneType] = None,
is_inverted: Union[bool, NoneType] = None,
is_in_edit_mode: Union[bool, NoneType] = None
)
View Source
def __init__(self, color=attr_dict['color'].default, alpha=attr_dict['alpha'].default, intensity_range=attr_dict['intensity_range'].default, min=attr_dict['min'].default, max=attr_dict['max'].default, is_disabled=attr_dict['is_disabled'].default, is_inverted=attr_dict['is_inverted'].default, is_in_edit_mode=attr_dict['is_in_edit_mode'].default): _setattr = _cached_setattr.__get__(self, self.__class__) _setattr('color', color) _setattr('alpha', alpha) _setattr('intensity_range', intensity_range) _setattr('min', min) _setattr('max', max) _setattr('is_disabled', is_disabled) _setattr('is_inverted', is_inverted) _setattr('is_in_edit_mode', is_in_edit_mode)
Method generated by attrs for class LayerViewConfiguration.
#  
color: Union[Tuple[int, int, int], NoneType] = <member 'color' of 'LayerViewConfiguration' objects>
#  
intensity_range: Union[Tuple[float, float], NoneType] = <member 'intensity_range' of 'LayerViewConfiguration' objects>
#  
is_disabled: Union[bool, NoneType] = <member 'is_disabled' of 'LayerViewConfiguration' objects>
#  
is_inverted: Union[bool, NoneType] = <member 'is_inverted' of 'LayerViewConfiguration' objects>
#  
is_in_edit_mode: Union[bool, NoneType] = <member 'is_in_edit_mode' of 'LayerViewConfiguration' objects>