setiastrosuitepro 1.7.5__py3-none-any.whl → 1.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. setiastro/saspro/_generated/build_info.py +2 -2
  2. setiastro/saspro/accel_installer.py +21 -8
  3. setiastro/saspro/accel_workers.py +11 -12
  4. setiastro/saspro/blink_comparator_pro.py +146 -2
  5. setiastro/saspro/comet_stacking.py +113 -85
  6. setiastro/saspro/cosmicclarity.py +604 -826
  7. setiastro/saspro/cosmicclarity_engines/benchmark_engine.py +715 -0
  8. setiastro/saspro/cosmicclarity_engines/darkstar_engine.py +576 -0
  9. setiastro/saspro/cosmicclarity_engines/denoise_engine.py +567 -0
  10. setiastro/saspro/cosmicclarity_engines/satellite_engine.py +620 -0
  11. setiastro/saspro/cosmicclarity_engines/sharpen_engine.py +587 -0
  12. setiastro/saspro/cosmicclarity_engines/superres_engine.py +412 -0
  13. setiastro/saspro/gui/main_window.py +14 -0
  14. setiastro/saspro/gui/mixins/menu_mixin.py +2 -0
  15. setiastro/saspro/model_manager.py +306 -0
  16. setiastro/saspro/model_workers.py +65 -0
  17. setiastro/saspro/ops/benchmark.py +320 -0
  18. setiastro/saspro/ops/settings.py +308 -9
  19. setiastro/saspro/remove_stars.py +424 -442
  20. setiastro/saspro/resources.py +73 -10
  21. setiastro/saspro/runtime_torch.py +107 -22
  22. setiastro/saspro/signature_insert.py +14 -3
  23. setiastro/saspro/stacking_suite.py +539 -115
  24. {setiastrosuitepro-1.7.5.dist-info → setiastrosuitepro-1.8.0.dist-info}/METADATA +2 -1
  25. {setiastrosuitepro-1.7.5.dist-info → setiastrosuitepro-1.8.0.dist-info}/RECORD +29 -20
  26. {setiastrosuitepro-1.7.5.dist-info → setiastrosuitepro-1.8.0.dist-info}/WHEEL +0 -0
  27. {setiastrosuitepro-1.7.5.dist-info → setiastrosuitepro-1.8.0.dist-info}/entry_points.txt +0 -0
  28. {setiastrosuitepro-1.7.5.dist-info → setiastrosuitepro-1.8.0.dist-info}/licenses/LICENSE +0 -0
  29. {setiastrosuitepro-1.7.5.dist-info → setiastrosuitepro-1.8.0.dist-info}/licenses/license.txt +0 -0
@@ -15,6 +15,7 @@ from __future__ import annotations
15
15
  import os
16
16
  import sys
17
17
  from functools import lru_cache
18
+ from pathlib import Path
18
19
 
19
20
 
20
21
  def _get_base_path() -> str:
@@ -340,16 +341,6 @@ class Icons:
340
341
  FUNCTION_BUNDLES = property(lambda self: _resource_path('functionbundle.png'))
341
342
  VIEW_BUNDLES = property(lambda self: _resource_path('viewbundle.png'))
342
343
 
343
-
344
- class Resources:
345
- """
346
- Centralized data resource paths.
347
- """
348
- SASP_DATA = property(lambda self: _resource_path('data/SASP_data.fits'))
349
- ASTROBIN_FILTERS_CSV = property(lambda self: _resource_path('data/catalogs/astrobin_filters.csv'))
350
- SPINNER_GIF = property(lambda self: _resource_path('spinner.gif'))
351
-
352
-
353
344
  # Singleton instances for easy access
354
345
  _icons_instance = None
355
346
  _resources_instance = None
@@ -557,6 +548,78 @@ def _init_legacy_paths():
557
548
  'planetprojection_path': get_icon_path('3dplanet.png'),
558
549
  }
559
550
 
551
+ class Resources:
552
+ """
553
+ Centralized data resource paths.
554
+ """
555
+ SASP_DATA = property(lambda self: _resource_path('data/SASP_data.fits'))
556
+ ASTROBIN_FILTERS_CSV = property(lambda self: _resource_path('data/catalogs/astrobin_filters.csv'))
557
+ SPINNER_GIF = property(lambda self: _resource_path('spinner.gif'))
558
+
559
+ # --- Models root ---
560
+ MODELS_DIR = property(lambda self: get_models_dir())
561
+
562
+ # --- Cosmic Clarity Sharpen ---
563
+ CC_STELLAR_SHARP_PTH = property(lambda self: model_path('deep_sharp_stellar_cnn_AI3_5s.pth'))
564
+ CC_STELLAR_SHARP_ONNX = property(lambda self: model_path('deep_sharp_stellar_cnn_AI3_5s.onnx'))
565
+
566
+ CC_NS1_PTH = property(lambda self: model_path('deep_nonstellar_sharp_cnn_radius_1AI3_5s.pth'))
567
+ CC_NS1_ONNX = property(lambda self: model_path('deep_nonstellar_sharp_cnn_radius_1AI3_5s.onnx'))
568
+
569
+ CC_NS2_PTH = property(lambda self: model_path('deep_nonstellar_sharp_cnn_radius_2AI3_5s.pth'))
570
+ CC_NS2_ONNX = property(lambda self: model_path('deep_nonstellar_sharp_cnn_radius_2AI3_5s.onnx'))
571
+ CC_NS4_PTH = property(lambda self: model_path('deep_nonstellar_sharp_cnn_radius_4AI3_5s.pth'))
572
+ CC_NS4_ONNX = property(lambda self: model_path('deep_nonstellar_sharp_cnn_radius_4AI3_5s.onnx'))
573
+
574
+ CC_NS8_PTH = property(lambda self: model_path('deep_nonstellar_sharp_cnn_radius_8AI3_5s.pth'))
575
+ CC_NS8_ONNX = property(lambda self: model_path('deep_nonstellar_sharp_cnn_radius_8AI3_5s.onnx'))
576
+
577
+ CC_DENOISE_PTH = property(lambda self: model_path('deep_denoise_cnn_AI3_6.pth'))
578
+ CC_DENOISE_ONNX = property(lambda self: model_path('deep_denoise_cnn_AI3_6.onnx'))
579
+
580
+ # --- Super Resolution ---
581
+ CC_SUPERRES_2X_PTH = property(lambda self: model_path('superres_2x.pth'))
582
+ CC_SUPERRES_2X_ONNX = property(lambda self: model_path('superres_2x.onnx'))
583
+ CC_SUPERRES_3X_PTH = property(lambda self: model_path('superres_3x.pth'))
584
+ CC_SUPERRES_3X_ONNX = property(lambda self: model_path('superres_3x.onnx'))
585
+
586
+ CC_SUPERRES_4X_PTH = property(lambda self: model_path('superres_4x.pth'))
587
+ CC_SUPERRES_4X_ONNX = property(lambda self: model_path('superres_4x.onnx'))
588
+
589
+ # --- Dark Star (Star Removal) ---
590
+ CC_DARKSTAR_MONO_PTH = property(lambda self: model_path('darkstar_v2.1.pth'))
591
+ CC_DARKSTAR_MONO_ONNX = property(lambda self: model_path('darkstar_v2.1.onnx'))
592
+ CC_DARKSTAR_COLOR_PTH = property(lambda self: model_path('darkstar_v2.1c.pth'))
593
+ CC_DARKSTAR_COLOR_ONNX = property(lambda self: model_path('darkstar_v2.1c.onnx'))
594
+
595
+ # --- Cosmic Clarity Satellite Removal ---
596
+ CC_SAT_DETECT1_PTH = property(lambda self: model_path('satellite_trail_detector_AI3.5.pth'))
597
+ CC_SAT_DETECT1_ONNX = property(lambda self: model_path('satellite_trail_detector_AI3.5.onnx'))
598
+ CC_SAT_DETECT2_PTH = property(lambda self: model_path('satellite_trail_detector_mobilenetv2.5.pth'))
599
+ CC_SAT_DETECT2_ONNX = property(lambda self: model_path('satellite_trail_detector_mobilenetv2.5.onnx'))
600
+
601
+ CC_SAT_REMOVE_PTH = property(lambda self: model_path('satelliteremovalAI3.5.pth'))
602
+ CC_SAT_REMOVE_ONNX = property(lambda self: model_path('satelliteremovalAI3.5.onnx'))
603
+
604
+ @lru_cache(maxsize=8)
605
+ def get_models_dir() -> str:
606
+ # Prefer user-installed models
607
+ try:
608
+ from setiastro.saspro.model_manager import models_root
609
+ p = Path(models_root())
610
+ if p.is_dir():
611
+ if any(p.rglob("*.pth")) or any(p.rglob("*.onnx")):
612
+ return str(p)
613
+ except Exception:
614
+ pass
615
+
616
+ # Fallback to packaged (dev/frozen) path
617
+ return _resource_path('data/models')
618
+
619
+ def model_path(filename: str) -> str:
620
+ base = Path(get_models_dir())
621
+ path = base / filename
622
+ return str(path)
560
623
 
561
624
  # Export all legacy paths as module-level variables
562
625
  _legacy = _init_legacy_paths()
@@ -172,6 +172,24 @@ def _purge_bad_torch_from_sysmodules(status_cb=print) -> None:
172
172
  except Exception:
173
173
  pass
174
174
 
175
+ def _torch_stack_sanity_check(status_cb=print) -> None:
176
+ """
177
+ Ensure torch imports sanely AND torchvision/torchaudio are importable.
178
+ (Satellite engine requires torchvision; we install torchaudio too for safety.)
179
+ """
180
+ _torch_sanity_check(status_cb=status_cb)
181
+
182
+ try:
183
+ import torchvision # noqa
184
+ except Exception as e:
185
+ raise RuntimeError(f"torchvision import failed: {e}") from e
186
+
187
+ try:
188
+ import torchaudio # noqa
189
+ except Exception as e:
190
+ raise RuntimeError(f"torchaudio import failed: {e}") from e
191
+
192
+
175
193
  def _torch_sanity_check(status_cb=print):
176
194
  try:
177
195
  import torch
@@ -459,7 +477,8 @@ except Exception as e:
459
477
  return bool(data.get("has_xpu")), data.get("err")
460
478
 
461
479
 
462
- def _install_torch(venv_python: Path, prefer_cuda: bool, prefer_xpu: bool, status_cb=print):
480
+ def _install_torch(venv_python: Path, prefer_cuda: bool, prefer_xpu: bool, prefer_dml: bool, status_cb=print):
481
+
463
482
  """
464
483
  Install torch into the per-user venv with best-effort backend detection:
465
484
  • macOS arm64 → PyPI (MPS)
@@ -476,14 +495,6 @@ def _install_torch(venv_python: Path, prefer_cuda: bool, prefer_xpu: bool, statu
476
495
  return subprocess.run([str(venv_python), "-m", "pip", *args],
477
496
  stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, env=e)
478
497
 
479
- def _pip_ok(cmd: list[str]) -> bool:
480
- r = _pip(*cmd)
481
- if r.returncode != 0:
482
- # surface tail of pip log for the UI
483
- tail = (r.stdout or "").strip()
484
- status_cb(tail[-4000:])
485
- return r.returncode == 0
486
-
487
498
  def _pyver() -> tuple[int, int]:
488
499
  code = "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"
489
500
  out = subprocess.check_output([str(venv_python), "-c", code], text=True).strip()
@@ -494,6 +505,36 @@ def _install_torch(venv_python: Path, prefer_cuda: bool, prefer_xpu: bool, statu
494
505
  machine = _plat.machine().lower()
495
506
  py_major, py_minor = _pyver()
496
507
 
508
+ def _pip_ok(cmd: list[str]) -> bool:
509
+ r = _pip(*cmd)
510
+ if r.returncode != 0:
511
+ tail = (r.stdout or "").strip()
512
+ status_cb(tail[-4000:])
513
+ return r.returncode == 0
514
+
515
+ # NEW: DirectML FIRST (Windows, non-NVIDIA)
516
+ if sysname == "Windows" and prefer_dml:
517
+ status_cb("Installing PyTorch with DirectML (torch-directml)…")
518
+
519
+ # Clean slate helps resolver if something already got partially installed
520
+ _pip_ok(["uninstall", "-y", "torch", "torchvision", "torchaudio", "torch-directml"])
521
+
522
+ if not _pip_ok(["install", "--prefer-binary", "--no-cache-dir", "torch-directml"]):
523
+ raise RuntimeError("Failed to install torch-directml.")
524
+
525
+ # You still need torchvision/torchaudio for your app; let pip resolve compatible versions.
526
+ _pip_ok(["install", "--prefer-binary", "--no-cache-dir", "torchvision", "torchaudio"])
527
+
528
+ # Verify import + device creation
529
+ code = "import torch, torch_directml; d=torch_directml.device(); x=torch.tensor([1]).to(d); y=torch.tensor([2]).to(d); print(int((x+y).item()))"
530
+ r = subprocess.run([str(venv_python), "-c", code], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
531
+ if r.returncode != 0 or "3" not in (r.stdout or ""):
532
+ status_cb((r.stdout or "")[-2000:])
533
+ raise RuntimeError("torch-directml installed, but DirectML verification failed.")
534
+
535
+ status_cb("Installed DirectML backend successfully.")
536
+ return
537
+
497
538
  if sysname == "Darwin" and ("arm64" in machine or "aarch64" in machine):
498
539
  if py_minor >= 13:
499
540
  raise RuntimeError(
@@ -513,13 +554,16 @@ def _install_torch(venv_python: Path, prefer_cuda: bool, prefer_xpu: bool, statu
513
554
  base = ["install", "--prefer-binary", "--no-cache-dir"]
514
555
  if index_url:
515
556
  base += ["--index-url", index_url]
516
- # latest for that index first
517
- if _pip_ok(base + ["torch"]):
557
+
558
+ # First try "latest trio" from that index
559
+ if _pip_ok(base + ["torch", "torchvision", "torchaudio"]):
518
560
  return True
519
- # walk the ladder
561
+
562
+ # Walk the ladder: pin all three to the same version family
520
563
  for v in versions:
521
- if _pip_ok(base + [f"torch=={v}"]):
564
+ if _pip_ok(base + [f"torch=={v}", f"torchvision=={v}", f"torchaudio=={v}"]):
522
565
  return True
566
+
523
567
  return False
524
568
 
525
569
  # macOS Apple Silicon → MPS wheels on PyPI
@@ -587,7 +631,7 @@ def _install_torch(venv_python: Path, prefer_cuda: bool, prefer_xpu: bool, statu
587
631
  # Public entry points
588
632
  # ──────────────────────────────────────────────────────────────────────────────
589
633
 
590
- def import_torch(prefer_cuda: bool = True, prefer_xpu: bool = False, status_cb=print):
634
+ def import_torch(prefer_cuda: bool = True, prefer_xpu: bool = False, prefer_dml: bool = False, status_cb=print):
591
635
  """
592
636
  Ensure a per-user venv exists with torch installed; return the imported module.
593
637
  Hardened against shadow imports, broken wheels, concurrent installs, and partial markers.
@@ -596,10 +640,12 @@ def import_torch(prefer_cuda: bool = True, prefer_xpu: bool = False, status_cb=p
596
640
  _ban_shadow_torch_paths(status_cb=status_cb)
597
641
  _purge_bad_torch_from_sysmodules(status_cb=status_cb)
598
642
 
643
+ add_runtime_to_sys_path(status_cb=lambda *_: None)
644
+
599
645
  # Fast path: if torch already importable and sane, use it
600
646
  try:
601
647
  import torch # noqa
602
- _torch_sanity_check(status_cb=status_cb)
648
+ _torch_stack_sanity_check(status_cb=status_cb)
603
649
  return torch
604
650
  except Exception:
605
651
  pass
@@ -621,7 +667,7 @@ def import_torch(prefer_cuda: bool = True, prefer_xpu: bool = False, status_cb=p
621
667
  with _install_lock(rt):
622
668
  # Re-check inside lock in case another process finished
623
669
  if not marker.exists():
624
- _install_torch(vp, prefer_cuda=prefer_cuda, prefer_xpu=prefer_xpu, status_cb=status_cb)
670
+ _install_torch(vp, prefer_cuda=prefer_cuda, prefer_xpu=prefer_xpu, prefer_dml=prefer_dml, status_cb=status_cb)
625
671
  except Exception as e:
626
672
  if _is_access_denied(e):
627
673
  raise OSError(_access_denied_msg(rt)) from e
@@ -638,13 +684,28 @@ def import_torch(prefer_cuda: bool = True, prefer_xpu: bool = False, status_cb=p
638
684
  status_cb("Detected broken/shadowed Torch import → attempting clean repair…")
639
685
  except Exception:
640
686
  pass
641
- subprocess.run([str(vp), "-m", "pip", "uninstall", "-y", "torch"], check=False)
687
+
688
+ # remove marker so future launches don't skip install
689
+ try:
690
+ marker.unlink()
691
+ except Exception:
692
+ pass
693
+
694
+ subprocess.run([str(vp), "-m", "pip", "uninstall", "-y",
695
+ "torch", "torchvision", "torchaudio"], check=False)
642
696
  subprocess.run([str(vp), "-m", "pip", "cache", "purge"], check=False)
643
697
  with _install_lock(rt):
644
- _install_torch(vp, prefer_cuda=prefer_cuda, prefer_xpu=prefer_xpu, status_cb=status_cb)
698
+ _install_torch(
699
+ vp,
700
+ prefer_cuda=prefer_cuda,
701
+ prefer_xpu=prefer_xpu,
702
+ prefer_dml=prefer_dml,
703
+ status_cb=status_cb,
704
+ )
645
705
  importlib.invalidate_caches()
646
706
  _demote_shadow_torch_paths(status_cb=status_cb)
647
707
 
708
+
648
709
  try:
649
710
  _ensure_numpy(vp, status_cb=status_cb)
650
711
  except Exception:
@@ -652,21 +713,45 @@ def import_torch(prefer_cuda: bool = True, prefer_xpu: bool = False, status_cb=p
652
713
 
653
714
  try:
654
715
  import torch # noqa
655
- _torch_sanity_check(status_cb=status_cb)
716
+ _torch_stack_sanity_check(status_cb=status_cb)
656
717
  # write/update marker only when sane
657
718
  if not marker.exists():
658
719
  pyver = f"{sys.version_info.major}.{sys.version_info.minor}"
659
- marker.write_text(json.dumps({"installed": True, "python": pyver, "when": int(time.time())}), encoding="utf-8")
720
+ try:
721
+ import torch, torchvision, torchaudio
722
+ marker.write_text(json.dumps({
723
+ "installed": True,
724
+ "python": pyver,
725
+ "when": int(time.time()),
726
+ "torch": getattr(torch, "__version__", None),
727
+ "torchvision": getattr(torchvision, "__version__", None),
728
+ "torchaudio": getattr(torchaudio, "__version__", None),
729
+ }), encoding="utf-8")
730
+ except Exception:
731
+ marker.write_text(json.dumps({"installed": True, "python": pyver, "when": int(time.time())}), encoding="utf-8")
732
+
660
733
  return torch
661
734
  except Exception:
662
735
  _force_repair()
663
736
  _purge_bad_torch_from_sysmodules(status_cb=status_cb)
664
737
  _ban_shadow_torch_paths(status_cb=status_cb)
665
738
  import torch # retry
666
- _torch_sanity_check(status_cb=status_cb)
739
+ _torch_stack_sanity_check(status_cb=status_cb)
667
740
  if not marker.exists():
668
741
  pyver = f"{sys.version_info.major}.{sys.version_info.minor}"
669
- marker.write_text(json.dumps({"installed": True, "python": pyver, "when": int(time.time())}), encoding="utf-8")
742
+ try:
743
+ import torch, torchvision, torchaudio
744
+ marker.write_text(json.dumps({
745
+ "installed": True,
746
+ "python": pyver,
747
+ "when": int(time.time()),
748
+ "torch": getattr(torch, "__version__", None),
749
+ "torchvision": getattr(torchvision, "__version__", None),
750
+ "torchaudio": getattr(torchaudio, "__version__", None),
751
+ }), encoding="utf-8")
752
+ except Exception:
753
+ marker.write_text(json.dumps({"installed": True, "python": pyver, "when": int(time.time())}), encoding="utf-8")
754
+
670
755
  return torch
671
756
 
672
757
  def _find_system_python_cmd() -> list[str]:
@@ -11,7 +11,7 @@ from PyQt6.QtWidgets import (
11
11
  QDialog, QVBoxLayout, QHBoxLayout, QGridLayout, QGroupBox, QLabel, QPushButton,
12
12
  QSlider, QCheckBox, QColorDialog, QComboBox, QFileDialog, QInputDialog, QMenu,
13
13
  QMessageBox, QWidget, QGraphicsView, QGraphicsScene, QGraphicsItem,QFontComboBox, QGraphicsTextItem,
14
- QGraphicsPixmapItem, QGraphicsEllipseItem, QGraphicsRectItem, QSpinBox
14
+ QGraphicsPixmapItem, QGraphicsEllipseItem, QGraphicsRectItem, QSpinBox, QScrollArea
15
15
  )
16
16
  from setiastro.saspro.widgets.themed_buttons import themed_toolbtn
17
17
 
@@ -844,8 +844,19 @@ class SignatureInsertDialogPro(QDialog):
844
844
  row_commit.addStretch(1)
845
845
  col.addLayout(row_commit)
846
846
 
847
- left = QWidget(); left.setLayout(col)
848
- root.addWidget(left, 0)
847
+ left = QWidget()
848
+ left.setLayout(col)
849
+
850
+ scroll = QScrollArea(self)
851
+ scroll.setWidgetResizable(True)
852
+ scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
853
+ scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
854
+ scroll.setWidget(left)
855
+
856
+ # Optional: make it feel less “webby”
857
+ scroll.setFrameShape(QScrollArea.Shape.NoFrame)
858
+
859
+ root.addWidget(scroll, 0) # instead of root.addWidget(left, 0)
849
860
  root.addWidget(self.view, 1)
850
861
 
851
862
  def _tech_active_doc(self):