coralnet-toolbox 0.0.74__py2.py3-none-any.whl → 0.0.76__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/Explorer/QtDataItem.py +52 -22
- coralnet_toolbox/Explorer/QtExplorer.py +277 -1600
- coralnet_toolbox/Explorer/QtSettingsWidgets.py +101 -15
- coralnet_toolbox/Explorer/QtViewers.py +1568 -0
- coralnet_toolbox/Explorer/transformer_models.py +70 -0
- coralnet_toolbox/Explorer/yolo_models.py +112 -0
- coralnet_toolbox/IO/QtExportMaskAnnotations.py +538 -403
- coralnet_toolbox/Icons/system_monitor.png +0 -0
- coralnet_toolbox/MachineLearning/ImportDataset/QtBase.py +239 -147
- coralnet_toolbox/MachineLearning/VideoInference/YOLO3D/run.py +102 -16
- coralnet_toolbox/QtAnnotationWindow.py +16 -10
- coralnet_toolbox/QtEventFilter.py +4 -4
- coralnet_toolbox/QtImageWindow.py +3 -7
- coralnet_toolbox/QtMainWindow.py +104 -64
- coralnet_toolbox/QtProgressBar.py +1 -0
- coralnet_toolbox/QtSystemMonitor.py +370 -0
- coralnet_toolbox/Rasters/RasterTableModel.py +20 -0
- coralnet_toolbox/Results/ConvertResults.py +14 -8
- coralnet_toolbox/Results/ResultsProcessor.py +3 -2
- coralnet_toolbox/SAM/QtDeployGenerator.py +2 -5
- coralnet_toolbox/SAM/QtDeployPredictor.py +11 -3
- coralnet_toolbox/SeeAnything/QtDeployGenerator.py +146 -116
- coralnet_toolbox/SeeAnything/QtDeployPredictor.py +55 -9
- coralnet_toolbox/Tile/QtTileBatchInference.py +4 -4
- coralnet_toolbox/Tools/QtPolygonTool.py +42 -3
- coralnet_toolbox/Tools/QtRectangleTool.py +30 -0
- coralnet_toolbox/Tools/QtSAMTool.py +140 -91
- 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 +120 -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 +21 -15
- {coralnet_toolbox-0.0.74.dist-info → coralnet_toolbox-0.0.76.dist-info}/METADATA +13 -10
- {coralnet_toolbox-0.0.74.dist-info → coralnet_toolbox-0.0.76.dist-info}/RECORD +45 -40
- 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.74.dist-info → coralnet_toolbox-0.0.76.dist-info}/WHEEL +0 -0
- {coralnet_toolbox-0.0.74.dist-info → coralnet_toolbox-0.0.76.dist-info}/entry_points.txt +0 -0
- {coralnet_toolbox-0.0.74.dist-info → coralnet_toolbox-0.0.76.dist-info}/licenses/LICENSE.txt +0 -0
- {coralnet_toolbox-0.0.74.dist-info → coralnet_toolbox-0.0.76.dist-info}/top_level.txt +0 -0
@@ -189,6 +189,21 @@ Examples:
|
|
189
189
|
help='Filter by specific class IDs (e.g., --classes 0 1 2 for persons, bicycles, cars)'
|
190
190
|
)
|
191
191
|
|
192
|
+
# Frame range parameters
|
193
|
+
parser.add_argument(
|
194
|
+
'--start_at',
|
195
|
+
type=int,
|
196
|
+
default=0,
|
197
|
+
help='Start processing at this frame number (0-based)'
|
198
|
+
)
|
199
|
+
parser.add_argument(
|
200
|
+
'--end_at',
|
201
|
+
type=int,
|
202
|
+
default=None,
|
203
|
+
help='End processing at this frame number (inclusive, 0-based). If not provided, '
|
204
|
+
'process until the end of the video.'
|
205
|
+
)
|
206
|
+
|
192
207
|
# Device settings
|
193
208
|
parser.add_argument(
|
194
209
|
'--device',
|
@@ -222,6 +237,8 @@ Examples:
|
|
222
237
|
action='store_true',
|
223
238
|
help='Show all visualization windows (result, depth, detection). By default, only the result frame is shown.'
|
224
239
|
)
|
240
|
+
|
241
|
+
|
225
242
|
return parser.parse_args()
|
226
243
|
|
227
244
|
|
@@ -271,19 +288,25 @@ def main():
|
|
271
288
|
enable_bev = not args.no_bev
|
272
289
|
enable_display = not args.no_display
|
273
290
|
show_all_frames = args.yes_display
|
291
|
+
|
292
|
+
# Frame range parameters
|
293
|
+
start_frame = args.start_at
|
294
|
+
end_frame = args.end_at
|
295
|
+
|
274
296
|
# Camera parameters - simplified approach
|
275
297
|
camera_params_file = None # Path to camera parameters file (None to use default parameters)
|
276
298
|
# ===============================================
|
277
|
-
print(
|
299
|
+
print("\nConfiguration:")
|
278
300
|
print(f"Input source: {source}")
|
279
301
|
print(f"Output path: {output_path}")
|
280
302
|
if target_size is not None:
|
281
303
|
print(f"Target size: {target_size}px (longest edge)")
|
282
304
|
else:
|
283
|
-
print(
|
305
|
+
print("Using original resolution (no scaling)")
|
284
306
|
print(f"YOLO model: {'Custom path: ' + yolo_model_path if yolo_model_path else 'Size: ' + yolo_model_size}")
|
285
307
|
print(f"Depth model size: {depth_model_size}")
|
286
308
|
print(f"Device: {device}")
|
309
|
+
print(f"Frame range: {start_frame} to {end_frame if end_frame is not None else 'end'}")
|
287
310
|
print(f"Tracking: {'enabled' if enable_tracking else 'disabled'}")
|
288
311
|
print(f"Bird's Eye View: {'enabled' if enable_bev else 'disabled'}")
|
289
312
|
print(f"Display: {'enabled' if enable_display else 'disabled'}")
|
@@ -382,9 +405,52 @@ def main():
|
|
382
405
|
# Use a scale that works well for the 1-5 meter range
|
383
406
|
bev = BirdEyeView(image_shape=(width, height), scale=100) # Increased scale to spread objects out
|
384
407
|
|
385
|
-
# Initialize video writer
|
386
|
-
|
387
|
-
|
408
|
+
# Initialize video writer - use a more reliable codec and check output path
|
409
|
+
output_dir = os.path.dirname(output_path)
|
410
|
+
if output_dir and not os.path.exists(output_dir):
|
411
|
+
os.makedirs(output_dir)
|
412
|
+
|
413
|
+
# On Windows, try different codec options
|
414
|
+
if sys.platform == 'win32':
|
415
|
+
try:
|
416
|
+
# First try H264 codec
|
417
|
+
fourcc = cv2.VideoWriter_fourcc(*'H264')
|
418
|
+
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
|
419
|
+
|
420
|
+
# Check if writer was successfully initialized
|
421
|
+
if not out.isOpened():
|
422
|
+
# Fallback to XVID codec
|
423
|
+
fourcc = cv2.VideoWriter_fourcc(*'XVID')
|
424
|
+
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
|
425
|
+
|
426
|
+
if not out.isOpened():
|
427
|
+
# Last resort, try MJPG
|
428
|
+
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
|
429
|
+
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
|
430
|
+
|
431
|
+
if not out.isOpened():
|
432
|
+
print(f"Warning: Could not create output video with standard codecs. Trying AVI format.")
|
433
|
+
# Try changing extension to .avi
|
434
|
+
output_path = os.path.splitext(output_path)[0] + '.avi'
|
435
|
+
fourcc = cv2.VideoWriter_fourcc(*'XVID')
|
436
|
+
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
|
437
|
+
except Exception as e:
|
438
|
+
print(f"Error initializing video writer: {e}")
|
439
|
+
# Last fallback to MP4V
|
440
|
+
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
441
|
+
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
|
442
|
+
else:
|
443
|
+
# For other platforms, use mp4v codec
|
444
|
+
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
445
|
+
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
|
446
|
+
|
447
|
+
# Check if writer was successfully initialized
|
448
|
+
if not out.isOpened():
|
449
|
+
print(f"Error: Could not create output video file at {output_path}")
|
450
|
+
print("Continuing without saving output video.")
|
451
|
+
out = None
|
452
|
+
else:
|
453
|
+
print(f"Successfully opened output video file: {output_path}")
|
388
454
|
|
389
455
|
# Initialize variables for FPS calculation
|
390
456
|
frame_count = 0
|
@@ -394,6 +460,7 @@ def main():
|
|
394
460
|
print("Starting processing...")
|
395
461
|
|
396
462
|
# Main loop
|
463
|
+
current_frame = 0
|
397
464
|
while True:
|
398
465
|
# Check for key press at the beginning of each loop
|
399
466
|
if enable_display:
|
@@ -401,11 +468,22 @@ def main():
|
|
401
468
|
if key == ord('q') or key == 27 or (key & 0xFF) == ord('q') or (key & 0xFF) == 27:
|
402
469
|
print("Exiting program...")
|
403
470
|
break
|
404
|
-
try:
|
471
|
+
try:
|
472
|
+
# Read frame
|
405
473
|
ret, frame = cap.read()
|
406
474
|
if not ret:
|
407
475
|
break
|
408
476
|
|
477
|
+
# Skip frames before start_frame
|
478
|
+
if current_frame < start_frame:
|
479
|
+
current_frame += 1
|
480
|
+
continue
|
481
|
+
|
482
|
+
# Stop if we've processed the end_frame
|
483
|
+
if end_frame is not None and current_frame > end_frame:
|
484
|
+
print(f"Reached end frame {end_frame}, stopping processing.")
|
485
|
+
break
|
486
|
+
|
409
487
|
# Apply resizing if needed
|
410
488
|
if target_size is not None:
|
411
489
|
frame = cv2.resize(frame, (width, height))
|
@@ -543,7 +621,7 @@ def main():
|
|
543
621
|
fps_display = f"FPS: {fps_value:.1f}"
|
544
622
|
|
545
623
|
# Add FPS and device info to the result frame (top-right corner)
|
546
|
-
text = f"{fps_display} | Device: {device}"
|
624
|
+
text = f"{fps_display} | Device: {device} | Frame: {current_frame}"
|
547
625
|
|
548
626
|
# Calculate text size for right alignment
|
549
627
|
(text_width, text_height), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.7, 2)
|
@@ -584,8 +662,9 @@ def main():
|
|
584
662
|
except Exception as e:
|
585
663
|
print(f"Error adding depth map to result: {e}")
|
586
664
|
|
587
|
-
# Write frame to output video
|
588
|
-
out.
|
665
|
+
# Write frame to output video (only if writer is valid)
|
666
|
+
if out is not None and out.isOpened():
|
667
|
+
out.write(result_frame)
|
589
668
|
|
590
669
|
# Display frames only if display is enabled
|
591
670
|
if enable_display:
|
@@ -594,12 +673,15 @@ def main():
|
|
594
673
|
cv2.imshow("Depth Map", depth_colored)
|
595
674
|
cv2.imshow("Object Detection", detection_frame)
|
596
675
|
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
676
|
+
# Check for key press again at the end of the loop
|
677
|
+
key = cv2.waitKey(1)
|
678
|
+
if key == ord('q') or key == 27 or (key & 0xFF) == ord('q') or (key & 0xFF) == 27:
|
679
|
+
print("Exiting program...")
|
680
|
+
break
|
681
|
+
|
682
|
+
# Increment frame counter
|
683
|
+
current_frame += 1
|
684
|
+
|
603
685
|
except Exception as e:
|
604
686
|
print(f"Error processing frame: {e}")
|
605
687
|
# Also check for key press during exception handling
|
@@ -608,12 +690,16 @@ def main():
|
|
608
690
|
if key == ord('q') or key == 27 or (key & 0xFF) == ord('q') or (key & 0xFF) == 27:
|
609
691
|
print("Exiting program...")
|
610
692
|
break
|
693
|
+
|
694
|
+
# Still increment frame counter even if there was an error
|
695
|
+
current_frame += 1
|
611
696
|
continue
|
612
697
|
|
613
698
|
# Clean up
|
614
699
|
print("Cleaning up resources...")
|
615
700
|
cap.release()
|
616
|
-
out
|
701
|
+
if out is not None:
|
702
|
+
out.release()
|
617
703
|
cv2.destroyAllWindows()
|
618
704
|
|
619
705
|
print(f"Processing complete. Output saved to {output_path}")
|
@@ -492,9 +492,6 @@ class AnnotationWindow(QGraphicsView):
|
|
492
492
|
relative_area = 1.0 # fallback, treat as full image
|
493
493
|
|
494
494
|
# Step 3: Map ratio to padding factor (smaller annotation = more padding)
|
495
|
-
# Example: padding_factor = clamp(0.5 * (1/relative_area)**0.5, 0.1, 0.5)
|
496
|
-
# - For very small annotations, padding approaches 0.5 (50%)
|
497
|
-
# - For large annotations, padding approaches 0.1 (10%)
|
498
495
|
import math
|
499
496
|
min_padding = 0.1 # 10%
|
500
497
|
max_padding = 0.5 # 50%
|
@@ -503,23 +500,32 @@ class AnnotationWindow(QGraphicsView):
|
|
503
500
|
else:
|
504
501
|
padding_factor = min_padding
|
505
502
|
|
506
|
-
# Step 4: Apply dynamic padding
|
507
|
-
|
508
|
-
|
503
|
+
# Step 4: Apply dynamic padding with minimum values to prevent zero width/height
|
504
|
+
min_padding_absolute = 1.0 # Minimum padding in pixels
|
505
|
+
padding_x = max(annotation_rect.width() * padding_factor, min_padding_absolute)
|
506
|
+
padding_y = max(annotation_rect.height() * padding_factor, min_padding_absolute)
|
509
507
|
padded_rect = annotation_rect.adjusted(-padding_x, -padding_y, padding_x, padding_y)
|
510
508
|
|
511
509
|
# Fit the padded annotation rect in the view
|
512
510
|
self.fitInView(padded_rect, Qt.KeepAspectRatio)
|
513
511
|
|
514
|
-
# Update the zoom factor based on the new view transformation
|
512
|
+
# Update the zoom factor based on the new view transformation with safety checks
|
515
513
|
view_rect = self.viewport().rect()
|
516
|
-
|
517
|
-
|
514
|
+
if padded_rect.width() > 0:
|
515
|
+
zoom_x = view_rect.width() / padded_rect.width()
|
516
|
+
else:
|
517
|
+
zoom_x = 1.0 # Default zoom if width is zero
|
518
|
+
|
519
|
+
if padded_rect.height() > 0:
|
520
|
+
zoom_y = view_rect.height() / padded_rect.height()
|
521
|
+
else:
|
522
|
+
zoom_y = 1.0 # Default zoom if height is zero
|
523
|
+
|
518
524
|
self.zoom_factor = min(zoom_x, zoom_y)
|
519
525
|
|
520
526
|
# Signal that the view has changed
|
521
527
|
self.viewChanged.emit(*self.get_image_dimensions())
|
522
|
-
|
528
|
+
|
523
529
|
def cycle_annotations(self, direction):
|
524
530
|
"""Cycle through annotations in the specified direction."""
|
525
531
|
# Get the annotations for the current image
|
@@ -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
|
@@ -578,13 +578,9 @@ class ImageWindow(QWidget):
|
|
578
578
|
|
579
579
|
# Immediately update filtered paths to include the new image
|
580
580
|
# This ensures the image will be visible in the table right away
|
581
|
-
if self.table_model.filtered_paths == self.raster_manager.image_paths
|
582
|
-
# No filters are active, so
|
583
|
-
self.table_model.
|
584
|
-
self.table_model.dataChanged.emit(
|
585
|
-
self.table_model.index(0, 0),
|
586
|
-
self.table_model.index(len(self.table_model.filtered_paths) - 1,
|
587
|
-
self.table_model.columnCount() - 1))
|
581
|
+
if len(self.table_model.filtered_paths) == len(self.raster_manager.image_paths) - 1:
|
582
|
+
# No filters are active, so efficiently add the new path to the model
|
583
|
+
self.table_model.add_path(image_path)
|
588
584
|
else:
|
589
585
|
# Filters are active, so run filtering again
|
590
586
|
self.filter_images()
|
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."""
|