coralnet-toolbox 0.0.72__py2.py3-none-any.whl → 0.0.74__py2.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 (57) hide show
  1. coralnet_toolbox/Annotations/QtAnnotation.py +28 -69
  2. coralnet_toolbox/Annotations/QtMaskAnnotation.py +408 -0
  3. coralnet_toolbox/Annotations/QtMultiPolygonAnnotation.py +72 -56
  4. coralnet_toolbox/Annotations/QtPatchAnnotation.py +165 -216
  5. coralnet_toolbox/Annotations/QtPolygonAnnotation.py +497 -353
  6. coralnet_toolbox/Annotations/QtRectangleAnnotation.py +126 -116
  7. coralnet_toolbox/AutoDistill/QtDeployModel.py +23 -12
  8. coralnet_toolbox/CoralNet/QtDownload.py +2 -1
  9. coralnet_toolbox/Explorer/QtDataItem.py +1 -1
  10. coralnet_toolbox/Explorer/QtExplorer.py +159 -17
  11. coralnet_toolbox/Explorer/QtSettingsWidgets.py +160 -86
  12. coralnet_toolbox/IO/QtExportTagLabAnnotations.py +30 -10
  13. coralnet_toolbox/IO/QtImportTagLabAnnotations.py +21 -15
  14. coralnet_toolbox/IO/QtOpenProject.py +46 -78
  15. coralnet_toolbox/IO/QtSaveProject.py +18 -43
  16. coralnet_toolbox/MachineLearning/DeployModel/QtDetect.py +22 -11
  17. coralnet_toolbox/MachineLearning/DeployModel/QtSegment.py +22 -10
  18. coralnet_toolbox/MachineLearning/ExportDataset/QtBase.py +61 -24
  19. coralnet_toolbox/MachineLearning/ExportDataset/QtClassify.py +5 -1
  20. coralnet_toolbox/MachineLearning/ExportDataset/QtDetect.py +19 -6
  21. coralnet_toolbox/MachineLearning/ExportDataset/QtSegment.py +21 -8
  22. coralnet_toolbox/MachineLearning/ImportDataset/QtBase.py +42 -22
  23. coralnet_toolbox/MachineLearning/VideoInference/QtBase.py +0 -4
  24. coralnet_toolbox/QtAnnotationWindow.py +42 -14
  25. coralnet_toolbox/QtEventFilter.py +19 -2
  26. coralnet_toolbox/QtImageWindow.py +134 -86
  27. coralnet_toolbox/QtLabelWindow.py +14 -2
  28. coralnet_toolbox/QtMainWindow.py +122 -9
  29. coralnet_toolbox/QtProgressBar.py +52 -27
  30. coralnet_toolbox/Rasters/QtRaster.py +59 -7
  31. coralnet_toolbox/Rasters/RasterTableModel.py +42 -14
  32. coralnet_toolbox/SAM/QtBatchInference.py +0 -2
  33. coralnet_toolbox/SAM/QtDeployGenerator.py +22 -11
  34. coralnet_toolbox/SAM/QtDeployPredictor.py +10 -0
  35. coralnet_toolbox/SeeAnything/QtBatchInference.py +19 -221
  36. coralnet_toolbox/SeeAnything/QtDeployGenerator.py +1634 -0
  37. coralnet_toolbox/SeeAnything/QtDeployPredictor.py +107 -154
  38. coralnet_toolbox/SeeAnything/QtTrainModel.py +115 -45
  39. coralnet_toolbox/SeeAnything/__init__.py +2 -0
  40. coralnet_toolbox/Tools/QtCutSubTool.py +18 -2
  41. coralnet_toolbox/Tools/QtResizeSubTool.py +19 -2
  42. coralnet_toolbox/Tools/QtSAMTool.py +222 -57
  43. coralnet_toolbox/Tools/QtSeeAnythingTool.py +223 -55
  44. coralnet_toolbox/Tools/QtSelectSubTool.py +6 -4
  45. coralnet_toolbox/Tools/QtSelectTool.py +27 -3
  46. coralnet_toolbox/Tools/QtSubtractSubTool.py +66 -0
  47. coralnet_toolbox/Tools/QtWorkAreaTool.py +25 -13
  48. coralnet_toolbox/Tools/__init__.py +2 -0
  49. coralnet_toolbox/__init__.py +1 -1
  50. coralnet_toolbox/utilities.py +137 -47
  51. coralnet_toolbox-0.0.74.dist-info/METADATA +375 -0
  52. {coralnet_toolbox-0.0.72.dist-info → coralnet_toolbox-0.0.74.dist-info}/RECORD +56 -53
  53. coralnet_toolbox-0.0.72.dist-info/METADATA +0 -341
  54. {coralnet_toolbox-0.0.72.dist-info → coralnet_toolbox-0.0.74.dist-info}/WHEEL +0 -0
  55. {coralnet_toolbox-0.0.72.dist-info → coralnet_toolbox-0.0.74.dist-info}/entry_points.txt +0 -0
  56. {coralnet_toolbox-0.0.72.dist-info → coralnet_toolbox-0.0.74.dist-info}/licenses/LICENSE.txt +0 -0
  57. {coralnet_toolbox-0.0.72.dist-info → coralnet_toolbox-0.0.74.dist-info}/top_level.txt +0 -0
@@ -87,9 +87,16 @@ class CutSubTool(SubTool):
87
87
  self._update_cut_line_path(position)
88
88
 
89
89
  def keyPressEvent(self, event):
90
- """Handle key press events for canceling the cut."""
91
- if event.key() in (Qt.Key_Backspace, Qt.Key_Escape):
90
+ """Handle key press events for cutting operations."""
91
+ # Check for Ctrl+X to toggle cutting mode off
92
+ if event.modifiers() & Qt.ControlModifier and event.key() == Qt.Key_X:
92
93
  self.parent_tool.deactivate_subtool()
94
+ return
95
+
96
+ # Handle Backspace to clear the current cutting line but stay in cutting mode
97
+ if event.key() == Qt.Key_Backspace:
98
+ self._clear_cutting_line()
99
+ return
93
100
 
94
101
  def _start_drawing_cut_line(self, position):
95
102
  """Start drawing the cut line from the given position."""
@@ -115,6 +122,15 @@ class CutSubTool(SubTool):
115
122
  path.lineTo(point)
116
123
  self.cutting_path_item.setPath(path)
117
124
 
125
+ def _clear_cutting_line(self):
126
+ """Clear the current cutting line but remain in cutting mode."""
127
+ self.cutting_points = []
128
+ self.drawing_in_progress = False
129
+ if self.cutting_path_item:
130
+ self.annotation_window.scene.removeItem(self.cutting_path_item)
131
+ self.cutting_path_item = None
132
+ self.annotation_window.scene.update()
133
+
118
134
  def _break_apart_multipolygon(self):
119
135
  """Handle the special case of 'cutting' a MultiPolygonAnnotation."""
120
136
  new_annotations = self.target_annotation.cut()
@@ -124,5 +124,22 @@ class ResizeSubTool(SubTool):
124
124
  }
125
125
 
126
126
  def _get_polygon_handles(self, annotation):
127
- """Return resize handles for a polygon annotation."""
128
- return {f"point_{i}": QPointF(p.x(), p.y()) for i, p in enumerate(annotation.points)}
127
+ """
128
+ Return resize handles for a polygon, including its outer boundary and all holes.
129
+ Uses the new handle format: 'point_{poly_index}_{vertex_index}'.
130
+ """
131
+ handles = {}
132
+
133
+ # 1. Create handles for the outer boundary using the 'outer' keyword.
134
+ for i, p in enumerate(annotation.points):
135
+ handle_name = f"point_outer_{i}"
136
+ handles[handle_name] = QPointF(p.x(), p.y())
137
+
138
+ # 2. Create handles for each of the inner holes using their index.
139
+ if hasattr(annotation, 'holes'):
140
+ for hole_index, hole in enumerate(annotation.holes):
141
+ for vertex_index, p in enumerate(hole):
142
+ handle_name = f"point_{hole_index}_{vertex_index}"
143
+ handles[handle_name] = QPointF(p.x(), p.y())
144
+
145
+ return handles
@@ -1,7 +1,7 @@
1
1
  import warnings
2
2
  import numpy as np
3
3
 
4
- from PyQt5.QtCore import Qt, QPointF, QRectF, QTimer
4
+ from PyQt5.QtCore import Qt, QPointF, QRectF
5
5
  from PyQt5.QtGui import QMouseEvent, QKeyEvent, QPen, QColor, QBrush, QPainterPath
6
6
  from PyQt5.QtWidgets import QMessageBox, QGraphicsEllipseItem, QGraphicsRectItem, QGraphicsPathItem, QApplication
7
7
 
@@ -12,6 +12,7 @@ from coralnet_toolbox.QtWorkArea import WorkArea
12
12
 
13
13
  from coralnet_toolbox.utilities import pixmap_to_numpy
14
14
  from coralnet_toolbox.utilities import simplify_polygon
15
+ from coralnet_toolbox.utilities import polygonize_mask_with_holes
15
16
 
16
17
  warnings.filterwarnings("ignore", category=DeprecationWarning)
17
18
 
@@ -64,6 +65,11 @@ class SAMTool(Tool):
64
65
 
65
66
  # Flag to track if we have active prompts
66
67
  self.has_active_prompts = False
68
+
69
+ # Add state variables for custom working area creation
70
+ self.creating_working_area = False
71
+ self.working_area_start = None
72
+ self.working_area_temp_graphics = None
67
73
 
68
74
  def activate(self):
69
75
  """
@@ -82,6 +88,7 @@ class SAMTool(Tool):
82
88
  self.sam_dialog = None
83
89
  self.cancel_working_area()
84
90
  self.has_active_prompts = False
91
+ self.cancel_working_area_creation()
85
92
 
86
93
  def set_working_area(self):
87
94
  """
@@ -141,7 +148,116 @@ class SAMTool(Tool):
141
148
 
142
149
  self.annotation_window.setCursor(Qt.CrossCursor)
143
150
  self.annotation_window.scene.update()
144
-
151
+
152
+ def set_custom_working_area(self, start_point, end_point):
153
+ """
154
+ Create a working area from custom points selected by the user.
155
+
156
+ Args:
157
+ start_point (QPointF): First corner of the working area
158
+ end_point (QPointF): Opposite corner of the working area
159
+ """
160
+ self.annotation_window.setCursor(Qt.WaitCursor)
161
+
162
+ # Cancel any existing working area
163
+ self.cancel_working_area()
164
+
165
+ # Calculate the rectangle bounds
166
+ left = max(0, int(min(start_point.x(), end_point.x())))
167
+ top = max(0, int(min(start_point.y(), end_point.y())))
168
+ right = min(int(self.annotation_window.pixmap_image.size().width()),
169
+ int(max(start_point.x(), end_point.x())))
170
+ bottom = min(int(self.annotation_window.pixmap_image.size().height()),
171
+ int(max(start_point.y(), end_point.y())))
172
+
173
+ # Ensure minimum size (at least 10x10 pixels)
174
+ if right - left < 10:
175
+ right = min(left + 10, int(self.annotation_window.pixmap_image.size().width()))
176
+ if bottom - top < 10:
177
+ bottom = min(top + 10, int(self.annotation_window.pixmap_image.size().height()))
178
+
179
+ # Original image information
180
+ self.image_path = self.annotation_window.current_image_path
181
+ self.original_image = pixmap_to_numpy(self.annotation_window.pixmap_image)
182
+ self.original_width = self.annotation_window.pixmap_image.size().width()
183
+ self.original_height = self.annotation_window.pixmap_image.size().height()
184
+
185
+ # Create the WorkArea instance
186
+ self.working_area = WorkArea(left, top, right - left, bottom - top, self.image_path)
187
+
188
+ # Get the thickness for the working area graphics
189
+ pen_width = self.graphics_utility.get_workarea_thickness(self.annotation_window)
190
+
191
+ # Create and add the working area graphics
192
+ self.working_area.create_graphics(self.annotation_window.scene, pen_width)
193
+ self.working_area.set_remove_button_visibility(False)
194
+ self.working_area.removed.connect(self.on_working_area_removed)
195
+
196
+ # Create shadow overlay
197
+ shadow_brush = QBrush(QColor(0, 0, 0, 150))
198
+ shadow_path = QPainterPath()
199
+ shadow_path.addRect(self.annotation_window.scene.sceneRect())
200
+ shadow_path.addRect(self.working_area.rect)
201
+ shadow_path = shadow_path.simplified()
202
+
203
+ self.shadow_area = QGraphicsPathItem(shadow_path)
204
+ self.shadow_area.setBrush(shadow_brush)
205
+ self.shadow_area.setPen(QPen(Qt.NoPen))
206
+ self.annotation_window.scene.addItem(self.shadow_area)
207
+
208
+ # Update the working area image in the SAM model
209
+ self.image = self.original_image[top:bottom, left:right]
210
+ self.sam_dialog.set_image(self.image, self.image_path)
211
+
212
+ self.annotation_window.setCursor(Qt.CrossCursor)
213
+ self.annotation_window.scene.update()
214
+
215
+ def display_working_area_preview(self, current_pos):
216
+ """
217
+ Display a preview rectangle for the working area being created.
218
+
219
+ Args:
220
+ current_pos (QPointF): Current mouse position
221
+ """
222
+ if not self.working_area_start:
223
+ return
224
+
225
+ # Remove previous preview if it exists
226
+ if self.working_area_temp_graphics:
227
+ self.annotation_window.scene.removeItem(self.working_area_temp_graphics)
228
+ self.working_area_temp_graphics = None
229
+
230
+ # Create preview rectangle
231
+ rect = QRectF(
232
+ min(self.working_area_start.x(), current_pos.x()),
233
+ min(self.working_area_start.y(), current_pos.y()),
234
+ abs(current_pos.x() - self.working_area_start.x()),
235
+ abs(current_pos.y() - self.working_area_start.y())
236
+ )
237
+
238
+ # Create a dashed blue pen for the working area preview
239
+ pen = QPen(QColor(0, 120, 215))
240
+ pen.setStyle(Qt.DashLine)
241
+ pen.setWidth(2)
242
+
243
+ self.working_area_temp_graphics = QGraphicsRectItem(rect)
244
+ self.working_area_temp_graphics.setPen(pen)
245
+ self.working_area_temp_graphics.setBrush(QBrush(QColor(0, 120, 215, 30))) # Light blue transparent fill
246
+ self.annotation_window.scene.addItem(self.working_area_temp_graphics)
247
+
248
+ def cancel_working_area_creation(self):
249
+ """
250
+ Cancel the process of creating a working area.
251
+ """
252
+ self.creating_working_area = False
253
+ self.working_area_start = None
254
+
255
+ if self.working_area_temp_graphics:
256
+ self.annotation_window.scene.removeItem(self.working_area_temp_graphics)
257
+ self.working_area_temp_graphics = None
258
+
259
+ self.annotation_window.scene.update()
260
+
145
261
  def on_working_area_removed(self, work_area):
146
262
  """
147
263
  Handle when the work area is removed via its internal mechanism.
@@ -254,40 +370,47 @@ class SAMTool(Tool):
254
370
  QApplication.restoreOverrideCursor()
255
371
  return
256
372
 
257
- # Get the points of the top1 mask
373
+ # Get the top confidence prediction's mask tensor
258
374
  top1_index = np.argmax(results.boxes.conf)
259
- predictions = results[top1_index].masks.xy[0]
375
+ mask_tensor = results[top1_index].masks.data
260
376
 
261
- # Safety check: make sure we have predicted points
262
- if len(predictions) == 0:
263
- QApplication.restoreOverrideCursor()
264
- return
377
+ # Check if holes are allowed from the SAM dialog
378
+ allow_holes = self.sam_dialog.get_allow_holes()
265
379
 
266
- # Clean the polygon using Ramer-Douglas-Peucker algorithm
267
- predictions = simplify_polygon(predictions, 0.1)
380
+ # Polygonize the mask to get the exterior and holes
381
+ exterior_coords, holes_coords_list = polygonize_mask_with_holes(mask_tensor)
268
382
 
269
383
  # Safety check: need at least 3 points for a valid polygon
270
- if len(predictions) < 3:
384
+ if len(exterior_coords) < 3:
271
385
  QApplication.restoreOverrideCursor()
272
386
  return
273
387
 
274
- # Move the points back to the original image space
388
+ # --- Process and Clean the Polygon Points ---
275
389
  working_area_top_left = self.working_area.rect.topLeft()
276
- points = [(point[0] + working_area_top_left.x(),
277
- point[1] + working_area_top_left.y()) for point in predictions]
390
+ offset_x, offset_y = working_area_top_left.x(), working_area_top_left.y()
278
391
 
279
- # Convert to QPointF for graphics
280
- self.points = [QPointF(*point) for point in points]
392
+ # Simplify, offset, and convert the exterior points
393
+ simplified_exterior = simplify_polygon(exterior_coords, 0.1)
394
+ self.points = [QPointF(p[0] + offset_x, p[1] + offset_y) for p in simplified_exterior]
281
395
 
282
- # Create the temporary annotation
396
+ # Simplify, offset, and convert each hole only if allowed
397
+ final_holes = []
398
+ if allow_holes:
399
+ for hole_coords in holes_coords_list:
400
+ if len(hole_coords) >= 3: # Ensure holes are also valid polygons
401
+ simplified_hole = simplify_polygon(hole_coords, 0.1)
402
+ final_holes.append([QPointF(p[0] + offset_x, p[1] + offset_y) for p in simplified_hole])
403
+
404
+ # Create the temporary annotation, now with holes (or not)
283
405
  self.temp_annotation = PolygonAnnotation(
284
- self.points,
285
- self.annotation_window.selected_label.short_label_code,
286
- self.annotation_window.selected_label.long_label_code,
287
- self.annotation_window.selected_label.color,
288
- self.annotation_window.current_image_path,
289
- self.annotation_window.selected_label.id,
290
- self.main_window.label_window.active_label.transparency
406
+ points=self.points,
407
+ holes=final_holes,
408
+ short_label_code=self.annotation_window.selected_label.short_label_code,
409
+ long_label_code=self.annotation_window.selected_label.long_label_code,
410
+ color=self.annotation_window.selected_label.color,
411
+ image_path=self.annotation_window.current_image_path,
412
+ label_id=self.annotation_window.selected_label.id,
413
+ transparency=self.main_window.label_window.active_label.transparency
291
414
  )
292
415
 
293
416
  # Create the graphics item for the temporary annotation
@@ -357,11 +480,24 @@ class SAMTool(Tool):
357
480
  "A label must be selected before adding an annotation.")
358
481
  return
359
482
 
360
- if not self.working_area:
361
- return
362
-
363
483
  # Get position in scene coordinates
364
484
  scene_pos = self.annotation_window.mapToScene(event.pos())
485
+
486
+ # Handle working area creation mode
487
+ if not self.working_area and event.button() == Qt.LeftButton:
488
+ if not self.creating_working_area:
489
+ # Start working area creation
490
+ self.creating_working_area = True
491
+ self.working_area_start = scene_pos
492
+ return
493
+ elif self.creating_working_area and self.working_area_start:
494
+ # Finish working area creation
495
+ self.set_custom_working_area(self.working_area_start, scene_pos)
496
+ self.cancel_working_area_creation()
497
+ return
498
+
499
+ if not self.working_area:
500
+ return
365
501
 
366
502
  # Check if position is within working area
367
503
  if not self.working_area.contains_point(scene_pos):
@@ -430,12 +566,17 @@ class SAMTool(Tool):
430
566
  """
431
567
  Handle mouse move events.
432
568
  """
433
- if not self.working_area:
434
- return
435
-
436
569
  scene_pos = self.annotation_window.mapToScene(event.pos())
437
570
  self.hover_pos = scene_pos
438
571
 
572
+ # Update working area preview during creation
573
+ if self.creating_working_area and self.working_area_start:
574
+ self.display_working_area_preview(scene_pos)
575
+ return
576
+
577
+ if not self.working_area:
578
+ return
579
+
439
580
  # Update rectangle during drawing
440
581
  if self.drawing_rectangle and self.start_point:
441
582
  self.end_point = scene_pos
@@ -461,6 +602,11 @@ class SAMTool(Tool):
461
602
  Handle key press events.
462
603
  """
463
604
  if event.key() == Qt.Key_Space:
605
+ # If creating working area, confirm it
606
+ if self.creating_working_area and self.working_area_start and self.hover_pos:
607
+ self.set_custom_working_area(self.working_area_start, self.hover_pos)
608
+ self.cancel_working_area_creation()
609
+ return
464
610
 
465
611
  # If no working area, set it up
466
612
  if not self.working_area:
@@ -473,12 +619,13 @@ class SAMTool(Tool):
473
619
  # Use existing temporary annotation
474
620
  final_annotation = PolygonAnnotation(
475
621
  self.points,
476
- self.annotation_window.selected_label.short_label_code,
477
- self.annotation_window.selected_label.long_label_code,
478
- self.annotation_window.selected_label.color,
479
- self.annotation_window.current_image_path,
480
- self.annotation_window.selected_label.id,
481
- self.main_window.label_window.active_label.transparency
622
+ self.temp_annotation.label.short_label_code,
623
+ self.temp_annotation.label.long_label_code,
624
+ self.temp_annotation.label.color,
625
+ self.temp_annotation.image_path,
626
+ self.temp_annotation.label.id,
627
+ self.temp_annotation.label.transparency,
628
+ holes=self.temp_annotation.holes
482
629
  )
483
630
 
484
631
  # Copy confidence data
@@ -499,7 +646,7 @@ class SAMTool(Tool):
499
646
  final_annotation = self.create_annotation(True)
500
647
  if final_annotation:
501
648
  self.annotation_window.add_annotation_from_tool(final_annotation)
502
- self.clear_prompt_graphics()
649
+ self.clear_prompt_graphics()
503
650
  # If no active prompts, cancel the working area
504
651
  else:
505
652
  self.cancel_working_area()
@@ -507,6 +654,11 @@ class SAMTool(Tool):
507
654
  self.annotation_window.scene.update()
508
655
 
509
656
  elif event.key() == Qt.Key_Backspace:
657
+ # If creating working area, cancel it
658
+ if self.creating_working_area:
659
+ self.cancel_working_area_creation()
660
+ return
661
+
510
662
  # If drawing rectangle, cancel it
511
663
  if self.drawing_rectangle:
512
664
  self.cancel_rectangle_drawing()
@@ -584,24 +736,36 @@ class SAMTool(Tool):
584
736
  QApplication.restoreOverrideCursor()
585
737
  return None
586
738
 
587
- # Get the top confidence prediction
739
+ # Get the top confidence prediction's mask tensor
588
740
  top1_index = np.argmax(results.boxes.conf)
589
- predictions = results[top1_index].masks.xy[0]
741
+ mask_tensor = results[top1_index].masks.data
742
+
743
+ # Check if holes are allowed from the SAM dialog
744
+ allow_holes = self.sam_dialog.get_allow_holes()
590
745
 
591
- # Safety check for predictions
592
- if len(predictions) == 0:
746
+ # Polygonize the mask using the new method to get the exterior and holes
747
+ exterior_coords, holes_coords_list = polygonize_mask_with_holes(mask_tensor)
748
+
749
+ # Safety check for an empty result
750
+ if not exterior_coords:
593
751
  QApplication.restoreOverrideCursor()
594
752
  return None
595
753
 
596
- # Clean polygon points
597
- predictions = simplify_polygon(predictions, 0.1)
598
-
599
- # Move points back to original image space
754
+ # --- Process and Clean the Polygon Points ---
600
755
  working_area_top_left = self.working_area.rect.topLeft()
601
- points = [(point[0] + working_area_top_left.x(),
602
- point[1] + working_area_top_left.y()) for point in predictions]
603
- # Convert to QPointF for graphics
604
- self.points = [QPointF(*point) for point in points]
756
+ offset_x, offset_y = working_area_top_left.x(), working_area_top_left.y()
757
+
758
+ # Simplify, offset, and convert the exterior points
759
+ simplified_exterior = simplify_polygon(exterior_coords, 0.1)
760
+ self.points = [QPointF(p[0] + offset_x, p[1] + offset_y) for p in simplified_exterior]
761
+
762
+ # Simplify, offset, and convert each hole only if allowed
763
+ final_holes = []
764
+ if allow_holes:
765
+ for hole_coords in holes_coords_list:
766
+ if len(hole_coords) >= 3:
767
+ simplified_hole = simplify_polygon(hole_coords, 0.1)
768
+ final_holes.append([QPointF(p[0] + offset_x, p[1] + offset_y) for p in simplified_hole])
605
769
 
606
770
  # Require at least 3 points for valid polygon
607
771
  if len(self.points) < 3:
@@ -611,15 +775,16 @@ class SAMTool(Tool):
611
775
  # Get confidence score
612
776
  confidence = results.boxes.conf[top1_index].item()
613
777
 
614
- # Create final annotation
778
+ # Create final annotation, now passing the holes argument
615
779
  annotation = PolygonAnnotation(
616
- self.points,
617
- self.annotation_window.selected_label.short_label_code,
618
- self.annotation_window.selected_label.long_label_code,
619
- self.annotation_window.selected_label.color,
620
- self.annotation_window.current_image_path,
621
- self.annotation_window.selected_label.id,
622
- self.main_window.label_window.active_label.transparency
780
+ points=self.points,
781
+ holes=final_holes,
782
+ short_label_code=self.annotation_window.selected_label.short_label_code,
783
+ long_label_code=self.annotation_window.selected_label.long_label_code,
784
+ color=self.annotation_window.selected_label.color,
785
+ image_path=self.annotation_window.current_image_path,
786
+ label_id=self.annotation_window.selected_label.id,
787
+ transparency=self.main_window.label_window.active_label.transparency
623
788
  )
624
789
 
625
790
  # Update confidence