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
@@ -1,27 +1,19 @@
1
1
  import warnings
2
2
 
3
- warnings.filterwarnings("ignore", category=DeprecationWarning)
4
-
5
- import cv2
6
- import math
7
- import numpy as np
8
-
9
3
  from rasterio.windows import Window
10
- from shapely.geometry import Point
11
- from shapely.geometry import Polygon, LineString
12
4
 
13
5
  from PyQt5.QtCore import Qt, QPointF
14
-
15
- from PyQt5.QtWidgets import (QGraphicsScene, QGraphicsPolygonItem, QGraphicsPathItem,
16
- QGraphicsItem, QGraphicsItemGroup, QMessageBox)
17
-
18
- from PyQt5.QtGui import (QPixmap, QColor, QPen, QBrush, QPolygonF,
19
- QPainter, QRegion, QImage, QPainterPath)
6
+ from PyQt5.QtWidgets import (QGraphicsScene, QGraphicsPathItem, QGraphicsItemGroup,)
7
+ from PyQt5.QtGui import (QPixmap, QColor, QPen,
8
+ QBrush, QPolygonF, QPainter,
9
+ QRegion, QImage, QPainterPath)
20
10
 
21
11
  from coralnet_toolbox.Annotations.QtAnnotation import Annotation
22
12
 
23
13
  from coralnet_toolbox.utilities import rasterio_to_cropped_image
24
14
 
15
+ warnings.filterwarnings("ignore", category=DeprecationWarning)
16
+
25
17
 
26
18
  # ----------------------------------------------------------------------------------------------------------------------
27
19
  # Classes
@@ -115,29 +107,42 @@ class MultiPolygonAnnotation(Annotation):
115
107
  return QPointF(self.cropped_bbox[2], self.cropped_bbox[3])
116
108
 
117
109
  def get_cropped_image_graphic(self):
118
- """Return a QPixmap of the cropped image masked by the annotation polygons."""
110
+ """Return a QPixmap of the cropped image masked by all annotation polygons and their holes."""
111
+ from PyQt5.QtGui import QTransform
112
+
119
113
  if not self.cropped_image:
120
114
  return None
121
115
 
122
- # Create a transparent image to use as a mask
116
+ # --- Create a single QPainterPath for all polygons, with translated coordinates ---
117
+ # The path needs coordinates relative to the cropped image's top-left corner.
118
+ offset_x = self.cropped_bbox[0]
119
+ offset_y = self.cropped_bbox[1]
120
+
121
+ # We will combine all sub-polygon paths into one main path for drawing.
122
+ full_path = QPainterPath()
123
+ for poly in self.polygons:
124
+ # Get the individual polygon's path (which includes its holes)
125
+ poly_path = poly.get_painter_path()
126
+
127
+ # Translate the path to the cropped image's coordinate system and add to the main path
128
+ transform = QTransform().translate(-offset_x, -offset_y)
129
+ full_path.addPath(transform.map(poly_path))
130
+
131
+ # --- Create the mask using the combined path ---
123
132
  masked_image = QImage(self.cropped_image.size(), QImage.Format_ARGB32)
124
133
  masked_image.fill(Qt.transparent)
125
- painter = QPainter(masked_image)
126
- painter.setBrush(QBrush(Qt.white))
127
- painter.setPen(Qt.NoPen)
134
+ mask_painter = QPainter(masked_image)
135
+ mask_painter.setBrush(QBrush(Qt.white))
136
+ mask_painter.setPen(Qt.NoPen)
137
+ mask_painter.drawPath(full_path) # Draw the complete path with all polygons and holes
138
+ mask_painter.end()
128
139
 
129
- # Draw each polygon (shifted to cropped bbox) in white on the mask
130
- for poly in self.polygons:
131
- cropped_points = [QPointF(p.x() - self.cropped_bbox[0], p.y() - self.cropped_bbox[1]) for p in poly.points]
132
- painter.drawPolygon(QPolygonF(cropped_points))
133
- painter.end()
134
-
135
- # Convert the mask image to a pixmap and create a mask bitmap
140
+ # Convert the mask image to a QRegion for clipping
136
141
  mask_pixmap = QPixmap.fromImage(masked_image)
137
142
  mask_bitmap = mask_pixmap.createMaskFromColor(Qt.white, Qt.MaskOutColor)
138
143
  mask_region = QRegion(mask_bitmap)
139
144
 
140
- # Prepare the final pixmap for the cropped image
145
+ # --- Compose the final graphic ---
141
146
  cropped_image_graphic = QPixmap(self.cropped_image.size())
142
147
  result_painter = QPainter(cropped_image_graphic)
143
148
  result_painter.setRenderHint(QPainter.Antialiasing)
@@ -151,15 +156,11 @@ class MultiPolygonAnnotation(Annotation):
151
156
  result_painter.setClipRegion(mask_region)
152
157
  result_painter.drawPixmap(0, 0, self.cropped_image)
153
158
 
154
- # Draw dashed outline for each polygon
155
- pen = QPen(Qt.black)
156
- pen.setStyle(Qt.SolidLine) # Solid line
157
- pen.setWidth(1) # Line width
159
+ # Draw the outline for the combined path
160
+ pen = QPen(Qt.black, 1, Qt.SolidLine)
158
161
  result_painter.setPen(pen)
159
162
  result_painter.setClipping(False)
160
- for poly in self.polygons:
161
- cropped_points = [QPointF(p.x() - self.cropped_bbox[0], p.y() - self.cropped_bbox[1]) for p in poly.points]
162
- result_painter.drawPolygon(QPolygonF(cropped_points))
163
+ result_painter.drawPath(full_path) # Draw outlines for all polygons and holes
163
164
  result_painter.end()
164
165
 
165
166
  return cropped_image_graphic
@@ -190,6 +191,9 @@ class MultiPolygonAnnotation(Annotation):
190
191
 
191
192
  def create_graphics_item(self, scene: QGraphicsScene):
192
193
  """Create and add QGraphicsItems for all polygons to the scene."""
194
+ # This import is necessary to check the type of the created item
195
+ from PyQt5.QtWidgets import QGraphicsPathItem
196
+
193
197
  # Remove old group if it exists
194
198
  if self.graphics_item_group and self.graphics_item_group.scene():
195
199
  self.graphics_item_group.scene().removeItem(self.graphics_item_group)
@@ -200,16 +204,20 @@ class MultiPolygonAnnotation(Annotation):
200
204
  # Create a new group to hold all polygon items
201
205
  self.graphics_item_group = QGraphicsItemGroup()
202
206
 
203
- # Add each polygon as a QGraphicsPolygonItem to the group
207
+ # Add each polygon as a QGraphicsPathItem to the group
204
208
  for poly in self.polygons:
205
- item = QGraphicsPolygonItem(QPolygonF(poly.points))
209
+ # Get the path with holes from the PolygonAnnotation object
210
+ path = poly.get_painter_path()
211
+
212
+ # Create a QGraphicsPathItem which can render holes
213
+ item = QGraphicsPathItem(path)
214
+
215
+ # Apply styling
206
216
  color = QColor(self.label.color)
207
217
  color.setAlpha(self.transparency)
208
-
209
- # Use the consolidated pen creation method
210
218
  item.setPen(self._create_pen(color))
211
219
  item.setBrush(QBrush(color))
212
- item.setData(0, self.id) # <-- Enable selection by id
220
+ item.setData(0, self.id)
213
221
  self.graphics_item_group.addToGroup(item)
214
222
 
215
223
  # Add centroid and bounding box helper graphics to the group
@@ -225,27 +233,35 @@ class MultiPolygonAnnotation(Annotation):
225
233
 
226
234
  def update_graphics_item(self):
227
235
  """Update the QGraphicsItems for all polygons and helper graphics."""
236
+ # This import is necessary to check the type of the created item
237
+ from PyQt5.QtWidgets import QGraphicsPathItem
238
+
239
+ scene = None
228
240
  # If a graphics item group exists and is in a scene, remove it before updating
229
241
  if self.graphics_item_group and self.graphics_item_group.scene():
230
242
  scene = self.graphics_item_group.scene()
231
243
  scene.removeItem(self.graphics_item_group)
232
- self.graphics_item_group = QGraphicsItemGroup()
233
- self.center_graphics_item = None
234
- self.bounding_box_graphics_item = None
235
- self.polygon_graphics_item = None
236
- else:
237
- scene = None
238
244
 
239
- # Recreate QGraphicsPolygonItems for each polygon and add to the group
245
+ # Create a new group to hold all polygon items
246
+ self.graphics_item_group = QGraphicsItemGroup()
247
+ self.center_graphics_item = None
248
+ self.bounding_box_graphics_item = None
249
+ self.polygon_graphics_item = None
250
+
251
+ # Recreate QGraphicsPathItems for each polygon and add to the group
240
252
  for poly in self.polygons:
241
- item = QGraphicsPolygonItem(QPolygonF(poly.points))
253
+ # Get the path with holes from the PolygonAnnotation object
254
+ path = poly.get_painter_path()
255
+
256
+ # Create a QGraphicsPathItem which can render holes
257
+ item = QGraphicsPathItem(path)
258
+
259
+ # Apply styling
242
260
  color = QColor(self.label.color)
243
261
  color.setAlpha(self.transparency)
244
-
245
- # Use the consolidated pen creation method
246
262
  item.setPen(self._create_pen(color))
247
263
  item.setBrush(QBrush(color))
248
- item.setData(0, self.id) # <-- Enable selection by id
264
+ item.setData(0, self.id)
249
265
  self.graphics_item_group.addToGroup(item)
250
266
 
251
267
  # Add centroid and bounding box helper graphics to the group
@@ -257,13 +273,14 @@ class MultiPolygonAnnotation(Annotation):
257
273
  add_to_group=True
258
274
  )
259
275
 
260
- # Add the updated group back to the scene if available
276
+ # Add the updated group back to the scene if it exists
261
277
  if scene:
262
278
  scene.addItem(self.graphics_item_group)
263
279
 
264
280
  def _update_pen_styles(self):
265
281
  """Update pen styles with current animated line offset for all polygon items."""
266
- if not self.is_selected:
282
+ # This check can be simplified to guard the whole method
283
+ if not self.is_selected and not self.animation_timer.isActive():
267
284
  return
268
285
 
269
286
  color = QColor(self.label.color)
@@ -271,17 +288,16 @@ class MultiPolygonAnnotation(Annotation):
271
288
 
272
289
  # Update all polygon items in the group
273
290
  if self.graphics_item_group:
291
+ # We need to check for QGraphicsPathItem, not QGraphicsPolygonItem
274
292
  for item in self.graphics_item_group.childItems():
275
- if isinstance(item, QGraphicsPolygonItem):
293
+ if isinstance(item, QGraphicsPathItem):
276
294
  item.setPen(pen)
277
295
 
278
- # Update helper graphics items
296
+ # Update helper graphics items (no change needed here)
279
297
  if self.center_graphics_item:
280
298
  self.center_graphics_item.setPen(pen)
281
299
  if self.bounding_box_graphics_item:
282
300
  self.bounding_box_graphics_item.setPen(pen)
283
- if self.polygon_graphics_item:
284
- self.polygon_graphics_item.setPen(pen)
285
301
 
286
302
  def update_polygon(self, delta):
287
303
  """Show a warning that MultiPolygonAnnotations should be cut before updating."""