setiastrosuitepro 1.6.4__py3-none-any.whl → 1.6.12__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.
Potentially problematic release.
This version of setiastrosuitepro might be problematic. Click here for more details.
- setiastro/images/abeicon.svg +16 -0
- setiastro/images/acv_icon.png +0 -0
- setiastro/images/colorwheel.svg +97 -0
- setiastro/images/cosmic.svg +40 -0
- setiastro/images/cosmicsat.svg +24 -0
- setiastro/images/first_quarter.png +0 -0
- setiastro/images/full_moon.png +0 -0
- setiastro/images/graxpert.svg +19 -0
- setiastro/images/last_quarter.png +0 -0
- setiastro/images/linearfit.svg +32 -0
- setiastro/images/new_moon.png +0 -0
- setiastro/images/pixelmath.svg +42 -0
- setiastro/images/waning_crescent_1.png +0 -0
- setiastro/images/waning_crescent_2.png +0 -0
- setiastro/images/waning_crescent_3.png +0 -0
- setiastro/images/waning_crescent_4.png +0 -0
- setiastro/images/waning_crescent_5.png +0 -0
- setiastro/images/waning_gibbous_1.png +0 -0
- setiastro/images/waning_gibbous_2.png +0 -0
- setiastro/images/waning_gibbous_3.png +0 -0
- setiastro/images/waning_gibbous_4.png +0 -0
- setiastro/images/waning_gibbous_5.png +0 -0
- setiastro/images/waxing_crescent_1.png +0 -0
- setiastro/images/waxing_crescent_2.png +0 -0
- setiastro/images/waxing_crescent_3.png +0 -0
- setiastro/images/waxing_crescent_4.png +0 -0
- setiastro/images/waxing_crescent_5.png +0 -0
- setiastro/images/waxing_gibbous_1.png +0 -0
- setiastro/images/waxing_gibbous_2.png +0 -0
- setiastro/images/waxing_gibbous_3.png +0 -0
- setiastro/images/waxing_gibbous_4.png +0 -0
- setiastro/images/waxing_gibbous_5.png +0 -0
- setiastro/qml/ResourceMonitor.qml +84 -82
- setiastro/saspro/__main__.py +20 -1
- setiastro/saspro/_generated/build_info.py +2 -2
- setiastro/saspro/abe.py +37 -4
- setiastro/saspro/aberration_ai.py +237 -21
- setiastro/saspro/acv_exporter.py +379 -0
- setiastro/saspro/add_stars.py +33 -6
- setiastro/saspro/backgroundneutral.py +108 -40
- setiastro/saspro/blemish_blaster.py +4 -1
- setiastro/saspro/blink_comparator_pro.py +74 -24
- setiastro/saspro/clahe.py +4 -1
- setiastro/saspro/continuum_subtract.py +4 -1
- setiastro/saspro/convo.py +13 -7
- setiastro/saspro/cosmicclarity.py +129 -18
- setiastro/saspro/crop_dialog_pro.py +123 -7
- setiastro/saspro/curve_editor_pro.py +109 -42
- setiastro/saspro/doc_manager.py +245 -15
- setiastro/saspro/exoplanet_detector.py +120 -28
- setiastro/saspro/frequency_separation.py +1158 -204
- setiastro/saspro/ghs_dialog_pro.py +81 -16
- setiastro/saspro/graxpert.py +1 -0
- setiastro/saspro/gui/main_window.py +429 -228
- setiastro/saspro/gui/mixins/dock_mixin.py +245 -24
- setiastro/saspro/gui/mixins/menu_mixin.py +27 -1
- setiastro/saspro/gui/mixins/theme_mixin.py +160 -14
- setiastro/saspro/gui/mixins/toolbar_mixin.py +384 -18
- setiastro/saspro/gui/mixins/update_mixin.py +138 -36
- setiastro/saspro/gui/mixins/view_mixin.py +42 -0
- setiastro/saspro/halobgon.py +4 -0
- setiastro/saspro/histogram.py +5 -1
- setiastro/saspro/image_combine.py +4 -0
- setiastro/saspro/image_peeker_pro.py +4 -0
- setiastro/saspro/imageops/starbasedwhitebalance.py +23 -52
- setiastro/saspro/imageops/stretch.py +582 -62
- setiastro/saspro/isophote.py +4 -0
- setiastro/saspro/layers.py +13 -9
- setiastro/saspro/layers_dock.py +183 -3
- setiastro/saspro/legacy/image_manager.py +154 -20
- setiastro/saspro/legacy/numba_utils.py +67 -47
- setiastro/saspro/legacy/xisf.py +240 -98
- setiastro/saspro/live_stacking.py +180 -79
- setiastro/saspro/luminancerecombine.py +228 -27
- setiastro/saspro/mask_creation.py +174 -15
- setiastro/saspro/mfdeconv.py +113 -35
- setiastro/saspro/mfdeconvcudnn.py +119 -70
- setiastro/saspro/mfdeconvsport.py +112 -35
- setiastro/saspro/morphology.py +4 -0
- setiastro/saspro/multiscale_decomp.py +51 -12
- setiastro/saspro/numba_utils.py +72 -57
- setiastro/saspro/ops/commands.py +18 -18
- setiastro/saspro/ops/script_editor.py +10 -2
- setiastro/saspro/ops/scripts.py +122 -0
- setiastro/saspro/perfect_palette_picker.py +37 -3
- setiastro/saspro/plate_solver.py +84 -49
- setiastro/saspro/psf_viewer.py +119 -37
- setiastro/saspro/resources.py +67 -0
- setiastro/saspro/rgbalign.py +4 -0
- setiastro/saspro/selective_color.py +4 -1
- setiastro/saspro/sfcc.py +364 -152
- setiastro/saspro/shortcuts.py +160 -29
- setiastro/saspro/signature_insert.py +692 -33
- setiastro/saspro/stacking_suite.py +1331 -484
- setiastro/saspro/star_alignment.py +247 -123
- setiastro/saspro/star_spikes.py +4 -0
- setiastro/saspro/star_stretch.py +38 -3
- setiastro/saspro/stat_stretch.py +743 -128
- setiastro/saspro/subwindow.py +786 -360
- setiastro/saspro/supernovaasteroidhunter.py +1 -1
- setiastro/saspro/wavescale_hdr.py +4 -1
- setiastro/saspro/wavescalede.py +4 -1
- setiastro/saspro/whitebalance.py +84 -12
- setiastro/saspro/widgets/common_utilities.py +28 -21
- setiastro/saspro/widgets/resource_monitor.py +109 -59
- setiastro/saspro/widgets/spinboxes.py +10 -13
- setiastro/saspro/wimi.py +27 -656
- setiastro/saspro/wims.py +13 -3
- setiastro/saspro/xisf.py +101 -11
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/METADATA +2 -1
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/RECORD +115 -82
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/WHEEL +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/entry_points.txt +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/licenses/LICENSE +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.6.12.dist-info}/licenses/license.txt +0 -0
|
@@ -13,7 +13,7 @@ IS_APPLE_ARM = (sys.platform == "darwin" and platform.machine() == "arm64")
|
|
|
13
13
|
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QStandardPaths, QSettings
|
|
14
14
|
from PyQt6.QtWidgets import (
|
|
15
15
|
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFileDialog,
|
|
16
|
-
QComboBox, QSpinBox, QProgressBar, QMessageBox, QCheckBox
|
|
16
|
+
QComboBox, QSpinBox, QProgressBar, QMessageBox, QCheckBox, QLineEdit
|
|
17
17
|
)
|
|
18
18
|
from PyQt6.QtGui import QIcon
|
|
19
19
|
from setiastro.saspro.config import Config
|
|
@@ -315,11 +315,29 @@ class AberrationAIDialog(QDialog):
|
|
|
315
315
|
row.addWidget(QLabel(self.tr("Model:")))
|
|
316
316
|
self.model_label = QLabel("—")
|
|
317
317
|
self.model_label.setToolTip("")
|
|
318
|
-
btn_browse = QPushButton(self.tr("Browse…")); btn_browse.clicked.connect(self.
|
|
318
|
+
btn_browse = QPushButton(self.tr("Browse…")); btn_browse.clicked.connect(self._browse_active_model)
|
|
319
319
|
row.addWidget(self.model_label, 1)
|
|
320
320
|
row.addWidget(btn_browse)
|
|
321
321
|
v.addLayout(row)
|
|
322
|
+
# Custom model row (NEW)
|
|
323
|
+
row_custom = QHBoxLayout()
|
|
324
|
+
self.chk_use_custom = QCheckBox(self.tr("Use custom model file"))
|
|
325
|
+
self.chk_use_custom.setChecked(False)
|
|
326
|
+
self.chk_use_custom.toggled.connect(self._on_use_custom_toggled)
|
|
322
327
|
|
|
328
|
+
self.le_custom_model = QLineEdit()
|
|
329
|
+
self.le_custom_model.setReadOnly(True)
|
|
330
|
+
self.le_custom_model.setPlaceholderText(self.tr("No custom model selected"))
|
|
331
|
+
self.le_custom_model.setToolTip("")
|
|
332
|
+
|
|
333
|
+
btn_custom_clear = QPushButton(self.tr("Clear"))
|
|
334
|
+
btn_custom_clear.clicked.connect(self._clear_custom_model)
|
|
335
|
+
|
|
336
|
+
row_custom.addWidget(self.chk_use_custom)
|
|
337
|
+
row_custom.addWidget(self.le_custom_model, 1)
|
|
338
|
+
|
|
339
|
+
row_custom.addWidget(btn_custom_clear)
|
|
340
|
+
v.addLayout(row_custom)
|
|
323
341
|
# Providers row
|
|
324
342
|
row2 = QHBoxLayout()
|
|
325
343
|
self.chk_auto = QCheckBox(self.tr("Auto GPU (if available)"))
|
|
@@ -373,7 +391,9 @@ class AberrationAIDialog(QDialog):
|
|
|
373
391
|
self._model_path = None
|
|
374
392
|
self._refresh_providers()
|
|
375
393
|
self._load_last_model_from_settings()
|
|
376
|
-
|
|
394
|
+
self._load_last_custom_model_from_settings()
|
|
395
|
+
use_custom = QSettings().value("AberrationAI/use_custom_model", False, type=bool)
|
|
396
|
+
self.chk_use_custom.setChecked(bool(use_custom))
|
|
377
397
|
if IS_APPLE_ARM:
|
|
378
398
|
self.chk_auto.setChecked(False)
|
|
379
399
|
self.chk_auto.setEnabled(False)
|
|
@@ -395,11 +415,73 @@ class AberrationAIDialog(QDialog):
|
|
|
395
415
|
if p and os.path.isfile(p):
|
|
396
416
|
self._set_model_path(p)
|
|
397
417
|
|
|
398
|
-
def
|
|
399
|
-
|
|
418
|
+
def _browse_active_model(self):
|
|
419
|
+
"""
|
|
420
|
+
Single Browse button.
|
|
421
|
+
- If user picks a file inside the app model folder -> treat as "downloaded" selection (use_custom_model=False)
|
|
422
|
+
- If user picks a file outside -> treat as "custom" (use_custom_model=True)
|
|
423
|
+
"""
|
|
424
|
+
app_dir = os.path.abspath(_app_model_dir())
|
|
425
|
+
|
|
426
|
+
# Start in last-used folder if possible
|
|
427
|
+
last_custom = QSettings().value("AberrationAI/custom_model_path", type=str) or ""
|
|
428
|
+
last_downloaded = QSettings().value("AberrationAI/model_path", type=str) or ""
|
|
429
|
+
start_dir = None
|
|
430
|
+
for candidate in (last_custom, last_downloaded):
|
|
431
|
+
if candidate and os.path.isfile(candidate):
|
|
432
|
+
d = os.path.dirname(candidate)
|
|
433
|
+
if os.path.isdir(d):
|
|
434
|
+
start_dir = d
|
|
435
|
+
break
|
|
436
|
+
if start_dir is None:
|
|
437
|
+
start_dir = app_dir
|
|
438
|
+
|
|
400
439
|
p, _ = QFileDialog.getOpenFileName(self, "Select ONNX model", start_dir, "ONNX (*.onnx)")
|
|
401
|
-
if p:
|
|
402
|
-
|
|
440
|
+
if not p:
|
|
441
|
+
return
|
|
442
|
+
|
|
443
|
+
p_abs = os.path.abspath(p)
|
|
444
|
+
# Determine if picked file is inside app model folder
|
|
445
|
+
in_app_dir = False
|
|
446
|
+
try:
|
|
447
|
+
in_app_dir = os.path.commonpath([app_dir, p_abs]) == app_dir
|
|
448
|
+
except Exception:
|
|
449
|
+
in_app_dir = p_abs.startswith(app_dir)
|
|
450
|
+
|
|
451
|
+
if in_app_dir:
|
|
452
|
+
# "Downloaded" selection
|
|
453
|
+
self._set_model_path(p_abs)
|
|
454
|
+
self._set_custom_model_path(None)
|
|
455
|
+
QSettings().setValue("AberrationAI/use_custom_model", False)
|
|
456
|
+
if hasattr(self, "chk_use_custom"):
|
|
457
|
+
self.chk_use_custom.setChecked(False)
|
|
458
|
+
else:
|
|
459
|
+
# "Custom" selection
|
|
460
|
+
self._set_custom_model_path(p_abs)
|
|
461
|
+
QSettings().setValue("AberrationAI/use_custom_model", True)
|
|
462
|
+
if hasattr(self, "chk_use_custom"):
|
|
463
|
+
self.chk_use_custom.setChecked(True)
|
|
464
|
+
|
|
465
|
+
# Keep visuals in sync
|
|
466
|
+
self._refresh_model_label()
|
|
467
|
+
self._refresh_custom_row_visibility()
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def _refresh_model_label(self):
|
|
471
|
+
downloaded = QSettings().value("AberrationAI/model_path", type=str) or ""
|
|
472
|
+
custom = QSettings().value("AberrationAI/custom_model_path", type=str) or ""
|
|
473
|
+
use_custom = QSettings().value("AberrationAI/use_custom_model", False, type=bool)
|
|
474
|
+
|
|
475
|
+
if use_custom and custom:
|
|
476
|
+
self.model_label.setText(f"Custom: {os.path.basename(custom)}")
|
|
477
|
+
self.model_label.setToolTip(custom)
|
|
478
|
+
elif downloaded:
|
|
479
|
+
self.model_label.setText(f"Downloaded: {os.path.basename(downloaded)}")
|
|
480
|
+
self.model_label.setToolTip(downloaded)
|
|
481
|
+
else:
|
|
482
|
+
self.model_label.setText("—")
|
|
483
|
+
self.model_label.setToolTip("")
|
|
484
|
+
|
|
403
485
|
|
|
404
486
|
def _open_model_folder(self):
|
|
405
487
|
d = _app_model_dir()
|
|
@@ -412,6 +494,108 @@ class AberrationAIDialog(QDialog):
|
|
|
412
494
|
import subprocess; subprocess.Popen(["xdg-open", d])
|
|
413
495
|
except Exception:
|
|
414
496
|
webbrowser.open(f"file://{d}")
|
|
497
|
+
# ----- custom model helpers (NEW) -----
|
|
498
|
+
def _set_custom_model_path(self, p: str | None):
|
|
499
|
+
if p:
|
|
500
|
+
self.le_custom_model.setText(os.path.basename(p))
|
|
501
|
+
self.le_custom_model.setToolTip(p)
|
|
502
|
+
QSettings().setValue("AberrationAI/custom_model_path", p)
|
|
503
|
+
else:
|
|
504
|
+
self.le_custom_model.clear()
|
|
505
|
+
self.le_custom_model.setToolTip("")
|
|
506
|
+
QSettings().remove("AberrationAI/custom_model_path")
|
|
507
|
+
|
|
508
|
+
def _load_last_custom_model_from_settings(self):
|
|
509
|
+
p = QSettings().value("AberrationAI/custom_model_path", type=str)
|
|
510
|
+
if p:
|
|
511
|
+
if os.path.isfile(p):
|
|
512
|
+
self._set_custom_model_path(p)
|
|
513
|
+
else:
|
|
514
|
+
# Keep the broken path visible in tooltip for debugging
|
|
515
|
+
if hasattr(self, "le_custom_model"):
|
|
516
|
+
self.le_custom_model.setText(os.path.basename(p) + " (missing)")
|
|
517
|
+
self.le_custom_model.setToolTip(p)
|
|
518
|
+
|
|
519
|
+
# After both loads, sync labels/visibility
|
|
520
|
+
self._refresh_model_label()
|
|
521
|
+
self._refresh_custom_row_visibility()
|
|
522
|
+
|
|
523
|
+
def _refresh_custom_row_visibility(self):
|
|
524
|
+
"""
|
|
525
|
+
If you keep the custom row in the UI, hide the path field unless custom is enabled.
|
|
526
|
+
"""
|
|
527
|
+
if not hasattr(self, "le_custom_model"):
|
|
528
|
+
return
|
|
529
|
+
use_custom = QSettings().value("AberrationAI/use_custom_model", False, type=bool)
|
|
530
|
+
self.le_custom_model.setVisible(bool(use_custom))
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def _refresh_model_label(self):
|
|
534
|
+
downloaded = QSettings().value("AberrationAI/model_path", type=str) or ""
|
|
535
|
+
custom = QSettings().value("AberrationAI/custom_model_path", type=str) or ""
|
|
536
|
+
use_custom = QSettings().value("AberrationAI/use_custom_model", False, type=bool)
|
|
537
|
+
|
|
538
|
+
# Prefer custom only if enabled AND the file exists
|
|
539
|
+
if use_custom and custom:
|
|
540
|
+
if os.path.isfile(custom):
|
|
541
|
+
self.model_label.setText(f"Custom: {os.path.basename(custom)}")
|
|
542
|
+
self.model_label.setToolTip(custom)
|
|
543
|
+
return
|
|
544
|
+
else:
|
|
545
|
+
self.model_label.setText(f"Custom: {os.path.basename(custom)} (missing)")
|
|
546
|
+
self.model_label.setToolTip(custom)
|
|
547
|
+
return
|
|
548
|
+
|
|
549
|
+
# Otherwise show downloaded if valid
|
|
550
|
+
if downloaded and os.path.isfile(downloaded):
|
|
551
|
+
self.model_label.setText(f"Downloaded: {os.path.basename(downloaded)}")
|
|
552
|
+
self.model_label.setToolTip(downloaded)
|
|
553
|
+
else:
|
|
554
|
+
self.model_label.setText("—")
|
|
555
|
+
self.model_label.setToolTip("")
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def _browse_custom_model(self):
|
|
559
|
+
# Start at last dir if possible, else app model dir
|
|
560
|
+
last = QSettings().value("AberrationAI/custom_model_path", type=str) or ""
|
|
561
|
+
start_dir = os.path.dirname(last) if last and os.path.isdir(os.path.dirname(last)) else _app_model_dir()
|
|
562
|
+
p, _ = QFileDialog.getOpenFileName(self, "Select custom ONNX model", start_dir, "ONNX (*.onnx)")
|
|
563
|
+
if p:
|
|
564
|
+
self._set_custom_model_path(p)
|
|
565
|
+
QSettings().setValue("AberrationAI/use_custom_model", True)
|
|
566
|
+
if not self.chk_use_custom.isChecked():
|
|
567
|
+
self.chk_use_custom.setChecked(True)
|
|
568
|
+
|
|
569
|
+
def _clear_custom_model(self):
|
|
570
|
+
self._set_custom_model_path(None)
|
|
571
|
+
QSettings().setValue("AberrationAI/use_custom_model", False)
|
|
572
|
+
if hasattr(self, "chk_use_custom"):
|
|
573
|
+
self.chk_use_custom.setChecked(False)
|
|
574
|
+
|
|
575
|
+
self._refresh_model_label()
|
|
576
|
+
self._refresh_custom_row_visibility()
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def _on_use_custom_toggled(self, on: bool):
|
|
580
|
+
QSettings().setValue("AberrationAI/use_custom_model", bool(on))
|
|
581
|
+
|
|
582
|
+
if on:
|
|
583
|
+
p = QSettings().value("AberrationAI/custom_model_path", type=str) or ""
|
|
584
|
+
if not (p and os.path.isfile(p)):
|
|
585
|
+
# Don’t spawn another browse button path; use the ONE browse if they want
|
|
586
|
+
QMessageBox.information(
|
|
587
|
+
self,
|
|
588
|
+
self.tr("Custom model"),
|
|
589
|
+
self.tr("Custom model is enabled, but no custom file is selected.\n"
|
|
590
|
+
"Click Browse… to choose a model file.")
|
|
591
|
+
)
|
|
592
|
+
# Optional: auto-open the single browse:
|
|
593
|
+
# self._browse_active_model()
|
|
594
|
+
# return
|
|
595
|
+
|
|
596
|
+
self._refresh_model_label()
|
|
597
|
+
self._refresh_custom_row_visibility()
|
|
598
|
+
|
|
415
599
|
|
|
416
600
|
# ----- provider UI -----
|
|
417
601
|
def _log(self, msg: str): # NEW
|
|
@@ -477,8 +661,16 @@ class AberrationAIDialog(QDialog):
|
|
|
477
661
|
def _on_download_ok(self, path: str):
|
|
478
662
|
self.progress.setValue(100)
|
|
479
663
|
self._set_model_path(path)
|
|
664
|
+
|
|
665
|
+
# Download becomes the active model unless custom is explicitly enabled
|
|
666
|
+
if not QSettings().value("AberrationAI/use_custom_model", False, type=bool):
|
|
667
|
+
self._set_custom_model_path(None)
|
|
668
|
+
|
|
480
669
|
QMessageBox.information(self, "Model", f"Downloaded: {os.path.basename(path)}")
|
|
481
670
|
|
|
671
|
+
self._refresh_model_label()
|
|
672
|
+
self._refresh_custom_row_visibility()
|
|
673
|
+
|
|
482
674
|
# ----- run -----
|
|
483
675
|
def _run(self):
|
|
484
676
|
if ort is None:
|
|
@@ -489,7 +681,22 @@ class AberrationAIDialog(QDialog):
|
|
|
489
681
|
"Please try installing an earlier version (for example 1.19.x) and try again."
|
|
490
682
|
)
|
|
491
683
|
return
|
|
492
|
-
|
|
684
|
+
|
|
685
|
+
# Choose model path (normal vs custom)
|
|
686
|
+
use_custom = QSettings().value("AberrationAI/use_custom_model", False, type=bool)
|
|
687
|
+
downloaded = QSettings().value("AberrationAI/model_path", type=str) or ""
|
|
688
|
+
custom = QSettings().value("AberrationAI/custom_model_path", type=str) or ""
|
|
689
|
+
|
|
690
|
+
model_path = custom if use_custom else downloaded
|
|
691
|
+
if self.chk_use_custom.isChecked():
|
|
692
|
+
cp = QSettings().value("AberrationAI/custom_model_path", type=str)
|
|
693
|
+
if cp and os.path.isfile(cp):
|
|
694
|
+
model_path = cp
|
|
695
|
+
else:
|
|
696
|
+
QMessageBox.warning(self, "Model", "Custom model is enabled but the file is missing. Please browse to a valid .onnx.")
|
|
697
|
+
return
|
|
698
|
+
|
|
699
|
+
if not model_path or not os.path.isfile(model_path):
|
|
493
700
|
QMessageBox.warning(self, "Model", "Please select or download a valid .onnx model first.")
|
|
494
701
|
return
|
|
495
702
|
|
|
@@ -516,7 +723,7 @@ class AberrationAIDialog(QDialog):
|
|
|
516
723
|
providers = [sel] if sel else ["CPUExecutionProvider"]
|
|
517
724
|
|
|
518
725
|
# --- make patch match the model's requirement (if fixed) ---
|
|
519
|
-
req = _model_required_patch(
|
|
726
|
+
req = _model_required_patch(model_path)
|
|
520
727
|
if req and req > 0:
|
|
521
728
|
patch = req
|
|
522
729
|
try:
|
|
@@ -537,14 +744,16 @@ class AberrationAIDialog(QDialog):
|
|
|
537
744
|
|
|
538
745
|
self._t_start = time.perf_counter()
|
|
539
746
|
prov_txt = ("auto" if self.chk_auto.isChecked() else self.cmb_provider.currentText() or "CPU")
|
|
540
|
-
self._log(f"🚀 Aberration AI: model={os.path.basename(
|
|
747
|
+
self._log(f"🚀 Aberration AI: model={os.path.basename(model_path)}, "
|
|
541
748
|
f"provider={prov_txt}, patch={patch}, overlap={overlap}")
|
|
749
|
+
|
|
750
|
+
self._effective_model_path = model_path
|
|
542
751
|
|
|
543
752
|
# -------- run worker --------
|
|
544
753
|
self.progress.setValue(0)
|
|
545
754
|
self.btn_run.setEnabled(False)
|
|
546
755
|
|
|
547
|
-
self._worker = _ONNXWorker(
|
|
756
|
+
self._worker = _ONNXWorker(model_path, img, patch, overlap, providers)
|
|
548
757
|
self._worker.progressed.connect(self.progress.setValue)
|
|
549
758
|
self._worker.failed.connect(self._on_failed)
|
|
550
759
|
self._worker.finished_ok.connect(self._on_ok)
|
|
@@ -553,10 +762,15 @@ class AberrationAIDialog(QDialog):
|
|
|
553
762
|
|
|
554
763
|
|
|
555
764
|
def _on_failed(self, msg: str):
|
|
556
|
-
|
|
765
|
+
model_path = getattr(self, "_effective_model_path", self._model_path)
|
|
766
|
+
self._log(f"❌ Aberration AI failed: {msg}")
|
|
557
767
|
QMessageBox.critical(self, "ONNX Error", msg)
|
|
768
|
+
self.reject() # closes the dialog
|
|
558
769
|
|
|
559
770
|
def _on_ok(self, out: np.ndarray):
|
|
771
|
+
used = getattr(self._worker, "used_provider", None) or \
|
|
772
|
+
(self.cmb_provider.currentText() if not self.chk_auto.isChecked() else "auto")
|
|
773
|
+
model_path = getattr(self, "_effective_model_path", self._model_path)
|
|
560
774
|
doc = self.get_active_doc()
|
|
561
775
|
if doc is None or getattr(doc, "image", None) is None:
|
|
562
776
|
QMessageBox.warning(self, "Image", "No active image.")
|
|
@@ -578,11 +792,10 @@ class AberrationAIDialog(QDialog):
|
|
|
578
792
|
"processing_parameters": {
|
|
579
793
|
**(getattr(doc, "metadata", {}) or {}).get("processing_parameters", {}),
|
|
580
794
|
"AberrationAI": {
|
|
581
|
-
"model_path":
|
|
795
|
+
"model_path": model_path,
|
|
582
796
|
"patch_size": int(self.spin_patch.value()),
|
|
583
797
|
"overlap": int(self.spin_overlap.value()),
|
|
584
|
-
"provider":
|
|
585
|
-
if not self.chk_auto.isChecked() else "auto"),
|
|
798
|
+
"provider": used,
|
|
586
799
|
"border_px": BORDER_PX,
|
|
587
800
|
}
|
|
588
801
|
}
|
|
@@ -615,7 +828,7 @@ class AberrationAIDialog(QDialog):
|
|
|
615
828
|
if main is not None:
|
|
616
829
|
auto_gpu = bool(self.chk_auto.isChecked())
|
|
617
830
|
preset = {
|
|
618
|
-
"model":
|
|
831
|
+
"model": model_path,
|
|
619
832
|
"patch": int(self.spin_patch.value()),
|
|
620
833
|
"overlap": int(self.spin_overlap.value()),
|
|
621
834
|
"border_px": int(BORDER_PX),
|
|
@@ -674,21 +887,24 @@ class AberrationAIDialog(QDialog):
|
|
|
674
887
|
BORDER_PX = 10 # same value used above
|
|
675
888
|
self._log(
|
|
676
889
|
f"✅ Aberration AI applied "
|
|
677
|
-
f"(model={os.path.basename(
|
|
890
|
+
f"(model={os.path.basename(model_path)}, provider={used}, "
|
|
678
891
|
f"patch={int(self.spin_patch.value())}, overlap={int(self.spin_overlap.value())}, "
|
|
679
892
|
f"border={BORDER_PX}px, time={dt:.2f}s)"
|
|
680
893
|
)
|
|
681
894
|
|
|
682
895
|
self.progress.setValue(100)
|
|
683
|
-
#
|
|
896
|
+
# NEW: close this UI after a successful run
|
|
897
|
+
self.accept() # or self.close()
|
|
898
|
+
return
|
|
684
899
|
|
|
685
900
|
def _on_worker_finished(self):
|
|
686
|
-
#
|
|
687
|
-
|
|
901
|
+
# Dialog might have been closed by _on_ok()
|
|
902
|
+
if not self.isVisible():
|
|
903
|
+
return
|
|
904
|
+
|
|
688
905
|
if hasattr(self, "btn_run"):
|
|
689
906
|
try:
|
|
690
907
|
self.btn_run.setEnabled(True)
|
|
691
908
|
except RuntimeError:
|
|
692
|
-
# Button already deleted; ignore
|
|
693
909
|
pass
|
|
694
910
|
self._worker = None
|