Commit 998ca587 authored by dmMaze's avatar dmMaze
Browse files

add rect tool

parent 83dfda2c
Loading
Loading
Loading
Loading
+25 −11
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ class Canvas(QGraphicsScene):

    scalefactor_changed = pyqtSignal()
    end_create_textblock = pyqtSignal(QRectF)
    end_create_rect = pyqtSignal(QRectF)
    delete_textblks = pyqtSignal()
    finish_painting = pyqtSignal(StrokeItem)
    finish_erasing = pyqtSignal(StrokeItem)
@@ -78,9 +79,8 @@ class Canvas(QGraphicsScene):
    canvas_undostack_changed = pyqtSignal()
    
    imgtrans_proj: ProjImgTrans = None
    painting = False
    painting_pen = QPen()
    image_edit_mode = None
    image_edit_mode = ImageEditMode.NONE
    alt_pressed = False
    scale_tool_mode = False

@@ -241,7 +241,7 @@ class Canvas(QGraphicsScene):
        item.setPen(self.painting_pen)
        item.setParentItem(self.drawingLayer)

    def startCreateTextblock(self, pos: QPointF):
    def startCreateTextblock(self, pos: QPointF, hide_control: bool = False):
        self.creating_textblock = True
        self.create_block_origin = pos
        self.gv.viewport().setCursor(Qt.CrossCursor)
@@ -249,11 +249,16 @@ class Canvas(QGraphicsScene):
        self.txtblkShapeControl.setPos(0, 0)
        self.txtblkShapeControl.setRotation(0)
        self.txtblkShapeControl.setRect(QRectF(pos, QSizeF(1, 1)))
        if hide_control:
            self.txtblkShapeControl.hideControls()
        self.txtblkShapeControl.show()

    def endCreateTextblock(self):
        self.creating_textblock = False
        self.gv.viewport().setCursor(Qt.ArrowCursor)
        if self.creating_normal_rect:
            self.end_create_rect.emit(self.txtblkShapeControl.rect())
        else:
            self.txtblkShapeControl.hide()
            self.end_create_textblock.emit(self.txtblkShapeControl.rect())

@@ -290,6 +295,8 @@ class Canvas(QGraphicsScene):
            elif self.painting:
                self.stroke_path_item = PenStrokeItem(self.imgLayer.mapFromScene(event.scenePos()))
                self.addStrokeItem(self.stroke_path_item)
            elif self.creating_normal_rect:
                return self.startCreateTextblock(event.scenePos(), hide_control=True)
                
        elif event.button() == Qt.MouseButton.RightButton:
            if self.painting:
@@ -298,11 +305,15 @@ class Canvas(QGraphicsScene):

        return super().mousePressEvent(event)

    @property
    def creating_normal_rect(self):
        return self.image_edit_mode == ImageEditMode.RectTool

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None:
        if event.button() == Qt.RightButton:
        if self.creating_textblock:
            return self.endCreateTextblock()
            elif self.stroke_path_item is not None:
        elif event.button() == Qt.RightButton:
            if self.stroke_path_item is not None:
                self.finish_erasing.emit(self.stroke_path_item)
        elif event.button() == Qt.MouseButton.LeftButton:
            if self.stroke_path_item is not None:
@@ -351,7 +362,11 @@ class Canvas(QGraphicsScene):
            self.maskLayer.setVisible(False)
            self.gv.setCursor(self.default_cursor)
            self.gv.setDragMode(QGraphicsView.ScrollHandDrag)
            self.painting = False
            self.image_edit_mode = ImageEditMode.NONE

    @property
    def painting(self):
        return self.image_edit_mode == ImageEditMode.PenTool or self.image_edit_mode == ImageEditMode.InpaintTool

    def setMaskTransparencyBySlider(self, slider_value: int):
        self.setMaskTransparency(slider_value / 100)
@@ -378,7 +393,6 @@ class Canvas(QGraphicsScene):
    def on_hide_canvas(self):
        self.alt_pressed = False
        self.scale_tool_mode = False
        self.textblock_mode = False
        self.creating_textblock = False
        self.create_block_origin = None
        self.editing_textblkitem = None
+1 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ CONFIG_FONTSIZE_HEADER = 24
CONFIG_FONTSIZE_TABLE = 14
CONFIG_FONTSIZE_CONTENT = 14

CONFIG_COMBOBOX_HEIGHT = 45 
CONFIG_COMBOBOX_SHORT = 300
CONFIG_COMBOBOX_MIDEAN = 500
CONFIG_COMBOBOX_LONG = 700
+133 −49
Original line number Diff line number Diff line
from PyQt5.QtCore import pyqtSignal, Qt, QPointF, QSize, QLineF
from PyQt5.QtWidgets import QBoxLayout, QCheckBox, QHBoxLayout, QGraphicsView, QUndoCommand, QStackedWidget, QVBoxLayout, QLabel, QGraphicsEllipseItem
from PyQt5.QtCore import pyqtSignal, Qt, QPointF, QSize, QLineF, QRect, QRectF
from PyQt5.QtWidgets import QComboBox, QSizePolicy, QBoxLayout, QCheckBox, QHBoxLayout, QGraphicsView, QUndoCommand, QStackedWidget, QVBoxLayout, QLabel, QGraphicsEllipseItem
from PyQt5.QtGui import QPen, QColor, QCursor, QPainter, QPixmap, QBrush, QFontMetrics
from .stylewidgets import Widget, SeparatorWidget, ColorPicker, PaintQSlider
from .canvas import Canvas
from .misc import DrawPanelConfig
from utils.imgproc_utils import enlarge_window

from typing import Union, Tuple, List
import numpy as np
import cv2

from utils.imgproc_utils import enlarge_window
from utils.textblock_mask import canny_flood, connected_canny_flood

from .dl_manager import DLManager
from .image_edit import ImageEditMode, StrokeItem
from .configpanel import InpaintConfigPanel
from .stylewidgets import Widget, SeparatorWidget, ColorPicker, PaintQSlider
from .canvas import Canvas
from .misc import DrawPanelConfig
from .constants import CONFIG_COMBOBOX_SHORT, CONFIG_COMBOBOX_HEIGHT

INPAINT_BRUSH_COLOR = QColor(127, 0, 127, 127)
MAX_PEN_SIZE = 1000
@@ -145,6 +149,41 @@ class PenConfigPanel(Widget):
        self.colorChanged.emit(color)


class RectPanel(Widget):
    method_changed = pyqtSignal(int)

    def __init__(self, inpainter_panel: InpaintConfigPanel, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.inpainter_panel = inpainter_panel
        self.methodComboBox = QComboBox()
        self.methodComboBox.setFixedHeight(CONFIG_COMBOBOX_HEIGHT)
        self.methodComboBox.setFixedWidth(CONFIG_COMBOBOX_SHORT)
        self.methodComboBox.addItems([self.tr('method 1'), self.tr('method 2')])
        layout = QVBoxLayout(self)
        layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        layout.addWidget(self.methodComboBox)
        layout.addWidget(inpainter_panel)
        layout.setSpacing(20)
        self.vlayout = layout

    def showEvent(self, e) -> None:
        self.inpainter_panel.needInpaintChecker.setVisible(False)
        self.vlayout.addWidget(self.inpainter_panel)
        super().showEvent(e)


    def hideEvent(self, e) -> None:
        self.vlayout.removeWidget(self.inpainter_panel)
        self.inpainter_panel.needInpaintChecker.setVisible(True)
        return super().hideEvent(e)

    def get_maskseg_method(self):
        if self.methodComboBox.currentIndex() == 0:
            return canny_flood
        else:
            return connected_canny_flood


class DrawingPanel(Widget):

    scale_tool_pos: QPointF = None
@@ -163,11 +202,13 @@ class DrawingPanel(Widget):
        canvas.scale_tool.connect(self.on_scale_tool)
        canvas.end_scale_tool.connect(self.on_end_scale_tool)
        canvas.scalefactor_changed.connect(self.on_canvas_scalefactor_changed)
        canvas.end_create_rect.connect(self.on_end_create_rect)

        self.currentTool: DrawToolCheckBox = None
        self.handTool = DrawToolCheckBox()
        self.handTool.setObjectName("DrawHandTool")
        self.handTool.checked.connect(self.on_use_handtool)
        self.handTool.stateChanged.connect(self.on_handchecker_changed)
        self.inpaintTool = DrawToolCheckBox()
        self.inpaintTool.setObjectName("DrawInpaintTool")
        self.inpaintTool.checked.connect(self.on_use_inpainttool)
@@ -176,7 +217,8 @@ class DrawingPanel(Widget):

        self.rectTool = DrawToolCheckBox()
        self.rectTool.setObjectName("DrawRectTool")
        self.rectTool.checked.connect(self.on_use_rectremoval_tool)
        self.rectTool.checked.connect(self.on_use_rect_tool)
        self.rectPanel = RectPanel(inpainter_panel)
        
        self.penTool = DrawToolCheckBox()
        self.penTool.setObjectName("DrawPenTool")
@@ -201,8 +243,10 @@ class DrawingPanel(Widget):
        self.setPenToolColor([0, 0, 0, 127])

        self.toolConfigStackwidget = QStackedWidget()
        self.toolConfigStackwidget.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum)
        self.toolConfigStackwidget.addWidget(self.inpaintConfigPanel)
        self.toolConfigStackwidget.addWidget(self.penConfigPanel)
        self.toolConfigStackwidget.addWidget(self.rectPanel)

        self.maskTransperancySlider = PaintQSlider(' value%', Qt.Orientation.Horizontal)
        self.maskTransperancySlider.setFixedHeight(40)
@@ -244,16 +288,13 @@ class DrawingPanel(Widget):
    def on_use_handtool(self) -> None:
        if self.currentTool is not None and self.currentTool != self.handTool:
            self.currentTool.setChecked(False)
        self.toolConfigStackwidget.hide()
        self.currentTool = self.handTool
        self.canvas.gv.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
        self.canvas.painting = False
        self.canvas.image_edit_mode = ImageEditMode.HandTool

    def on_use_inpainttool(self) -> None:
        if self.currentTool is not None and self.currentTool != self.inpaintTool:
            self.currentTool.setChecked(False)
        self.toolConfigStackwidget.show()
        self.currentTool = self.inpaintTool
        self.canvas.image_edit_mode = ImageEditMode.InpaintTool
        self.canvas.painting_pen = self.inpaint_pen
@@ -261,12 +302,10 @@ class DrawingPanel(Widget):
        if self.isVisible():
            self.canvas.gv.setDragMode(QGraphicsView.DragMode.NoDrag)
            self.setInpaintCursor()
            self.canvas.painting = True

    def on_use_pentool(self) -> None:
        if self.currentTool is not None and self.currentTool != self.penTool:
            self.currentTool.setChecked(False)
        self.toolConfigStackwidget.show()
        self.currentTool = self.penTool
        self.canvas.painting_pen = self.pentool_pen
        self.canvas.image_edit_mode = ImageEditMode.PenTool
@@ -274,10 +313,15 @@ class DrawingPanel(Widget):
        if self.isVisible():
            self.canvas.gv.setDragMode(QGraphicsView.DragMode.NoDrag)
            self.setPenCursor()
            self.canvas.painting = True

    def on_use_rectremoval_tool(self) -> None:
        print('use rect removal tool')
    def on_use_rect_tool(self) -> None:
        if self.currentTool is not None and self.currentTool != self.rectTool:
            self.currentTool.setChecked(False)
        self.currentTool = self.rectTool
        self.toolConfigStackwidget.setCurrentWidget(self.rectPanel)
        self.canvas.gv.setDragMode(QGraphicsView.DragMode.NoDrag)
        self.canvas.image_edit_mode = ImageEditMode.RectTool


    def get_config(self) -> DrawPanelConfig:
        config = DrawPanelConfig()
@@ -408,8 +452,9 @@ class DrawingPanel(Widget):
                self.inpaint_stroke.addStroke(inpaint_stroke.stroke)
            self.canvas.removeItem(inpaint_stroke)

    def runInpaint(self):
    def runInpaint(self, inpaint_dict=None):

        if inpaint_dict is None:
            if self.inpaint_stroke is None:
                return
            mask = self.inpaint_stroke.getSubimg(convert_mask=True)
@@ -434,9 +479,9 @@ class DrawingPanel(Widget):
            mask = cv2.copyMakeBorder(mask, top, bottom, left, right, cv2.BORDER_CONSTANT, value=0)
            inpaint_rect = rect_enlarged
            img = img[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]]
        
        self.canvas.painting = False
            inpaint_dict = {'img': img, 'mask': mask, 'inpaint_rect': inpaint_rect}

        self.canvas.image_edit_mode = ImageEditMode.NONE
        self.dl_manager.canvas_inpaint(inpaint_dict)

    def on_inpaint_finished(self, inpaint_dict):
@@ -444,12 +489,16 @@ class DrawingPanel(Widget):
        inpaint_rect = inpaint_dict['inpaint_rect']
        mask_array = self.canvas.imgtrans_proj.mask_array
        mask = cv2.bitwise_or(inpaint_dict['mask'], mask_array[inpaint_rect[1]: inpaint_rect[3], inpaint_rect[0]: inpaint_rect[2]])
        
        if self.inpaint_stroke is not None:
            self.canvas.removeItem(self.inpaint_stroke)
            self.inpaint_stroke = None
        self.canvas.undoStack.push(InpaintUndoCommand(self.canvas, inpainted, mask, inpaint_rect))
       
        self.canvas.undoStack.push(InpaintUndoCommand(self.canvas, inpainted, mask, inpaint_rect))
        if self.currentTool == self.inpaintTool:
            self.canvas.painting = True
            self.canvas.image_edit_mode = ImageEditMode.InpaintTool
        elif self.currentTool == self.rectTool:
            self.canvas.image_edit_mode = ImageEditMode.RectTool

    def on_finish_erasing(self, stroke_item: StrokeItem):

@@ -479,7 +528,7 @@ class DrawingPanel(Widget):

    def on_inpaint_failed(self):
        if self.currentTool == self.inpaintTool and self.inpaint_stroke is not None:
            self.canvas.painting = True
            self.canvas.image_edit_mode = ImageEditMode.InpaintTool
            self.canvas.removeItem(self.inpaint_stroke)
            self.inpaint_stroke = None

@@ -549,6 +598,40 @@ class DrawingPanel(Widget):
    def setInpaintCursor(self):
        self.canvas.gv.setCursor(self.get_pen_cursor(INPAINT_BRUSH_COLOR, self.inpaint_pen.width()))

    def on_handchecker_changed(self):
        if self.handTool.isChecked():
            self.toolConfigStackwidget.hide()
        else:
            self.toolConfigStackwidget.show()

    def on_end_create_rect(self, rect: QRectF):
        if self.currentTool == self.rectTool:
            self.canvas.image_edit_mode = ImageEditMode.NONE
            img = self.canvas.imgtrans_proj.inpainted_array
            im_h, im_w = img.shape[:2]

            xyxy = [rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height()]
            xyxy = np.array(xyxy) / self.canvas.scale_factor
            xyxy[[0, 2]] = np.clip(xyxy[[0, 2]], 0, im_w - 1)
            xyxy[[1, 3]] = np.clip(xyxy[[1, 3]], 0, im_h - 1)
            x1, y1, x2, y2 = xyxy.astype(np.int64)
            if y2 - y1 < 2 or x2 - x1 < 2:
                self.canvas.txtblkShapeControl.showControls()
                self.canvas.txtblkShapeControl.hide()
                self.canvas.image_edit_mode = ImageEditMode.RectTool
                return

            im = img[y1: y2, x1: x2]
            maskseg_method = self.rectPanel.get_maskseg_method()
            mask, ballon_mask, bub_dict = maskseg_method(im)
            inpaint_dict = {'img': im, 'mask': mask, 'inpaint_rect': [x1, y1, x2, y2]}
            self.runInpaint(inpaint_dict=inpaint_dict)

            # self.canvas.image_edit_mode = ImageEditMode.RectTool
            # cv2.imshow('img', im)
            # cv2.imshow('mask', mask)
            # cv2.imshow('ballon_mask', ballon_mask)
            # cv2.waitKey(0)

class InpaintUndoCommand(QUndoCommand):
    def __init__(self, canvas: Canvas, inpainted: np.ndarray, mask: np.ndarray, inpaint_rect: list):
@@ -585,3 +668,4 @@ class InpaintUndoCommand(QUndoCommand):
        mask_view[:] = self.undo_mask
        self.canvas.setInpaintLayer()
        self.canvas.setMaskLayer()
+2 −0
Original line number Diff line number Diff line
@@ -10,9 +10,11 @@ import cv2
SIZE_MAX = 2147483647

class ImageEditMode:
    NONE = 0
    HandTool = 0
    InpaintTool = 1
    PenTool = 2
    RectTool = 3

class StrokeItem(QGraphicsPathItem):
    def __init__(self, origin_point: QPointF):
+5 −2
Original line number Diff line number Diff line
@@ -56,12 +56,14 @@ class MainWindow(QMainWindow):
                if osp.exists(proj_dir):
                    self.openDir(proj_dir)

        textblock_mode = self.config.imgtrans_textblock
        self.bottomBar.texteditChecker.click()
        self.bottomBar.paintChecker.click()
        if self.config.imgtrans_textedit:
            if textblock_mode:
                self.bottomBar.textblockChecker.setChecked(True)
            self.bottomBar.texteditChecker.click()
            if self.config.imgtrans_textblock:
                self.bottomBar.textblockChecker.click()

        elif not self.config.imgtrans_paintmode:
            self.bottomBar.paintChecker.click()
        self.comicTransSplitter.setStretchFactor(2, 0.5)
@@ -315,6 +317,7 @@ class MainWindow(QMainWindow):
            self.rightComicTransStackPanel.setCurrentIndex(1)
            self.st_manager.setTextEditMode(True)
            self.bottomBar.originalSlider.setHidden(True)
            self.setTextBlockMode()
        else:
            self.bottomBar.textblockChecker.hide()
            self.rightComicTransStackPanel.setHidden(True)
Loading