Exemple #1
0
def prepare_stack(
    stack: Array,
    channel_labels: Series,
    compartment: str = "nuclear",
    channel_exclude: Series = None,
) -> Array:
    assert compartment in ["nuclear", "cytoplasm", "both"]

    if channel_exclude is not None:
        stack = stack[~channel_exclude]
        channel_labels = channel_labels[~channel_exclude.values]

    # Get nuclear channels
    nuclear_chs = channel_labels.str.contains("DNA")
    nucl = np.asarray([eq(x) for x in stack[nuclear_chs]]).mean(0)
    if compartment == "nuclear":
        return nucl

    # Get cytoplasmatic channels
    cyto_chs = ~channel_labels.str.contains("DNA|Ki67|SMA")
    cyto = np.asarray([eq(x) for x in stack[~cyto_chs]]).mean(0)
    if compartment == "cytoplasm":
        return cyto

    # Combined together and expand to 4D
    return np.stack((nucl, cyto))
Exemple #2
0
def stardist_segment_nuclei(image: Array,
                            model_str: str = "2D_versatile_fluo") -> Array:
    from stardist.models import StarDist2D

    model = StarDist2D.from_pretrained(model_str)
    mask, _ = model.predict_instances(eq(image))
    return mask
Exemple #3
0
    def cell_type_adjancency(
        self,
        rois: Optional[List["ROI"]] = None,
        output_prefix: Optional[Path] = None,
    ) -> None:
        rois = rois or self.rois

        output_prefix = output_prefix or self.root_dir / "single_cell" / (
            self.name + ".cluster_adjacency_graph.")

        # TODO: check default input
        # Plot adjancency for all ROIs next to each other and across
        adj_matrices = pd.concat([
            pd.read_csv(
                f"{output_prefix}{roi.name}.norm_over_random.csv",
                index_col=0,
            ).assign(roi=roi.roi_number) for roi in rois
        ])
        # g2 = nx.readwrite.read_gpickle(roi_prefix + "neighbor_graph.gpickle")

        mean_ = (adj_matrices.drop(
            "roi", axis=1).groupby(level=0).mean().sort_index(0).sort_index(1))
        adj_matrices = adj_matrices.append(mean_.assign(roi="mean"))

        m = self.n_rois + 1
        nrows = 3
        fig, _ax = plt.subplots(nrows,
                                m,
                                figsize=(4 * m, 4 * nrows),
                                sharex=False,
                                sharey=False)
        v = np.nanstd(adj_matrices.drop("roi", axis=1).values)
        kws = dict(
            cmap="RdBu_r",
            center=0,
            square=True,
            xticklabels=True,
            yticklabels=True,
            vmin=-v,
            vmax=v,
        )
        for i, roi in enumerate(rois):
            _ax[0, i].set_title(roi.name)
            _ax[0, i].imshow(eq(roi.stack.mean(0)))
            _ax[0, i].axis("off")
            __x = (
                adj_matrices.loc[adj_matrices["roi"] == roi.roi_number].drop(
                    "roi", axis=1).reindex(index=mean_.index,
                                           columns=mean_.index).fillna(0))
            sns.heatmap(__x, ax=_ax[1, i], **kws)
            sns.heatmap(__x - mean_, ax=_ax[2, i], **kws)
        _ax[1, 0].set_ylabel("Observed ratios")
        _ax[2, 0].set_ylabel("Ratio difference to mean")
        _ax[1, -1].set_title("ROI mean")
        sns.heatmap(mean_, ax=_ax[1, -1], **kws)
        _ax[0, -1].axis("off")
        _ax[2, -1].axis("off")
        share_axes_by(_ax[1:], "both")
        fig.savefig(output_prefix + "roi_over_mean_rois.image_clustermap.svg",
                    **FIG_KWS)
Exemple #4
0
    def plot_probabilities_and_segmentation(
            self,
            axes: Optional[Sequence[Axis]] = None,
            add_scale: bool = True) -> Optional[Figure]:
        """
        Visualize channel mean, DNA channel, segmentation probabilities
        and the segmented nuclei and cells.

        If `axes` is given it must have length 5
        """
        probabilities = self.probabilities
        if probabilities.shape != self.cell_mask.shape:
            probabilities = ndi.zoom(self.probabilities, (1, 0.5, 0.5))
        probabilities = np.moveaxis(probabilities, 0, -1)
        probabilities = probabilities / probabilities.max()

        dna_label, dna, minmax = self._get_channel("DNA", dont_warn=True)

        nuclei = self._get_input_filename("nuclei_mask").exists()
        ncols = 5 if nuclei else 4

        if axes is None:
            fig, _axes = plt.subplots(
                1,
                ncols,
                figsize=(ncols * 4, 4),
                gridspec_kw=dict(wspace=0.05),
                sharex=True,
                sharey=True,
            )
        else:
            _axes = axes
        _axes[0].set_ylabel(self.name)
        _axes[0].set_title("Channel mean")
        _axes[0].imshow(
            self._get_channel("mean", equalize=True, minmax=True)[1])
        _axes[1].set_title(dna_label)
        _axes[1].imshow(eq(dna))
        _axes[2].set_title("Probabilities")
        _axes[2].imshow(probabilities)
        i = 0
        if nuclei:
            _axes[3 + i].set_title("Nuclei")
            _axes[3 + i].imshow(self.nuclei_mask > 0, cmap="binary")
            i += 1
        _axes[3 + i].set_title("Cells")
        _axes[3 + i].imshow(self.cell_mask > 0, cmap="binary")
        if add_scale:
            _add_scale(_axes[3 + i])
        # To plot jointly
        # _axes[5].imshow(probabilities)
        # _axes[5].contour(self.cell_mask, cmap="Blues")
        # _axes[5].contour(self.nuclei_mask, cmap="Reds")
        for _ax in _axes:
            _ax.axis("off")

        return fig if axes is None else None
Exemple #5
0
def write_image_to_file(
    arr: Array,
    channel_labels: Sequence,
    output_prefix: Path,
    file_format: str = "png",
) -> None:
    if len(arr.shape) != 3:
        skimage.io.imsave(
            output_prefix + "." + "channel_mean" + "." + file_format, arr)
    else:
        __s = np.multiply(eq(arr.mean(axis=0)), 256).astype(np.uint8)
        skimage.io.imsave(
            output_prefix + "." + "channel_mean" + "." + file_format, __s)
        for channel, label in tqdm(enumerate(channel_labels),
                                   total=arr.shape[0]):
            skimage.io.imsave(
                output_prefix + "." + label + "." + file_format,
                np.multiply(arr[channel], 256).astype(np.uint8),
            )
Exemple #6
0
def merge_channels(
    arr: Array,
    output_colors: Optional[List[str]] = None,
    return_colors: bool = False,
) -> Union[Array, Tuple[Array, List[Tuple[float, float, float]]]]:
    """
    Assumes [0, 1] float array.
    to is a tuple of 3 colors.
    """
    # defaults = list(matplotlib.colors.TABLEAU_COLORS.values())
    n_channels = arr.shape[0]
    if output_colors is None:
        target_colors = [
            matplotlib.colors.to_rgb(col)
            for col in DEFAULT_CHANNEL_COLORS[:n_channels]
        ]

    if (n_channels == 3) and output_colors is None:
        m = np.moveaxis(np.asarray([eq(x) for x in arr]), 0, -1)
        res = (m - m.min((0, 1))) / (m.max((0, 1)) - m.min((0, 1)))
        return res if not return_colors else (res, target_colors)

    elif isinstance(output_colors, (list, tuple)):
        assert len(output_colors) == n_channels
        target_colors = [matplotlib.colors.to_rgb(col) for col in output_colors]

    # work in int space to avoid float underflow
    if arr.min() >= 0 and arr.max() <= 1:
        arr *= 256
    else:
        arr = saturize(arr) * 256
    res = np.zeros(arr.shape[1:] + (3,))
    for i in range(n_channels):
        for j in range(3):
            res[:, :, j] = res[:, :, j] + arr[i] * target_colors[i][j]
    # return saturize(res) if not return_colors else (saturize(res), target_colors)
    return res if not return_colors else (res, target_colors)
Exemple #7
0
def read_image_from_file(file: Path, equalize: bool = False) -> Array:
    """
    Read images from a tiff or hdf5 file into a numpy array.
    Channels, if existing will be in first array dimension.
    If `equalize` is :obj:`True`, convert to float type bounded at [0, 1].
    """
    if not file.exists():
        raise FileNotFoundError(f"Could not find file: '{file}")
    # if str(file).endswith("_mask.tiff"):
    # arr = tifffile.imread(file) > 0
    if file.endswith(".ome.tiff"):
        arr = tifffile.imread(str(file), is_ome=True)
    elif file.endswith(".tiff"):
        arr = tifffile.imread(str(file))
    elif file.endswith(".h5"):
        with h5py.File(file, "r") as __f:
            arr = np.asarray(__f[list(__f.keys())[0]])

    if len(arr.shape) == 3:
        if min(arr.shape) == arr.shape[-1]:
            arr = np.moveaxis(arr, -1, 0)
    if equalize:
        arr = eq(arr)
    return arr
Exemple #8
0
 def get_mean_all_channels(self) -> Array:
     """Get an array with mean of all channels"""
     return eq(self.stack.mean(axis=0))
Exemple #9
0
 def stack_eq(self):
     """Same as `stack` but equalized per channel."""
     if self._stack_eq is not None:
         return self._stack_eq
     return np.asarray([eq(x) for x in self.stack])