2023-09-11 15:45:31 +08:00

247 lines
9.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
# @Author : LG
from PyQt5 import QtCore, QtWidgets, QtGui
from sam_ann.annotation import Object
import typing
class Vertex(QtWidgets.QGraphicsPathItem):
def __init__(self, polygon, color, nohover_size=2):
super(Vertex, self).__init__()
self.polygon = polygon
self.color = color
self.nohover_size = nohover_size
self.hover_size = self.nohover_size + 2
self.line_width = 0
self.nohover = QtGui.QPainterPath()
self.nohover.addEllipse(QtCore.QRectF(-self.nohover_size//2, -self.nohover_size//2, self.nohover_size, self.nohover_size))
self.hover = QtGui.QPainterPath()
self.hover.addEllipse(QtCore.QRectF(-self.hover_size//2, -self.hover_size//2, self.hover_size, self.hover_size))
self.setPath(self.nohover)
self.setBrush(self.color)
self.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor))
self.setPen(QtGui.QPen(self.color, self.line_width))
self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges, True)
self.setAcceptHoverEvents(True)
self.setZValue(1e5)
def itemChange(self, change: 'QtWidgets.QGraphicsItem.GraphicsItemChange', value: typing.Any):
if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemSelectedHasChanged:
self.scene().mainwindow.actionDelete.setEnabled(self.isSelected())
if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemPositionChange and self.isEnabled():
# 限制顶点移动到图外
if value.x() < 0:
value.setX(0)
if value.x() > self.scene().width()-1:
value.setX(self.scene().width()-1)
if value.y() < 0:
value.setY(0)
if value.y() > self.scene().height()-1:
value.setY(self.scene().height()-1)
index = self.polygon.vertexs.index(self)
self.polygon.movePoint(index, value)
return super(Vertex, self).itemChange(change, value)
def hoverEnterEvent(self, event: 'QGraphicsSceneHoverEvent'):
self.setPath(self.hover)
super(Vertex, self).hoverEnterEvent(event)
def hoverLeaveEvent(self, event: 'QGraphicsSceneHoverEvent'):
self.setPath(self.nohover)
super(Vertex, self).hoverLeaveEvent(event)
class Polygon(QtWidgets.QGraphicsPolygonItem):
def __init__(self):
super(Polygon, self).__init__(parent=None)
self.line_width = 0
self.hover_alpha = 150
self.nohover_alpha = 80
self.points = []
self.vertexs = []
self.category = ''
self.group = 0
self.iscrowd = 0
self.note = ''
self.rxmin, self.rxmax, self.rymin, self.rymax = 0, 0, 0, 0 # 用于绘画完成后记录多边形的各边界此处与points对应
self.color = QtGui.QColor('#ff0000')
self.is_drawing = True
self.setPen(QtGui.QPen(self.color, self.line_width))
self.setBrush(QtGui.QBrush(self.color, QtCore.Qt.BrushStyle.FDiagPattern))
self.setAcceptHoverEvents(True)
self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges, True)
self.setZValue(1e5)
def addPoint(self, point):
self.points.append(point)
vertex = Vertex(self, self.color, self.scene().mainwindow.cfg['vertex_size'])
# 添加路径点
self.scene().addItem(vertex)
self.vertexs.append(vertex)
vertex.setPos(point)
def movePoint(self, index, point):
if not 0 <= index < len(self.points):
return
self.points[index] = self.mapFromScene(point)
self.redraw()
if self.scene().mainwindow.load_finished and not self.is_drawing:
self.scene().mainwindow.set_saved_state(False)
def removePoint(self, index):
if not self.points:
return
self.points.pop(index)
vertex = self.vertexs.pop(index)
self.scene().removeItem(vertex)
del vertex
self.redraw()
def delete(self):
self.points.clear()
while self.vertexs:
vertex = self.vertexs.pop()
self.scene().removeItem(vertex)
del vertex
def moveVertex(self, index, point):
if not 0 <= index < len(self.vertexs):
return
vertex = self.vertexs[index]
vertex.setEnabled(False)
vertex.setPos(point)
vertex.setEnabled(True)
def itemChange(self, change: 'QGraphicsItem.GraphicsItemChange', value: typing.Any):
if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemSelectedHasChanged and not self.is_drawing: # 选中改变
if self.isSelected():
color = QtGui.QColor('#00A0FF')
color.setAlpha(self.hover_alpha)
self.setBrush(color)
else:
self.color.setAlpha(self.nohover_alpha)
self.setBrush(self.color)
self.scene().mainwindow.annos_dock_widget.set_selected(self) # 更新label面板
if change == QtWidgets.QGraphicsItem.GraphicsItemChange.ItemPositionChange: # ItemPositionHasChanged
bias = value
l, t, b, r = self.boundingRect().left(), self.boundingRect().top(), self.boundingRect().bottom(), self.boundingRect().right()
if l + bias.x() < 0: bias.setX(-l)
if r + bias.x() > self.scene().width(): bias.setX(self.scene().width()-r)
if t + bias.y() < 0: bias.setY(-t)
if b + bias.y() > self.scene().height(): bias.setY(self.scene().height()-b)
for index, point in enumerate(self.points):
self.moveVertex(index, point+bias)
if self.scene().mainwindow.load_finished and not self.is_drawing:
self.scene().mainwindow.set_saved_state(False)
return super(Polygon, self).itemChange(change, value)
def hoverEnterEvent(self, event: 'QGraphicsSceneHoverEvent'):
if not self.is_drawing:
self.color.setAlpha(self.hover_alpha)
self.setBrush(self.color)
super(Polygon, self).hoverEnterEvent(event)
def hoverLeaveEvent(self, event: 'QGraphicsSceneHoverEvent'):
if not self.is_drawing:
self.color.setAlpha(self.nohover_alpha)
self.setBrush(self.color)
super(Polygon, self).hoverEnterEvent(event)
def mouseDoubleClickEvent(self, event: 'QGraphicsSceneMouseEvent'):
if event.button() == QtCore.Qt.MouseButton.LeftButton:
self.scene().mainwindow.category_edit_widget.polygon = self
self.scene().mainwindow.category_edit_widget.load_cfg()
self.scene().mainwindow.category_edit_widget.show()
def redraw(self):
if len(self.points) < 1:
return
xs = [p.x() for p in self.points]
ys = [p.y() for p in self.points]
self.rxmin, self.rymin, self.rxmax, self.rymax = min(xs), min(ys), max(xs), max(ys)
self.setPolygon(QtGui.QPolygonF(self.points))
def change_color(self, color):
self.color = color
self.color.setAlpha(self.nohover_alpha)
self.setPen(QtGui.QPen(self.color, self.line_width))
self.setBrush(self.color)
for vertex in self.vertexs:
vertex_color = self.color
vertex_color.setAlpha(255)
vertex.setPen(QtGui.QPen(vertex_color, self.line_width))
vertex.setBrush(vertex_color)
def set_drawed(self, category, group, iscrowd, note, color:QtGui.QColor, layer=None):
self.is_drawing = False
self.category = category
if isinstance(group, str):
group = 0 if group == '' else int(group)
self.group = group
self.iscrowd = iscrowd
self.note = note
self.color = color
self.color.setAlpha(self.nohover_alpha)
self.setPen(QtGui.QPen(self.color, self.line_width))
self.setBrush(self.color)
if layer is not None:
self.setZValue(layer)
for vertex in self.vertexs:
vertex_color = self.color
vertex_color.setAlpha(255)
vertex.setPen(QtGui.QPen(vertex_color, self.line_width))
vertex.setBrush(vertex_color)
if layer is not None:
vertex.setZValue(layer)
def calculate_area(self):
area = 0
num_points = len(self.points)
for i in range(num_points):
p1 = self.points[i]
p2 = self.points[(i + 1) % num_points]
d = p1.x() * p2.y() - p2.x() * p1.y()
area += d
return abs(area) / 2
def load_object(self, object):
segmentation = object.segmentation
for x, y in segmentation:
point = QtCore.QPointF(x, y)
self.addPoint(point)
color = self.scene().mainwindow.category_color_dict.get(object.category, '#000000')
self.set_drawed(object.category, object.group, object.iscrowd, object.note, QtGui.QColor(color), object.layer) # ...
def to_object(self):
if self.is_drawing:
return None
segmentation = []
for point in self.points:
point = point + self.pos()
segmentation.append((round(point.x(), 2), round(point.y(), 2)))
xmin = self.boundingRect().x() + self.pos().x()
ymin = self.boundingRect().y() + self.pos().y()
xmax = xmin + self.boundingRect().width()
ymax = ymin + self.boundingRect().height()
object = Object(self.category, group=self.group, segmentation=segmentation,
area=self.calculate_area(), layer=self.zValue(), bbox=(xmin, ymin, xmax, ymax), iscrowd=self.iscrowd, note=self.note)
return object