Skip to content

Commit

Permalink
Merge pull request #161 from Alias-z/master
Browse files Browse the repository at this point in the history
[Experimental] batch vertexes modification (issue #160)
  • Loading branch information
yatengLG committed May 7, 2024
2 parents 40cf192 + 5ef6dd1 commit 81a06c3
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 43 deletions.
133 changes: 90 additions & 43 deletions ISAT/widgets/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,48 @@
from PIL import Image
import numpy as np
import cv2
import time # 拖动鼠标描点
import time # 拖动鼠标描点


class AnnotationScene(QtWidgets.QGraphicsScene):
def __init__(self, mainwindow):
super(AnnotationScene, self).__init__()
self.mainwindow = mainwindow
self.image_item:QtWidgets.QGraphicsPixmapItem = None
self.image_item: QtWidgets.QGraphicsPixmapItem = None
self.image_data = None
self.current_graph:Polygon = None
self.current_graph: Polygon = None
self.mode = STATUSMode.VIEW
self.click = CLICKMode.POSITIVE
self.draw_mode = DRAWMode.SEGMENTANYTHING # 默认使用segment anything进行快速标注
self.contour_mode = CONTOURMode.SAVE_EXTERNAL # 默认SAM只保留外轮廓
self.click_points = [] # SAM point prompt
self.click_points_mode = [] # SAM point prompt
self.prompt_points = []
self.masks:np.ndarray = None
self.masks: np.ndarray = None
self.mask_alpha = 0.5
self.top_layer = 1

self.guide_line_x:QtWidgets.QGraphicsLineItem = None
self.guide_line_y:QtWidgets.QGraphicsLineItem = None
self.guide_line_x: QtWidgets.QGraphicsLineItem = None
self.guide_line_y: QtWidgets.QGraphicsLineItem = None

# 拖动鼠标描点
# 拖动鼠标描点
self.last_draw_time = time.time()
self.draw_interval = 0.15
self.pressd = False

def load_image(self, image_path:str):
# 批量点修改 (issue 160) 需要选定起始点和终点,默认按‘R’开启模式
self.batch_vertexes_mode = False
self.batch_start_vertex = None
self.batch_end_vertex = None
self.batch_clicked_point = None

def load_image(self, image_path: str):
self.clear()
if self.mainwindow.use_segment_anything:
self.mainwindow.segany.reset_image()

self.image_data = np.array(Image.open(image_path))

self.image_item = QtWidgets.QGraphicsPixmapItem()
self.image_item.setZValue(0)
self.addItem(self.image_item)
Expand Down Expand Up @@ -116,6 +123,13 @@ def change_mode_to_edit(self):
self.mainwindow.actionDelete.setEnabled(True)
self.mainwindow.actionSave.setEnabled(True)

# 批量点修改 (issue 160) 需要选定起始点和终点,默认按‘R’开启模式
def switch_batch_vertexes_mode(self):
self.batch_vertexes_mode = not self.batch_vertexes_mode # toggle batch_vertexes_mode
self.batch_start_vertex = None # reset start point on toggle
self.batch_end_vertex = None # reset end point on toggle
print('Batch vertexs mode: ', self.batch_vertexes_mode)

def change_click_to_positive(self):
self.click = CLICKMode.POSITIVE

Expand Down Expand Up @@ -287,7 +301,7 @@ def finish_draw(self):
def cancel_draw(self):
if self.current_graph is None:
return
self.current_graph.delete() # 清除所有路径
self.current_graph.delete() # 清除所有路径
self.removeItem(self.current_graph)

self.current_graph = None
Expand Down Expand Up @@ -337,6 +351,7 @@ def delete_selected_graph(self):
if p.zValue() > deleted_layer:
p.setZValue(p.zValue() - 1)
self.mainwindow.annos_dock_widget.update_listwidget()
self.batch_vertexes_mode = False

def edit_polygon(self):
selectd_items = self.selectedItems()
Expand Down Expand Up @@ -392,9 +407,9 @@ def mousePressEvent(self, event: 'QtWidgets.QGraphicsSceneMouseEvent'):
if self.mode == STATUSMode.CREATE:
sceneX, sceneY = event.scenePos().x(), event.scenePos().y()
sceneX = 0 if sceneX < 0 else sceneX
sceneX = self.width()-1 if sceneX > self.width()-1 else sceneX
sceneX = self.width() - 1 if sceneX > self.width() - 1 else sceneX
sceneY = 0 if sceneY < 0 else sceneY
sceneY = self.height()-1 if sceneY > self.height()-1 else sceneY
sceneY = self.height() - 1 if sceneY > self.height() - 1 else sceneY

if event.button() == QtCore.Qt.MouseButton.LeftButton:
if self.draw_mode == DRAWMode.SEGMENTANYTHING:
Expand Down Expand Up @@ -429,39 +444,51 @@ def mousePressEvent(self, event: 'QtWidgets.QGraphicsSceneMouseEvent'):
raise ValueError('The draw mode named {} not supported.')
if self.draw_mode == DRAWMode.SEGMENTANYTHING:
self.update_mask()

# 拖动鼠标描点
self.last_draw_time = time.time()
self.pressd = True
super(AnnotationScene, self).mousePressEvent(event)
# 批量点修改 (issue 160) 需要选定起始点和终点,默认按‘R’开启模式
if self.batch_vertexes_mode:
self.batch_clicked_point = (event.scenePos().x(), event.scenePos().y())

# 拖动鼠标描点
def mouseReleaseEvent(self, event: 'QtWidgets.QGraphicsSceneMouseEvent'):
# 拖动鼠标描点
def mouseReleaseEvent(self, event: 'QtWidgets.QGraphicsSceneMouseEvent'):
self.pressd = False
super(AnnotationScene, self).mouseReleaseEvent(event)
# 批量点修改 (issue 160) 需要选定起始点和终点,默认按‘R’开启模式
if self.batch_vertexes_mode:
selected_vertexes = [item for item in self.selectedItems() if isinstance(item, Vertex)]
selected_polygons = set([vertex.polygon for vertex in selected_vertexes])
if len(selected_polygons) != 1: # ensure the user selecting the same single polygon
self.batch_start_vertex = None
self.batch_end_vertex = None
for vertex in selected_vertexes:
vertex.setSelected(False)
elif len(selected_polygons) == 1:
if not self.batch_start_vertex:
self.batch_start_vertex = self.batch_clicked_point
print("batch_vertexes_mode: Start point set at", self.batch_start_vertex)
elif not self.batch_end_vertex:
self.batch_end_vertex = self.batch_clicked_point
print("batch_vertexes_mode: End point set at", self.batch_end_vertex)
else:
selected_polygon = list(selected_polygons)[0]
start_vertex_index = selected_polygon.vertexs.index(selected_vertexes[0])
end_vertex_index = selected_polygon.vertexs.index(selected_vertexes[1])
start_vertex_index = min((start_vertex_index, end_vertex_index))
end_vertex_index = max((start_vertex_index, end_vertex_index))
direct_path_length = end_vertex_index - start_vertex_index + 1
total_vertexes = len(selected_polygon.vertexs)
if direct_path_length <= (total_vertexes - direct_path_length):
selected_indices = range(start_vertex_index, end_vertex_index + 1)
else:
selected_indices = list(range(end_vertex_index, total_vertexes)) + list(range(0, start_vertex_index))
for idx in selected_indices:
selected_polygon.vertexs[idx].setSelected(True)

def mouseMoveEvent(self, event: 'QtWidgets.QGraphicsSceneMouseEvent'):
# 拖动鼠标描点
if self.pressd: # 拖动鼠标
current_time = time.time()
if self.last_draw_time is not None and current_time - self.last_draw_time < self.draw_interval:
return # 时间小于给定值不画点
self.last_draw_time = current_time
sceneX, sceneY = event.scenePos().x(), event.scenePos().y()
sceneX = 0 if sceneX < 0 else sceneX
sceneX = self.width()-1 if sceneX > self.width()-1 else sceneX
sceneY = 0 if sceneY < 0 else sceneY
sceneY = self.height()-1 if sceneY > self.height()-1 else sceneY

if self.current_graph is not None:
if self.draw_mode == DRAWMode.POLYGON:
# 移除随鼠标移动的点
self.current_graph.removePoint(len(self.current_graph.points) - 1)
# 添加当前点
self.current_graph.addPoint(QtCore.QPointF(sceneX, sceneY))
# 添加随鼠标移动的点
self.current_graph.addPoint(QtCore.QPointF(sceneX, sceneY))

# 辅助线
if self.guide_line_x is not None and self.guide_line_y is not None:
if self.guide_line_x in self.items():
Expand All @@ -475,22 +502,22 @@ def mouseMoveEvent(self, event: 'QtWidgets.QGraphicsSceneMouseEvent'):

pos = event.scenePos()
if pos.x() < 0: pos.setX(0)
if pos.x() > self.width()-1: pos.setX(self.width()-1)
if pos.x() > self.width() - 1: pos.setX(self.width() - 1)
if pos.y() < 0: pos.setY(0)
if pos.y() > self.height()-1: pos.setY(self.height()-1)
if pos.y() > self.height() - 1: pos.setY(self.height() - 1)
# 限制在图片范围内

if self.mode == STATUSMode.CREATE:
if self.draw_mode == DRAWMode.POLYGON:
# 随鼠标位置实时更新多边形
self.current_graph.movePoint(len(self.current_graph.points)-1, pos)
self.current_graph.movePoint(len(self.current_graph.points) - 1, pos)

# 辅助线
if self.guide_line_x is None and self.width()>0 and self.height()>0:
if self.guide_line_x is None and self.width() > 0 and self.height() > 0:
self.guide_line_x = QtWidgets.QGraphicsLineItem(QtCore.QLineF(pos.x(), 0, pos.x(), self.height()))
self.guide_line_x.setZValue(1)
self.addItem(self.guide_line_x)
if self.guide_line_y is None and self.width()>0 and self.height()>0:
if self.guide_line_y is None and self.width() > 0 and self.height() > 0:
self.guide_line_y = QtWidgets.QGraphicsLineItem(QtCore.QLineF(0, pos.y(), self.width(), pos.y()))
self.guide_line_y.setZValue(1)
self.addItem(self.guide_line_y)
Expand All @@ -509,6 +536,26 @@ def mouseMoveEvent(self, event: 'QtWidgets.QGraphicsSceneMouseEvent'):
else:
self.mainwindow.labelData.setText('pix: [{}]'.format(data))

# 拖动鼠标描点
if self.pressd: # 拖动鼠标
current_time = time.time()
if self.last_draw_time is not None and current_time - self.last_draw_time < self.draw_interval:
return # 时间小于给定值不画点
self.last_draw_time = current_time
sceneX, sceneY = event.scenePos().x(), event.scenePos().y()
sceneX = 0 if sceneX < 0 else sceneX
sceneX = self.width() - 1 if sceneX > self.width() - 1 else sceneX
sceneY = 0 if sceneY < 0 else sceneY
sceneY = self.height() - 1 if sceneY > self.height() - 1 else sceneY

if self.current_graph is not None:
if self.draw_mode == DRAWMode.POLYGON:
# 移除随鼠标移动的点
self.current_graph.removePoint(len(self.current_graph.points) - 1)
# 添加当前点
self.current_graph.addPoint(QtCore.QPointF(sceneX, sceneY))
# 添加随鼠标移动的点
self.current_graph.addPoint(QtCore.QPointF(sceneX, sceneY))
super(AnnotationScene, self).mouseMoveEvent(event)

def update_mask(self):
Expand Down Expand Up @@ -574,7 +621,7 @@ def __init__(self, parent=None):
def wheelEvent(self, event: QtGui.QWheelEvent):
angel = event.angleDelta()
angelX, angelY = angel.x(), angel.y()
point = event.pos() # 当前鼠标位置
point = event.pos() # 当前鼠标位置
if angelY > 0:
self.zoom(self.factor, point)
else:
Expand All @@ -587,7 +634,7 @@ def zoom_out(self):
self.zoom(1/self.factor)

def zoomfit(self):
self.fitInView(0, 0, self.scene().width(), self.scene().height(), QtCore.Qt.AspectRatioMode.KeepAspectRatio)
self.fitInView(0, 0, self.scene().width(), self.scene().height(), QtCore.Qt.AspectRatioMode.KeepAspectRatio)

def zoom(self, factor, point=None):
mouse_old = self.mapToScene(point) if point is not None else None
Expand Down
4 changes: 4 additions & 0 deletions ISAT/widgets/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@ def init_ui(self):
self.category_choice_widget = CategoryChoiceDialog(self, mainwindow=self, scene=self.scene)
self.category_edit_widget = CategoryEditDialog(self, self, self.scene)

# 批量点修改 (issue 160) 快捷键
self.batch_vertexs_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("R"), self)
self.batch_vertexs_shortcut.activated.connect(self.scene.switch_batch_vertexes_mode)

self.Converter_dialog = ConverterDialog(self, mainwindow=self)
self.auto_segment_dialog = AutoSegmentDialog(self, self)

Expand Down

0 comments on commit 81a06c3

Please sign in to comment.