setiastrosuitepro 1.6.4__py3-none-any.whl → 1.7.1.post2__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.
- setiastro/images/TextureClarity.svg +56 -0
- 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/narrowbandnormalization.png +0 -0
- setiastro/images/new_moon.png +0 -0
- setiastro/images/pixelmath.svg +42 -0
- setiastro/images/planetarystacker.png +0 -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 +364 -33
- setiastro/saspro/aberration_ai_preset.py +29 -3
- setiastro/saspro/acv_exporter.py +379 -0
- setiastro/saspro/add_stars.py +33 -6
- setiastro/saspro/astrospike_python.py +45 -3
- setiastro/saspro/backgroundneutral.py +108 -40
- setiastro/saspro/blemish_blaster.py +4 -1
- setiastro/saspro/blink_comparator_pro.py +150 -55
- 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 +181 -64
- setiastro/saspro/curves_preset.py +249 -47
- 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 +706 -264
- 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 +35 -1
- setiastro/saspro/gui/mixins/theme_mixin.py +160 -14
- setiastro/saspro/gui/mixins/toolbar_mixin.py +499 -24
- 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 +184 -8
- setiastro/saspro/image_combine.py +4 -0
- setiastro/saspro/image_peeker_pro.py +4 -0
- setiastro/saspro/imageops/narrowband_normalization.py +816 -0
- setiastro/saspro/imageops/serloader.py +1345 -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 +68 -48
- setiastro/saspro/legacy/xisf.py +240 -98
- setiastro/saspro/live_stacking.py +203 -82
- 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 +81 -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 +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/remove_green.py +1 -1
- setiastro/saspro/resources.py +73 -0
- setiastro/saspro/rgbalign.py +460 -12
- setiastro/saspro/selective_color.py +4 -1
- setiastro/saspro/ser_stack_config.py +82 -0
- setiastro/saspro/ser_stacker.py +2321 -0
- setiastro/saspro/ser_stacker_dialog.py +1838 -0
- setiastro/saspro/ser_tracking.py +206 -0
- setiastro/saspro/serviewer.py +1625 -0
- setiastro/saspro/sfcc.py +662 -216
- setiastro/saspro/shortcuts.py +171 -33
- setiastro/saspro/signature_insert.py +692 -33
- setiastro/saspro/stacking_suite.py +1347 -485
- 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 +892 -129
- setiastro/saspro/subwindow.py +787 -363
- setiastro/saspro/supernovaasteroidhunter.py +1 -1
- setiastro/saspro/texture_clarity.py +593 -0
- 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 +209 -111
- 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.7.1.post2.dist-info}/METADATA +4 -2
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.dist-info}/RECORD +132 -87
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.dist-info}/WHEEL +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.dist-info}/entry_points.txt +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.dist-info}/licenses/LICENSE +0 -0
- {setiastrosuitepro-1.6.4.dist-info → setiastrosuitepro-1.7.1.post2.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.
|
|
@@ -219,6 +222,10 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
219
222
|
self.setWindowFlag(Qt.WindowType.Window, True)
|
|
220
223
|
self.setWindowModality(Qt.WindowModality.NonModal)
|
|
221
224
|
self.setModal(False)
|
|
225
|
+
try:
|
|
226
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
|
|
227
|
+
except Exception:
|
|
228
|
+
pass # older PyQt6 versions
|
|
222
229
|
self.setMinimumSize(1050, 700)
|
|
223
230
|
self.residual_enabled = True
|
|
224
231
|
self._layer_noise = None # list[float] per detail layer
|
|
@@ -451,7 +458,7 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
451
458
|
|
|
452
459
|
# --- Spin boxes ---
|
|
453
460
|
self.spin_gain = QDoubleSpinBox()
|
|
454
|
-
self.spin_gain.setRange(0.0,
|
|
461
|
+
self.spin_gain.setRange(0.0, 10.0)
|
|
455
462
|
self.spin_gain.setSingleStep(0.05)
|
|
456
463
|
self.spin_gain.setValue(1.0)
|
|
457
464
|
self.spin_gain.setToolTip(
|
|
@@ -487,7 +494,7 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
487
494
|
|
|
488
495
|
# --- Sliders (int ranges, mapped to spins) ---
|
|
489
496
|
self.slider_gain = QSlider(Qt.Orientation.Horizontal)
|
|
490
|
-
self.slider_gain.setRange(0,
|
|
497
|
+
self.slider_gain.setRange(0, 1000) # 0..10.00
|
|
491
498
|
self.slider_gain.setToolTip(self.spin_gain.toolTip())
|
|
492
499
|
|
|
493
500
|
self.slider_thr = QSlider(Qt.Orientation.Horizontal)
|
|
@@ -575,19 +582,29 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
575
582
|
|
|
576
583
|
# ---------- Preview plumbing ----------
|
|
577
584
|
def _spinner_on(self):
|
|
578
|
-
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:
|
|
579
596
|
return
|
|
580
|
-
self.busy_spinner.setVisible(True)
|
|
581
|
-
if getattr(self, "_busy_movie", None) is not None:
|
|
582
|
-
if self._busy_movie.state() != QMovie.MovieState.Running:
|
|
583
|
-
self._busy_movie.start()
|
|
584
597
|
|
|
585
598
|
def _spinner_off(self):
|
|
586
|
-
|
|
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:
|
|
587
607
|
return
|
|
588
|
-
if getattr(self, "_busy_movie", None) is not None:
|
|
589
|
-
self._busy_movie.stop()
|
|
590
|
-
self.busy_spinner.setVisible(False)
|
|
591
608
|
|
|
592
609
|
|
|
593
610
|
def _show_busy_overlay(self):
|
|
@@ -619,11 +636,13 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
619
636
|
self._schedule_preview()
|
|
620
637
|
|
|
621
638
|
def _schedule_preview(self):
|
|
622
|
-
|
|
639
|
+
if getattr(self, "_closing", False):
|
|
640
|
+
return
|
|
623
641
|
self._preview_timer.start(60)
|
|
624
642
|
|
|
625
643
|
def _schedule_roi_preview(self):
|
|
626
|
-
|
|
644
|
+
if getattr(self, "_closing", False):
|
|
645
|
+
return
|
|
627
646
|
self._preview_timer.start(60)
|
|
628
647
|
|
|
629
648
|
def _connect_viewport_signals(self):
|
|
@@ -732,6 +751,9 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
732
751
|
cfg = self.cfgs[i]
|
|
733
752
|
if not cfg.enabled:
|
|
734
753
|
return i, np.zeros_like(w)
|
|
754
|
+
|
|
755
|
+
layer_sigma = self.base_sigma * (2 ** i)
|
|
756
|
+
|
|
735
757
|
sigma = self._layer_noise[i] if self._layer_noise and i < len(self._layer_noise) else None
|
|
736
758
|
out = apply_layer_ops(
|
|
737
759
|
w,
|
|
@@ -740,6 +762,7 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
740
762
|
cfg.amount,
|
|
741
763
|
cfg.denoise,
|
|
742
764
|
sigma,
|
|
765
|
+
layer_index=i,
|
|
743
766
|
mode=mode,
|
|
744
767
|
)
|
|
745
768
|
return i, out
|
|
@@ -760,8 +783,15 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
760
783
|
return tuned, residual
|
|
761
784
|
|
|
762
785
|
def _rebuild_preview(self):
|
|
786
|
+
if getattr(self, "_closing", False):
|
|
787
|
+
return
|
|
763
788
|
self._spinner_on()
|
|
764
|
-
|
|
789
|
+
QTimer.singleShot(0, self._rebuild_preview_impl)
|
|
790
|
+
|
|
791
|
+
def _rebuild_preview_impl(self):
|
|
792
|
+
if getattr(self, "_closing", False):
|
|
793
|
+
return
|
|
794
|
+
|
|
765
795
|
#self._begin_busy()
|
|
766
796
|
try:
|
|
767
797
|
# ROI preview can't work until we have *some* pixmap in the scene to derive visible rects from.
|
|
@@ -1241,11 +1271,17 @@ class MultiscaleDecompDialog(QDialog):
|
|
|
1241
1271
|
cfg = self.cfgs[i]
|
|
1242
1272
|
if not cfg.enabled:
|
|
1243
1273
|
return i, np.zeros_like(w)
|
|
1274
|
+
|
|
1275
|
+
layer_sigma = base_sigma * (2 ** i)
|
|
1276
|
+
|
|
1244
1277
|
return i, apply_layer_ops(
|
|
1245
1278
|
w, cfg.bias_gain, cfg.thr, cfg.amount, cfg.denoise,
|
|
1246
|
-
layer_noise[i],
|
|
1279
|
+
layer_noise[i],
|
|
1280
|
+
layer_index=i,
|
|
1281
|
+
mode=mode
|
|
1247
1282
|
)
|
|
1248
1283
|
|
|
1284
|
+
|
|
1249
1285
|
tuned = [None] * len(details)
|
|
1250
1286
|
max_workers = min(os.cpu_count() or 4, len(details) or 1)
|
|
1251
1287
|
with ThreadPoolExecutor(max_workers=max_workers) as ex:
|
|
@@ -1745,3 +1781,19 @@ class _MultiScaleDecompPresetDialog(QDialog):
|
|
|
1745
1781
|
"linked_rgb": bool(self.cb_linked.isChecked()),
|
|
1746
1782
|
"layers_cfg": out_layers,
|
|
1747
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)
|