Source code for arthropod_describer.common.tool
import abc
import typing
from typing import Dict, Tuple, List, Any, Optional
import numpy as np
from PySide2.QtCore import QPoint, Slot, QObject, Signal, QRectF, QRect
from PySide2.QtGui import QPainter, QImage, QBitmap, Qt, QRegion, QIcon, QPen, QPainterPath, QBrush
from PySide2.QtWidgets import QGraphicsItem, QStyleOptionGraphicsItem, QWidget
from arthropod_describer.common.label_change import LabelChange, CommandEntry
from arthropod_describer.common.photo import LabelImg, Photo
from arthropod_describer.common.state import State
from arthropod_describer.common.user_params import UserParam
PaintCommand = typing.Callable[[QPainter], None]
[docs]class EditContext:
def __init__(self, label_img: LabelImg, label: int, image: QImage, colormap: Dict[int, Tuple[int, int, int]],
label_viz: QImage, photo: Photo, label_level: int, edit_mask: np.ndarray, clip_region: Optional[QRegion], clip_mask: np.ndarray):
self.label_img: LabelImg = label_img
self.image = image
self.label = label
self.colormap = colormap
self.label_viz = label_viz
self.tool_viz_commands: List[Any] = []
self.photo: Photo = photo
self.label_level: int = label_level
self.edit_mask: np.ndarray = edit_mask
self.clip_region = clip_region
self.clip_mask: np.ndarray = clip_mask
[docs]class ToolCursor(QGraphicsItem):
def __init__(self, parent):
QGraphicsItem.__init__(self, parent)
self.cursor_image: QImage = QImage()
self.cursor_shape: Qt.CursorShape = Qt.ArrowCursor
self.cursor_pos = QPoint()
self.setAcceptedMouseButtons(Qt.NoButton)
[docs] def boundingRect(self) -> QRectF:
if self.cursor_image is None:
return QRectF()
return self.cursor_image.rect()
[docs] def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget:typing.Optional[QWidget]=...):
if self.cursor_image is None:
return
painter.save()
painter.drawImage(self.boundingRect().x() - self.cursor_image.width() // 2,
self.boundingRect().y() - self.cursor_image.height() // 2,
self.cursor_image)
painter.restore()
[docs] def set_pos(self, pos: QPoint):
self.cursor_pos = self.mapFromScene(pos)
self.setPos(pos)
#self.setVisible(True)
self.update()
[docs] def set_cursor(self, curs: typing.Optional[typing.Union[QImage, Qt.CursorShape]]):
self.prepareGeometryChange()
if curs is None:
self.setVisible(False)
self.cursor_image = None
else:
self.cursor_image = curs
[docs]class Tool(QObject):
DEFAULT_AUTO_SCROLL_DISTANCE = 64
cursor_changed = Signal([int, QImage])
current_value = Signal(list)
update_viz = Signal()
def __init__(self, state: State, parent: QObject = None):
QObject.__init__(self, parent)
self.tool_id = -42
self._tool_icon = None
self.state = state
@property
def tool_icon(self) -> Optional[QIcon]:
return self._tool_icon
@property
@abc.abstractmethod
def tool_name(self) -> str:
pass
@property
def cursor_image(self) -> Optional[typing.Union[QImage, Qt.CursorShape]]:
return Qt.ArrowCursor
@property
def user_params(self) -> typing.Dict[str, UserParam]:
return {}
@property
def active(self) -> bool:
return False
@property
def viz_active(self) -> bool:
return False
@property
def value_storage(self) -> Optional[Any]:
return None
[docs] def left_press(self, painter: QPainter, pos: QPoint, context: EditContext) -> Tuple[Optional[CommandEntry], QRect]:
return None, QRect()
[docs] def left_release(self, painter: QPainter, pos: QPoint, context: EditContext) -> Tuple[Optional[CommandEntry], QRect]:
return None, QRect()
[docs] def right_press(self, painter: QPainter, pos: QPoint, context: EditContext) -> Tuple[Optional[CommandEntry], QRect]:
lab_img = self.state.current_photo[self.state.current_label_name]
level_img = lab_img[self.state.current_label_level]
label = level_img[pos.y(), pos.x()]
self.state.primary_label = label
return None, QRect()
[docs] def right_release(self, painter: QPainter, pos: QPoint, context: EditContext) -> Tuple[Optional[CommandEntry], QRect]:
return None, QRect()
[docs] def middle_click(self, painter: QPainter, pos: QPoint, context: EditContext) -> Tuple[Optional[CommandEntry], QRect]:
return None, QRect()
[docs] def mouse_move(self, painter: QPainter, new_pos: QPoint, old_pos: QPoint, context: EditContext) -> Tuple[Optional[CommandEntry], QRect]:
return None, QRect()
[docs] def mouse_double_click(self, painter: QPainter, pos: QPoint, context: EditContext) -> Tuple[Optional[CommandEntry], QRect]:
return None, QRect()
[docs] def mouse_wheel(self, delta: int, painter: QPainter, pos: QPoint, context: EditContext) -> Tuple[Optional[CommandEntry], QRect]:
return None, QRect()
[docs] @Slot(dict)
def color_map_changed(self, cmap: typing.Dict[int, typing.Tuple[int, int, int]]):
pass
@property
def viz_commands(self) -> List[PaintCommand]:
return []
[docs]def qimage2ndarray(img: QImage) -> np.ndarray:
img_ = img
dtype = np.uint8
shape = img_.size().toTuple()[::-1]
if img.format() == QImage.Format_ARGB32:
img_ = img.convertToFormat(QImage.Format_RGB32)
dtype = np.uint32
elif img.format() == QImage.Format_RGB32:
img_ = QImage.convertToFormat(img_, QImage.Format_RGB888)
dtype = np.uint8
shape = shape + (3,)
elif img.format() == QImage.Format_Grayscale8:
dtype = np.uint8
nd = np.frombuffer(img_.constBits(), dtype, count=img_.sizeInBytes())
buffer = np.ascontiguousarray(nd, dtype)
#return np.reshape(np.frombuffer(img_.constBits(), dtype), shape).astype(dtype)
return np.reshape(buffer, shape)
[docs]def clip_mask_from_bool_nd(bin_img: np.ndarray) -> QBitmap:
clip_mask = 255 * (bin_img > 0).astype(np.uint8)
clip_mask = np.require(clip_mask, np.uint8, 'C')
clip_img = QImage(clip_mask.data, clip_mask.shape[1], clip_mask.shape[0], clip_mask.strides[0],
QImage.Format_Grayscale8)
clip_img.invertPixels()
return QBitmap.fromImage(clip_img, Qt.AutoColor)
[docs]def line_command(p1: QPoint, p2: QPoint, pen: QPen) -> PaintCommand:
def paint(painter: QPainter):
painter.save()
painter.setPen(pen)
painter.drawLine(p1, p2)
painter.restore()
return paint
[docs]def text_command(p: QPoint, text: str, pen: QPen) -> PaintCommand:
def paint(painter: QPainter):
painter.save()
painter.setPen(pen)
painter.drawText(p, text)
painter.restore()
return paint
[docs]def fill_path_command(path: QPainterPath, pen: QPen, brush: QBrush) -> PaintCommand:
def paint(painter: QPainter):
painter.save()
painter.setPen(pen)
painter.fillPath(path, brush)
painter.restore()
return paint