Source code for arthropod_describer.tools.knife

import importlib.resources
import math
import typing
from typing import List, Tuple, Dict

import numpy as np
from PySide2.QtCore import QPoint, QRect
from PySide2.QtGui import QPainter, QColor, QPen, QIcon
import skimage.draw
import skimage.measure
from skimage import io
from skimage.morphology import binary_dilation, disk

from arthropod_describer.common.label_change import LabelChange, CommandEntry
from arthropod_describer.common.state import State
from arthropod_describer.common.tool import Tool, EditContext, PaintCommand, line_command
from arthropod_describer.common.user_params import UserParam, ParamType


[docs]class Tool_Knife(Tool): def __init__(self, state: State): Tool.__init__(self, state) self._tool_name = "Knife" self._active = False self._first_endpoint: Tuple[int, int] = (-1, -1) self._user_params = {'Cut width': UserParam('Cut width', ParamType.INT, 3, min_val=1, max_val=9, step=2)} with importlib.resources.path("tools.icons", "cutter.png") as path: self._tool_icon = QIcon(str(path)) self.pen = QPen() self.state = state @property def tool_name(self) -> str: return self._tool_name @property def active(self) -> bool: return self._active @property def viz_active(self) -> bool: return self._active @property def user_params(self) -> typing.Dict[str, UserParam]: return self._user_params
[docs] def left_press(self, painter: QPainter, pos: QPoint, context: EditContext) -> Tuple[ typing.Optional[CommandEntry], QRect]: return None, QRect()
[docs] def left_release(self, painter: QPainter, pos: QPoint, ctx: EditContext) -> Tuple[ typing.Optional[CommandEntry], QRect]: ctx.tool_viz_commands = [] line_coords = skimage.draw.line(*self._first_endpoint, *(pos.toTuple()[::-1])) line_width = self.user_params['Cut width'].value if line_width > 1: top, left = np.min(line_coords[0]) - line_width // 2, np.min(line_coords[1]) - line_width // 2 bottom, right = np.max(line_coords[0]) + line_width // 2, np.max(line_coords[1]) + line_width // 2 line_box = np.zeros((bottom - top + 1, right - left + 1), np.bool) local_coords = (line_coords[0] - top, line_coords[1] - left) line_box[local_coords[0], local_coords[1]] = 1 dil = binary_dilation(line_box, disk(line_width // 2)) line_coords = np.nonzero(dil) line_coords = line_coords[0] + top, line_coords[1] + left rr_cc = [(r, c) for r, c in zip(line_coords[0], line_coords[1]) if 0 <= r < ctx.label_img.label_image.shape[0] and 0 <= c < ctx.label_img.label_image.shape[1]] line_coords = [r for r, c in rr_cc], [c for r, c in rr_cc] label_profile = np.where(~ctx.clip_mask > 0, ctx.label_img.label_image, 0)[line_coords] labels = np.unique(label_profile) if len(labels) < 2: return None, QRect() #labels = set() lab_coords: Dict[int, Tuple[List[int], List[int]]] = {label: ([], []) for label in labels} painter = QPainter(ctx.label_viz) painter.setCompositionMode(QPainter.CompositionMode_Source) for i, y, x in zip(range(len(label_profile)), *line_coords): label = label_profile[i] if label == 0: continue lab_coords[label][0].append(y) lab_coords[label][1].append(x) color = QColor.fromRgb(*ctx.colormap[ctx.label]) if ctx.label == 0: color.setAlpha(0) painter.setPen(color) painter.drawPoint(x, y) painter.end() if 0 in lab_coords: del lab_coords[0] cmd = CommandEntry([LabelChange(coords, ctx.label, label, ctx.label_img.label_semantic) for label, coords in lab_coords.items()]) cmd.source = self.tool_name return cmd, cmd.bbox
[docs] def mouse_move(self, painter: QPainter, new_pos: QPoint, old_pos: QPoint, ctx: EditContext) -> List[LabelChange]: #ctx.tool_viz_commands = [draw_line(QPoint(*self._first_endpoint[::-1]), new_pos, ctx)] return super().mouse_move(painter, new_pos, old_pos, ctx)
[docs] def viz_left_press(self, pos: QPoint) -> List[PaintCommand]: self._first_endpoint = pos.toTuple()[::-1] color = self.state.label_hierarchy.nodes[self.state.primary_label].color self.pen.setColor(QColor(*color, 255)) self.pen.setWidth(self._user_params['Cut width'].value) self._active = True return self.viz_mouse_move(pos, pos)
[docs] def viz_mouse_move(self, new_pos: QPoint, old_pos: QPoint) -> List[PaintCommand]: return [line_command(QPoint(*self._first_endpoint[::-1]), new_pos, self.pen)]
[docs] def viz_left_release(self, pos: QPoint) -> List[PaintCommand]: self._active = False return []
[docs]def draw_line(pos1: QPoint, pos2: QPoint, ctx: EditContext): def draw(painter: QPainter): painter.save() pen = QPen(QColor.fromRgb(*ctx.colormap[ctx.label])) pen.setWidth(4) painter.setPen(pen) painter.drawLine(pos1, pos2) painter.restore() return draw