PyImageLabeling 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. PyImageLabeling/__init__.py +22 -0
  2. PyImageLabeling/config.json +289 -0
  3. PyImageLabeling/controller/Controller.py +25 -0
  4. PyImageLabeling/controller/Events.py +147 -0
  5. PyImageLabeling/controller/FileEvents.py +69 -0
  6. PyImageLabeling/controller/ImageEvents.py +32 -0
  7. PyImageLabeling/controller/LabelEvents.py +219 -0
  8. PyImageLabeling/controller/LabelingEvents.py +123 -0
  9. PyImageLabeling/controller/settings/ContourFillinSetting.py +93 -0
  10. PyImageLabeling/controller/settings/CoutourFillingApplyCancel.py +37 -0
  11. PyImageLabeling/controller/settings/EraserSetting.py +73 -0
  12. PyImageLabeling/controller/settings/LabelSetting.py +91 -0
  13. PyImageLabeling/controller/settings/MagicPenSetting.py +125 -0
  14. PyImageLabeling/controller/settings/OpacitySetting.py +66 -0
  15. PyImageLabeling/controller/settings/PaintBrushSetting.py +66 -0
  16. PyImageLabeling/icons/apply.png +0 -0
  17. PyImageLabeling/icons/asterisk-green.png +0 -0
  18. PyImageLabeling/icons/asterisk-red.png +0 -0
  19. PyImageLabeling/icons/back.png +0 -0
  20. PyImageLabeling/icons/border.png +0 -0
  21. PyImageLabeling/icons/cancel.png +0 -0
  22. PyImageLabeling/icons/cleaner.png +0 -0
  23. PyImageLabeling/icons/close.png +0 -0
  24. PyImageLabeling/icons/down.png +0 -0
  25. PyImageLabeling/icons/ellipse.png +0 -0
  26. PyImageLabeling/icons/eraser.png +0 -0
  27. PyImageLabeling/icons/filling.png +0 -0
  28. PyImageLabeling/icons/logoMAIA.png +0 -0
  29. PyImageLabeling/icons/magic.png +0 -0
  30. PyImageLabeling/icons/maia.png +0 -0
  31. PyImageLabeling/icons/maia1.png +0 -0
  32. PyImageLabeling/icons/maia3.ico +0 -0
  33. PyImageLabeling/icons/maia_icon.png +0 -0
  34. PyImageLabeling/icons/move.png +0 -0
  35. PyImageLabeling/icons/opacity.png +0 -0
  36. PyImageLabeling/icons/open_image.png +0 -0
  37. PyImageLabeling/icons/open_layer.png +0 -0
  38. PyImageLabeling/icons/paint.png +0 -0
  39. PyImageLabeling/icons/plus.png +0 -0
  40. PyImageLabeling/icons/polygon.png +0 -0
  41. PyImageLabeling/icons/rectangle.png +0 -0
  42. PyImageLabeling/icons/reset.png +0 -0
  43. PyImageLabeling/icons/save.png +0 -0
  44. PyImageLabeling/icons/setting.png +0 -0
  45. PyImageLabeling/icons/transparency.png:Zone.Identifier +4 -0
  46. PyImageLabeling/icons/up.png +0 -0
  47. PyImageLabeling/icons/visibility.png +0 -0
  48. PyImageLabeling/icons/zoom_minus.png +0 -0
  49. PyImageLabeling/icons/zoom_plus.png +0 -0
  50. PyImageLabeling/model/Core.py +795 -0
  51. PyImageLabeling/model/File/Files.py +166 -0
  52. PyImageLabeling/model/File/NextImage.py +36 -0
  53. PyImageLabeling/model/File/PreviousImage.py +19 -0
  54. PyImageLabeling/model/Image/MoveImage.py +32 -0
  55. PyImageLabeling/model/Image/ResetMoveZoomImage.py +16 -0
  56. PyImageLabeling/model/Image/ZoomMinus.py +25 -0
  57. PyImageLabeling/model/Image/ZoomPlus.py +16 -0
  58. PyImageLabeling/model/Labeling/ClearAll.py +22 -0
  59. PyImageLabeling/model/Labeling/ContourFilling.py +135 -0
  60. PyImageLabeling/model/Labeling/Ellipse.py +350 -0
  61. PyImageLabeling/model/Labeling/Eraser.py +131 -0
  62. PyImageLabeling/model/Labeling/MagicPen.py +131 -0
  63. PyImageLabeling/model/Labeling/PaintBrush.py +207 -0
  64. PyImageLabeling/model/Labeling/Polygon.py +279 -0
  65. PyImageLabeling/model/Labeling/Rectangle.py +248 -0
  66. PyImageLabeling/model/Labeling/Undo.py +12 -0
  67. PyImageLabeling/model/Model.py +40 -0
  68. PyImageLabeling/model/Utils.py +40 -0
  69. PyImageLabeling/old_version/label_rectangle_properties.json +6 -0
  70. PyImageLabeling/old_version/main.py +2073 -0
  71. PyImageLabeling/old_version/models/EraseSettingsDialog.py +51 -0
  72. PyImageLabeling/old_version/models/LabeledRectangle.py +80 -0
  73. PyImageLabeling/old_version/models/MagicSettingsDialog.py +119 -0
  74. PyImageLabeling/old_version/models/OverlayOpacityDialog.py +63 -0
  75. PyImageLabeling/old_version/models/PaintSettingsDialog.py +289 -0
  76. PyImageLabeling/old_version/models/PointItem.py +66 -0
  77. PyImageLabeling/old_version/models/ProcessWorker.py +52 -0
  78. PyImageLabeling/old_version/models/ZoomableGraphicsView.py +1214 -0
  79. PyImageLabeling/old_version/models/tools/ContourTool.py +279 -0
  80. PyImageLabeling/old_version/models/tools/EraserTool.py +290 -0
  81. PyImageLabeling/old_version/models/tools/MagicPenTool.py +199 -0
  82. PyImageLabeling/old_version/models/tools/OverlayTool.py +179 -0
  83. PyImageLabeling/old_version/models/tools/PaintTool.py +68 -0
  84. PyImageLabeling/old_version/models/tools/PolygonTool.py +786 -0
  85. PyImageLabeling/old_version/models/tools/RectangleTool.py +1036 -0
  86. PyImageLabeling/parameters.json +1 -0
  87. PyImageLabeling/style.css +611 -0
  88. PyImageLabeling/view/Builder.py +333 -0
  89. PyImageLabeling/view/QBackgroundItem.py +30 -0
  90. PyImageLabeling/view/QWidgets.py +10 -0
  91. PyImageLabeling/view/View.py +226 -0
  92. PyImageLabeling/view/ZoomableGraphicsView.py +91 -0
  93. PyImageLabeling/view/__init__.py +0 -0
  94. pyimagelabeling-1.0.0.dist-info/METADATA +55 -0
  95. pyimagelabeling-1.0.0.dist-info/RECORD +99 -0
  96. pyimagelabeling-1.0.0.dist-info/WHEEL +5 -0
  97. pyimagelabeling-1.0.0.dist-info/licenses/LICENCE +22 -0
  98. pyimagelabeling-1.0.0.dist-info/top_level.txt +2 -0
  99. pypi/publish_pypi.py +18 -0
@@ -0,0 +1,199 @@
1
+ from PyQt6.QtCore import Qt, QPointF
2
+ from PyQt6.QtGui import QColor
3
+ from PyQt6.QtWidgets import QProgressDialog, QMessageBox, QApplication
4
+ from models.ProcessWorker import ProcessWorker
5
+ from models.PointItem import PointItem
6
+ import time
7
+
8
+ class MagicPenTool:
9
+ def fill_shape(self, scene_pos):
10
+ """Fill a shape with points using magic pen"""
11
+ if not self.base_pixmap:
12
+ return
13
+
14
+ # Create progress dialog
15
+ progress = QProgressDialog("Processing magic pen fill...", "Cancel", 0, 0, self)
16
+ progress.setWindowModality(Qt.WindowModality.WindowModal)
17
+ progress.show()
18
+
19
+ # Create worker thread for fill operation
20
+ self.worker = ProcessWorker(
21
+ self._fill_shape_worker,
22
+ args=[scene_pos],
23
+ timeout=self.process_timeout
24
+ )
25
+ self.worker.finished.connect(
26
+ lambda points: self._handle_fill_complete(points, progress)
27
+ )
28
+ self.worker.error.connect(
29
+ lambda error: self._handle_fill_error(error, progress)
30
+ )
31
+ self.worker.start()
32
+
33
+ def _fill_shape_worker(self, scene_pos):
34
+ """Worker function to fill shape (runs in separate thread, with hard limits)"""
35
+ if not self.raw_image:
36
+ return []
37
+
38
+ image_x = int(scene_pos.x())
39
+ image_y = int(scene_pos.y())
40
+
41
+ width, height = self.raw_image.width(), self.raw_image.height()
42
+
43
+ if not (0 <= image_x < width and 0 <= image_y < height):
44
+ return []
45
+
46
+ # Obtenir la couleur cible
47
+ target_color = QColor(self.raw_image.pixel(image_x, image_y))
48
+ target_hue = target_color.hue()
49
+ target_sat = target_color.saturation()
50
+ target_val = target_color.value()
51
+ tolerance = self.magic_pen_tolerance
52
+
53
+ points_to_create = []
54
+ visited = set()
55
+ start_time = time.time()
56
+
57
+ MAX_POINTS_LIMIT = self.max_points_limite
58
+ directions = [
59
+ (1, 0), (-1, 0), (0, 1), (0, -1),
60
+ (1, 1), (-1, -1), (1, -1), (-1, 1)
61
+ ]
62
+
63
+ from collections import deque
64
+ queue = deque([(image_x, image_y)])
65
+
66
+ try:
67
+ while queue:
68
+ if time.time() - start_time > self.process_timeout:
69
+ print(f"Timeout atteint ({self.process_timeout}s). Annulation du remplissage.")
70
+ return [] # Retourne une liste vide → pas de remplissage
71
+
72
+ if len(points_to_create) >= MAX_POINTS_LIMIT:
73
+ print(f"Trop de points ({MAX_POINTS_LIMIT}). Annulation du remplissage.")
74
+ return [] # Retourne une liste vide → pas de remplissage
75
+
76
+ x, y = queue.popleft()
77
+ if (x, y) in visited:
78
+ continue
79
+
80
+ visited.add((x, y))
81
+
82
+ if not (0 <= x < width and 0 <= y < height):
83
+ continue
84
+
85
+ # Vérification de la couleur avec la tolérance
86
+ current_color = QColor(self.raw_image.pixel(x, y))
87
+ current_hue = current_color.hue()
88
+ current_sat = current_color.saturation()
89
+ current_val = current_color.value()
90
+
91
+ if target_hue == -1 or current_hue == -1:
92
+ if abs(current_val - target_val) > tolerance:
93
+ continue
94
+ else:
95
+ hue_diff = min(abs(current_hue - target_hue),
96
+ 360 - abs(current_hue - target_hue))
97
+
98
+ if (hue_diff > tolerance or
99
+ abs(current_sat - target_sat) > tolerance or
100
+ abs(current_val - target_val) > tolerance):
101
+ continue
102
+
103
+ # Ajouter le point
104
+ points_to_create.append((x, y))
105
+
106
+ # Ajouter les voisins
107
+ for dx, dy in directions:
108
+ new_x, new_y = x + dx, y + dy
109
+ if (new_x, new_y) not in visited:
110
+ queue.append((new_x, new_y))
111
+
112
+ except Exception as e:
113
+ print(f"Erreur pendant le remplissage : {e}")
114
+
115
+ return points_to_create
116
+
117
+ def _handle_fill_complete(self, point_coords, progress):
118
+ """Handle completion of fill operation"""
119
+ if not progress:
120
+ return
121
+
122
+ progress.close()
123
+
124
+ if not point_coords:
125
+ return
126
+
127
+ # Prepare for UI update and point creation
128
+ QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
129
+
130
+ try:
131
+ # Capture point parameters IMMEDIATELY at the time of fill
132
+ current_point_radius = self.point_radius
133
+ current_point_color = self.point_color
134
+ current_point_opacity = self.point_opacity
135
+ current_point_label = self.point_label
136
+
137
+ # Create a batch of points
138
+ chunk_size = 5000 # Process in larger batches for efficiency
139
+ all_new_points = []
140
+
141
+ for i in range(0, len(point_coords), chunk_size):
142
+ chunk = point_coords[i:i + chunk_size]
143
+ batch_points = []
144
+
145
+ # Update progress dialog
146
+ if progress and not progress.wasCanceled():
147
+ progress.setValue(i)
148
+ progress.setMaximum(len(point_coords))
149
+ progress.setLabelText(f"Creating points: {i}/{len(point_coords)}")
150
+ QApplication.processEvents()
151
+
152
+ for x, y in chunk:
153
+ try:
154
+ point_item = PointItem(
155
+ current_point_label,
156
+ x, y,
157
+ current_point_radius, # Use captured radius
158
+ current_point_color, # Use captured color
159
+ current_point_opacity # Use captured opacity
160
+ )
161
+ self.scene.addItem(point_item)
162
+ self.points.append(point_item)
163
+ batch_points.append(point_item)
164
+ except Exception as e:
165
+ print(f"Error creating point at ({x}, {y}): {e}")
166
+ continue
167
+
168
+ all_new_points.extend(batch_points)
169
+
170
+ # Allow UI to update between chunks
171
+ QApplication.processEvents()
172
+
173
+ # Store for undo history
174
+ if all_new_points:
175
+ self.points_history.append(all_new_points)
176
+
177
+ # Update the points overlay
178
+ self.update_points_overlay()
179
+ except Exception as e:
180
+ print(f"Error during fill completion: {e}")
181
+ finally:
182
+ QApplication.restoreOverrideCursor()
183
+
184
+ def _handle_fill_error(self, error, progress):
185
+ """Handle errors during fill operation"""
186
+ if progress:
187
+ progress.close()
188
+ QMessageBox.warning(self, "Error", f"Magic pen fill operation failed: {error}")
189
+
190
+
191
+ def toggle_magic_pen(self, enabled):
192
+ """Toggle magic pen mode on/off"""
193
+ self.magic_pen_mode = enabled
194
+ if enabled:
195
+ self.paint_mode = False
196
+ self.erase_mode = False
197
+ self.setCursor(Qt.CursorShape.CrossCursor)
198
+ else:
199
+ self.setCursor(Qt.CursorShape.ArrowCursor)
@@ -0,0 +1,179 @@
1
+ from PyQt6.QtCore import Qt, QRectF
2
+ from PyQt6.QtGui import QPixmap, QBrush, QPainter, QColor
3
+ from models.OverlayOpacityDialog import OverlayOpacityDialog
4
+
5
+ class OverlayTool:
6
+ def add_overlay(self, overlay_pixmap):
7
+ """
8
+ Add an overlay layer on top of the base image.
9
+
10
+ Parameters:
11
+ overlay_pixmap (QPixmap): The overlay image to be added
12
+ """
13
+ # Remove any existing overlay first
14
+ self.remove_overlay()
15
+
16
+ # Scale the overlay to match the base image size if needed
17
+ if self.base_pixmap and overlay_pixmap.size() != self.base_pixmap.size():
18
+ overlay_pixmap = overlay_pixmap.scaled(
19
+ self.base_pixmap.size(),
20
+ Qt.AspectRatioMode.IgnoreAspectRatio,
21
+ Qt.TransformationMode.SmoothTransformation
22
+ )
23
+
24
+ # Create a pixmap item for the overlay and add it to the scene
25
+ self.overlay_pixmap_item = self.scene.addPixmap(overlay_pixmap)
26
+
27
+ # Position the overlay to align with the base image
28
+ if self.pixmap_item:
29
+ self.overlay_pixmap_item.setPos(self.pixmap_item.pos())
30
+
31
+ # Set the overlay above the base image
32
+ self.overlay_pixmap_item.setZValue(1) # Base image is typically at Z=0
33
+
34
+ # Set default overlay opacity (semi-transparent)
35
+ self.overlay_opacity = 128 # 0-255 range, 128 is 50% opacity
36
+ self.overlay_pixmap_item.setOpacity(self.overlay_opacity / 255.0)
37
+
38
+ # Store a reference to the original overlay pixmap
39
+ self.overlay_original_pixmap = overlay_pixmap
40
+
41
+ # Update the scene
42
+ self.scene.update()
43
+
44
+ return True
45
+
46
+ def remove_overlay(self):
47
+ """Remove overlay if exists"""
48
+ if self.overlay_pixmap_item:
49
+ self.scene.removeItem(self.overlay_pixmap_item)
50
+ self.overlay_pixmap_item = None
51
+ return True
52
+ return False
53
+
54
+ def show_opacity_dialog(self):
55
+ """Show or create the opacity dialog"""
56
+ # Only show if there's an overlay or points
57
+ if not (self.overlay_pixmap_item or self.points):
58
+ return
59
+
60
+ # Always create a fresh dialog with the current opacity value
61
+ # This ensures the dialog always shows the current value
62
+ current_opacity = self.points_opacity
63
+
64
+ # Close any existing dialog
65
+ if hasattr(self, "opacity_dialog") and self.opacity_dialog:
66
+ self.opacity_dialog.close()
67
+
68
+ # Create new dialog with current opacity value
69
+ self.opacity_dialog = OverlayOpacityDialog(self, current_opacity)
70
+ self.opacity_dialog.show()
71
+
72
+ def update_overlay_opacity(self, value):
73
+ """Updates the opacity for all points and overlay with consistent rendering for overlapping points"""
74
+
75
+ self.points_opacity = value # Store the opacity value
76
+
77
+ # If there are no points, just update the overlay opacity if it exists
78
+ if not self.points:
79
+ if self.overlay_pixmap_item:
80
+ self.overlay_opacity = value
81
+ self.overlay_pixmap_item.setOpacity(value / 255.0)
82
+ return
83
+
84
+ # Update the unified point overlay
85
+ self.update_points_overlay()
86
+
87
+ def update_points_overlay(self):
88
+ """Creates or updates the unified points overlay using batched rendering with improved synchronization"""
89
+ # Skip if no base pixmap
90
+ if not self.base_pixmap:
91
+ return
92
+
93
+ # Get the base image dimensions
94
+ width = self.base_pixmap.width()
95
+ height = self.base_pixmap.height()
96
+
97
+ # Create a transparent pixmap for the points if it doesn't exist already
98
+ if not hasattr(self, 'points_pixmap') or self.points_pixmap is None:
99
+ self.points_pixmap = QPixmap(width, height)
100
+ self.points_pixmap.fill(Qt.GlobalColor.transparent)
101
+ self.last_rendered_points_count = 0
102
+
103
+ # Handle undo operations - rebuild the pixmap if points were removed
104
+ if hasattr(self, 'last_rendered_points_count') and self.last_rendered_points_count > len(self.points):
105
+ # Reset the pixmap and force full redraw
106
+ self.points_pixmap.fill(Qt.GlobalColor.transparent)
107
+ self.last_rendered_points_count = 0
108
+
109
+ # Always render all points when last_rendered_points_count is 0
110
+ if self.last_rendered_points_count == 0:
111
+ points_to_render = self.points
112
+ else:
113
+ # Only render the new points that haven't been rendered yet
114
+ points_to_render = self.points[self.last_rendered_points_count:]
115
+
116
+ # Create a painter even if there are no new points to render
117
+ # This ensures the pixmap is properly updated when undoing to empty state
118
+ painter = QPainter(self.points_pixmap)
119
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
120
+ painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Source)
121
+
122
+ # Store the last used color and brush to avoid unnecessary brush changes
123
+ last_color = None
124
+
125
+ # Draw only the new points onto the unified pixmap
126
+ for point_item in points_to_render:
127
+ pos = point_item.get_position()
128
+ radius = point_item._fixed_radius # Use the stored radius
129
+
130
+ # Get the point's individual color (if points can have different colors)
131
+ if hasattr(point_item, '_color'):
132
+ current_color = point_item._color
133
+ else:
134
+ current_color = self.point_color
135
+
136
+ # Only change the brush if the color changes
137
+ if current_color != last_color:
138
+ color = QColor(current_color)
139
+ painter.setBrush(QBrush(color))
140
+ painter.setPen(Qt.PenStyle.NoPen) # No outline, just filled circles
141
+ last_color = current_color
142
+
143
+ painter.drawEllipse(pos, radius, radius)
144
+
145
+ # Ensure point is properly hidden
146
+ point_item.setVisible(False)
147
+
148
+ painter.end()
149
+
150
+ # Update our count of rendered points
151
+ self.last_rendered_points_count = len(self.points)
152
+
153
+ # Create or update the overlay item rather than removing and recreating
154
+ if not hasattr(self, 'points_overlay_item') or self.points_overlay_item is None:
155
+ # Create new overlay item with the unified points
156
+ self.points_overlay_item = self.scene.addPixmap(self.points_pixmap)
157
+ self.points_overlay_item.setZValue(1) # Above base image, below any other overlays
158
+ else:
159
+ # Update existing overlay with new pixmap
160
+ self.points_overlay_item.setPixmap(self.points_pixmap)
161
+
162
+ # Apply current opacity
163
+ self.points_overlay_item.setOpacity(self.points_opacity / 255.0)
164
+
165
+ # Calculate the region that needs updating
166
+ update_rect = QRectF()
167
+ for point in points_to_render:
168
+ pos = point.get_position()
169
+ r = point._fixed_radius
170
+ point_rect = QRectF(pos.x() - r, pos.y() - r, r * 2, r * 2)
171
+ if update_rect.isEmpty():
172
+ update_rect = point_rect
173
+ else:
174
+ update_rect = update_rect.united(point_rect)
175
+
176
+ # Add a small margin and update only the changed area
177
+ if not update_rect.isEmpty():
178
+ update_rect.adjust(-2, -2, 2, 2)
179
+ self.scene.update(update_rect)
@@ -0,0 +1,68 @@
1
+ from PyQt6.QtCore import Qt
2
+ from models.PointItem import PointItem
3
+ import numpy as np
4
+
5
+ class PaintTool:
6
+ def add_point(self, scene_pos):
7
+ current_radius = self.point_radius
8
+ current_color = self.point_color
9
+ current_opacity = self.point_opacity
10
+ current_label = self.point_label
11
+ point_item = PointItem(
12
+ current_label,
13
+ scene_pos.x(), scene_pos.y(),
14
+ current_radius,
15
+ current_color,
16
+ current_opacity
17
+ )
18
+ self.scene.addItem(point_item)
19
+ point_item.setVisible(False)
20
+ self.points.append(point_item)
21
+ self.current_stroke.append(point_item)
22
+ self.update_points_overlay()
23
+
24
+ def draw_continuous_line(self, start_pos, end_pos):
25
+ if not start_pos or not end_pos:
26
+ return
27
+ current_point_radius = self.point_radius
28
+ current_point_color = self.point_color
29
+ current_point_opacity = self.point_opacity
30
+ current_point_label = self.point_label
31
+ distance = ((end_pos.x() - start_pos.x()) ** 2 + (end_pos.y() - start_pos.y()) ** 2) ** 0.5
32
+ num_steps = max(int(distance * 2), 1)
33
+ t_values = np.linspace(0, 1, num_steps + 1)
34
+ for t in t_values:
35
+ x = start_pos.x() + t * (end_pos.x() - start_pos.x())
36
+ y = start_pos.y() + t * (end_pos.y() - start_pos.y())
37
+ point_item = PointItem(
38
+ current_point_label,
39
+ x, y,
40
+ current_point_radius,
41
+ current_point_color,
42
+ current_point_opacity
43
+ )
44
+ self.scene.addItem(point_item)
45
+ point_item.setVisible(self.drawn_points_visible)
46
+ self.points.append(point_item)
47
+ self.current_stroke.append(point_item)
48
+ self.update_points_overlay()
49
+
50
+ def toggle_paint_mode(self, enabled):
51
+ self.paint_mode = enabled
52
+ if enabled:
53
+ self.erase_mode = False
54
+ self.magic_pen_mode = False
55
+ self.setCursor(Qt.CursorShape.CrossCursor)
56
+ self.setDragMode(self.DragMode.NoDrag)
57
+ else:
58
+ self.setCursor(Qt.CursorShape.ArrowCursor)
59
+ self.setDragMode(self.DragMode.ScrollHandDrag)
60
+
61
+ def clear_points(self):
62
+ self.points_history.append(self.points[:])
63
+ self.points = []
64
+ if hasattr(self, 'points_overlay_item') and self.points_overlay_item:
65
+ self.scene.removeItem(self.points_overlay_item)
66
+ self.points_overlay_item = None
67
+ self.points_pixmap = None
68
+ self.last_rendered_points_count = 0