setiastrosuitepro 1.6.7__py3-none-any.whl → 1.7.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.
Potentially problematic release.
This version of setiastrosuitepro might be problematic. Click here for more details.
- setiastro/images/abeicon.svg +16 -0
- setiastro/images/colorwheel.svg +97 -0
- setiastro/images/cosmic.svg +40 -0
- setiastro/images/cosmicsat.svg +24 -0
- setiastro/images/graxpert.svg +19 -0
- setiastro/images/linearfit.svg +32 -0
- setiastro/images/narrowbandnormalization.png +0 -0
- setiastro/images/pixelmath.svg +42 -0
- setiastro/images/planetarystacker.png +0 -0
- setiastro/saspro/__main__.py +1 -1
- setiastro/saspro/_generated/build_info.py +2 -2
- setiastro/saspro/aberration_ai.py +49 -11
- setiastro/saspro/aberration_ai_preset.py +29 -3
- setiastro/saspro/add_stars.py +29 -5
- setiastro/saspro/backgroundneutral.py +73 -33
- setiastro/saspro/blink_comparator_pro.py +150 -55
- setiastro/saspro/convo.py +9 -6
- setiastro/saspro/cosmicclarity.py +125 -18
- setiastro/saspro/crop_dialog_pro.py +96 -2
- setiastro/saspro/curve_editor_pro.py +132 -61
- setiastro/saspro/curves_preset.py +249 -47
- setiastro/saspro/doc_manager.py +178 -11
- setiastro/saspro/frequency_separation.py +1159 -208
- setiastro/saspro/gui/main_window.py +340 -88
- setiastro/saspro/gui/mixins/dock_mixin.py +245 -24
- setiastro/saspro/gui/mixins/file_mixin.py +35 -16
- setiastro/saspro/gui/mixins/menu_mixin.py +31 -1
- setiastro/saspro/gui/mixins/theme_mixin.py +160 -14
- setiastro/saspro/gui/mixins/toolbar_mixin.py +132 -10
- setiastro/saspro/gui/mixins/update_mixin.py +121 -33
- setiastro/saspro/histogram.py +179 -7
- setiastro/saspro/imageops/narrowband_normalization.py +816 -0
- setiastro/saspro/imageops/serloader.py +769 -0
- setiastro/saspro/imageops/starbasedwhitebalance.py +23 -52
- setiastro/saspro/imageops/stretch.py +582 -62
- setiastro/saspro/layers.py +13 -9
- setiastro/saspro/layers_dock.py +183 -3
- setiastro/saspro/legacy/numba_utils.py +68 -48
- setiastro/saspro/live_stacking.py +181 -73
- setiastro/saspro/multiscale_decomp.py +77 -29
- setiastro/saspro/narrowband_normalization.py +1618 -0
- setiastro/saspro/numba_utils.py +72 -57
- setiastro/saspro/ops/commands.py +18 -18
- setiastro/saspro/ops/script_editor.py +5 -0
- setiastro/saspro/ops/scripts.py +119 -0
- setiastro/saspro/remove_green.py +1 -1
- setiastro/saspro/resources.py +4 -0
- setiastro/saspro/ser_stack_config.py +68 -0
- setiastro/saspro/ser_stacker.py +2245 -0
- setiastro/saspro/ser_stacker_dialog.py +1481 -0
- setiastro/saspro/ser_tracking.py +206 -0
- setiastro/saspro/serviewer.py +1242 -0
- setiastro/saspro/sfcc.py +602 -214
- setiastro/saspro/shortcuts.py +154 -25
- setiastro/saspro/signature_insert.py +688 -33
- setiastro/saspro/stacking_suite.py +853 -401
- setiastro/saspro/star_alignment.py +243 -122
- setiastro/saspro/stat_stretch.py +878 -131
- setiastro/saspro/subwindow.py +303 -74
- setiastro/saspro/whitebalance.py +24 -0
- setiastro/saspro/widgets/common_utilities.py +28 -21
- setiastro/saspro/widgets/resource_monitor.py +128 -80
- {setiastrosuitepro-1.6.7.dist-info → setiastrosuitepro-1.7.0.dist-info}/METADATA +2 -2
- {setiastrosuitepro-1.6.7.dist-info → setiastrosuitepro-1.7.0.dist-info}/RECORD +68 -51
- {setiastrosuitepro-1.6.7.dist-info → setiastrosuitepro-1.7.0.dist-info}/WHEEL +0 -0
- {setiastrosuitepro-1.6.7.dist-info → setiastrosuitepro-1.7.0.dist-info}/entry_points.txt +0 -0
- {setiastrosuitepro-1.6.7.dist-info → setiastrosuitepro-1.7.0.dist-info}/licenses/LICENSE +0 -0
- {setiastrosuitepro-1.6.7.dist-info → setiastrosuitepro-1.7.0.dist-info}/licenses/license.txt +0 -0
|
@@ -120,37 +120,41 @@ def soft_threshold(x: np.ndarray, t: float):
|
|
|
120
120
|
def apply_layer_ops(
|
|
121
121
|
w: np.ndarray,
|
|
122
122
|
bias_gain: float,
|
|
123
|
-
thr_sigma: float,
|
|
123
|
+
thr_sigma: float,
|
|
124
124
|
amount: float,
|
|
125
125
|
denoise_strength: float = 0.0,
|
|
126
126
|
sigma: float | np.ndarray | None = None,
|
|
127
|
+
layer_index: int | None = None,
|
|
127
128
|
*,
|
|
128
129
|
mode: str = "μ–σ Thresholding",
|
|
129
130
|
):
|
|
130
131
|
w2 = w
|
|
131
132
|
|
|
132
|
-
# Normalize mode to something robust to label wording
|
|
133
133
|
m = (mode or "").strip().lower()
|
|
134
134
|
is_linear = m.startswith("linear")
|
|
135
|
-
|
|
136
|
-
# --- Linear mode: strictly linear multiscale transform ---
|
|
137
135
|
if is_linear:
|
|
138
|
-
|
|
139
|
-
if abs(bias_gain - 1.0) > 1e-6:
|
|
140
|
-
return w * bias_gain
|
|
141
|
-
return w
|
|
136
|
+
return w * bias_gain if abs(bias_gain - 1.0) > 1e-6 else w
|
|
142
137
|
|
|
143
|
-
# --- μ–σ Thresholding mode (robust nonlinear) ---
|
|
144
138
|
# 1) Noise reduction step (MMT-style NR)
|
|
145
139
|
if denoise_strength > 0.0:
|
|
146
140
|
if sigma is None:
|
|
147
141
|
sigma = _robust_sigma(w2)
|
|
148
142
|
sigma_f = float(sigma)
|
|
149
|
-
|
|
150
|
-
|
|
143
|
+
|
|
144
|
+
i = int(layer_index or 0)
|
|
145
|
+
|
|
146
|
+
# --- SMOOTH scaling option (pick ONE) ---
|
|
147
|
+
# Option A: linear growth (very controllable)
|
|
148
|
+
# scale = 1.0 + 0.75 * i
|
|
149
|
+
|
|
150
|
+
# Option B: sqrt growth of 2^i (gentle, "natural")
|
|
151
|
+
scale = (2.0 ** i) ** 0.5 # 1, 1.41, 2, 2.83, 4, ...
|
|
152
|
+
|
|
153
|
+
# Base: 3σ at denoise=1 for layer 0, increases by scale
|
|
154
|
+
t_dn = denoise_strength * 3.0 * scale * sigma_f
|
|
155
|
+
|
|
151
156
|
if t_dn > 0.0:
|
|
152
157
|
w_dn = soft_threshold(w2, t_dn)
|
|
153
|
-
# Blend original vs denoised based on denoise_strength
|
|
154
158
|
w2 = (1.0 - denoise_strength) * w2 + denoise_strength * w_dn
|
|
155
159
|
|
|
156
160
|
# 2) Threshold in σ units + bias shaping
|
|
@@ -158,7 +162,7 @@ def apply_layer_ops(
|
|
|
158
162
|
if sigma is None:
|
|
159
163
|
sigma = _robust_sigma(w2)
|
|
160
164
|
sigma_f = float(sigma)
|
|
161
|
-
t = thr_sigma * sigma_f
|
|
165
|
+
t = thr_sigma * sigma_f
|
|
162
166
|
if t > 0.0:
|
|
163
167
|
wt = soft_threshold(w2, t)
|
|
164
168
|
w2 = (1.0 - amount) * w2 + amount * wt
|
|
@@ -167,7 +171,6 @@ def apply_layer_ops(
|
|
|
167
171
|
w2 = w2 * bias_gain
|
|
168
172
|
return w2
|
|
169
173
|
|
|
170
|
-
|
|
171
174
|
def _robust_sigma(arr: np.ndarray) -> float:
|
|
172
175
|
"""
|
|
173
176
|
Robust per-layer sigma estimate using MAD, fallback to std if needed.
|
|
@@ -455,7 +458,7 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
455
458
|
|
|
456
459
|
# --- Spin boxes ---
|
|
457
460
|
self.spin_gain = QDoubleSpinBox()
|
|
458
|
-
self.spin_gain.setRange(0.0,
|
|
461
|
+
self.spin_gain.setRange(0.0, 10.0)
|
|
459
462
|
self.spin_gain.setSingleStep(0.05)
|
|
460
463
|
self.spin_gain.setValue(1.0)
|
|
461
464
|
self.spin_gain.setToolTip(
|
|
@@ -491,7 +494,7 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
491
494
|
|
|
492
495
|
# --- Sliders (int ranges, mapped to spins) ---
|
|
493
496
|
self.slider_gain = QSlider(Qt.Orientation.Horizontal)
|
|
494
|
-
self.slider_gain.setRange(0,
|
|
497
|
+
self.slider_gain.setRange(0, 1000) # 0..10.00
|
|
495
498
|
self.slider_gain.setToolTip(self.spin_gain.toolTip())
|
|
496
499
|
|
|
497
500
|
self.slider_thr = QSlider(Qt.Orientation.Horizontal)
|
|
@@ -579,19 +582,29 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
579
582
|
|
|
580
583
|
# ---------- Preview plumbing ----------
|
|
581
584
|
def _spinner_on(self):
|
|
582
|
-
if getattr(self, "
|
|
585
|
+
if getattr(self, "_closing", False):
|
|
586
|
+
return
|
|
587
|
+
try:
|
|
588
|
+
sp = getattr(self, "busy_spinner", None)
|
|
589
|
+
if sp is None:
|
|
590
|
+
return
|
|
591
|
+
sp.setVisible(True)
|
|
592
|
+
mv = getattr(self, "_busy_movie", None)
|
|
593
|
+
if mv is not None and mv.state() != QMovie.MovieState.Running:
|
|
594
|
+
mv.start()
|
|
595
|
+
except RuntimeError:
|
|
583
596
|
return
|
|
584
|
-
self.busy_spinner.setVisible(True)
|
|
585
|
-
if getattr(self, "_busy_movie", None) is not None:
|
|
586
|
-
if self._busy_movie.state() != QMovie.MovieState.Running:
|
|
587
|
-
self._busy_movie.start()
|
|
588
597
|
|
|
589
598
|
def _spinner_off(self):
|
|
590
|
-
|
|
599
|
+
try:
|
|
600
|
+
sp = getattr(self, "busy_spinner", None)
|
|
601
|
+
mv = getattr(self, "_busy_movie", None)
|
|
602
|
+
if mv is not None:
|
|
603
|
+
mv.stop()
|
|
604
|
+
if sp is not None:
|
|
605
|
+
sp.setVisible(False)
|
|
606
|
+
except RuntimeError:
|
|
591
607
|
return
|
|
592
|
-
if getattr(self, "_busy_movie", None) is not None:
|
|
593
|
-
self._busy_movie.stop()
|
|
594
|
-
self.busy_spinner.setVisible(False)
|
|
595
608
|
|
|
596
609
|
|
|
597
610
|
def _show_busy_overlay(self):
|
|
@@ -623,11 +636,13 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
623
636
|
self._schedule_preview()
|
|
624
637
|
|
|
625
638
|
def _schedule_preview(self):
|
|
626
|
-
|
|
639
|
+
if getattr(self, "_closing", False):
|
|
640
|
+
return
|
|
627
641
|
self._preview_timer.start(60)
|
|
628
642
|
|
|
629
643
|
def _schedule_roi_preview(self):
|
|
630
|
-
|
|
644
|
+
if getattr(self, "_closing", False):
|
|
645
|
+
return
|
|
631
646
|
self._preview_timer.start(60)
|
|
632
647
|
|
|
633
648
|
def _connect_viewport_signals(self):
|
|
@@ -736,6 +751,9 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
736
751
|
cfg = self.cfgs[i]
|
|
737
752
|
if not cfg.enabled:
|
|
738
753
|
return i, np.zeros_like(w)
|
|
754
|
+
|
|
755
|
+
layer_sigma = self.base_sigma * (2 ** i)
|
|
756
|
+
|
|
739
757
|
sigma = self._layer_noise[i] if self._layer_noise and i < len(self._layer_noise) else None
|
|
740
758
|
out = apply_layer_ops(
|
|
741
759
|
w,
|
|
@@ -744,6 +762,7 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
744
762
|
cfg.amount,
|
|
745
763
|
cfg.denoise,
|
|
746
764
|
sigma,
|
|
765
|
+
layer_index=i,
|
|
747
766
|
mode=mode,
|
|
748
767
|
)
|
|
749
768
|
return i, out
|
|
@@ -764,8 +783,15 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
764
783
|
return tuned, residual
|
|
765
784
|
|
|
766
785
|
def _rebuild_preview(self):
|
|
786
|
+
if getattr(self, "_closing", False):
|
|
787
|
+
return
|
|
767
788
|
self._spinner_on()
|
|
768
|
-
|
|
789
|
+
QTimer.singleShot(0, self._rebuild_preview_impl)
|
|
790
|
+
|
|
791
|
+
def _rebuild_preview_impl(self):
|
|
792
|
+
if getattr(self, "_closing", False):
|
|
793
|
+
return
|
|
794
|
+
|
|
769
795
|
#self._begin_busy()
|
|
770
796
|
try:
|
|
771
797
|
# ROI preview can't work until we have *some* pixmap in the scene to derive visible rects from.
|
|
@@ -1245,11 +1271,17 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
1245
1271
|
cfg = self.cfgs[i]
|
|
1246
1272
|
if not cfg.enabled:
|
|
1247
1273
|
return i, np.zeros_like(w)
|
|
1274
|
+
|
|
1275
|
+
layer_sigma = base_sigma * (2 ** i)
|
|
1276
|
+
|
|
1248
1277
|
return i, apply_layer_ops(
|
|
1249
1278
|
w, cfg.bias_gain, cfg.thr, cfg.amount, cfg.denoise,
|
|
1250
|
-
layer_noise[i],
|
|
1279
|
+
layer_noise[i],
|
|
1280
|
+
layer_index=i,
|
|
1281
|
+
mode=mode
|
|
1251
1282
|
)
|
|
1252
1283
|
|
|
1284
|
+
|
|
1253
1285
|
tuned = [None] * len(details)
|
|
1254
1286
|
max_workers = min(os.cpu_count() or 4, len(details) or 1)
|
|
1255
1287
|
with ThreadPoolExecutor(max_workers=max_workers) as ex:
|
|
@@ -1749,3 +1781,19 @@ class _MultiScaleDecompPresetDialog(QDialog):
|
|
|
1749
1781
|
"linked_rgb": bool(self.cb_linked.isChecked()),
|
|
1750
1782
|
"layers_cfg": out_layers,
|
|
1751
1783
|
}
|
|
1784
|
+
def closeEvent(self, ev):
|
|
1785
|
+
self._closing = True
|
|
1786
|
+
try:
|
|
1787
|
+
if hasattr(self, "_preview_timer"):
|
|
1788
|
+
self._preview_timer.stop()
|
|
1789
|
+
if hasattr(self, "_busy_show_timer"):
|
|
1790
|
+
self._busy_show_timer.stop()
|
|
1791
|
+
# Optional: disconnect scrollbars to stop ROI scheduling
|
|
1792
|
+
try:
|
|
1793
|
+
self.view.horizontalScrollBar().valueChanged.disconnect(self._schedule_roi_preview)
|
|
1794
|
+
self.view.verticalScrollBar().valueChanged.disconnect(self._schedule_roi_preview)
|
|
1795
|
+
except Exception:
|
|
1796
|
+
pass
|
|
1797
|
+
except Exception:
|
|
1798
|
+
pass
|
|
1799
|
+
super().closeEvent(ev)
|