coralnet-toolbox 0.0.73__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.
- coralnet_toolbox/Annotations/QtAnnotation.py +28 -69
- coralnet_toolbox/Annotations/QtMaskAnnotation.py +408 -0
- coralnet_toolbox/Annotations/QtMultiPolygonAnnotation.py +72 -56
- coralnet_toolbox/Annotations/QtPatchAnnotation.py +165 -216
- coralnet_toolbox/Annotations/QtPolygonAnnotation.py +497 -353
- coralnet_toolbox/Annotations/QtRectangleAnnotation.py +126 -116
- coralnet_toolbox/CoralNet/QtDownload.py +2 -1
- coralnet_toolbox/Explorer/QtExplorer.py +16 -14
- coralnet_toolbox/Explorer/QtSettingsWidgets.py +114 -82
- coralnet_toolbox/IO/QtExportTagLabAnnotations.py +30 -10
- coralnet_toolbox/IO/QtImportTagLabAnnotations.py +21 -15
- coralnet_toolbox/IO/QtOpenProject.py +46 -78
- coralnet_toolbox/IO/QtSaveProject.py +18 -43
- coralnet_toolbox/MachineLearning/ExportDataset/QtBase.py +1 -1
- coralnet_toolbox/MachineLearning/ImportDataset/QtBase.py +42 -22
- coralnet_toolbox/MachineLearning/VideoInference/QtBase.py +0 -4
- coralnet_toolbox/QtEventFilter.py +11 -0
- coralnet_toolbox/QtImageWindow.py +117 -68
- coralnet_toolbox/QtLabelWindow.py +13 -1
- coralnet_toolbox/QtMainWindow.py +5 -27
- coralnet_toolbox/QtProgressBar.py +52 -27
- coralnet_toolbox/Rasters/RasterTableModel.py +8 -8
- coralnet_toolbox/SAM/QtDeployPredictor.py +10 -0
- coralnet_toolbox/SeeAnything/QtDeployGenerator.py +779 -161
- coralnet_toolbox/SeeAnything/QtDeployPredictor.py +86 -149
- coralnet_toolbox/Tools/QtCutSubTool.py +18 -2
- coralnet_toolbox/Tools/QtResizeSubTool.py +19 -2
- coralnet_toolbox/Tools/QtSAMTool.py +72 -50
- coralnet_toolbox/Tools/QtSeeAnythingTool.py +8 -5
- coralnet_toolbox/Tools/QtSelectTool.py +27 -3
- coralnet_toolbox/Tools/QtSubtractSubTool.py +66 -0
- coralnet_toolbox/Tools/__init__.py +2 -0
- coralnet_toolbox/__init__.py +1 -1
- coralnet_toolbox/utilities.py +137 -47
- coralnet_toolbox-0.0.74.dist-info/METADATA +375 -0
- {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.74.dist-info}/RECORD +40 -38
- coralnet_toolbox-0.0.73.dist-info/METADATA +0 -341
- {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.74.dist-info}/WHEEL +0 -0
- {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.74.dist-info}/entry_points.txt +0 -0
- {coralnet_toolbox-0.0.73.dist-info → coralnet_toolbox-0.0.74.dist-info}/licenses/LICENSE.txt +0 -0
- {coralnet_toolbox-0.0.73.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.
|
16
|
-
|
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
|
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
|
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
|
-
|
126
|
-
|
127
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
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
|
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
|
207
|
+
# Add each polygon as a QGraphicsPathItem to the group
|
204
208
|
for poly in self.polygons:
|
205
|
-
|
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)
|
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
|
-
#
|
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
|
-
|
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)
|
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
|
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
|
-
|
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,
|
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."""
|