coralnet-toolbox 0.0.73__py2.py3-none-any.whl → 0.0.75__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 (50) 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/CoralNet/QtDownload.py +2 -1
  8. coralnet_toolbox/Explorer/QtDataItem.py +52 -22
  9. coralnet_toolbox/Explorer/QtExplorer.py +293 -1614
  10. coralnet_toolbox/Explorer/QtSettingsWidgets.py +203 -85
  11. coralnet_toolbox/Explorer/QtViewers.py +1568 -0
  12. coralnet_toolbox/Explorer/transformer_models.py +59 -0
  13. coralnet_toolbox/Explorer/yolo_models.py +112 -0
  14. coralnet_toolbox/IO/QtExportTagLabAnnotations.py +30 -10
  15. coralnet_toolbox/IO/QtImportTagLabAnnotations.py +21 -15
  16. coralnet_toolbox/IO/QtOpenProject.py +46 -78
  17. coralnet_toolbox/IO/QtSaveProject.py +18 -43
  18. coralnet_toolbox/MachineLearning/ExportDataset/QtBase.py +1 -1
  19. coralnet_toolbox/MachineLearning/ImportDataset/QtBase.py +253 -141
  20. coralnet_toolbox/MachineLearning/VideoInference/QtBase.py +0 -4
  21. coralnet_toolbox/MachineLearning/VideoInference/YOLO3D/run.py +102 -16
  22. coralnet_toolbox/QtAnnotationWindow.py +16 -10
  23. coralnet_toolbox/QtEventFilter.py +11 -0
  24. coralnet_toolbox/QtImageWindow.py +120 -75
  25. coralnet_toolbox/QtLabelWindow.py +13 -1
  26. coralnet_toolbox/QtMainWindow.py +5 -27
  27. coralnet_toolbox/QtProgressBar.py +52 -27
  28. coralnet_toolbox/Rasters/RasterTableModel.py +28 -8
  29. coralnet_toolbox/SAM/QtDeployGenerator.py +1 -4
  30. coralnet_toolbox/SAM/QtDeployPredictor.py +11 -3
  31. coralnet_toolbox/SeeAnything/QtDeployGenerator.py +805 -162
  32. coralnet_toolbox/SeeAnything/QtDeployPredictor.py +130 -151
  33. coralnet_toolbox/Tools/QtCutSubTool.py +18 -2
  34. coralnet_toolbox/Tools/QtPolygonTool.py +42 -3
  35. coralnet_toolbox/Tools/QtRectangleTool.py +30 -0
  36. coralnet_toolbox/Tools/QtResizeSubTool.py +19 -2
  37. coralnet_toolbox/Tools/QtSAMTool.py +72 -50
  38. coralnet_toolbox/Tools/QtSeeAnythingTool.py +8 -5
  39. coralnet_toolbox/Tools/QtSelectTool.py +27 -3
  40. coralnet_toolbox/Tools/QtSubtractSubTool.py +66 -0
  41. coralnet_toolbox/Tools/__init__.py +2 -0
  42. coralnet_toolbox/__init__.py +1 -1
  43. coralnet_toolbox/utilities.py +158 -47
  44. coralnet_toolbox-0.0.75.dist-info/METADATA +378 -0
  45. {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.75.dist-info}/RECORD +49 -44
  46. coralnet_toolbox-0.0.73.dist-info/METADATA +0 -341
  47. {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.75.dist-info}/WHEEL +0 -0
  48. {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.75.dist-info}/entry_points.txt +0 -0
  49. {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.75.dist-info}/licenses/LICENSE.txt +0 -0
  50. {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.75.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,6 @@
1
1
  import warnings
2
2
 
3
- import time
4
-
5
- from PyQt5.QtCore import pyqtSignal
3
+ from PyQt5.QtCore import pyqtSignal, QPropertyAnimation, QEventLoop
6
4
  from PyQt5.QtWidgets import QProgressBar, QVBoxLayout, QDialog, QPushButton, QApplication
7
5
 
8
6
  warnings.filterwarnings("ignore", category=DeprecationWarning)
@@ -112,16 +110,35 @@ class ProgressBar(QDialog):
112
110
  def update_progress(self, new_title=None):
113
111
  """
114
112
  Increment the progress by one step.
115
- Updates the UI and checks if progress is complete.
113
+ Updates the UI intermittently to improve performance and checks if progress is complete.
116
114
  """
117
115
  if new_title is not None:
118
116
  self.setWindowTitle(new_title)
119
117
 
120
- if not self.canceled:
121
- self.value += 1
118
+ if self.canceled:
119
+ return
120
+
121
+ self.value += 1
122
+
123
+ # --- Performance Improvement ---
124
+ # To avoid excessive UI repaints that slow down the process, we only update
125
+ # the visual progress bar periodically. This aims for about 100 updates
126
+ # over the entire range, ensuring a smooth look without bogging down the main task.
127
+ # 'max(1, ...)' ensures we always have an interval of at least 1.
128
+ update_interval = max(1, self.max_value // 100)
129
+
130
+ # We update the bar visually only under two conditions:
131
+ # 1. It's the very last step, to ensure it always finishes at 100%.
132
+ # 2. The current value is a multiple of our calculated interval.
133
+ is_last_step = self.value >= self.max_value
134
+ is_update_step = self.value % update_interval == 0
135
+
136
+ if is_update_step or is_last_step:
122
137
  self.progress_bar.setValue(self.value)
123
- if self.value >= self.max_value:
124
- self.stop_progress()
138
+
139
+ # This is crucial. It processes pending events, allowing the GUI to
140
+ # redraw with the new progress value and to respond to user input,
141
+ # like clicking the 'Cancel' button.
125
142
  QApplication.processEvents()
126
143
 
127
144
  def update_progress_percentage(self, percentage):
@@ -141,30 +158,38 @@ class ProgressBar(QDialog):
141
158
 
142
159
  def finish_progress(self, duration_ms=500):
143
160
  """
144
- Animate the progress bar to its maximum value regardless of current value.
145
- This creates a visual effect of the progress bar completing over a short duration.
161
+ Animate the progress bar to its maximum value using a non-blocking animation.
162
+ This creates a smooth visual effect of completion without freezing the UI.
146
163
 
147
164
  Args:
148
165
  duration_ms: The duration in milliseconds for the animation (default: 500)
149
166
  """
150
-
151
- # Calculate the steps and delay
152
- start_value = self.value
153
- steps_needed = self.max_value - start_value
154
- if steps_needed <= 0:
155
- self.progress_bar.setValue(self.max_value)
156
- QApplication.processEvents()
167
+ # If the progress is already complete, just set the final value and exit.
168
+ if self.value >= self.max_value:
169
+ self.stop_progress()
157
170
  return
158
-
159
- # Calculate delay between steps (minimum 1ms)
160
- delay = max(duration_ms / steps_needed / 1000, 0.001)
161
-
162
- # Animate the progress
163
- for current in range(start_value + 1, self.max_value + 1):
164
- self.value = current
165
- self.progress_bar.setValue(current)
166
- QApplication.processEvents()
167
- time.sleep(delay)
171
+
172
+ # --- Non-Blocking Animation using QPropertyAnimation ---
173
+ # QPropertyAnimation is the standard Qt way to animate widget properties.
174
+ # It runs on the main event loop, so it does not freeze the application
175
+ # like the previous time.sleep() implementation. The property name "value"
176
+ # is passed as a bytes object (b"value").
177
+ self.animation = QPropertyAnimation(self.progress_bar, b"value")
178
+ self.animation.setDuration(duration_ms)
179
+ self.animation.setStartValue(self.value)
180
+ self.animation.setEndValue(self.max_value)
181
+ self.animation.start()
182
+
183
+ # We run a local event loop that waits for the animation's 'finished'
184
+ # signal. This ensures that the animation completes visually before
185
+ # this method returns control to the calling code, which is often
186
+ # the desired behavior for a "finishing" step.
187
+ loop = QEventLoop()
188
+ self.animation.finished.connect(loop.quit)
189
+ loop.exec_()
190
+
191
+ # Finally, update our internal state variable to match the final progress.
192
+ self.value = self.max_value
168
193
 
169
194
  def stop_progress(self):
170
195
  """
@@ -20,8 +20,9 @@ class RasterTableModel(QAbstractTableModel):
20
20
  Custom table model for displaying a list of Raster objects.
21
21
  """
22
22
  # Column indices
23
- FILENAME_COL = 0
24
- ANNOTATION_COUNT_COL = 1
23
+ CHECKBOX_COL = 0
24
+ FILENAME_COL = 1
25
+ ANNOTATION_COUNT_COL = 2
25
26
 
26
27
  # Row colors
27
28
  HIGHLIGHTED_COLOR = QColor(173, 216, 230) # Light blue
@@ -39,13 +40,10 @@ class RasterTableModel(QAbstractTableModel):
39
40
  self.raster_manager = raster_manager
40
41
  self.filtered_paths: List[str] = []
41
42
 
42
- # We'll remove this separate tracking mechanism to avoid inconsistency
43
- # self.highlighted_paths: Set[str] = set()
44
-
45
- self.column_headers = ["Image Name", "Annotations"]
43
+ self.column_headers = ["\u2713", "Image Name", "Annotations"]
46
44
 
47
45
  # Column widths
48
- self.column_widths = [-1, 120] # -1 means stretch
46
+ self.column_widths = [30, -1, 120] # -1 means stretch
49
47
 
50
48
  # Connect to manager signals
51
49
  self.raster_manager.rasterAdded.connect(self.on_raster_added)
@@ -82,7 +80,9 @@ class RasterTableModel(QAbstractTableModel):
82
80
  raster.set_display_name(max_length=25)
83
81
 
84
82
  if role == Qt.DisplayRole:
85
- if index.column() == self.FILENAME_COL:
83
+ if index.column() == self.CHECKBOX_COL:
84
+ return "\u2713" if raster.checkbox_state else ""
85
+ elif index.column() == self.FILENAME_COL:
86
86
  return raster.display_name
87
87
  elif index.column() == self.ANNOTATION_COUNT_COL:
88
88
  return str(raster.annotation_count)
@@ -149,6 +149,26 @@ class RasterTableModel(QAbstractTableModel):
149
149
  return Qt.NoItemFlags
150
150
 
151
151
  return Qt.ItemIsEnabled | Qt.ItemIsSelectable
152
+
153
+ def add_path(self, path: str):
154
+ """
155
+ Efficiently add a single path by signaling a row insertion.
156
+
157
+ Args:
158
+ path (str): The image path to add.
159
+ """
160
+ if path in self.raster_manager.image_paths and path not in self.filtered_paths:
161
+ # The position for the new row is at the end of the current list
162
+ row_position = len(self.filtered_paths)
163
+
164
+ # Signal that we are about to insert one row at this position
165
+ self.beginInsertRows(QModelIndex(), row_position, row_position)
166
+
167
+ # Add the data
168
+ self.filtered_paths.append(path)
169
+
170
+ # Signal that the insertion is complete
171
+ self.endInsertRows()
152
172
 
153
173
  def highlight_path(self, path: str, highlighted: bool = True):
154
174
  """
@@ -168,7 +168,7 @@ class DeployGeneratorDialog(QDialog):
168
168
 
169
169
  # Image size control
170
170
  self.imgsz_spinbox = QSpinBox()
171
- self.imgsz_spinbox.setRange(512, 65536)
171
+ self.imgsz_spinbox.setRange(1024, 65536)
172
172
  self.imgsz_spinbox.setSingleStep(1024)
173
173
  self.imgsz_spinbox.setValue(self.imgsz)
174
174
  layout.addRow("Image Size (imgsz):", self.imgsz_spinbox)
@@ -460,9 +460,6 @@ class DeployGeneratorDialog(QDialog):
460
460
  progress_bar.stop_progress()
461
461
  progress_bar.close()
462
462
 
463
- # Exit the dialog box
464
- self.accept()
465
-
466
463
  def get_imgsz(self):
467
464
  """Get the image size for the model."""
468
465
  self.imgsz = self.imgsz_spinbox.value()
@@ -142,6 +142,12 @@ class DeployPredictorDialog(QDialog):
142
142
  """
143
143
  group_box = QGroupBox("Parameters")
144
144
  layout = QFormLayout()
145
+
146
+ # Allow holes dropdown
147
+ self.allow_holes_dropdown = QComboBox()
148
+ self.allow_holes_dropdown.addItems(["True", "False"])
149
+ self.allow_holes_dropdown.setCurrentIndex(1) # Default to False
150
+ layout.addRow("Allow Holes:", self.allow_holes_dropdown)
145
151
 
146
152
  # Resize image dropdown
147
153
  self.resize_image_dropdown = QComboBox()
@@ -152,7 +158,7 @@ class DeployPredictorDialog(QDialog):
152
158
 
153
159
  # Image size control
154
160
  self.imgsz_spinbox = QSpinBox()
155
- self.imgsz_spinbox.setRange(512, 65536)
161
+ self.imgsz_spinbox.setRange(1024, 65536)
156
162
  self.imgsz_spinbox.setSingleStep(1024)
157
163
  self.imgsz_spinbox.setValue(self.imgsz)
158
164
  layout.addRow("Image Size (imgsz):", self.imgsz_spinbox)
@@ -236,6 +242,10 @@ class DeployPredictorDialog(QDialog):
236
242
  group_box.setLayout(layout)
237
243
  self.layout.addWidget(group_box)
238
244
 
245
+ def get_allow_holes(self):
246
+ """Return the current setting for allowing holes."""
247
+ return self.allow_holes_dropdown.currentText() == "True"
248
+
239
249
  def initialize_uncertainty_threshold(self):
240
250
  """Initialize the uncertainty threshold slider with the current value"""
241
251
  current_value = self.main_window.get_uncertainty_thresh()
@@ -348,8 +358,6 @@ class DeployPredictorDialog(QDialog):
348
358
  progress_bar.stop_progress()
349
359
  progress_bar.close()
350
360
 
351
- self.accept()
352
-
353
361
  def resize_image(self, image):
354
362
  """
355
363
  Resize the image to the specified size.