setiastrosuitepro 1.6.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/__init__.py +2 -0
- setiastro/saspro/__init__.py +20 -0
- setiastro/saspro/__main__.py +784 -0
- setiastro/saspro/_generated/__init__.py +7 -0
- setiastro/saspro/_generated/build_info.py +2 -0
- setiastro/saspro/abe.py +1295 -0
- setiastro/saspro/abe_preset.py +196 -0
- setiastro/saspro/aberration_ai.py +694 -0
- setiastro/saspro/aberration_ai_preset.py +224 -0
- setiastro/saspro/accel_installer.py +218 -0
- setiastro/saspro/accel_workers.py +30 -0
- setiastro/saspro/add_stars.py +621 -0
- setiastro/saspro/astrobin_exporter.py +1007 -0
- setiastro/saspro/astrospike.py +153 -0
- setiastro/saspro/astrospike_python.py +1839 -0
- setiastro/saspro/autostretch.py +196 -0
- setiastro/saspro/backgroundneutral.py +560 -0
- setiastro/saspro/batch_convert.py +325 -0
- setiastro/saspro/batch_renamer.py +519 -0
- setiastro/saspro/blemish_blaster.py +488 -0
- setiastro/saspro/blink_comparator_pro.py +2923 -0
- setiastro/saspro/bundles.py +61 -0
- setiastro/saspro/bundles_dock.py +114 -0
- setiastro/saspro/cheat_sheet.py +168 -0
- setiastro/saspro/clahe.py +342 -0
- setiastro/saspro/comet_stacking.py +1377 -0
- setiastro/saspro/config.py +38 -0
- setiastro/saspro/config_bootstrap.py +40 -0
- setiastro/saspro/config_manager.py +316 -0
- setiastro/saspro/continuum_subtract.py +1617 -0
- setiastro/saspro/convo.py +1397 -0
- setiastro/saspro/convo_preset.py +414 -0
- setiastro/saspro/copyastro.py +187 -0
- setiastro/saspro/cosmicclarity.py +1564 -0
- setiastro/saspro/cosmicclarity_preset.py +407 -0
- setiastro/saspro/crop_dialog_pro.py +948 -0
- setiastro/saspro/crop_preset.py +189 -0
- setiastro/saspro/curve_editor_pro.py +2544 -0
- setiastro/saspro/curves_preset.py +375 -0
- setiastro/saspro/debayer.py +670 -0
- setiastro/saspro/debug_utils.py +29 -0
- setiastro/saspro/dnd_mime.py +35 -0
- setiastro/saspro/doc_manager.py +2634 -0
- setiastro/saspro/exoplanet_detector.py +2166 -0
- setiastro/saspro/file_utils.py +284 -0
- setiastro/saspro/fitsmodifier.py +744 -0
- setiastro/saspro/free_torch_memory.py +48 -0
- setiastro/saspro/frequency_separation.py +1343 -0
- setiastro/saspro/function_bundle.py +1594 -0
- setiastro/saspro/ghs_dialog_pro.py +660 -0
- setiastro/saspro/ghs_preset.py +284 -0
- setiastro/saspro/graxpert.py +634 -0
- setiastro/saspro/graxpert_preset.py +287 -0
- setiastro/saspro/gui/__init__.py +0 -0
- setiastro/saspro/gui/main_window.py +8494 -0
- setiastro/saspro/gui/mixins/__init__.py +33 -0
- setiastro/saspro/gui/mixins/dock_mixin.py +263 -0
- setiastro/saspro/gui/mixins/file_mixin.py +445 -0
- setiastro/saspro/gui/mixins/geometry_mixin.py +403 -0
- setiastro/saspro/gui/mixins/header_mixin.py +441 -0
- setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
- setiastro/saspro/gui/mixins/menu_mixin.py +361 -0
- setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
- setiastro/saspro/gui/mixins/toolbar_mixin.py +1324 -0
- setiastro/saspro/gui/mixins/update_mixin.py +309 -0
- setiastro/saspro/gui/mixins/view_mixin.py +435 -0
- setiastro/saspro/halobgon.py +462 -0
- setiastro/saspro/header_viewer.py +445 -0
- setiastro/saspro/headless_utils.py +88 -0
- setiastro/saspro/histogram.py +753 -0
- setiastro/saspro/history_explorer.py +939 -0
- setiastro/saspro/image_combine.py +414 -0
- setiastro/saspro/image_peeker_pro.py +1596 -0
- setiastro/saspro/imageops/__init__.py +37 -0
- setiastro/saspro/imageops/mdi_snap.py +292 -0
- setiastro/saspro/imageops/scnr.py +36 -0
- setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
- setiastro/saspro/imageops/stretch.py +244 -0
- setiastro/saspro/isophote.py +1179 -0
- setiastro/saspro/layers.py +208 -0
- setiastro/saspro/layers_dock.py +714 -0
- setiastro/saspro/lazy_imports.py +193 -0
- setiastro/saspro/legacy/__init__.py +2 -0
- setiastro/saspro/legacy/image_manager.py +2226 -0
- setiastro/saspro/legacy/numba_utils.py +3659 -0
- setiastro/saspro/legacy/xisf.py +1071 -0
- setiastro/saspro/linear_fit.py +534 -0
- setiastro/saspro/live_stacking.py +1830 -0
- setiastro/saspro/log_bus.py +5 -0
- setiastro/saspro/logging_config.py +460 -0
- setiastro/saspro/luminancerecombine.py +309 -0
- setiastro/saspro/main_helpers.py +201 -0
- setiastro/saspro/mask_creation.py +928 -0
- setiastro/saspro/masks_core.py +56 -0
- setiastro/saspro/mdi_widgets.py +353 -0
- setiastro/saspro/memory_utils.py +666 -0
- setiastro/saspro/metadata_patcher.py +75 -0
- setiastro/saspro/mfdeconv.py +3826 -0
- setiastro/saspro/mfdeconv_earlystop.py +71 -0
- setiastro/saspro/mfdeconvcudnn.py +3263 -0
- setiastro/saspro/mfdeconvsport.py +2382 -0
- setiastro/saspro/minorbodycatalog.py +567 -0
- setiastro/saspro/morphology.py +382 -0
- setiastro/saspro/multiscale_decomp.py +1290 -0
- setiastro/saspro/nbtorgb_stars.py +531 -0
- setiastro/saspro/numba_utils.py +3044 -0
- setiastro/saspro/numba_warmup.py +141 -0
- setiastro/saspro/ops/__init__.py +9 -0
- setiastro/saspro/ops/command_help_dialog.py +623 -0
- setiastro/saspro/ops/command_runner.py +217 -0
- setiastro/saspro/ops/commands.py +1594 -0
- setiastro/saspro/ops/script_editor.py +1102 -0
- setiastro/saspro/ops/scripts.py +1413 -0
- setiastro/saspro/ops/settings.py +560 -0
- setiastro/saspro/parallel_utils.py +554 -0
- setiastro/saspro/pedestal.py +121 -0
- setiastro/saspro/perfect_palette_picker.py +1053 -0
- setiastro/saspro/pipeline.py +110 -0
- setiastro/saspro/pixelmath.py +1600 -0
- setiastro/saspro/plate_solver.py +2435 -0
- setiastro/saspro/project_io.py +797 -0
- setiastro/saspro/psf_utils.py +136 -0
- setiastro/saspro/psf_viewer.py +549 -0
- setiastro/saspro/pyi_rthook_astroquery.py +95 -0
- setiastro/saspro/remove_green.py +314 -0
- setiastro/saspro/remove_stars.py +1625 -0
- setiastro/saspro/remove_stars_preset.py +404 -0
- setiastro/saspro/resources.py +472 -0
- setiastro/saspro/rgb_combination.py +207 -0
- setiastro/saspro/rgb_extract.py +19 -0
- setiastro/saspro/rgbalign.py +723 -0
- setiastro/saspro/runtime_imports.py +7 -0
- setiastro/saspro/runtime_torch.py +754 -0
- setiastro/saspro/save_options.py +72 -0
- setiastro/saspro/selective_color.py +1552 -0
- setiastro/saspro/sfcc.py +1425 -0
- setiastro/saspro/shortcuts.py +2807 -0
- setiastro/saspro/signature_insert.py +1099 -0
- setiastro/saspro/stacking_suite.py +17712 -0
- setiastro/saspro/star_alignment.py +7420 -0
- setiastro/saspro/star_alignment_preset.py +329 -0
- setiastro/saspro/star_metrics.py +49 -0
- setiastro/saspro/star_spikes.py +681 -0
- setiastro/saspro/star_stretch.py +470 -0
- setiastro/saspro/stat_stretch.py +502 -0
- setiastro/saspro/status_log_dock.py +78 -0
- setiastro/saspro/subwindow.py +3267 -0
- setiastro/saspro/supernovaasteroidhunter.py +1712 -0
- setiastro/saspro/swap_manager.py +99 -0
- setiastro/saspro/torch_backend.py +89 -0
- setiastro/saspro/torch_rejection.py +434 -0
- setiastro/saspro/view_bundle.py +1555 -0
- setiastro/saspro/wavescale_hdr.py +624 -0
- setiastro/saspro/wavescale_hdr_preset.py +100 -0
- setiastro/saspro/wavescalede.py +657 -0
- setiastro/saspro/wavescalede_preset.py +228 -0
- setiastro/saspro/wcs_update.py +374 -0
- setiastro/saspro/whitebalance.py +456 -0
- setiastro/saspro/widgets/__init__.py +48 -0
- setiastro/saspro/widgets/common_utilities.py +305 -0
- setiastro/saspro/widgets/graphics_views.py +122 -0
- setiastro/saspro/widgets/image_utils.py +518 -0
- setiastro/saspro/widgets/preview_dialogs.py +280 -0
- setiastro/saspro/widgets/spinboxes.py +275 -0
- setiastro/saspro/widgets/themed_buttons.py +13 -0
- setiastro/saspro/widgets/wavelet_utils.py +299 -0
- setiastro/saspro/window_shelf.py +185 -0
- setiastro/saspro/xisf.py +1123 -0
- setiastrosuitepro-1.6.0.dist-info/METADATA +266 -0
- setiastrosuitepro-1.6.0.dist-info/RECORD +174 -0
- setiastrosuitepro-1.6.0.dist-info/WHEEL +4 -0
- setiastrosuitepro-1.6.0.dist-info/entry_points.txt +6 -0
- setiastrosuitepro-1.6.0.dist-info/licenses/LICENSE +674 -0
- setiastrosuitepro-1.6.0.dist-info/licenses/license.txt +2580 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# imageops/stretch.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
# ---- Try Numba kernels from legacy ----
|
|
6
|
+
try:
|
|
7
|
+
from setiastro.saspro.legacy.numba_utils import (
|
|
8
|
+
numba_mono_final_formula,
|
|
9
|
+
numba_color_final_formula_linked,
|
|
10
|
+
numba_color_final_formula_unlinked,
|
|
11
|
+
)
|
|
12
|
+
_HAS_NUMBA = True
|
|
13
|
+
except Exception:
|
|
14
|
+
_HAS_NUMBA = False
|
|
15
|
+
|
|
16
|
+
# Vectorized fallbacks (no Numba)
|
|
17
|
+
def numba_mono_final_formula(rescaled, median_rescaled, target_median):
|
|
18
|
+
r = rescaled
|
|
19
|
+
med = float(median_rescaled)
|
|
20
|
+
num = (med - 1.0) * target_median * r
|
|
21
|
+
den = med * (target_median + r - 1.0) - target_median * r
|
|
22
|
+
den = np.where(np.abs(den) < 1e-12, 1e-12, den)
|
|
23
|
+
return num / den
|
|
24
|
+
|
|
25
|
+
def numba_color_final_formula_linked(rescaled, median_rescaled, target_median):
|
|
26
|
+
r = rescaled
|
|
27
|
+
med = float(median_rescaled)
|
|
28
|
+
num = (med - 1.0) * target_median * r
|
|
29
|
+
den = med * (target_median + r - 1.0) - target_median * r
|
|
30
|
+
den = np.where(np.abs(den) < 1e-12, 1e-12, den)
|
|
31
|
+
return num / den
|
|
32
|
+
|
|
33
|
+
def numba_color_final_formula_unlinked(rescaled, medians_rescaled, target_median):
|
|
34
|
+
r = rescaled
|
|
35
|
+
med = np.asarray(medians_rescaled, dtype=np.float32).reshape((1, 1, 3))
|
|
36
|
+
num = (med - 1.0) * target_median * r
|
|
37
|
+
den = med * (target_median + r - 1.0) - target_median * r
|
|
38
|
+
den = np.where(np.abs(den) < 1e-12, 1e-12, den)
|
|
39
|
+
return num / den
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ---- Optional curves boost (gentle S-curve) ----
|
|
43
|
+
from functools import lru_cache
|
|
44
|
+
|
|
45
|
+
@lru_cache(maxsize=128)
|
|
46
|
+
def _calculate_curve_points(target_median: float, curves_boost: float):
|
|
47
|
+
"""Calculate curve control points with caching."""
|
|
48
|
+
tm = float(target_median)
|
|
49
|
+
cb = float(curves_boost)
|
|
50
|
+
|
|
51
|
+
# These match your original formula
|
|
52
|
+
p3x = 0.25 * (1.0 - tm) + tm
|
|
53
|
+
p4x = 0.75 * (1.0 - tm) + tm
|
|
54
|
+
p3y = p3x ** (1.0 - cb)
|
|
55
|
+
p4y = (p4x ** (1.0 - cb)) ** (1.0 - cb)
|
|
56
|
+
|
|
57
|
+
# Original 6-point curve
|
|
58
|
+
xvals = np.array([
|
|
59
|
+
0.0,
|
|
60
|
+
0.5 * tm,
|
|
61
|
+
tm,
|
|
62
|
+
p3x,
|
|
63
|
+
p4x,
|
|
64
|
+
1.0
|
|
65
|
+
], dtype=np.float32)
|
|
66
|
+
|
|
67
|
+
yvals = np.array([
|
|
68
|
+
0.0,
|
|
69
|
+
0.5 * tm,
|
|
70
|
+
tm,
|
|
71
|
+
p3y,
|
|
72
|
+
p4y,
|
|
73
|
+
1.0
|
|
74
|
+
], dtype=np.float32)
|
|
75
|
+
|
|
76
|
+
return xvals, yvals
|
|
77
|
+
|
|
78
|
+
def apply_curves_adjustment(image: np.ndarray,
|
|
79
|
+
target_median: float,
|
|
80
|
+
curves_boost: float) -> np.ndarray:
|
|
81
|
+
"""
|
|
82
|
+
curves_boost ∈ [0,1]. 0 = no change, 1 = strong S-curve.
|
|
83
|
+
|
|
84
|
+
This reproduces the original Statistical Stretch curves behavior:
|
|
85
|
+
we build a 1D curve from 6 control points and apply it as a
|
|
86
|
+
piecewise-linear LUT over [0,1].
|
|
87
|
+
"""
|
|
88
|
+
# No curve? Just return as-is (but float32 / clipped)
|
|
89
|
+
if curves_boost <= 0.0:
|
|
90
|
+
return np.clip(image, 0.0, 1.0).astype(np.float32, copy=False)
|
|
91
|
+
|
|
92
|
+
img = np.clip(image.astype(np.float32, copy=False), 0.0, 1.0)
|
|
93
|
+
|
|
94
|
+
# Get cached curve points
|
|
95
|
+
xvals, yvals = _calculate_curve_points(target_median, curves_boost)
|
|
96
|
+
|
|
97
|
+
# Apply the 1D LUT per channel using np.interp (piecewise linear)
|
|
98
|
+
if img.ndim == 2:
|
|
99
|
+
flat = img.ravel()
|
|
100
|
+
out = np.interp(flat, xvals, yvals).reshape(img.shape).astype(np.float32)
|
|
101
|
+
elif img.ndim == 3 and img.shape[2] in (3, 4):
|
|
102
|
+
h, w, c = img.shape
|
|
103
|
+
out = np.empty_like(img, dtype=np.float32)
|
|
104
|
+
# Apply same curve to each color channel
|
|
105
|
+
for ch in range(c):
|
|
106
|
+
flat = img[..., ch].ravel()
|
|
107
|
+
out[..., ch] = np.interp(flat, xvals, yvals).reshape(h, w)
|
|
108
|
+
else:
|
|
109
|
+
# Fallback: just return clamped image
|
|
110
|
+
out = img
|
|
111
|
+
|
|
112
|
+
return np.clip(out, 0.0, 1.0)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# ---- Public API used by Pro ----
|
|
117
|
+
def stretch_mono_image(image: np.ndarray,
|
|
118
|
+
target_median: float,
|
|
119
|
+
normalize: bool = False,
|
|
120
|
+
apply_curves: bool = False,
|
|
121
|
+
curves_boost: float = 0.0) -> np.ndarray:
|
|
122
|
+
"""
|
|
123
|
+
image: float32 preferred, ~[0..1]. Returns float32 in [0..1].
|
|
124
|
+
"""
|
|
125
|
+
img = image.astype(np.float32, copy=False)
|
|
126
|
+
|
|
127
|
+
# Black point from SASv2 logic
|
|
128
|
+
med = float(np.median(img))
|
|
129
|
+
std = float(np.std(img))
|
|
130
|
+
bp = max(float(img.min()), med - 2.7 * std)
|
|
131
|
+
denom = 1.0 - bp
|
|
132
|
+
if abs(denom) < 1e-12:
|
|
133
|
+
denom = 1e-12
|
|
134
|
+
|
|
135
|
+
rescaled = (img - bp) / denom
|
|
136
|
+
med_rescaled = float(np.median(rescaled))
|
|
137
|
+
|
|
138
|
+
out = numba_mono_final_formula(rescaled, med_rescaled, float(target_median))
|
|
139
|
+
|
|
140
|
+
if apply_curves:
|
|
141
|
+
out = apply_curves_adjustment(out, float(target_median), float(curves_boost))
|
|
142
|
+
if normalize:
|
|
143
|
+
mx = float(out.max())
|
|
144
|
+
if mx > 0:
|
|
145
|
+
out = out / mx
|
|
146
|
+
|
|
147
|
+
return np.clip(out, 0.0, 1.0).astype(np.float32, copy=False)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def stretch_color_image(image: np.ndarray,
|
|
151
|
+
target_median: float,
|
|
152
|
+
linked: bool = True,
|
|
153
|
+
normalize: bool = False,
|
|
154
|
+
apply_curves: bool = False,
|
|
155
|
+
curves_boost: float = 0.0) -> np.ndarray:
|
|
156
|
+
"""
|
|
157
|
+
image: float32 preferred, ~[0..1]. Returns float32 in [0..1].
|
|
158
|
+
"""
|
|
159
|
+
img = image.astype(np.float32, copy=False)
|
|
160
|
+
|
|
161
|
+
# Mono/single-channel → reuse mono path and broadcast to 3-ch for display
|
|
162
|
+
if img.ndim == 2 or (img.ndim == 3 and img.shape[2] == 1):
|
|
163
|
+
mono = img.squeeze()
|
|
164
|
+
mono_out = stretch_mono_image(mono, target_median, normalize=normalize,
|
|
165
|
+
apply_curves=apply_curves, curves_boost=curves_boost)
|
|
166
|
+
return np.stack([mono_out] * 3, axis=-1)
|
|
167
|
+
|
|
168
|
+
# Color
|
|
169
|
+
if linked:
|
|
170
|
+
comb_med = float(np.median(img))
|
|
171
|
+
comb_std = float(np.std(img))
|
|
172
|
+
bp = max(float(img.min()), comb_med - 2.7 * comb_std)
|
|
173
|
+
denom = 1.0 - bp
|
|
174
|
+
if abs(denom) < 1e-12:
|
|
175
|
+
denom = 1e-12
|
|
176
|
+
|
|
177
|
+
rescaled = (img - bp) / denom
|
|
178
|
+
med_rescaled = float(np.median(rescaled))
|
|
179
|
+
out = numba_color_final_formula_linked(rescaled, med_rescaled, float(target_median))
|
|
180
|
+
else:
|
|
181
|
+
# Optimized: compute all channel statistics in single vectorized calls
|
|
182
|
+
# instead of per-channel loop (2-3x faster)
|
|
183
|
+
ch_meds = np.median(img, axis=(0, 1)) # Shape: (3,)
|
|
184
|
+
ch_stds = np.std(img, axis=(0, 1)) # Shape: (3,)
|
|
185
|
+
ch_mins = img.min(axis=(0, 1)) # Shape: (3,)
|
|
186
|
+
|
|
187
|
+
# Compute black points for all channels at once
|
|
188
|
+
bp = np.maximum(ch_mins, ch_meds - 2.7 * ch_stds)
|
|
189
|
+
denom = np.maximum(1.0 - bp, 1e-12) # Avoid divide by zero
|
|
190
|
+
|
|
191
|
+
# Rescale all channels at once using broadcasting
|
|
192
|
+
rescaled = (img - bp.reshape(1, 1, 3)) / denom.reshape(1, 1, 3)
|
|
193
|
+
rescaled = rescaled.astype(np.float32, copy=False)
|
|
194
|
+
|
|
195
|
+
# Compute rescaled medians
|
|
196
|
+
meds = np.median(rescaled, axis=(0, 1)).astype(np.float32)
|
|
197
|
+
|
|
198
|
+
out = numba_color_final_formula_unlinked(rescaled, meds, float(target_median))
|
|
199
|
+
|
|
200
|
+
if apply_curves:
|
|
201
|
+
out = apply_curves_adjustment(out, float(target_median), float(curves_boost))
|
|
202
|
+
if normalize:
|
|
203
|
+
mx = float(out.max())
|
|
204
|
+
if mx > 0:
|
|
205
|
+
out = out / mx
|
|
206
|
+
|
|
207
|
+
return np.clip(out, 0.0, 1.0).astype(np.float32, copy=False)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def siril_style_autostretch(image, sigma=3.0):
|
|
211
|
+
"""
|
|
212
|
+
Perform a 'Siril-style histogram stretch' using MAD for robust contrast enhancement.
|
|
213
|
+
|
|
214
|
+
Parameters:
|
|
215
|
+
image (np.ndarray): Input image, assumed to be normalized to [0, 1] range.
|
|
216
|
+
sigma (float): How many MADs to stretch from the median.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
np.ndarray: Stretched image in [0, 1] range.
|
|
220
|
+
"""
|
|
221
|
+
def stretch_channel(channel):
|
|
222
|
+
median = np.median(channel)
|
|
223
|
+
mad = np.median(np.abs(channel - median))
|
|
224
|
+
min_val = np.min(channel)
|
|
225
|
+
max_val = np.max(channel)
|
|
226
|
+
|
|
227
|
+
# Convert MAD to an equivalent of std (optional, keep raw MAD if preferred)
|
|
228
|
+
mad_std_equiv = mad * 1.4826
|
|
229
|
+
|
|
230
|
+
black_point = max(min_val, median - sigma * mad_std_equiv)
|
|
231
|
+
white_point = min(max_val, median + sigma * mad_std_equiv)
|
|
232
|
+
|
|
233
|
+
if white_point - black_point <= 1e-6:
|
|
234
|
+
return np.zeros_like(channel) # Avoid divide-by-zero
|
|
235
|
+
|
|
236
|
+
stretched = (channel - black_point) / (white_point - black_point)
|
|
237
|
+
return np.clip(stretched, 0, 1)
|
|
238
|
+
|
|
239
|
+
if image.ndim == 2:
|
|
240
|
+
return stretch_channel(image)
|
|
241
|
+
elif image.ndim == 3 and image.shape[2] == 3:
|
|
242
|
+
return np.stack([stretch_channel(image[..., c]) for c in range(3)], axis=-1)
|
|
243
|
+
else:
|
|
244
|
+
raise ValueError("Unsupported image format for histogram stretch.")
|