Source code for neuralib.cellpose.core

from __future__ import annotations

from pathlib import Path
from typing import Self

import numpy as np
from neuralib.typing import PathLike

__all__ = [
    'read_cellpose',
    'cellpose_point_roi_helper',
    'CellposeSegmentation'
]


[docs] def read_cellpose(file: PathLike) -> CellposeSegmentation: """ Read a cellpose segmentation result file :param file: cellpose segmentation result ``.npy`` file :return: :class:`CellposeSegmentation` """ return CellposeSegmentation.load(file)
[docs] def cellpose_point_roi_helper(file: PathLike, output: PathLike) -> None: """ Read a cellpose segmentation result and convert the segmentation result to point coordinates :param file: cellpose segmentation result ``.npy`` file :param output: ``*.roi`` output file path """ CellposeSegmentation.load(file).to_roi(output)
[docs] class CellposeSegmentation: """`Cellpose <https://github.com/MouseLand/cellpose>`_ segmentation results `Dimension parameters`: N = Number of segmented cells W = Image width H = Image height .. seealso:: `Cellpose Native Doc <https://cellpose.readthedocs.io/en/latest/outputs.html#seg-npy-output>`_ """
[docs] def __init__(self, outlines, masks, chan_choose, ismanual, flows, diameter, filename): self._outlines = outlines self._masks = masks self._chan_choose = chan_choose self._is_manual = ismanual self._flows = flows self._diameter = diameter self._filename = filename
[docs] @classmethod def load(cls, file: PathLike) -> Self: """ Load a cellpose segmentation result :param file: cellpose segmentation result ``.npy`` file :return: :class:`CellposeSegmentation` """ dat = np.load(file, allow_pickle=True).item() return cls(**dat)
@property def n_segmentation(self) -> int: """number of segmented cells""" return len(self._is_manual) @property def width(self): """image width""" return self._outlines.shape[1] @property def height(self): """image height""" return self._outlines.shape[0] @property def filename(self) -> Path: """filepath of image""" return Path(self._filename) @property def outlines(self) -> np.ndarray: """outlines of ROIs (0 = NO outline; 1,2,… = outline labels). `Array[uint16, [H, W]]`""" return self._outlines @property def masks(self) -> np.ndarray: """each pixel in the image is assigned to an ROI (0 = NO ROI; 1,2,… = ROI labels). `Array[uint16, [H, W]]` """ return self._masks @property def chan_choose(self) -> list[int]: """channels that you chose in GUI (0=gray/none, 1=red, 2=green, 3=blue)""" return self._chan_choose @property def flows(self) -> list[np.ndarray]: """ flows[0] is XY flow in RGB flows[1] is the cell probability in range 0-255 instead of -10.0 to 10.0 flows[2] is Z flow in range 0-255 (if it exists, otherwise zeros), flows[3] is [dY, dX, cellprob] (or [dZ, dY, dX, cellprob] for 3D), flows[4] is pixel destinations (for internal use) """ return self._flows @property def diameter(self) -> float: """cell body diameter""" return self._diameter @property def is_manual(self) -> np.ndarray: """whether or not mask k was manually drawn or computed by the cellpose algorithm. `Array[bool, N]`""" return self._is_manual @property def nan_masks(self) -> np.ndarray: """value 0 in :attr:`CellposeSegmentation.masks` to nan""" masks = self.masks.copy().astype(np.float64) masks[masks == 0] = np.nan return masks @property def nan_outlines(self) -> np.ndarray: """value 0 in :attr:`CellposeSegmentation.outlines` to nan""" outlines = self.outlines.copy().astype(np.float64) outlines[outlines == 0] = np.nan return outlines @property def points(self) -> np.ndarray: """Calculate center of each segmented area in XY pixel. `Array[int, [N, 2]]`""" centers = self._calculate_centers() return np.round(centers).astype(int) # noinspection PyTypeChecker
[docs] def to_roi(self, output_file: PathLike): """ Covert segmented roi to point roi, and save it as ``.roi`` for imageJ. :param output_file: ``*.roi`` output file path """ from roifile import ROI_OPTIONS, ROI_TYPE, ImagejRoi if Path(output_file).suffix != '.roi': raise ValueError('output file must have .roi extension') points = np.fliplr(self.points) # XY rotate in .roi format roi = ImagejRoi( roitype=ROI_TYPE.POINT, options=ROI_OPTIONS.PROMPT_BEFORE_DELETING | ROI_OPTIONS.SUB_PIXEL_RESOLUTION, n_coordinates=self.points.shape[0], integer_coordinates=points, subpixel_coordinates=points ) roi.tofile(output_file)
def _calculate_centers(self): """calculate center of each segmented area in XY pixel""" labels = np.unique(self.masks) labels = labels[labels != 0] # remove background n_neurons = len(labels) centers = np.zeros((n_neurons, 2)) for i, label in enumerate(labels): segment_coords = np.argwhere(self.masks == label) center = segment_coords.mean(axis=0) centers[i] = center return centers