self.hover_size = 3
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.PointingHandCursor))
self.setPen(QtGui.QPen(self.color, self.line_width))
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
self.setAcceptHoverEvents(True)
self.setZValue(1e5)
def itemChange(self, change: 'QtWidgets.QGraphicsItem.GraphicsItemChange', value: typing.Any):
if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.isEnabled():
# Limit vertex movement within the scene bounds
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)
self.polygon.movePoint(self.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)
self.rxmin, self.rxmax, self.rymin, self.rymax = 0, 0, 0, 0
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.FDiagPattern))
self.setAcceptHoverEvents(True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True)
self.setZValue(1e5)
def addPoint(self, point):
self.points.append(point)
vertex = Vertex(self, len(self.points)-1, self.color)
# Add the vertex to the scene
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.ItemSelectedHasChanged and not self.is_drawing:
# Selection changed
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.labels_dock_widget.set_selected(self)
if change == QtWidgets.QGraphicsItem.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).hoverLeaveEvent(event)
def mouseDoubleClickEvent(self, event: 'QGraphicsSceneMouseEvent'):
if event.button() == QtCore.Qt.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
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()
x_str = '{:.20f}'.format(point.x()) # Use the format specifier to display full precision
y_str = '{:.20f}'.format(point.y()) # Use the format specifier to display full precision
segmentation.append((x_str, y_str)) # Save as strings with full precision
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