def __init__( self, pixel_ticks: Union[Mapping[Axes, ArrayLike[int]], Mapping[str, ArrayLike[int]]], physical_ticks: Union[Mapping[Coordinates, ArrayLike[Number]], Mapping[str, ArrayLike[Number]]], masks: Sequence[MaskData], log: Optional[Log], ): self._pixel_ticks: Mapping[Axes, ArrayLike[int]] = _normalize_pixel_ticks(pixel_ticks) self._physical_ticks: Mapping[Coordinates, ArrayLike[Number]] = _normalize_physical_ticks( physical_ticks) self._masks: MutableMapping[int, MaskData] = {} self._log: Log = log or Log() for ix, mask_data in enumerate(masks): if mask_data.binary_mask.ndim not in (2, 3): raise TypeError(f"expected 2 or 3 dimensions; got {mask_data.binary_mask.ndim}") if mask_data.binary_mask.dtype != bool: raise ValueError(f"expected dtype of bool; got {mask_data.binary_mask.dtype}") self._masks[ix] = mask_data if len(self._pixel_ticks) != len(self._physical_ticks): raise ValueError( "pixel_ticks should have the same cardinality as physical_ticks") for axis, coord in zip(*_get_axes_names(len(self._pixel_ticks))): if axis not in self._pixel_ticks: raise ValueError(f"pixel ticks missing {axis.value} data") if coord not in self._physical_ticks: raise ValueError(f"physical coordinate ticks missing {coord.value} data") if len(self._pixel_ticks[axis]) != len(self._physical_ticks[coord]): raise ValueError( f"pixel ticks for {axis.name} does not have the same cardinality as physical " f"coordinates ticks for {coord.name}")
def __init__(self, data: xr.DataArray, tile_data: Optional[TileCollectionData] = None): self._data = data self._data_loaded = False self._tile_data = tile_data self._log: Log = Log()
def read_binary_mask(self, tf: tarfile.TarFile) -> bm.BinaryMaskCollection: log: Optional[Log] = None masks: MutableSequence[bm.MaskData] = [] pixel_ticks: Optional[Mapping[Axes, ArrayLike[int]]] = None physical_ticks: Optional[Mapping[Coordinates, ArrayLike[Number]]] = None while True: tarinfo: Optional[tarfile.TarInfo] = tf.next() if tarinfo is None: break # wrap it in a BytesIO object to ensure we never seek backwards. extracted_fh = tf.extractfile(tarinfo) if extracted_fh is None: raise ValueError(f"Unable to extract file {tarinfo.name}") byte_stream = io.BytesIO(extracted_fh.read()) if tarinfo.name == v0_0.LOG_FILENAME: string_stream = codecs.getreader("utf-8")(byte_stream) log = Log.decode(string_stream.read()) elif tarinfo.name == v0_0.PIXEL_TICKS_FILE: pixel_ticks = pickle.load(byte_stream) elif tarinfo.name == v0_0.PHYSICAL_TICKS_FILE: physical_ticks = pickle.load(byte_stream) elif tarinfo.name.startswith(v0_0.MASK_PREFIX): mask_on_disk: v0_0.MaskOnDisk = pickle.load(byte_stream) if not isinstance(mask_on_disk, v0_0.MaskOnDisk): raise TypeError( f"mask does not conform to expected mask structure") masks.append( bm.MaskData(mask_on_disk.binary_mask, mask_on_disk.offsets, None)) else: warnings.warn( f"Unexpected file in binary mask collection {tarinfo.name}", DataFormatWarning) if pixel_ticks is None: raise ValueError("pixel coordinates not found") if physical_ticks is None: raise ValueError("physical coordinates not found") return bm.BinaryMaskCollection(pixel_ticks, physical_ticks, masks, log)
def __init__(self, label_image: xr.DataArray): # verify that the data array has the required elements. if label_image.dtype.kind not in ("i", "u"): raise TypeError("label image should be an integer type") for axis in (Axes.X, Axes.Y): if axis.value not in label_image.coords: raise ValueError( f"label image should have an {axis.value} axis") expected_physical_coordinates: Tuple[Coordinates, ...] if label_image.ndim == 5: expected_physical_coordinates = (Coordinates.X, Coordinates.Y, Coordinates.Z) else: expected_physical_coordinates = (Coordinates.X, Coordinates.Y) for coord in expected_physical_coordinates: if coord.value not in label_image.coords: raise ValueError( f"label image should have a {coord.value} coordinates") self.label_image = label_image.copy(deep=False) if AttrKeys.DOCTYPE not in self.label_image.attrs: self.label_image.attrs[AttrKeys.DOCTYPE] = DOCTYPE_STRING if AttrKeys.LOG not in self.label_image.attrs: self.label_image.attrs[AttrKeys.LOG] = Log().encode()
def from_label_array_and_ticks( cls, array: np.ndarray, pixel_ticks: Optional[Union[Mapping[Axes, ArrayLike[int]], Mapping[str, ArrayLike[int]]]], physical_ticks: Union[Mapping[Coordinates, ArrayLike[Number]], Mapping[str, ArrayLike[Number]]], log: Optional[Log], ) -> "LabelImage": """Constructs a LabelImage from an array containing the labels, a set of physical coordinates, and an optional log of how this label image came to be. Parameters ---------- array : np.ndarray A 2D or 3D array containing the labels. The ordering of the axes must be Y, X for 2D images and ZPLANE, Y, X for 3D images. pixel_ticks : Optional[Union[Mapping[Axes, ArrayLike[int]], Mapping[str, ArrayLike[int]]]] A map from the axis to the values for that axis. For any axis that exist in the array but not in pixel_coordinates, the pixel coordinates are assigned from 0..N-1, where N is the size along that axis. physical_ticks : Union[Mapping[Coordinates, ArrayLike[Number]], Mapping[str, ArrayLike[Number]]] A map from the physical coordinate type to the values for axis. For 2D label images, X and Y physical coordinates must be provided. For 3D label images, Z physical coordinates must also be provided. log : Optional[Log] A log of how this label image came to be. """ # normalize the pixel coordinates to Mapping[Axes, ArrayLike[int]] pixel_ticks = _normalize_pixel_ticks(pixel_ticks) # normalize the physical coordinates to Mapping[Coordinates, ArrayLike[Number]] physical_ticks = _normalize_physical_ticks(physical_ticks) img_axes, img_coords = _get_axes_names(array.ndim) xr_axes = [axis.value for axis in img_axes] try: xr_coords: MutableMapping[Hashable, Any] = { coord.value: (axis.value, physical_ticks[coord]) for axis, coord in zip(img_axes, img_coords) } except KeyError as ex: raise KeyError( f"missing physical coordinates {ex.args[0]}") from ex for ix, axis in enumerate(img_axes): xr_coords[axis.value] = pixel_ticks.get( axis, np.arange(0, array.shape[ix])) dataarray = xr.DataArray( array, dims=xr_axes, coords=xr_coords, ) dataarray.attrs.update({ AttrKeys.LOG: (log or Log()).encode(), AttrKeys.DOCTYPE: DOCTYPE_STRING, AttrKeys.VERSION: str(CURRENT_VERSION), }) return LabelImage(dataarray)
def log(self) -> Log: """Returns a copy of the provenance data. Modifications to this copy will not affect the log stored on this label image.""" return Log.decode(self.label_image.attrs[AttrKeys.LOG])