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,196 @@
|
|
|
1
|
+
# pro/autostretch.py
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
_MAX_STATS_PIXELS = 1_000_000
|
|
5
|
+
_DEFAULT_SIGMA = 3
|
|
6
|
+
_U8_MAX = 4095 # 12-bit output for better gradations than 255
|
|
7
|
+
_U16_MAX = 65535
|
|
8
|
+
|
|
9
|
+
# ---------- helpers (generic N-level pipeline) ----------
|
|
10
|
+
def _to_uN(a: np.ndarray, maxv: int) -> np.ndarray:
|
|
11
|
+
"""Convert to uint8/uint16 [0..maxv] for cheap hist/LUT work."""
|
|
12
|
+
tgt_dtype = np.uint16 if maxv > _U8_MAX else np.uint8
|
|
13
|
+
if a.dtype == tgt_dtype:
|
|
14
|
+
return a
|
|
15
|
+
if np.issubdtype(a.dtype, np.integer):
|
|
16
|
+
info = np.iinfo(a.dtype)
|
|
17
|
+
if info.max <= 0:
|
|
18
|
+
return np.zeros_like(a, dtype=tgt_dtype)
|
|
19
|
+
scaled = np.clip(a.astype(np.float32), 0, info.max) * (maxv / float(info.max))
|
|
20
|
+
return (scaled + 0.5).astype(tgt_dtype)
|
|
21
|
+
# float-ish
|
|
22
|
+
af = np.clip(a.astype(np.float32), 0.0, 1.0)
|
|
23
|
+
return (af * maxv + 0.5).astype(tgt_dtype)
|
|
24
|
+
|
|
25
|
+
def _choose_stride(h: int, w: int, max_pixels: int) -> tuple[int, int]:
|
|
26
|
+
n = h * w
|
|
27
|
+
if n <= max_pixels:
|
|
28
|
+
return 1, 1
|
|
29
|
+
s = max(1, int(np.sqrt(n / float(max_pixels))))
|
|
30
|
+
return s, s
|
|
31
|
+
|
|
32
|
+
def _compute_lut_from_sample(sample_uN: np.ndarray, target: float, sigma: float, maxv: int,
|
|
33
|
+
qfloor: float = 0.001) -> np.ndarray:
|
|
34
|
+
"""Return a 0..maxv -> [0..1] LUT using the same math as _fast_channel_autostretch_uN."""
|
|
35
|
+
hist = np.bincount(sample_uN.ravel(), minlength=maxv + 1)
|
|
36
|
+
total = int(hist.sum()) or 1
|
|
37
|
+
|
|
38
|
+
cdf = np.cumsum(hist)
|
|
39
|
+
med = int(np.searchsorted(cdf, (total + 1) // 2))
|
|
40
|
+
|
|
41
|
+
bins = np.arange(maxv + 1, dtype=np.float64)
|
|
42
|
+
hist_low = hist[:med + 1]
|
|
43
|
+
total_low = int(hist_low.sum()) or 1
|
|
44
|
+
mean_low = float((hist_low * bins[:med + 1]).sum() / total_low)
|
|
45
|
+
var_low = float((hist_low * (bins[:med + 1] - mean_low)**2).sum() / total_low)
|
|
46
|
+
std_low = float(np.sqrt(max(1e-12, var_low)))
|
|
47
|
+
|
|
48
|
+
floor_idx = int(np.searchsorted(cdf, int(qfloor * total)))
|
|
49
|
+
bp = int(max(floor_idx, med - sigma * std_low))
|
|
50
|
+
bp = int(np.clip(bp, 0, maxv - 1))
|
|
51
|
+
|
|
52
|
+
return _build_lut_generic(bp, target, med, maxv)
|
|
53
|
+
|
|
54
|
+
def _stats_from_hist_generic(uN: np.ndarray, maxv: int) -> tuple[float, float, int, int]:
|
|
55
|
+
"""(median, std, minv, maxv) using a histogram of levels [0..maxv]."""
|
|
56
|
+
hist = np.bincount(uN.ravel(), minlength=maxv + 1)
|
|
57
|
+
total = int(hist.sum())
|
|
58
|
+
if total == 0:
|
|
59
|
+
return 0.0, 1.0, 0, 0
|
|
60
|
+
|
|
61
|
+
# min / max (first/last nonzero bin)
|
|
62
|
+
minv = int(np.argmax(hist > 0))
|
|
63
|
+
maxv_found = int(maxv - np.argmax(hist[::-1] > 0))
|
|
64
|
+
|
|
65
|
+
cdf = np.cumsum(hist)
|
|
66
|
+
med_idx = int(np.searchsorted(cdf, (total + 1) // 2))
|
|
67
|
+
|
|
68
|
+
bins = np.arange(hist.size, dtype=np.float64)
|
|
69
|
+
s1 = float((hist * bins).sum())
|
|
70
|
+
s2 = float((hist * (bins * bins)).sum())
|
|
71
|
+
mean = s1 / total
|
|
72
|
+
var = max(0.0, s2 / total - mean * mean)
|
|
73
|
+
std = float(np.sqrt(var))
|
|
74
|
+
return float(med_idx), std, minv, maxv_found
|
|
75
|
+
|
|
76
|
+
def _build_lut_generic(bp: int, target_median: float, med_uN: float, maxv: int) -> np.ndarray:
|
|
77
|
+
denom_bp = max(1, maxv - int(bp))
|
|
78
|
+
median_rescaled = (med_uN - bp) / float(denom_bp)
|
|
79
|
+
median_rescaled = float(np.clip(median_rescaled, 1e-9, 1.0))
|
|
80
|
+
|
|
81
|
+
x = np.arange(maxv + 1, dtype=np.float32)
|
|
82
|
+
|
|
83
|
+
# ✅ KEY FIX: clamp r to [0,1] so x<=bp -> r=0 (maps to 0), x>=max -> r=1
|
|
84
|
+
r = np.clip((x - bp) / float(denom_bp), 0.0, 1.0)
|
|
85
|
+
|
|
86
|
+
denom = median_rescaled * (target_median + r - 1.0) - target_median * r
|
|
87
|
+
denom = np.where(np.abs(denom) < 1e-12, 1e-12, denom)
|
|
88
|
+
out = ((median_rescaled - 1.0) * target_median * r) / denom
|
|
89
|
+
return np.clip(out, 0.0, 1.0).astype(np.float32)
|
|
90
|
+
|
|
91
|
+
def _fast_channel_autostretch_uN(ch_uN: np.ndarray, target: float, sigma: float, maxv: int,
|
|
92
|
+
qfloor: float = 0.001) -> np.ndarray:
|
|
93
|
+
"""
|
|
94
|
+
qfloor: low-percentile floor (e.g. 0.1%) so BP doesn't peg to minv=0.
|
|
95
|
+
"""
|
|
96
|
+
# Subsample
|
|
97
|
+
h, w = ch_uN.shape
|
|
98
|
+
sy, sx = _choose_stride(h, w, _MAX_STATS_PIXELS)
|
|
99
|
+
sample = ch_uN[::sy, ::sx]
|
|
100
|
+
|
|
101
|
+
# Histogram on the sample
|
|
102
|
+
hist = np.bincount(sample.ravel(), minlength=maxv + 1)
|
|
103
|
+
total = int(hist.sum()) or 1
|
|
104
|
+
|
|
105
|
+
# CDF + median index
|
|
106
|
+
cdf = np.cumsum(hist)
|
|
107
|
+
med = int(np.searchsorted(cdf, (total + 1)//2))
|
|
108
|
+
|
|
109
|
+
# Robust std: only use bins <= median (avoids bright-tail inflation)
|
|
110
|
+
bins = np.arange(maxv + 1, dtype=np.float64)
|
|
111
|
+
hist_low = hist[:med + 1]
|
|
112
|
+
total_low = int(hist_low.sum()) or 1
|
|
113
|
+
mean_low = float((hist_low * bins[:med + 1]).sum() / total_low)
|
|
114
|
+
var_low = float((hist_low * (bins[:med + 1] - mean_low)**2).sum() / total_low)
|
|
115
|
+
std_low = float(np.sqrt(max(1e-12, var_low)))
|
|
116
|
+
|
|
117
|
+
# Percentile floor (avoid pegging to 0)
|
|
118
|
+
floor_idx = int(np.searchsorted(cdf, int(qfloor * total)))
|
|
119
|
+
|
|
120
|
+
# Black point driven by sigma, but never below the floor
|
|
121
|
+
bp = int(max(floor_idx, med - sigma * std_low))
|
|
122
|
+
bp = int(np.clip(bp, 0, maxv - 1))
|
|
123
|
+
|
|
124
|
+
# Build LUT and map
|
|
125
|
+
lut = _build_lut_generic(bp, target, med, maxv)
|
|
126
|
+
return lut[ch_uN]
|
|
127
|
+
|
|
128
|
+
# ---------- public API ----------
|
|
129
|
+
def autostretch(
|
|
130
|
+
img: np.ndarray,
|
|
131
|
+
target_median: float = 0.25,
|
|
132
|
+
linked: bool = False,
|
|
133
|
+
sigma: float = _DEFAULT_SIGMA,
|
|
134
|
+
*,
|
|
135
|
+
use_16bit: bool | None = None,
|
|
136
|
+
) -> np.ndarray:
|
|
137
|
+
"""
|
|
138
|
+
High-quality autostretch that can operate in 16-bit (HQ, default) or 8-bit (fast) mode.
|
|
139
|
+
|
|
140
|
+
• 16-bit mode: smooth gradients, minimal posterization (recommended).
|
|
141
|
+
• 8-bit mode: slightly faster on very large images, lower fidelity.
|
|
142
|
+
|
|
143
|
+
If use_16bit is None, we try to read QSettings("display/autostretch_16bit") and
|
|
144
|
+
default to True on failure (no Qt in context).
|
|
145
|
+
"""
|
|
146
|
+
if img is None:
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
# Optional auto-read from QSettings if caller didn’t pass a flag.
|
|
150
|
+
if use_16bit is None:
|
|
151
|
+
try:
|
|
152
|
+
from PyQt6.QtCore import QSettings
|
|
153
|
+
use_16bit = QSettings().value("display/autostretch_16bit", True, type=bool)
|
|
154
|
+
except Exception:
|
|
155
|
+
use_16bit = True
|
|
156
|
+
|
|
157
|
+
maxv = _U16_MAX if use_16bit else _U8_MAX
|
|
158
|
+
a = np.asarray(img)
|
|
159
|
+
|
|
160
|
+
# MONO (or pseudo-mono)
|
|
161
|
+
if a.ndim == 2 or (a.ndim == 3 and a.shape[2] == 1):
|
|
162
|
+
u = _to_uN(a.squeeze(), maxv)
|
|
163
|
+
out = _fast_channel_autostretch_uN(u, target_median, sigma, maxv)
|
|
164
|
+
return out.astype(np.float32, copy=False)
|
|
165
|
+
|
|
166
|
+
# color
|
|
167
|
+
u = _to_uN(a, maxv)
|
|
168
|
+
C = u.shape[2]
|
|
169
|
+
|
|
170
|
+
if linked:
|
|
171
|
+
# sample fewer pixels for stats
|
|
172
|
+
h, w, _ = u.shape
|
|
173
|
+
sy, sx = _choose_stride(h, w, max(1, _MAX_STATS_PIXELS // 3))
|
|
174
|
+
sample = u[::sy, ::sx]
|
|
175
|
+
|
|
176
|
+
# Rec.709-ish luminance, integer dtype preserved
|
|
177
|
+
# (weights sum to 1; cast back to u.dtype for the LUT builder)
|
|
178
|
+
lum = (0.2126 * sample[..., 0] + 0.7152 * sample[..., 1] + 0.0722 * sample[..., 2]).astype(u.dtype)
|
|
179
|
+
|
|
180
|
+
lut = _compute_lut_from_sample(lum, target_median, sigma, maxv)
|
|
181
|
+
|
|
182
|
+
out = np.empty_like(u, dtype=np.float32)
|
|
183
|
+
for c in range(min(3, C)):
|
|
184
|
+
out[..., c] = lut[u[..., c]]
|
|
185
|
+
if C > 3: # pass-through non-RGB channels
|
|
186
|
+
out[..., 3:] = u[..., 3:] / float(maxv)
|
|
187
|
+
return out
|
|
188
|
+
else:
|
|
189
|
+
out = np.empty_like(u, dtype=np.float32)
|
|
190
|
+
for c in range(min(3, C)):
|
|
191
|
+
out[..., c] = _fast_channel_autostretch_uN(u[..., c], target_median, sigma, maxv)
|
|
192
|
+
if C > 3:
|
|
193
|
+
out[..., 3:] = u[..., 3:] / float(maxv)
|
|
194
|
+
return out
|
|
195
|
+
|
|
196
|
+
print(f"med={med} std_low={std_low:.2f} floor={floor_idx} σ={sigma} → bp={bp}")
|