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.
Files changed (174) hide show
  1. setiastro/__init__.py +2 -0
  2. setiastro/saspro/__init__.py +20 -0
  3. setiastro/saspro/__main__.py +784 -0
  4. setiastro/saspro/_generated/__init__.py +7 -0
  5. setiastro/saspro/_generated/build_info.py +2 -0
  6. setiastro/saspro/abe.py +1295 -0
  7. setiastro/saspro/abe_preset.py +196 -0
  8. setiastro/saspro/aberration_ai.py +694 -0
  9. setiastro/saspro/aberration_ai_preset.py +224 -0
  10. setiastro/saspro/accel_installer.py +218 -0
  11. setiastro/saspro/accel_workers.py +30 -0
  12. setiastro/saspro/add_stars.py +621 -0
  13. setiastro/saspro/astrobin_exporter.py +1007 -0
  14. setiastro/saspro/astrospike.py +153 -0
  15. setiastro/saspro/astrospike_python.py +1839 -0
  16. setiastro/saspro/autostretch.py +196 -0
  17. setiastro/saspro/backgroundneutral.py +560 -0
  18. setiastro/saspro/batch_convert.py +325 -0
  19. setiastro/saspro/batch_renamer.py +519 -0
  20. setiastro/saspro/blemish_blaster.py +488 -0
  21. setiastro/saspro/blink_comparator_pro.py +2923 -0
  22. setiastro/saspro/bundles.py +61 -0
  23. setiastro/saspro/bundles_dock.py +114 -0
  24. setiastro/saspro/cheat_sheet.py +168 -0
  25. setiastro/saspro/clahe.py +342 -0
  26. setiastro/saspro/comet_stacking.py +1377 -0
  27. setiastro/saspro/config.py +38 -0
  28. setiastro/saspro/config_bootstrap.py +40 -0
  29. setiastro/saspro/config_manager.py +316 -0
  30. setiastro/saspro/continuum_subtract.py +1617 -0
  31. setiastro/saspro/convo.py +1397 -0
  32. setiastro/saspro/convo_preset.py +414 -0
  33. setiastro/saspro/copyastro.py +187 -0
  34. setiastro/saspro/cosmicclarity.py +1564 -0
  35. setiastro/saspro/cosmicclarity_preset.py +407 -0
  36. setiastro/saspro/crop_dialog_pro.py +948 -0
  37. setiastro/saspro/crop_preset.py +189 -0
  38. setiastro/saspro/curve_editor_pro.py +2544 -0
  39. setiastro/saspro/curves_preset.py +375 -0
  40. setiastro/saspro/debayer.py +670 -0
  41. setiastro/saspro/debug_utils.py +29 -0
  42. setiastro/saspro/dnd_mime.py +35 -0
  43. setiastro/saspro/doc_manager.py +2634 -0
  44. setiastro/saspro/exoplanet_detector.py +2166 -0
  45. setiastro/saspro/file_utils.py +284 -0
  46. setiastro/saspro/fitsmodifier.py +744 -0
  47. setiastro/saspro/free_torch_memory.py +48 -0
  48. setiastro/saspro/frequency_separation.py +1343 -0
  49. setiastro/saspro/function_bundle.py +1594 -0
  50. setiastro/saspro/ghs_dialog_pro.py +660 -0
  51. setiastro/saspro/ghs_preset.py +284 -0
  52. setiastro/saspro/graxpert.py +634 -0
  53. setiastro/saspro/graxpert_preset.py +287 -0
  54. setiastro/saspro/gui/__init__.py +0 -0
  55. setiastro/saspro/gui/main_window.py +8494 -0
  56. setiastro/saspro/gui/mixins/__init__.py +33 -0
  57. setiastro/saspro/gui/mixins/dock_mixin.py +263 -0
  58. setiastro/saspro/gui/mixins/file_mixin.py +445 -0
  59. setiastro/saspro/gui/mixins/geometry_mixin.py +403 -0
  60. setiastro/saspro/gui/mixins/header_mixin.py +441 -0
  61. setiastro/saspro/gui/mixins/mask_mixin.py +421 -0
  62. setiastro/saspro/gui/mixins/menu_mixin.py +361 -0
  63. setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
  64. setiastro/saspro/gui/mixins/toolbar_mixin.py +1324 -0
  65. setiastro/saspro/gui/mixins/update_mixin.py +309 -0
  66. setiastro/saspro/gui/mixins/view_mixin.py +435 -0
  67. setiastro/saspro/halobgon.py +462 -0
  68. setiastro/saspro/header_viewer.py +445 -0
  69. setiastro/saspro/headless_utils.py +88 -0
  70. setiastro/saspro/histogram.py +753 -0
  71. setiastro/saspro/history_explorer.py +939 -0
  72. setiastro/saspro/image_combine.py +414 -0
  73. setiastro/saspro/image_peeker_pro.py +1596 -0
  74. setiastro/saspro/imageops/__init__.py +37 -0
  75. setiastro/saspro/imageops/mdi_snap.py +292 -0
  76. setiastro/saspro/imageops/scnr.py +36 -0
  77. setiastro/saspro/imageops/starbasedwhitebalance.py +210 -0
  78. setiastro/saspro/imageops/stretch.py +244 -0
  79. setiastro/saspro/isophote.py +1179 -0
  80. setiastro/saspro/layers.py +208 -0
  81. setiastro/saspro/layers_dock.py +714 -0
  82. setiastro/saspro/lazy_imports.py +193 -0
  83. setiastro/saspro/legacy/__init__.py +2 -0
  84. setiastro/saspro/legacy/image_manager.py +2226 -0
  85. setiastro/saspro/legacy/numba_utils.py +3659 -0
  86. setiastro/saspro/legacy/xisf.py +1071 -0
  87. setiastro/saspro/linear_fit.py +534 -0
  88. setiastro/saspro/live_stacking.py +1830 -0
  89. setiastro/saspro/log_bus.py +5 -0
  90. setiastro/saspro/logging_config.py +460 -0
  91. setiastro/saspro/luminancerecombine.py +309 -0
  92. setiastro/saspro/main_helpers.py +201 -0
  93. setiastro/saspro/mask_creation.py +928 -0
  94. setiastro/saspro/masks_core.py +56 -0
  95. setiastro/saspro/mdi_widgets.py +353 -0
  96. setiastro/saspro/memory_utils.py +666 -0
  97. setiastro/saspro/metadata_patcher.py +75 -0
  98. setiastro/saspro/mfdeconv.py +3826 -0
  99. setiastro/saspro/mfdeconv_earlystop.py +71 -0
  100. setiastro/saspro/mfdeconvcudnn.py +3263 -0
  101. setiastro/saspro/mfdeconvsport.py +2382 -0
  102. setiastro/saspro/minorbodycatalog.py +567 -0
  103. setiastro/saspro/morphology.py +382 -0
  104. setiastro/saspro/multiscale_decomp.py +1290 -0
  105. setiastro/saspro/nbtorgb_stars.py +531 -0
  106. setiastro/saspro/numba_utils.py +3044 -0
  107. setiastro/saspro/numba_warmup.py +141 -0
  108. setiastro/saspro/ops/__init__.py +9 -0
  109. setiastro/saspro/ops/command_help_dialog.py +623 -0
  110. setiastro/saspro/ops/command_runner.py +217 -0
  111. setiastro/saspro/ops/commands.py +1594 -0
  112. setiastro/saspro/ops/script_editor.py +1102 -0
  113. setiastro/saspro/ops/scripts.py +1413 -0
  114. setiastro/saspro/ops/settings.py +560 -0
  115. setiastro/saspro/parallel_utils.py +554 -0
  116. setiastro/saspro/pedestal.py +121 -0
  117. setiastro/saspro/perfect_palette_picker.py +1053 -0
  118. setiastro/saspro/pipeline.py +110 -0
  119. setiastro/saspro/pixelmath.py +1600 -0
  120. setiastro/saspro/plate_solver.py +2435 -0
  121. setiastro/saspro/project_io.py +797 -0
  122. setiastro/saspro/psf_utils.py +136 -0
  123. setiastro/saspro/psf_viewer.py +549 -0
  124. setiastro/saspro/pyi_rthook_astroquery.py +95 -0
  125. setiastro/saspro/remove_green.py +314 -0
  126. setiastro/saspro/remove_stars.py +1625 -0
  127. setiastro/saspro/remove_stars_preset.py +404 -0
  128. setiastro/saspro/resources.py +472 -0
  129. setiastro/saspro/rgb_combination.py +207 -0
  130. setiastro/saspro/rgb_extract.py +19 -0
  131. setiastro/saspro/rgbalign.py +723 -0
  132. setiastro/saspro/runtime_imports.py +7 -0
  133. setiastro/saspro/runtime_torch.py +754 -0
  134. setiastro/saspro/save_options.py +72 -0
  135. setiastro/saspro/selective_color.py +1552 -0
  136. setiastro/saspro/sfcc.py +1425 -0
  137. setiastro/saspro/shortcuts.py +2807 -0
  138. setiastro/saspro/signature_insert.py +1099 -0
  139. setiastro/saspro/stacking_suite.py +17712 -0
  140. setiastro/saspro/star_alignment.py +7420 -0
  141. setiastro/saspro/star_alignment_preset.py +329 -0
  142. setiastro/saspro/star_metrics.py +49 -0
  143. setiastro/saspro/star_spikes.py +681 -0
  144. setiastro/saspro/star_stretch.py +470 -0
  145. setiastro/saspro/stat_stretch.py +502 -0
  146. setiastro/saspro/status_log_dock.py +78 -0
  147. setiastro/saspro/subwindow.py +3267 -0
  148. setiastro/saspro/supernovaasteroidhunter.py +1712 -0
  149. setiastro/saspro/swap_manager.py +99 -0
  150. setiastro/saspro/torch_backend.py +89 -0
  151. setiastro/saspro/torch_rejection.py +434 -0
  152. setiastro/saspro/view_bundle.py +1555 -0
  153. setiastro/saspro/wavescale_hdr.py +624 -0
  154. setiastro/saspro/wavescale_hdr_preset.py +100 -0
  155. setiastro/saspro/wavescalede.py +657 -0
  156. setiastro/saspro/wavescalede_preset.py +228 -0
  157. setiastro/saspro/wcs_update.py +374 -0
  158. setiastro/saspro/whitebalance.py +456 -0
  159. setiastro/saspro/widgets/__init__.py +48 -0
  160. setiastro/saspro/widgets/common_utilities.py +305 -0
  161. setiastro/saspro/widgets/graphics_views.py +122 -0
  162. setiastro/saspro/widgets/image_utils.py +518 -0
  163. setiastro/saspro/widgets/preview_dialogs.py +280 -0
  164. setiastro/saspro/widgets/spinboxes.py +275 -0
  165. setiastro/saspro/widgets/themed_buttons.py +13 -0
  166. setiastro/saspro/widgets/wavelet_utils.py +299 -0
  167. setiastro/saspro/window_shelf.py +185 -0
  168. setiastro/saspro/xisf.py +1123 -0
  169. setiastrosuitepro-1.6.0.dist-info/METADATA +266 -0
  170. setiastrosuitepro-1.6.0.dist-info/RECORD +174 -0
  171. setiastrosuitepro-1.6.0.dist-info/WHEEL +4 -0
  172. setiastrosuitepro-1.6.0.dist-info/entry_points.txt +6 -0
  173. setiastrosuitepro-1.6.0.dist-info/licenses/LICENSE +674 -0
  174. setiastrosuitepro-1.6.0.dist-info/licenses/license.txt +2580 -0
@@ -0,0 +1,208 @@
1
+ # pro/layers.py
2
+ from __future__ import annotations
3
+ from dataclasses import dataclass
4
+ from typing import Optional, List
5
+ import numpy as np
6
+
7
+ BLEND_MODES = [
8
+ "Normal",
9
+ "Multiply",
10
+ "Screen",
11
+ "Overlay",
12
+ "Soft Light",
13
+ "Hard Light",
14
+ "Color Dodge",
15
+ "Color Burn",
16
+ "Pin Light",
17
+ "Add",
18
+ "Lighten",
19
+ "Darken",
20
+ "Sigmoid",
21
+ ]
22
+
23
+ @dataclass
24
+ class ImageLayer:
25
+ name: str
26
+ src_doc: object # ImageDocument
27
+ visible: bool = True
28
+ opacity: float = 1.0 # 0..1
29
+ mode: str = "Normal" # one of BLEND_MODES
30
+ mask_doc: Optional[object] = None # ImageDocument whose active mask we read
31
+ mask_invert: bool = False
32
+ mask_feather: float = 0.0 # (reserved)
33
+ mask_use_luma: bool = False
34
+
35
+ # Sigmoid blend parameters
36
+ sigmoid_center: float = 0.5 # where transition happens (0..1)
37
+ sigmoid_strength: float = 10.0 # steepness of the curve
38
+
39
+ def _float01(arr: np.ndarray) -> np.ndarray:
40
+ a = np.asarray(arr)
41
+ if a.dtype.kind in "ui":
42
+ info = np.iinfo(a.dtype)
43
+ if info.max == 0:
44
+ return a.astype(np.float32)
45
+ return (a.astype(np.float32) / float(info.max))
46
+ return np.clip(a.astype(np.float32), 0.0, 1.0)
47
+
48
+ def _ensure_3c(a: np.ndarray) -> np.ndarray:
49
+ if a.ndim == 2:
50
+ return np.stack([a, a, a], axis=-1)
51
+ if a.ndim == 3 and a.shape[2] == 1:
52
+ return np.repeat(a, 3, axis=2)
53
+ return a
54
+
55
+ def _resize_like(src: np.ndarray, tgt_shape_hw: tuple[int, int]) -> np.ndarray:
56
+ """Nearest resize without dependencies. src: (H,W[,C]), target: (H,W)."""
57
+ Ht, Wt = int(tgt_shape_hw[0]), int(tgt_shape_hw[1])
58
+ Hs, Ws = src.shape[0], src.shape[1]
59
+ if (Hs, Ws) == (Ht, Wt):
60
+ return src
61
+ yi = (np.linspace(0, Hs - 1, Ht)).astype(np.int32)
62
+ xi = (np.linspace(0, Ws - 1, Wt)).astype(np.int32)
63
+ return src[yi][:, xi, ...] if src.ndim == 3 else src[yi][:, xi]
64
+
65
+ def _luminance01(img: np.ndarray) -> np.ndarray:
66
+ a = _float01(img)
67
+ a = _ensure_3c(a)
68
+ # Rec. 709 luma
69
+ y = 0.2126 * a[..., 0] + 0.7152 * a[..., 1] + 0.0722 * a[..., 2]
70
+ return np.clip(y.astype(np.float32, copy=False), 0.0, 1.0)
71
+
72
+ def _mask_from_doc(doc, *, use_luma: bool = False) -> Optional[np.ndarray]:
73
+ if doc is None:
74
+ return None
75
+ if use_luma:
76
+ img = getattr(doc, "image", None)
77
+ if img is None:
78
+ return None
79
+ return _luminance01(img)
80
+
81
+ # existing active-mask path
82
+ masks = getattr(doc, "masks", {}) or {}
83
+ mid = getattr(doc, "active_mask_id", None)
84
+ layer = masks.get(mid) if mid else None
85
+ data = getattr(layer, "data", None) if layer is not None else None
86
+ if data is None:
87
+ return None
88
+ m = np.asarray(data)
89
+ if m.ndim == 3 and m.shape[2] == 1:
90
+ m = m[..., 0]
91
+ if m.ndim != 2:
92
+ return None
93
+ return np.clip(m.astype(np.float32, copy=False), 0.0, 1.0)
94
+
95
+ # ---- blend ops (src over base) ---------------------------------------
96
+ def _apply_mode(base: np.ndarray, src: np.ndarray, layer: ImageLayer) -> np.ndarray:
97
+ """
98
+ base, src: float32, [0..1], same shape.
99
+ Uses layer.mode and optional extra params (e.g. sigmoid_center/strength).
100
+ """
101
+ mode = getattr(layer, "mode", "Normal") or "Normal"
102
+
103
+ if mode == "Multiply":
104
+ return base * src
105
+
106
+ if mode == "Screen":
107
+ return 1.0 - (1.0 - base) * (1.0 - src)
108
+
109
+ if mode == "Overlay":
110
+ return np.where(
111
+ base <= 0.5,
112
+ 2.0 * base * src,
113
+ 1.0 - 2.0 * (1.0 - base) * (1.0 - src),
114
+ )
115
+
116
+ if mode == "Soft Light":
117
+ # SVG / W3C-style soft light
118
+ return (1.0 - 2.0 * src) * (base * base) + 2.0 * src * base
119
+
120
+ if mode == "Hard Light":
121
+ # Overlay, but conditioned on src
122
+ return np.where(
123
+ src <= 0.5,
124
+ 2.0 * base * src,
125
+ 1.0 - 2.0 * (1.0 - base) * (1.0 - src),
126
+ )
127
+
128
+ if mode == "Color Dodge":
129
+ eps = 1e-6
130
+ denom = np.maximum(1.0 - src, eps)
131
+ out = base / denom
132
+ return np.clip(out, 0.0, 1.0)
133
+
134
+ if mode == "Color Burn":
135
+ eps = 1e-6
136
+ denom = np.maximum(src, eps)
137
+ out = 1.0 - (1.0 - base) / denom
138
+ return np.clip(out, 0.0, 1.0)
139
+
140
+ if mode == "Pin Light":
141
+ hi = np.maximum(base, 2.0 * src - 1.0)
142
+ lo = np.minimum(base, 2.0 * src)
143
+ return np.where(src > 0.5, hi, lo)
144
+
145
+ if mode == "Add":
146
+ return np.clip(base + src, 0.0, 1.0)
147
+
148
+ if mode == "Lighten":
149
+ return np.maximum(base, src)
150
+
151
+ if mode == "Darken":
152
+ return np.minimum(base, src)
153
+
154
+ if mode == "Sigmoid":
155
+ # Per-layer sigmoid blend:
156
+ # dark base → stay closer to base
157
+ # bright base → move towards src
158
+ luma = _luminance01(base) # (H, W)
159
+
160
+ center = float(getattr(layer, "sigmoid_center", 0.5) or 0.5)
161
+ strength = float(getattr(layer, "sigmoid_strength", 10.0) or 10.0)
162
+
163
+ # weight in [0..1]
164
+ w = 1.0 / (1.0 + np.exp(-strength * (luma - center)))
165
+ w = w[..., None] # broadcast over channels
166
+
167
+ return base * (1.0 - w) + src * w
168
+
169
+ # Normal
170
+ return src
171
+
172
+
173
+ def composite_stack(base_img: np.ndarray, layers: List[ImageLayer]) -> np.ndarray:
174
+ if base_img is None:
175
+ return None
176
+ out = _float01(base_img)
177
+ out = _ensure_3c(out)
178
+
179
+ H, W = out.shape[0], out.shape[1]
180
+
181
+ # iterate bottom → top so the top-most layer renders last
182
+ for L in reversed(layers or []):
183
+ if not L.visible:
184
+ continue
185
+ src = getattr(L.src_doc, "image", None)
186
+ if src is None:
187
+ continue
188
+ s = _ensure_3c(_float01(src))
189
+ s = _resize_like(s, (H, W))
190
+
191
+ if getattr(L, "mode", None) not in BLEND_MODES:
192
+ L.mode = "Normal"
193
+
194
+ blended = _apply_mode(out, s, L)
195
+
196
+ alpha = float(L.opacity if 0.0 <= L.opacity <= 1.0 else 1.0)
197
+ if L.mask_doc is not None:
198
+ m = _mask_from_doc(L.mask_doc, use_luma=bool(L.mask_use_luma))
199
+ if m is not None:
200
+ m = _resize_like(m, (H, W))
201
+ if L.mask_invert:
202
+ m = 1.0 - m
203
+ alpha_map = np.clip(alpha * m, 0.0, 1.0)[..., None]
204
+ out = out * (1.0 - alpha_map) + blended * alpha_map
205
+ continue
206
+ out = out * (1.0 - alpha) + blended * alpha
207
+
208
+ return out