coralnet-toolbox 0.0.75__py2.py3-none-any.whl → 0.0.77__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/QtPolygonAnnotation.py +57 -12
- coralnet_toolbox/Annotations/QtRectangleAnnotation.py +44 -14
- coralnet_toolbox/Common/QtGraphicsUtility.py +18 -8
- coralnet_toolbox/Explorer/transformer_models.py +13 -2
- coralnet_toolbox/IO/QtExportMaskAnnotations.py +576 -402
- coralnet_toolbox/IO/QtImportImages.py +7 -15
- coralnet_toolbox/IO/QtOpenProject.py +15 -19
- coralnet_toolbox/Icons/system_monitor.png +0 -0
- coralnet_toolbox/MachineLearning/ImportDataset/QtBase.py +33 -8
- coralnet_toolbox/QtAnnotationWindow.py +4 -0
- coralnet_toolbox/QtEventFilter.py +5 -5
- coralnet_toolbox/QtImageWindow.py +4 -0
- coralnet_toolbox/QtMainWindow.py +104 -64
- coralnet_toolbox/QtProgressBar.py +1 -0
- coralnet_toolbox/QtSystemMonitor.py +370 -0
- coralnet_toolbox/Rasters/RasterManager.py +5 -2
- coralnet_toolbox/Results/ConvertResults.py +14 -8
- coralnet_toolbox/Results/ResultsProcessor.py +3 -2
- coralnet_toolbox/SAM/QtDeployGenerator.py +1 -1
- coralnet_toolbox/SAM/QtDeployPredictor.py +10 -0
- coralnet_toolbox/SeeAnything/QtDeployGenerator.py +324 -177
- coralnet_toolbox/SeeAnything/QtDeployPredictor.py +10 -6
- coralnet_toolbox/Tile/QtTileBatchInference.py +4 -4
- coralnet_toolbox/Tools/QtPatchTool.py +6 -2
- coralnet_toolbox/Tools/QtPolygonTool.py +5 -3
- coralnet_toolbox/Tools/QtRectangleTool.py +17 -9
- coralnet_toolbox/Tools/QtSAMTool.py +144 -91
- coralnet_toolbox/Tools/QtSeeAnythingTool.py +4 -0
- coralnet_toolbox/Tools/QtTool.py +79 -3
- coralnet_toolbox/Tools/QtWorkAreaTool.py +4 -0
- coralnet_toolbox/Transformers/Models/GroundingDINO.py +72 -0
- coralnet_toolbox/Transformers/Models/OWLViT.py +72 -0
- coralnet_toolbox/Transformers/Models/OmDetTurbo.py +68 -0
- coralnet_toolbox/Transformers/Models/QtBase.py +121 -0
- coralnet_toolbox/{AutoDistill → Transformers}/Models/__init__.py +1 -1
- coralnet_toolbox/{AutoDistill → Transformers}/QtBatchInference.py +15 -15
- coralnet_toolbox/{AutoDistill → Transformers}/QtDeployModel.py +18 -16
- coralnet_toolbox/{AutoDistill → Transformers}/__init__.py +1 -1
- coralnet_toolbox/__init__.py +1 -1
- coralnet_toolbox/utilities.py +0 -15
- {coralnet_toolbox-0.0.75.dist-info → coralnet_toolbox-0.0.77.dist-info}/METADATA +9 -9
- {coralnet_toolbox-0.0.75.dist-info → coralnet_toolbox-0.0.77.dist-info}/RECORD +46 -44
- coralnet_toolbox/AutoDistill/Models/GroundingDINO.py +0 -81
- coralnet_toolbox/AutoDistill/Models/OWLViT.py +0 -76
- coralnet_toolbox/AutoDistill/Models/OmDetTurbo.py +0 -75
- coralnet_toolbox/AutoDistill/Models/QtBase.py +0 -112
- {coralnet_toolbox-0.0.75.dist-info → coralnet_toolbox-0.0.77.dist-info}/WHEEL +0 -0
- {coralnet_toolbox-0.0.75.dist-info → coralnet_toolbox-0.0.77.dist-info}/entry_points.txt +0 -0
- {coralnet_toolbox-0.0.75.dist-info → coralnet_toolbox-0.0.77.dist-info}/licenses/LICENSE.txt +0 -0
- {coralnet_toolbox-0.0.75.dist-info → coralnet_toolbox-0.0.77.dist-info}/top_level.txt +0 -0
@@ -50,7 +50,6 @@ class ImportImages:
|
|
50
50
|
|
51
51
|
def _process_image_files(self, file_names):
|
52
52
|
"""Helper method to process a list of image files with progress tracking."""
|
53
|
-
# Make the cursor busy
|
54
53
|
QApplication.setOverrideCursor(Qt.WaitCursor)
|
55
54
|
|
56
55
|
progress_bar = ProgressBar(self.image_window, title="Importing Images")
|
@@ -58,30 +57,24 @@ class ImportImages:
|
|
58
57
|
progress_bar.start_progress(len(file_names))
|
59
58
|
|
60
59
|
try:
|
61
|
-
# Keep track of successfully imported images
|
62
60
|
imported_paths = []
|
63
61
|
|
64
|
-
# Add images to the
|
62
|
+
# Add images directly to the manager without emitting signals
|
65
63
|
for file_name in file_names:
|
66
|
-
# Check if the image is already in the raster manager
|
67
64
|
if file_name not in self.image_window.raster_manager.image_paths:
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
except Exception as e:
|
73
|
-
print(f"Warning: Could not import image {file_name}. Error: {e}")
|
65
|
+
# Call the manager directly to add the raster silently,
|
66
|
+
# bypassing ImageWindow.add_image and its signal handlers.
|
67
|
+
if self.image_window.raster_manager.add_raster(file_name, emit_signal=False):
|
68
|
+
imported_paths.append(file_name)
|
74
69
|
else:
|
75
|
-
# Image already exists
|
76
70
|
imported_paths.append(file_name)
|
77
71
|
|
78
|
-
# Update the progress bar
|
79
72
|
progress_bar.update_progress()
|
80
73
|
|
81
|
-
#
|
74
|
+
# After the silent loop, manually update the UI exactly once.
|
75
|
+
self.image_window.update_search_bars()
|
82
76
|
self.image_window.filter_images()
|
83
77
|
|
84
|
-
# Show the last imported image if any were imported
|
85
78
|
if imported_paths:
|
86
79
|
self.image_window.load_image_by_path(imported_paths[-1])
|
87
80
|
|
@@ -89,7 +82,6 @@ class ImportImages:
|
|
89
82
|
except Exception as e:
|
90
83
|
self._show_error_message(str(e))
|
91
84
|
finally:
|
92
|
-
# Restore the cursor to the default cursor
|
93
85
|
QApplication.restoreOverrideCursor()
|
94
86
|
progress_bar.stop_progress()
|
95
87
|
progress_bar.close()
|
@@ -163,46 +163,42 @@ class OpenProject(QDialog):
|
|
163
163
|
# Exit
|
164
164
|
self.accept()
|
165
165
|
|
166
|
+
# In: QtOpenProject.py
|
167
|
+
|
166
168
|
def import_images(self, images_data, legacy_workareas=None):
|
167
169
|
"""Import images, states, and work areas from the given data."""
|
168
170
|
if not images_data:
|
169
171
|
return
|
170
172
|
|
171
|
-
# Determine if the format is old (list of strings) or new (list of dicts)
|
172
173
|
is_new_format = isinstance(images_data[0], dict)
|
173
|
-
|
174
174
|
image_paths = [img['path'] for img in images_data] if is_new_format else images_data
|
175
175
|
|
176
176
|
if not all([os.path.exists(path) for path in image_paths]):
|
177
177
|
image_paths, self.updated_paths = UpdateImagePaths.update_paths(image_paths)
|
178
178
|
|
179
|
-
# Start progress bar
|
180
179
|
total_images = len(image_paths)
|
181
180
|
progress_bar = ProgressBar(self.image_window, title="Importing Images")
|
182
181
|
progress_bar.show()
|
183
182
|
progress_bar.start_progress(total_images)
|
184
183
|
|
185
184
|
try:
|
186
|
-
# Create a map for quick data lookup if using the new format
|
187
185
|
image_data_map = {img['path']: img for img in images_data} if is_new_format else {}
|
188
186
|
|
189
|
-
# Add images to the
|
187
|
+
# Add images directly to the manager without emitting signals
|
190
188
|
for path in image_paths:
|
191
|
-
|
189
|
+
# Call the manager directly to add the raster silently
|
190
|
+
self.image_window.raster_manager.add_raster(path, emit_signal=False)
|
191
|
+
|
192
192
|
raster = self.image_window.raster_manager.get_raster(path)
|
193
193
|
if not raster:
|
194
|
+
progress_bar.update_progress()
|
194
195
|
continue
|
195
196
|
|
196
|
-
# If using the new format, apply saved state and work areas
|
197
197
|
if is_new_format and path in image_data_map:
|
198
198
|
data = image_data_map[path]
|
199
199
|
state = data.get('state', {})
|
200
200
|
work_areas_list = data.get('work_areas', [])
|
201
|
-
|
202
|
-
# Apply raster state
|
203
201
|
raster.checkbox_state = state.get('checkbox_state', False)
|
204
|
-
|
205
|
-
# Import work areas for this image
|
206
202
|
for work_area_data in work_areas_list:
|
207
203
|
try:
|
208
204
|
work_area = WorkArea.from_dict(work_area_data, path)
|
@@ -210,10 +206,8 @@ class OpenProject(QDialog):
|
|
210
206
|
except Exception as e:
|
211
207
|
print(f"Warning: Could not import work area {work_area_data}: {str(e)}")
|
212
208
|
|
213
|
-
# Update the progress bar
|
214
209
|
progress_bar.update_progress()
|
215
|
-
|
216
|
-
# Handle backward compatibility for old, top-level work areas
|
210
|
+
|
217
211
|
if legacy_workareas:
|
218
212
|
for image_path, work_areas_list in legacy_workareas.items():
|
219
213
|
current_path = self.updated_paths.get(image_path, image_path)
|
@@ -223,16 +217,18 @@ class OpenProject(QDialog):
|
|
223
217
|
work_area = WorkArea.from_dict(work_area_data, current_path)
|
224
218
|
raster.add_work_area(work_area)
|
225
219
|
|
226
|
-
# Show the last image if any were imported
|
227
|
-
if self.image_window.raster_manager.image_paths:
|
228
|
-
self.image_window.load_image_by_path(self.image_window.raster_manager.image_paths[-1])
|
229
|
-
|
230
220
|
except Exception as e:
|
231
221
|
QMessageBox.warning(self.annotation_window,
|
232
222
|
"Error Importing Image(s)",
|
233
223
|
f"An error occurred while importing image(s): {str(e)}")
|
234
224
|
finally:
|
235
|
-
#
|
225
|
+
# Manually perform the UI updates ONCE for all imported images
|
226
|
+
self.image_window.update_search_bars()
|
227
|
+
self.image_window.filter_images()
|
228
|
+
|
229
|
+
if self.image_window.raster_manager.image_paths:
|
230
|
+
self.image_window.load_image_by_path(self.image_window.raster_manager.image_paths[-1])
|
231
|
+
|
236
232
|
progress_bar.stop_progress()
|
237
233
|
progress_bar.close()
|
238
234
|
|
Binary file
|
@@ -346,6 +346,8 @@ class Base(QDialog):
|
|
346
346
|
self.button_box.rejected.connect(self.reject)
|
347
347
|
self.layout.addWidget(self.button_box)
|
348
348
|
|
349
|
+
# In: QtBase.py
|
350
|
+
|
349
351
|
def browse_data_yaml(self):
|
350
352
|
"""Open a file dialog to select the data YAML file and populate advanced options."""
|
351
353
|
options = QFileDialog.Options()
|
@@ -358,22 +360,40 @@ class Base(QDialog):
|
|
358
360
|
try:
|
359
361
|
with open(file_path, 'r') as file:
|
360
362
|
data = yaml.safe_load(file)
|
361
|
-
|
362
|
-
|
363
|
-
|
363
|
+
|
364
|
+
names_data = data.get('names')
|
365
|
+
if not names_data:
|
366
|
+
QMessageBox.warning(self, "Warning", "Could not find a 'names' entry in the selected YAML file.")
|
364
367
|
return
|
365
368
|
|
369
|
+
# Handle both dictionary and list formats for class names
|
370
|
+
names_to_display = []
|
371
|
+
if isinstance(names_data, dict):
|
372
|
+
# If it's a dictionary (e.g., {0: 'coral'}), extract the values, sorting by key
|
373
|
+
# to preserve the intended class order.
|
374
|
+
names_to_display = [str(names_data[key]) for key in sorted(names_data.keys())]
|
375
|
+
elif isinstance(names_data, list):
|
376
|
+
# If it's already a list, use it directly.
|
377
|
+
names_to_display = [str(name) for name in names_data]
|
378
|
+
else:
|
379
|
+
# Handle any other unexpected format.
|
380
|
+
QMessageBox.warning(self, "Format Error",
|
381
|
+
f"The 'names' entry in the YAML has an unexpected format: {type(names_data)}.")
|
382
|
+
return
|
383
|
+
|
366
384
|
self.yaml_path_label.setText(file_path)
|
367
385
|
yaml_dir = os.path.dirname(file_path)
|
368
386
|
self.output_dir_label.setText(yaml_dir)
|
369
387
|
self.output_folder_name.setText("data")
|
370
388
|
|
389
|
+
# Clear any existing checkboxes before adding new ones
|
371
390
|
for checkbox in self.class_checkboxes:
|
372
391
|
self.class_layout.removeWidget(checkbox)
|
373
392
|
checkbox.deleteLater()
|
374
393
|
self.class_checkboxes.clear()
|
375
394
|
|
376
|
-
|
395
|
+
# Create checkboxes using the processed list of names
|
396
|
+
for name in names_to_display:
|
377
397
|
checkbox = QCheckBox(name)
|
378
398
|
checkbox.setChecked(True)
|
379
399
|
self.class_layout.addWidget(checkbox)
|
@@ -504,8 +524,11 @@ class Base(QDialog):
|
|
504
524
|
added_paths = []
|
505
525
|
progress_bar.set_title(f"Adding {len(image_paths)} images...")
|
506
526
|
progress_bar.start_progress(len(image_paths))
|
527
|
+
|
507
528
|
for path in image_paths:
|
508
|
-
|
529
|
+
# Call the manager directly to add the raster silently,
|
530
|
+
# bypassing ImageWindow.add_image and its signal handlers.
|
531
|
+
if self.image_window.raster_manager.add_raster(path, emit_signal=False):
|
509
532
|
added_paths.append(path)
|
510
533
|
progress_bar.update_progress()
|
511
534
|
|
@@ -535,19 +558,21 @@ class Base(QDialog):
|
|
535
558
|
self.main_window.get_transparency_value())
|
536
559
|
|
537
560
|
self.annotation_window.add_annotation_to_dict(annotation)
|
538
|
-
newly_created_annotations.append(annotation)
|
561
|
+
newly_created_annotations.append(annotation)
|
539
562
|
|
540
563
|
progress_bar.update_progress()
|
541
564
|
|
542
|
-
# --- Call the restored, correct export function ---
|
543
565
|
progress_bar.set_title("Exporting annotations.json...")
|
544
566
|
self._export_annotations_to_json(newly_created_annotations, self.output_folder)
|
545
567
|
|
546
568
|
progress_bar.finish_progress()
|
547
569
|
progress_bar.stop_progress()
|
548
570
|
progress_bar.close()
|
549
|
-
|
571
|
+
|
572
|
+
# Manually perform a full UI update exactly once.
|
573
|
+
self.image_window.update_search_bars()
|
550
574
|
self.image_window.filter_images()
|
575
|
+
|
551
576
|
if added_paths:
|
552
577
|
self.image_window.load_image_by_path(added_paths[-1])
|
553
578
|
self.image_window.update_image_annotations(added_paths[-1])
|
@@ -401,6 +401,10 @@ class AnnotationWindow(QGraphicsView):
|
|
401
401
|
self.current_image_path = image_path
|
402
402
|
self.active_image = True
|
403
403
|
|
404
|
+
# Automatically mark this image as checked when viewed
|
405
|
+
raster.checkbox_state = True
|
406
|
+
self.main_window.image_window.table_model.update_raster_data(image_path)
|
407
|
+
|
404
408
|
self.tools["zoom"].reset_zoom()
|
405
409
|
self.scene.addItem(QGraphicsPixmapItem(self.pixmap_image))
|
406
410
|
self.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)
|
@@ -13,7 +13,7 @@ warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
13
13
|
|
14
14
|
class GlobalEventFilter(QObject):
|
15
15
|
def __init__(self, main_window):
|
16
|
-
super().__init__()
|
16
|
+
super().__init__(main_window)
|
17
17
|
self.main_window = main_window
|
18
18
|
self.label_window = main_window.label_window
|
19
19
|
self.annotation_window = main_window.annotation_window
|
@@ -24,7 +24,7 @@ class GlobalEventFilter(QObject):
|
|
24
24
|
self.segment_deploy_model_dialog = main_window.segment_deploy_model_dialog
|
25
25
|
self.sam_deploy_generator_dialog = main_window.sam_deploy_generator_dialog
|
26
26
|
self.see_anything_deploy_generator_dialog = main_window.see_anything_deploy_generator_dialog
|
27
|
-
self.
|
27
|
+
self.transformers_deploy_model_dialog = main_window.transformers_deploy_model_dialog
|
28
28
|
|
29
29
|
def eventFilter(self, obj, event):
|
30
30
|
# Check for explorer window first - this applies to all event types
|
@@ -75,10 +75,10 @@ class GlobalEventFilter(QObject):
|
|
75
75
|
if event.key() == Qt.Key_5:
|
76
76
|
self.see_anything_deploy_generator_dialog.predict()
|
77
77
|
return True
|
78
|
-
|
79
|
-
# Handle hotkey for
|
78
|
+
|
79
|
+
# Handle hotkey for transformers prediction
|
80
80
|
if event.key() == Qt.Key_6:
|
81
|
-
self.
|
81
|
+
self.transformers_deploy_model_dialog.predict()
|
82
82
|
return True
|
83
83
|
|
84
84
|
# Handle annotation cycling hotkeys
|
@@ -667,6 +667,10 @@ class ImageWindow(QWidget):
|
|
667
667
|
# Get the raster
|
668
668
|
raster = self.raster_manager.get_raster(image_path)
|
669
669
|
|
670
|
+
# Mark as checked when viewed
|
671
|
+
raster.checkbox_state = True
|
672
|
+
self.table_model.update_raster_data(image_path)
|
673
|
+
|
670
674
|
# Update selection
|
671
675
|
self.selected_image_path = image_path
|
672
676
|
self.table_model.set_selected_path(image_path)
|
coralnet_toolbox/QtMainWindow.py
CHANGED
@@ -9,6 +9,9 @@ import requests
|
|
9
9
|
|
10
10
|
from packaging import version
|
11
11
|
|
12
|
+
import torch
|
13
|
+
|
14
|
+
from PyQt5 import sip
|
12
15
|
from PyQt5.QtGui import QIcon, QMouseEvent
|
13
16
|
from PyQt5.QtCore import Qt, pyqtSignal, QEvent, QSize, QPoint
|
14
17
|
from PyQt5.QtWidgets import (QListWidget, QCheckBox, QFrame, QComboBox)
|
@@ -18,6 +21,7 @@ from PyQt5.QtWidgets import (QMainWindow, QApplication, QToolBar, QAction, QSize
|
|
18
21
|
QGroupBox)
|
19
22
|
|
20
23
|
from coralnet_toolbox.QtEventFilter import GlobalEventFilter
|
24
|
+
|
21
25
|
from coralnet_toolbox.QtAnnotationWindow import AnnotationWindow
|
22
26
|
from coralnet_toolbox.QtConfidenceWindow import ConfidenceWindow
|
23
27
|
from coralnet_toolbox.QtImageWindow import ImageWindow
|
@@ -99,9 +103,9 @@ from coralnet_toolbox.SeeAnything import (
|
|
99
103
|
BatchInferenceDialog as SeeAnythingBatchInferenceDialog
|
100
104
|
)
|
101
105
|
|
102
|
-
from coralnet_toolbox.
|
103
|
-
DeployModelDialog as
|
104
|
-
BatchInferenceDialog as
|
106
|
+
from coralnet_toolbox.Transformers import (
|
107
|
+
DeployModelDialog as TransformersDeployModelDialog,
|
108
|
+
BatchInferenceDialog as TransformersBatchInferenceDialog
|
105
109
|
)
|
106
110
|
|
107
111
|
from coralnet_toolbox.CoralNet import (
|
@@ -114,9 +118,9 @@ from coralnet_toolbox.BreakTime import (
|
|
114
118
|
BreakoutGame
|
115
119
|
)
|
116
120
|
|
117
|
-
from coralnet_toolbox.
|
121
|
+
from coralnet_toolbox.QtSystemMonitor import SystemMonitor
|
118
122
|
|
119
|
-
from coralnet_toolbox.
|
123
|
+
from coralnet_toolbox.Icons import get_icon
|
120
124
|
|
121
125
|
|
122
126
|
# ----------------------------------------------------------------------------------------------------------------------
|
@@ -155,6 +159,7 @@ class MainWindow(QMainWindow):
|
|
155
159
|
self.opaque_icon = get_icon("opaque.png")
|
156
160
|
self.all_icon = get_icon("all.png")
|
157
161
|
self.parameters_icon = get_icon("parameters.png")
|
162
|
+
self.system_monitor_icon = get_icon("system_monitor.png")
|
158
163
|
self.add_icon = get_icon("add.png")
|
159
164
|
self.remove_icon = get_icon("remove.png")
|
160
165
|
self.edit_icon = get_icon("edit.png")
|
@@ -193,6 +198,8 @@ class MainWindow(QMainWindow):
|
|
193
198
|
self.label_window = LabelWindow(self)
|
194
199
|
self.confidence_window = ConfidenceWindow(self)
|
195
200
|
|
201
|
+
self.system_monitor = None
|
202
|
+
|
196
203
|
self.explorer_window = None # Initialized in open_explorer_window
|
197
204
|
|
198
205
|
# TODO update IO classes to have dialogs
|
@@ -262,9 +269,9 @@ class MainWindow(QMainWindow):
|
|
262
269
|
self.see_anything_deploy_generator_dialog = SeeAnythingDeployGeneratorDialog(self)
|
263
270
|
self.see_anything_batch_inference_dialog = SeeAnythingBatchInferenceDialog(self)
|
264
271
|
|
265
|
-
# Create dialogs (
|
266
|
-
self.
|
267
|
-
self.
|
272
|
+
# Create dialogs (Transformers)
|
273
|
+
self.transformers_deploy_model_dialog = TransformersDeployModelDialog(self)
|
274
|
+
self.transformers_batch_inference_dialog = TransformersBatchInferenceDialog(self)
|
268
275
|
|
269
276
|
# Create dialogs (Tile)
|
270
277
|
self.classify_tile_dataset_dialog = ClassifyTileDatasetDialog(self)
|
@@ -601,6 +608,17 @@ class MainWindow(QMainWindow):
|
|
601
608
|
self.ml_segment_video_inference_action = QAction("Segment", self)
|
602
609
|
self.ml_segment_video_inference_action.triggered.connect(self.open_segment_video_inference_dialog)
|
603
610
|
self.ml_video_inference_menu.addAction(self.ml_segment_video_inference_action)
|
611
|
+
|
612
|
+
# Transformers menu
|
613
|
+
self.transformers_menu = self.menu_bar.addMenu("Transformers")
|
614
|
+
# Deploy Model
|
615
|
+
self.transformers_deploy_model_action = QAction("Deploy Model", self)
|
616
|
+
self.transformers_deploy_model_action.triggered.connect(self.open_transformers_deploy_model_dialog)
|
617
|
+
self.transformers_menu.addAction(self.transformers_deploy_model_action)
|
618
|
+
# Batch Inference
|
619
|
+
self.transformers_batch_inference_action = QAction("Batch Inference", self)
|
620
|
+
self.transformers_batch_inference_action.triggered.connect(self.open_transformers_batch_inference_dialog)
|
621
|
+
self.transformers_menu.addAction(self.transformers_batch_inference_action)
|
604
622
|
|
605
623
|
# SAM menu
|
606
624
|
self.sam_menu = self.menu_bar.addMenu("SAM")
|
@@ -640,17 +658,6 @@ class MainWindow(QMainWindow):
|
|
640
658
|
self.see_anything_batch_inference_action.triggered.connect(self.open_see_anything_batch_inference_dialog)
|
641
659
|
self.see_anything_menu.addAction(self.see_anything_batch_inference_action)
|
642
660
|
|
643
|
-
# Auto Distill menu
|
644
|
-
self.auto_distill_menu = self.menu_bar.addMenu("AutoDistill")
|
645
|
-
# Deploy Model
|
646
|
-
self.auto_distill_deploy_model_action = QAction("Deploy Model", self)
|
647
|
-
self.auto_distill_deploy_model_action.triggered.connect(self.open_auto_distill_deploy_model_dialog)
|
648
|
-
self.auto_distill_menu.addAction(self.auto_distill_deploy_model_action)
|
649
|
-
# Batch Inference
|
650
|
-
self.auto_distill_batch_inference_action = QAction("Batch Inference", self)
|
651
|
-
self.auto_distill_batch_inference_action.triggered.connect(self.open_auto_distill_batch_inference_dialog)
|
652
|
-
self.auto_distill_menu.addAction(self.auto_distill_batch_inference_action)
|
653
|
-
|
654
661
|
# Help menu
|
655
662
|
self.help_menu = self.menu_bar.addMenu("Help")
|
656
663
|
|
@@ -659,9 +666,13 @@ class MainWindow(QMainWindow):
|
|
659
666
|
self.check_for_updates_action.triggered.connect(self.open_check_for_updates_dialog)
|
660
667
|
self.help_menu.addAction(self.check_for_updates_action)
|
661
668
|
# Usage
|
662
|
-
self.usage_action = QAction("Usage", self)
|
669
|
+
self.usage_action = QAction("Usage / Hotkeys", self)
|
663
670
|
self.usage_action.triggered.connect(self.open_usage_dialog)
|
664
671
|
self.help_menu.addAction(self.usage_action)
|
672
|
+
# System Monitor
|
673
|
+
self.system_monitor_action = QAction("System Monitor", self)
|
674
|
+
self.system_monitor_action.triggered.connect(self.open_system_monitor_dialog)
|
675
|
+
self.help_menu.addAction(self.system_monitor_action)
|
665
676
|
# Issues / Feature Requests
|
666
677
|
self.create_issue_action = QAction("Issues / Feature Requests", self)
|
667
678
|
self.create_issue_action.triggered.connect(self.open_create_new_issue_dialog)
|
@@ -828,15 +839,15 @@ class MainWindow(QMainWindow):
|
|
828
839
|
self.toolbar.addWidget(spacer)
|
829
840
|
|
830
841
|
# Add the device label widget as an action in the toolbar
|
831
|
-
self.devices =
|
832
|
-
|
833
|
-
self.device = self.devices[
|
842
|
+
self.devices = self.get_available_devices()
|
843
|
+
# Get the 'best' device available
|
844
|
+
self.device = self.devices[-1]
|
834
845
|
|
835
846
|
if self.device.startswith('cuda'):
|
836
|
-
if len(self.devices)
|
837
|
-
device_icon = self.rabbit_icon
|
838
|
-
else:
|
847
|
+
if len([d for d in self.devices if d.endswith('cuda')]) > 1:
|
839
848
|
device_icon = self.rocket_icon
|
849
|
+
else:
|
850
|
+
device_icon = self.rabbit_icon
|
840
851
|
device_tooltip = self.device
|
841
852
|
elif self.device == 'mps':
|
842
853
|
device_icon = self.apple_icon
|
@@ -1028,9 +1039,9 @@ class MainWindow(QMainWindow):
|
|
1028
1039
|
self.left_layout.addWidget(self.annotation_window, 85)
|
1029
1040
|
self.left_layout.addWidget(self.label_window, 15)
|
1030
1041
|
|
1031
|
-
#
|
1032
|
-
self.right_layout.addWidget(self.image_window, 54)
|
1033
|
-
self.right_layout.addWidget(self.confidence_window, 46)
|
1042
|
+
# Adjust the right layout with new proportions
|
1043
|
+
self.right_layout.addWidget(self.image_window, 54) # 54% for image window
|
1044
|
+
self.right_layout.addWidget(self.confidence_window, 46) # 46% for confidence window
|
1034
1045
|
|
1035
1046
|
# Add left and right layouts to main layout
|
1036
1047
|
self.main_layout.addLayout(self.left_layout, 85)
|
@@ -1054,12 +1065,17 @@ class MainWindow(QMainWindow):
|
|
1054
1065
|
self.showMaximized()
|
1055
1066
|
|
1056
1067
|
def closeEvent(self, event):
|
1057
|
-
"""Ensure the explorer window
|
1068
|
+
"""Ensure the explorer window and system monitor are closed when the main window closes."""
|
1058
1069
|
if self.explorer_window:
|
1059
1070
|
# Setting parent to None prevents it from being deleted with main window
|
1060
1071
|
# before it can be properly handled.
|
1061
1072
|
self.explorer_window.setParent(None)
|
1062
1073
|
self.explorer_window.close()
|
1074
|
+
|
1075
|
+
# Close the system monitor if it exists
|
1076
|
+
if self.system_monitor:
|
1077
|
+
self.system_monitor.close()
|
1078
|
+
|
1063
1079
|
super().closeEvent(event)
|
1064
1080
|
|
1065
1081
|
def changeEvent(self, event):
|
@@ -1374,6 +1390,20 @@ class MainWindow(QMainWindow):
|
|
1374
1390
|
self.sam_tool_action.setChecked(False)
|
1375
1391
|
self.see_anything_tool_action.setChecked(False)
|
1376
1392
|
self.work_area_tool_action.setChecked(False)
|
1393
|
+
|
1394
|
+
def get_available_devices(self):
|
1395
|
+
"""
|
1396
|
+
Get available devices
|
1397
|
+
|
1398
|
+
:return:
|
1399
|
+
"""
|
1400
|
+
devices = ['cpu',]
|
1401
|
+
if torch.backends.mps.is_available():
|
1402
|
+
devices.append('mps')
|
1403
|
+
if torch.cuda.is_available():
|
1404
|
+
for i in range(torch.cuda.device_count()):
|
1405
|
+
devices.append(f'cuda:{i}')
|
1406
|
+
return devices
|
1377
1407
|
|
1378
1408
|
def toggle_device(self):
|
1379
1409
|
dialog = DeviceSelectionDialog(self.devices, self)
|
@@ -2211,6 +2241,40 @@ class MainWindow(QMainWindow):
|
|
2211
2241
|
self.segment_video_inference_dialog.exec_()
|
2212
2242
|
except Exception as e:
|
2213
2243
|
QMessageBox.critical(self, "Critical Error", f"{e}")
|
2244
|
+
|
2245
|
+
def open_transformers_deploy_model_dialog(self):
|
2246
|
+
"""Open the Transformers Deploy Model dialog to deploy an Transformers model."""
|
2247
|
+
if not self.image_window.raster_manager.image_paths:
|
2248
|
+
QMessageBox.warning(self,
|
2249
|
+
"Transformers Deploy Model",
|
2250
|
+
"No images are present in the project.")
|
2251
|
+
return
|
2252
|
+
|
2253
|
+
try:
|
2254
|
+
self.untoggle_all_tools()
|
2255
|
+
self.transformers_deploy_model_dialog.exec_()
|
2256
|
+
except Exception as e:
|
2257
|
+
QMessageBox.critical(self, "Critical Error", f"{e}")
|
2258
|
+
|
2259
|
+
def open_transformers_batch_inference_dialog(self):
|
2260
|
+
"""Open the Transformers Batch Inference dialog to run batch inference with Transformers."""
|
2261
|
+
if not self.image_window.raster_manager.image_paths:
|
2262
|
+
QMessageBox.warning(self,
|
2263
|
+
"Transformers Batch Inference",
|
2264
|
+
"No images are present in the project.")
|
2265
|
+
return
|
2266
|
+
|
2267
|
+
if not self.transformers_deploy_model_dialog.loaded_model:
|
2268
|
+
QMessageBox.warning(self,
|
2269
|
+
"Transformers Batch Inference",
|
2270
|
+
"Please deploy a model before running batch inference.")
|
2271
|
+
return
|
2272
|
+
|
2273
|
+
try:
|
2274
|
+
self.untoggle_all_tools()
|
2275
|
+
self.transformers_batch_inference_dialog.exec_()
|
2276
|
+
except Exception as e:
|
2277
|
+
QMessageBox.critical(self, "Critical Error", f"{e}")
|
2214
2278
|
|
2215
2279
|
def open_sam_deploy_predictor_dialog(self):
|
2216
2280
|
"""Open the SAM Deploy Predictor dialog to deploy a SAM predictor."""
|
@@ -2328,40 +2392,16 @@ class MainWindow(QMainWindow):
|
|
2328
2392
|
self.see_anything_batch_inference_dialog.exec_()
|
2329
2393
|
except Exception as e:
|
2330
2394
|
QMessageBox.critical(self, "Critical Error", f"{e}")
|
2331
|
-
|
2332
|
-
def
|
2333
|
-
"""Open the
|
2334
|
-
if
|
2335
|
-
|
2336
|
-
|
2337
|
-
|
2338
|
-
|
2339
|
-
|
2340
|
-
|
2341
|
-
self.untoggle_all_tools()
|
2342
|
-
self.auto_distill_deploy_model_dialog.exec_()
|
2343
|
-
except Exception as e:
|
2344
|
-
QMessageBox.critical(self, "Critical Error", f"{e}")
|
2345
|
-
|
2346
|
-
def open_auto_distill_batch_inference_dialog(self):
|
2347
|
-
"""Open the AutoDistill Batch Inference dialog to run batch inference with AutoDistill."""
|
2348
|
-
if not self.image_window.raster_manager.image_paths:
|
2349
|
-
QMessageBox.warning(self,
|
2350
|
-
"AutoDistill Batch Inference",
|
2351
|
-
"No images are present in the project.")
|
2352
|
-
return
|
2353
|
-
|
2354
|
-
if not self.auto_distill_deploy_model_dialog.loaded_model:
|
2355
|
-
QMessageBox.warning(self,
|
2356
|
-
"AutoDistill Batch Inference",
|
2357
|
-
"Please deploy a model before running batch inference.")
|
2358
|
-
return
|
2359
|
-
|
2360
|
-
try:
|
2361
|
-
self.untoggle_all_tools()
|
2362
|
-
self.auto_distill_batch_inference_dialog.exec_()
|
2363
|
-
except Exception as e:
|
2364
|
-
QMessageBox.critical(self, "Critical Error", f"{e}")
|
2395
|
+
|
2396
|
+
def open_system_monitor_dialog(self):
|
2397
|
+
"""Open the system system monitor window."""
|
2398
|
+
if self.system_monitor is None or sip.isdeleted(self.system_monitor):
|
2399
|
+
self.system_monitor = SystemMonitor()
|
2400
|
+
|
2401
|
+
# Show the monitor window
|
2402
|
+
self.system_monitor.show()
|
2403
|
+
self.system_monitor.activateWindow()
|
2404
|
+
self.system_monitor.raise_()
|
2365
2405
|
|
2366
2406
|
def open_usage_dialog(self):
|
2367
2407
|
"""Display QMessageBox with link to create new issue on GitHub."""
|