Mentions légales du service

Skip to content
Snippets Groups Projects
soma_validation.py 5.54 KiB
Newer Older
import tkinter as tknt
from typing import Union
import matplotlib.cm as mpcm
import numpy as nmpy
import skimage.segmentation as sisg
from PIL import Image as image_t
from PIL import ImageTk as tk_image_t
array_t = nmpy.ndarray


class soma_validation_window_t:
    __slots__ = (
        "lmap",
        "mip",
        "mip_4_display",
        "root",
DEBREUVE Eric's avatar
DEBREUVE Eric committed
        "canvas_gfp",
        "cursor_nfo",
DEBREUVE Eric's avatar
DEBREUVE Eric committed
        "tk_image_gfp",
        "tk_image",
        "canvas_image",
    )
    lmap: array_t
    mip: array_t
    mip_4_display: array_t
    root: tknt.Tk
DEBREUVE Eric's avatar
DEBREUVE Eric committed
    canvas_gfp: tknt.Canvas
    canvas: tknt.Canvas
    cursor_nfo: tknt.Label
DEBREUVE Eric's avatar
DEBREUVE Eric committed
    tk_image_gfp: tk_image_t
    tk_image: tk_image_t
    canvas_image: int

DEBREUVE Eric's avatar
DEBREUVE Eric committed
        gfp: array_t,
        lmap: array_t,
        mip_axis: int = -1,
        color_version: bool = True,
        with_cm: str = None,
    ):
        """
        with_cm: "plasma" and "viridis" seem to be good options
        """
DEBREUVE Eric's avatar
DEBREUVE Eric committed
        gfp_mip = nmpy.amax(gfp, axis=mip_axis)
        gfp_mip *= 255.0 / nmpy.amax(gfp_mip)
        mip = nmpy.amax(lmap, axis=mip_axis)
        if color_version:
            if with_cm is None:
                mip_4_display = _ColoredVersion(mip)
            else:
                mip_4_display = _ColoredVersionFromColormap(mip, with_cm)
        else:
            mip_4_display = _ScaledVersion(mip)

        root = tknt.Tk()
DEBREUVE Eric's avatar
DEBREUVE Eric committed

        canvas_gfp = tknt.Canvas(root, width=gfp_mip.shape[1], height=gfp_mip.shape[0])
        tk_image_gfp = _TkImageFromNumpyArray(gfp_mip, root)
        _ = canvas_gfp.create_image(0, 0, anchor="nw", image=tk_image_gfp)

        canvas = tknt.Canvas(root, width=mip.shape[1], height=mip.shape[0])
        tk_image = _TkImageFromNumpyArray(mip_4_display, root)
        canvas_image = canvas.create_image(0, 0, anchor="nw", image=tk_image)
        cursor_nfo = tknt.Label(root, text="")
        done_button = tknt.Button(root, text="Done", command=root.quit)
        canvas.bind("<Motion>", self._DisplayLabel)
        canvas.bind("<Button-1>", self._DeleteSoma)

DEBREUVE Eric's avatar
DEBREUVE Eric committed
        canvas_gfp.grid(row=0, column=0)
        canvas.grid(row=0, column=1)
        cursor_nfo.grid(row=1, column=0)
        done_button.grid(row=1, column=1)

        for field in self.__class__.__slots__:
            setattr(self, field, eval(field))

    def LaunchValidation(self) -> int:
        """"""
        self.root.mainloop()
        self.root.destroy()

        relabeled, _, _ = sisg.relabel_sequential(self.lmap)
        self.lmap[...] = relabeled

        return nmpy.amax(self.lmap)
    def _DisplayLabel(self, event: tknt.EventType.Motion) -> None:
        """"""
        row = event.y
        col = event.x
        try:
            label = self.mip[row, col]
        except IndexError:
            # This problem appeared when pack was replaced with grid. Setting ipad? and pad? to zero when adding the
            # canvas to the grid did not solve it. Is this a bug in TkInter grid?
            return
        self.cursor_nfo.configure(text=f"Label:{label}@{row}x{col}")
    def _DeleteSoma(self, event: tknt.EventType.ButtonPress) -> None:
        """"""
        row = event.y
        col = event.x
        try:
            label = self.mip[row, col]
        except IndexError:
            # This problem appeared when pack was replaced with grid. Setting ipad? and pad? to zero when adding the
            # canvas to the grid did not solve it. Is this a bug in TkInter grid?
            return
        if label > 0:
            self.lmap[self.lmap == label] = 0
            soma_bmap = self.mip == label
            self.mip[soma_bmap] = 0
            if self.mip_4_display.ndim == 1:
                self.mip_4_display[soma_bmap] = 0
            else:
                for channel in range(self.mip_4_display.shape[2]):
                    self.mip_4_display[..., channel][soma_bmap] = 0
            self.tk_image = _TkImageFromNumpyArray(self.mip_4_display, self.root)
            self.canvas.itemconfigure(self.canvas_image, image=self.tk_image)


def _ScaledVersion(image: array_t, offset: int = 50) -> array_t:
    """
    offset: Value of darkest non-background intensity
    """
    scaling = (255.0 - offset) / nmpy.max(image)
    output = scaling * image + offset
    output[image == 0] = 0

    return output.astype(nmpy.uint8)


def _ColoredVersion(image: array_t) -> array_t:
    """"""
    max_label = nmpy.amax(image)

    labels = tuple(range(1, max_label + 1))
    half_length = int(round(0.5 * max_label))
    shuffled_labels = labels[half_length:] + labels[:half_length]
    shuffled_image = nmpy.zeros_like(image)
    for label, shuffled_label in enumerate(shuffled_labels):
        shuffled_image[image == label] = shuffled_label

    output = nmpy.dstack((image, shuffled_image, max_label - image))
    output = (255.0 / max_label) * output
    output[image == 0] = 0

    return output.astype(nmpy.uint8)


def _ColoredVersionFromColormap(image: array_t, colormap_name: str) -> array_t:
    """"""
    output = nmpy.zeros(image.shape + (3,), dtype=nmpy.uint8)

    LinearValueToRGB = mpcm.get_cmap(colormap_name)
    max_label = nmpy.amax(image)
    for label in range(1, max_label + 1):
        color_01 = LinearValueToRGB((label - 1.0) / (max_label - 1.0))
        color_255 = nmpy.round(255.0 * nmpy.array(color_01[:3]))
        output[image == label, :] = color_255

    return output


def _TkImageFromNumpyArray(
    array: array_t, parent: Union[tknt.Widget, tknt.Tk]
) -> tk_image_t:
    """"""
    pil_image = image_t.fromarray(array)
    output = tk_image_t.PhotoImage(master=parent, image=pil_image)

    return output