Source code for pymaid.fetch.stack

from typing import Any, Optional, Union, Literal, Sequence, Tuple, Dict, List
import numpy as np
from ..utils import _eval_remote_instance
from ..client import CatmaidInstance
from enum import IntEnum
from dataclasses import dataclass, asdict

Dimension = Literal["x", "y", "z"]


class Orientation(IntEnum):
    XY = 0
    # todo: check these
    XZ = 1
    ZY = 2

    def __bool__(self) -> bool:
        return True

    def full_orientation(self, reverse=False) -> Tuple[Dimension, Dimension, Dimension]:
        out = [
            ("x", "y", "z"),
            ("x", "z", "y"),
            ("z", "y", "x"),
        ][self.value]
        if reverse:
            out = out[::-1]
        return out

    @classmethod
    def from_dims(cls, dims: Sequence[Dimension]):
        pair = (dims[0].lower(), dims[1].lower())
        out = {
            ("x", "y"): cls.XY,
            ("x", "z"): cls.XZ,
            ("z", "y"): cls.ZY,
        }.get(pair)
        if out is None:
            raise ValueError(f"Unknown dimensions: {dims}")
        return out


@dataclass
class StackSummary:
    id: int
    pid: int
    title: str
    comment: str


[docs] def get_stacks(remote_instance: Optional[CatmaidInstance] = None) -> List[StackSummary]: """Get summary of all stacks in the project. Parameters ---------- remote_instance : Optional[CatmaidInstance], optional By default global instance. Returns ------- stacks List of StackSummary objects. """ cm = _eval_remote_instance(remote_instance) url = cm.make_url(cm.project_id, "stacks") return [StackSummary(**r) for r in cm.fetch(url)]
@dataclass class MirrorInfo: id: int title: str image_base: str tile_width: int tile_height: int tile_source_type: int file_extension: str position: int def to_jso(self): return asdict(self) @dataclass class Color: r: float g: float b: float a: float @dataclass class StackInfo: sid: int pid: int ptitle: str stitle: str downsample_factors: Optional[List[Dict[Dimension, float]]] num_zoom_levels: int translation: Dict[Dimension, float] resolution: Dict[Dimension, float] dimension: Dict[Dimension, int] comment: str description: str metadata: Optional[str] broken_slices: Dict[int, int] mirrors: List[MirrorInfo] orientation: Orientation attribution: str canary_location: Dict[Dimension, int] placeholder_color: Color @classmethod def from_jso(cls, sinfo: Dict[str, Any]): sinfo["orientation"] = Orientation(sinfo["orientation"]) sinfo["placeholder_color"] = Color(**sinfo["placeholder_color"]) sinfo["mirrors"] = [MirrorInfo(**m) for m in sinfo["mirrors"]] return StackInfo(**sinfo) def to_jso(self): return asdict(self) def get_downsample(self, scale_level=0) -> Dict[Dimension, float]: """Get the downsample factors for a given scale level. If the downsample factors are explicit in the stack info, use that value. Otherwise, use the CATMAID default: scale by a factor of 2 per scale level in everything except the slicing dimension. If number of scale levels is known, ensure the scale level exists. Parameters ---------- scale_level : int, optional Returns ------- dict[Dimension, float] Raises ------ IndexError If the scale level is known not to exist """ if self.downsample_factors is not None: return self.downsample_factors[scale_level] if self.num_zoom_levels > 0 and scale_level >= self.num_zoom_levels: raise IndexError("list index out of range") first, second, slicing = self.orientation.full_orientation() return {first: 2**scale_level, second: 2**scale_level, slicing: 1} def get_coords(self, scale_level: int = 0) -> Dict[Dimension, np.ndarray]: dims = self.orientation.full_orientation() dims = dims[::-1] downsamples = self.get_downsample(scale_level) out: Dict[Dimension, np.ndarray] = dict() for d in dims: c = np.arange(self.dimension[d], dtype=float) c *= self.resolution[d] c *= downsamples[d] c += self.translation[d] out[d] = c return out
[docs] def get_stack_info( stack: Union[int, str], remote_instance: Optional[CatmaidInstance] = None ) -> StackInfo: """Get information about an image stack. Parameters ---------- stack : Union[int, str] Integer ID or string title of the stack. remote_instance : Optional[CatmaidInstance], optional By default global. Returns ------- StackInfo Raises ------ ValueError If an unknown stack title is given. """ cm = _eval_remote_instance(remote_instance) if isinstance(stack, str): stacks = get_stacks(cm) for s in stacks: if s.title == stack: stack_id = s.id break else: raise ValueError(f"No stack with title '{stack}'") else: stack_id = int(stack) url = cm.make_url(cm.project_id, "stack", stack_id, "info") sinfo = cm.fetch(url) return StackInfo.from_jso(sinfo)
[docs] def get_mirror_info( stack: Union[int, str, StackInfo], mirror: Union[int, str], remote_instance: Optional[CatmaidInstance] = None, ) -> MirrorInfo: """Get information about a stack mirror. Parameters ---------- stack : Union[int, str, StackInfo] Integer stack ID, string stack title, or an existing StackInfo object (avoids server request). mirror : Union[int, str] Integer mirror ID, or string mirror title. remote_instance : Optional[CatmaidInstance] By default, global. Returns ------- MirrorInfo Raises ------ ValueError No mirror matching given ID/ title. """ if isinstance(stack, StackInfo): stack_info = stack else: stack_info = get_stack_info(stack, remote_instance) if isinstance(mirror, str): key = "title" else: key = "id" mirror = int(mirror) for m in stack_info.mirrors: if getattr(m, key) == mirror: return m raise ValueError( f"No mirror for stack '{stack_info.stitle}' with {key} {repr(mirror)}" )