Source code for neuralib.scanbox.view

from pathlib import Path
from typing import Any, cast

import numpy as np
import sbxreader
from argclz import AbstractParser, argument, int_tuple_type, pos_argument, validator
from neuralib.scanbox import SBXInfo
from neuralib.typing import PathLike
from rich.pretty import pprint

__all__ = ['ScanBoxView',
           'ScanBoxViewOptions']

from neuralib.util.utils import uglob


[docs] class ScanBoxView: """Scanbox info and data `Dimension parameters`: F = number of frames P = number of optical planes C = number of PMT channels W = FOV width H = FOV height """ info: SBXInfo """:class:`~neuralib.imaging.scanbox.core.SBXInfo`""" sbx_map: sbxreader.sbx_memmap """`Array[float, [F, P, C, W, H]]`"""
[docs] def __init__(self, directory: PathLike): """ :param directory: directory contain ``.sbx`` & ``.mat`` files """ if not Path(directory).is_dir(): raise NotADirectoryError(f'{directory}') sbx = uglob(directory, '*.sbx') self.sbx_map = sbxreader.sbx_memmap(sbx) info = uglob(directory, '*.mat') self.info = SBXInfo.load(info)
@property def meta(self) -> dict[str, Any]: """scanbox meta information""" return self.info.asdict() @property def version(self) -> int: """scanbox version""" return int(self.info.scanbox_version) @property def height(self) -> int: """fov height""" return self.info.sz[0] @property def width(self) -> int: """fov width""" return self.info.sz[1] @property def n_planes(self) -> int: """number of optical imaging planes""" if not len(self.info.otwave) and self.info.volscan == 0: return 1 elif self.info.volscan == 1: return len(self.info.otwave) raise RuntimeError('') @property def n_channels(self) -> int: """sbx issue, a bit of hard-coded""" if self.info.nchan is None: if self.info.channels == 2: return 1 elif self.info.channels == 1: return 2 else: raise RuntimeError('') else: return self.info.nchan @property def n_frames(self) -> int: """number of frames per plane""" return int(self.info.config.frames / self.n_planes)
[docs] def show(self, frames: slice | np.ndarray | None, plane: int, channel: int): """play the selected frames using customized CV2 player :param frames: selected frames. If None, play all sequences :param plane: number of optical planes :param channel: number of PMT channel """ from neuralib.imglib.labeller import SequenceLabeller if frames is None: frames = np.arange(0, self.n_frames) data = self.sbx_map[frames, plane, channel, :, :] data = np.asarray(data) SequenceLabeller.load_sequences(data).main()
[docs] def to_tiff(self, frames: slice | np.ndarray | None, plane: int, channel: int, output: PathLike): """ Convert the selected frames to tiff file :param frames: selected frames. If None, convert all sequences :param plane: number of optical planes :param channel: number of PMT channel :param output: output filename """ import tifffile if frames is None: frames = np.arange(0, self.n_frames) data = self.sbx_map[frames, plane, channel, :, :] tifffile.imwrite(output, data)
[docs] class ScanBoxViewOptions(AbstractParser): DESCRIPTION = 'ScanBox data view & save options' directory: Path = pos_argument( 'PATH', validator.path.is_dir().is_exists(), help='directory containing .sbx/.mat scanbox output' ) # -- Config --------------- frames: tuple[int, int] | None = cast(Any, argument( '--frames', type=int_tuple_type, default=None, help='indices of image sequences, if None, then all frames' )) plane: int = argument('--plane', default=0, help='which optic plane') channel: int = argument('--channel', default=0, help='which pmt channel') # -- IO --------------------- verbose: bool = argument('--verbose', help='show meta verbose') show: bool = argument('--show', help='play the selected imaging sequences') to_tiff: Path | None = argument('--tiff', default=None, help='save sequence as tiff output')
[docs] def run(self): viewer = ScanBoxView(self.directory) frames = np.arange(*self.frames).astype(int) if self.frames is not None else None if self.verbose: pprint(viewer.info.asdict()) if self.show: viewer.show(frames, self.plane, self.channel) if self.to_tiff is not None: viewer.to_tiff(frames, self.plane, self.channel, output=self.to_tiff)
if __name__ == '__main__': ScanBoxViewOptions().main()