From 19ca88ab2b31c6754c6c5ed59d9534f325683624 Mon Sep 17 00:00:00 2001 From: PairZhu <1115306638@qq.com> Date: Tue, 18 Jun 2024 15:06:07 +0800 Subject: [PATCH] Optimize label\shape.py --- labelme/shape.py | 89 +++++++++++++++++++++++---------------- labelme/widgets/canvas.py | 20 +++++---- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/labelme/shape.py b/labelme/shape.py index 63a97371a..14aa1e794 100644 --- a/labelme/shape.py +++ b/labelme/shape.py @@ -1,5 +1,4 @@ import copy -import math import numpy as np import skimage.measure @@ -26,6 +25,8 @@ class Shape(object): # Flag for all other handles on the current shape NEAR_VERTEX = 1 + PEN_WIDTH = 2 + # The following class variables influence the drawing of all shape objects. line_color = None fill_color = None @@ -77,6 +78,9 @@ def __init__( # is used for drawing the pending line a different color. self.line_color = line_color + def scaleTranslate(self, point): + return QtCore.QPointF(point.x() * self.scale, point.y() * self.scale) + def setShapeRefined(self, shape_type, points, point_labels, mask=None): self._shape_raw = (self.shape_type, self.points, self.point_labels) self.shape_type = shape_type @@ -168,19 +172,17 @@ def isClosed(self): def setOpen(self): self._closed = False - def getRectFromLine(self, pt1, pt2): - x1, y1 = pt1.x(), pt1.y() - x2, y2 = pt2.x(), pt2.y() - return QtCore.QRectF(x1, y1, x2 - x1, y2 - y1) - def paint(self, painter): if self.mask is None and not self.points: return + painter.save() + painter.scale(1 / self.scale, 1 / self.scale) + color = self.select_line_color if self.selected else self.line_color pen = QtGui.QPen(color) # Try using integer sizes for smoother drawing(?) - pen.setWidth(max(1, int(round(2.0 / self.scale)))) + pen.setWidth(self.PEN_WIDTH) painter.setPen(pen) if self.mask is not None: @@ -192,9 +194,15 @@ def paint(self, painter): ) image_to_draw[self.mask] = fill_color qimage = QtGui.QImage.fromData(labelme.utils.img_arr_to_data(image_to_draw)) + qimage = qimage.scaled( + qimage.size() * self.scale, + QtCore.Qt.IgnoreAspectRatio, + QtCore.Qt.SmoothTransformation, + ) + painter.drawImage( - int(round(self.points[0].x())), - int(round(self.points[0].y())), + # Position error will increase with the scaling factor + self.scaleTranslate(self.points[0] + QtCore.QPointF(0.5, 0.5)), qimage, ) @@ -202,9 +210,13 @@ def paint(self, painter): contours = skimage.measure.find_contours(np.pad(self.mask, pad_width=1)) for contour in contours: contour += [self.points[0].y(), self.points[0].x()] - line_path.moveTo(contour[0, 1], contour[0, 0]) + line_path.moveTo( + self.scaleTranslate(QtCore.QPointF(contour[0, 1], contour[0, 0])) + ) for point in contour[1:]: - line_path.lineTo(point[1], point[0]) + line_path.lineTo( + self.scaleTranslate(QtCore.QPointF(point[1], point[0])) + ) painter.drawPath(line_path) if self.points: @@ -215,7 +227,10 @@ def paint(self, painter): if self.shape_type in ["rectangle", "mask"]: assert len(self.points) in [1, 2] if len(self.points) == 2: - rectangle = self.getRectFromLine(*self.points) + rectangle = QtCore.QRectF( + self.scaleTranslate(self.points[0]), + self.scaleTranslate(self.points[1]), + ) line_path.addRect(rectangle) if self.shape_type == "rectangle": for i in range(len(self.points)): @@ -223,14 +238,18 @@ def paint(self, painter): elif self.shape_type == "circle": assert len(self.points) in [1, 2] if len(self.points) == 2: - rectangle = self.getCircleRectFromLine(self.points) - line_path.addEllipse(rectangle) + raidus = labelme.utils.distance( + self.scaleTranslate(self.points[0] - self.points[1]) + ) + line_path.addEllipse( + self.scaleTranslate(self.points[0]), raidus, raidus + ) for i in range(len(self.points)): self.drawVertex(vrtx_path, i) elif self.shape_type == "linestrip": - line_path.moveTo(self.points[0]) + line_path.moveTo(self.scaleTranslate(self.points[0])) for i, p in enumerate(self.points): - line_path.lineTo(p) + line_path.lineTo(self.scaleTranslate(p)) self.drawVertex(vrtx_path, i) elif self.shape_type == "points": assert len(self.points) == len(self.point_labels) @@ -240,17 +259,17 @@ def paint(self, painter): else: self.drawVertex(negative_vrtx_path, i) else: - line_path.moveTo(self.points[0]) + line_path.moveTo(self.scaleTranslate(self.points[0])) # Uncommenting the following line will draw 2 paths # for the 1st vertex, and make it non-filled, which # may be desirable. # self.drawVertex(vrtx_path, 0) for i, p in enumerate(self.points): - line_path.lineTo(p) + line_path.lineTo(self.scaleTranslate(p)) self.drawVertex(vrtx_path, i) if self.isClosed(): - line_path.lineTo(self.points[0]) + line_path.lineTo(self.scaleTranslate(self.points[0])) painter.drawPath(line_path) if vrtx_path.length() > 0: @@ -265,10 +284,12 @@ def paint(self, painter): painter.drawPath(negative_vrtx_path) painter.fillPath(negative_vrtx_path, QtGui.QColor(255, 0, 0, 255)) + painter.restore() + def drawVertex(self, path, i): - d = self.point_size / self.scale + d = self.point_size shape = self.point_type - point = self.points[i] + point = self.scaleTranslate(self.points[i]) if i == self._highlightIndex: size, shape = self._highlightSettings[self._highlightMode] d *= size @@ -286,7 +307,9 @@ def drawVertex(self, path, i): def nearestVertex(self, point, epsilon): min_distance = float("inf") min_i = None + point = QtCore.QPointF(point.x() * self.scale, point.y() * self.scale) for i, p in enumerate(self.points): + p = QtCore.QPointF(p.x() * self.scale, p.y() * self.scale) dist = labelme.utils.distance(p - point) if dist <= epsilon and dist < min_distance: min_distance = dist @@ -296,8 +319,13 @@ def nearestVertex(self, point, epsilon): def nearestEdge(self, point, epsilon): min_distance = float("inf") post_i = None + point = QtCore.QPointF(point.x() * self.scale, point.y() * self.scale) for i in range(len(self.points)): - line = [self.points[i - 1], self.points[i]] + start = self.points[i - 1] + end = self.points[i] + start = QtCore.QPointF(start.x() * self.scale, start.y() * self.scale) + end = QtCore.QPointF(end.x() * self.scale, end.y() * self.scale) + line = [start, end] dist = labelme.utils.distancetoline(point, line) if dist <= epsilon and dist < min_distance: min_distance = dist @@ -319,27 +347,16 @@ def containsPoint(self, point): return self.mask[y, x] return self.makePath().contains(point) - def getCircleRectFromLine(self, line): - """Computes parameters to draw with `QPainterPath::addEllipse`""" - if len(line) != 2: - return None - (c, point) = line - r = line[0] - line[1] - d = math.sqrt(math.pow(r.x(), 2) + math.pow(r.y(), 2)) - rectangle = QtCore.QRectF(c.x() - d, c.y() - d, 2 * d, 2 * d) - return rectangle - def makePath(self): if self.shape_type in ["rectangle", "mask"]: path = QtGui.QPainterPath() if len(self.points) == 2: - rectangle = self.getRectFromLine(*self.points) - path.addRect(rectangle) + path.addRect(QtCore.QRectF(self.points[0], self.points[1])) elif self.shape_type == "circle": path = QtGui.QPainterPath() if len(self.points) == 2: - rectangle = self.getCircleRectFromLine(self.points) - path.addEllipse(rectangle) + raidus = labelme.utils.distance(self.points[0] - self.points[1]) + path.addEllipse(self.points[0], raidus, raidus) else: path = QtGui.QPainterPath(self.points[0]) for p in self.points[1:]: diff --git a/labelme/widgets/canvas.py b/labelme/widgets/canvas.py index b78a5fd2a..20402f4eb 100644 --- a/labelme/widgets/canvas.py +++ b/labelme/widgets/canvas.py @@ -331,8 +331,8 @@ def mouseMoveEvent(self, ev): for shape in reversed([s for s in self.shapes if self.isVisible(s)]): # Look for a nearby vertex to highlight. If that fails, # check if we happen to be inside a shape. - index = shape.nearestVertex(pos, self.epsilon / self.scale) - index_edge = shape.nearestEdge(pos, self.epsilon / self.scale) + index = shape.nearestVertex(pos, self.epsilon) + index_edge = shape.nearestEdge(pos, self.epsilon) if index is not None: if self.selectedVertex(): self.hShape.highlightClear() @@ -439,9 +439,11 @@ def mousePressEvent(self, ev): elif not self.outOfPixmap(pos): # Create new shape. self.current = Shape( - shape_type="points" - if self.createMode in ["ai_polygon", "ai_mask"] - else self.createMode + shape_type=( + "points" + if self.createMode in ["ai_polygon", "ai_mask"] + else self.createMode + ) ) self.current.addPoint(pos, label=0 if is_shift_pressed else 1) if self.createMode == "point": @@ -933,9 +935,11 @@ def wheelEvent(self, ev): else: self.scrollRequest.emit( ev.delta(), - QtCore.Qt.Horizontal - if (QtCore.Qt.ShiftModifier == int(mods)) - else QtCore.Qt.Vertical, + ( + QtCore.Qt.Horizontal + if (QtCore.Qt.ShiftModifier == int(mods)) + else QtCore.Qt.Vertical + ), ) else: self.scrollRequest.emit(ev.delta(), QtCore.Qt.Horizontal)