setiastrosuitepro 1.7.4__py3-none-any.whl → 1.7.5__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/clonestamp.png +0 -0
- setiastro/saspro/_generated/build_info.py +2 -2
- setiastro/saspro/blemish_blaster.py +54 -14
- setiastro/saspro/clone_stamp.py +753 -0
- setiastro/saspro/gui/main_window.py +22 -1
- setiastro/saspro/gui/mixins/menu_mixin.py +1 -0
- setiastro/saspro/gui/mixins/toolbar_mixin.py +8 -13
- setiastro/saspro/resources.py +2 -0
- setiastro/saspro/stacking_suite.py +123 -65
- {setiastrosuitepro-1.7.4.dist-info → setiastrosuitepro-1.7.5.dist-info}/METADATA +1 -1
- {setiastrosuitepro-1.7.4.dist-info → setiastrosuitepro-1.7.5.dist-info}/RECORD +15 -13
- {setiastrosuitepro-1.7.4.dist-info → setiastrosuitepro-1.7.5.dist-info}/WHEEL +0 -0
- {setiastrosuitepro-1.7.4.dist-info → setiastrosuitepro-1.7.5.dist-info}/entry_points.txt +0 -0
- {setiastrosuitepro-1.7.4.dist-info → setiastrosuitepro-1.7.5.dist-info}/licenses/LICENSE +0 -0
- {setiastrosuitepro-1.7.4.dist-info → setiastrosuitepro-1.7.5.dist-info}/licenses/license.txt +0 -0
|
@@ -196,7 +196,7 @@ from setiastro.saspro.resources import (
|
|
|
196
196
|
colorwheel_path, font_path, csv_icon_path, spinner_path, wims_path, narrowbandnormalization_path,
|
|
197
197
|
wimi_path, linearfit_path, debayer_path, aberration_path, acv_icon_path,
|
|
198
198
|
functionbundles_path, viewbundles_path, selectivecolor_path, rgbalign_path, planetarystacker_path,
|
|
199
|
-
background_path, script_icon_path, planetprojection_path,
|
|
199
|
+
background_path, script_icon_path, planetprojection_path,clonestampicon_path,
|
|
200
200
|
)
|
|
201
201
|
|
|
202
202
|
import faulthandler
|
|
@@ -3560,6 +3560,27 @@ class AstroSuiteProMainWindow(
|
|
|
3560
3560
|
dlg.resize(900, 650)
|
|
3561
3561
|
dlg.show()
|
|
3562
3562
|
|
|
3563
|
+
def _open_clone_stamp(self):
|
|
3564
|
+
from setiastro.saspro.clone_stamp import CloneStampDialogPro
|
|
3565
|
+
sw = self.mdi.activeSubWindow()
|
|
3566
|
+
if not sw:
|
|
3567
|
+
QMessageBox.information(self, "Clone Stamp", "No active image window.")
|
|
3568
|
+
return
|
|
3569
|
+
view = sw.widget()
|
|
3570
|
+
doc = getattr(view, "document", None)
|
|
3571
|
+
if doc is None or getattr(doc, "image", None) is None:
|
|
3572
|
+
QMessageBox.information(self, "Clone Stamp", "Active document has no image.")
|
|
3573
|
+
return
|
|
3574
|
+
|
|
3575
|
+
dlg = CloneStampDialogPro(self, doc)
|
|
3576
|
+
try:
|
|
3577
|
+
dlg.setWindowIcon(QIcon(clonestampicon_path))
|
|
3578
|
+
except Exception:
|
|
3579
|
+
pass
|
|
3580
|
+
dlg.resize(900, 650)
|
|
3581
|
+
dlg.show()
|
|
3582
|
+
|
|
3583
|
+
|
|
3563
3584
|
def _open_wavescale_hdr(self):
|
|
3564
3585
|
from setiastro.saspro.wavescale_hdr import WaveScaleHDRDialogPro
|
|
3565
3586
|
sw = self.mdi.activeSubWindow()
|
|
@@ -134,6 +134,7 @@ class MenuMixin:
|
|
|
134
134
|
m_fn.addAction(self.act_background_neutral)
|
|
135
135
|
m_fn.addAction(self.act_blemish)
|
|
136
136
|
m_fn.addAction(self.act_clahe)
|
|
137
|
+
m_fn.addAction(self.act_clone_stamp)
|
|
137
138
|
m_fn.addAction(self.act_convo)
|
|
138
139
|
m_fn.addAction(self.act_crop)
|
|
139
140
|
m_fn.addAction(self.act_curves)
|
|
@@ -36,7 +36,7 @@ from setiastro.saspro.resources import (
|
|
|
36
36
|
nbtorgb_path, freqsep_path, multiscale_decomp_path, contsub_path, halo_path, cosmic_path,
|
|
37
37
|
satellite_path, imagecombine_path, wims_path, wimi_path, linearfit_path,
|
|
38
38
|
debayer_path, aberration_path, functionbundles_path, viewbundles_path, planetarystacker_path,
|
|
39
|
-
selectivecolor_path, rgbalign_path, planetprojection_path,
|
|
39
|
+
selectivecolor_path, rgbalign_path, planetprojection_path, clonestampicon_path,
|
|
40
40
|
)
|
|
41
41
|
|
|
42
42
|
# Import shortcuts module
|
|
@@ -204,22 +204,11 @@ class ToolbarMixin:
|
|
|
204
204
|
tb_fn.addAction(self.act_remove_green)
|
|
205
205
|
tb_fn.addAction(self.act_convo)
|
|
206
206
|
tb_fn.addAction(self.act_extract_luma)
|
|
207
|
-
|
|
208
|
-
#btn_luma = tb_fn.widgetForAction(self.act_extract_luma)
|
|
209
|
-
#if isinstance(btn_luma, QToolButton):
|
|
210
|
-
# luma_menu = QMenu(btn_luma)
|
|
211
|
-
# luma_menu.addActions(self._luma_group.actions())
|
|
212
|
-
# btn_luma.setMenu(luma_menu)
|
|
213
|
-
# btn_luma.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
|
|
214
|
-
# btn_luma.setStyleSheet("""
|
|
215
|
-
# QToolButton { color: #dcdcdc; }
|
|
216
|
-
# QToolButton:pressed, QToolButton:checked { color: #DAA520; font-weight: 600; }
|
|
217
|
-
# """)
|
|
218
|
-
|
|
219
207
|
tb_fn.addAction(self.act_recombine_luma)
|
|
220
208
|
tb_fn.addAction(self.act_rgb_extract)
|
|
221
209
|
tb_fn.addAction(self.act_rgb_combine)
|
|
222
210
|
tb_fn.addAction(self.act_blemish)
|
|
211
|
+
tb_fn.addAction(self.act_clone_stamp)
|
|
223
212
|
tb_fn.addAction(self.act_wavescale_hdr)
|
|
224
213
|
tb_fn.addAction(self.act_wavescale_de)
|
|
225
214
|
tb_fn.addAction(self.act_clahe)
|
|
@@ -1005,6 +994,11 @@ class ToolbarMixin:
|
|
|
1005
994
|
self.act_blemish.setStatusTip(self.tr("Interactive blemish removal on the active view"))
|
|
1006
995
|
self.act_blemish.triggered.connect(self._open_blemish_blaster)
|
|
1007
996
|
|
|
997
|
+
self.act_clone_stamp = QAction(QIcon(clonestampicon_path), self.tr("Clone Stamp..."), self)
|
|
998
|
+
self.act_clone_stamp.setIconVisibleInMenu(True)
|
|
999
|
+
self.act_clone_stamp.setStatusTip(self.tr("Interactive clone stamp on the active view"))
|
|
1000
|
+
self.act_clone_stamp.triggered.connect(self._open_clone_stamp)
|
|
1001
|
+
|
|
1008
1002
|
self.act_wavescale_hdr = QAction(QIcon(hdr_path), self.tr("WaveScale HDR..."), self)
|
|
1009
1003
|
self.act_wavescale_hdr.setStatusTip(self.tr("Wave-scale HDR with luminance-masked starlet"))
|
|
1010
1004
|
self.act_wavescale_hdr.setIconVisibleInMenu(True)
|
|
@@ -1416,6 +1410,7 @@ class ToolbarMixin:
|
|
|
1416
1410
|
reg("rgb_extract", self.act_rgb_extract)
|
|
1417
1411
|
reg("rgb_combine", self.act_rgb_combine)
|
|
1418
1412
|
reg("blemish_blaster", self.act_blemish)
|
|
1413
|
+
reg("clone_stamp", self.act_clone_stamp)
|
|
1419
1414
|
reg("wavescale_hdr", self.act_wavescale_hdr)
|
|
1420
1415
|
reg("wavescale_dark_enhance", self.act_wavescale_de)
|
|
1421
1416
|
reg("clahe", self.act_clahe)
|
setiastro/saspro/resources.py
CHANGED
|
@@ -214,6 +214,7 @@ class Icons:
|
|
|
214
214
|
OPEN_FILE = property(lambda self: _resource_path('openfile.png'))
|
|
215
215
|
ABE = property(lambda self: _resource_path('abeicon.png'))
|
|
216
216
|
BLASTER = property(lambda self: _resource_path('blaster.png'))
|
|
217
|
+
CLONESTAMP = property(lambda self: _resource_path('clonestamp.png'))
|
|
217
218
|
|
|
218
219
|
# Undo/Redo
|
|
219
220
|
UNDO = property(lambda self: _resource_path('undoicon.png'))
|
|
@@ -475,6 +476,7 @@ def _init_legacy_paths():
|
|
|
475
476
|
'undoicon_path': get_icon_path('undoicon.png'),
|
|
476
477
|
'redoicon_path': get_icon_path('redoicon.png'),
|
|
477
478
|
'blastericon_path': get_icon_path('blaster.png'),
|
|
479
|
+
'clonestampicon_path': get_icon_path('clonestamp.png'),
|
|
478
480
|
'hdr_path': get_icon_path('hdr.png'),
|
|
479
481
|
'invert_path': get_icon_path('invert.png'),
|
|
480
482
|
'fliphorizontal_path': get_icon_path('fliphorizontal.png'),
|
|
@@ -2295,6 +2295,36 @@ def compute_safe_chunk(height, width, N, channels, dtype, pref_h, pref_w):
|
|
|
2295
2295
|
|
|
2296
2296
|
_DIM_RE = re.compile(r"\s*\(\d+\s*x\s*\d+\)\s*")
|
|
2297
2297
|
|
|
2298
|
+
|
|
2299
|
+
def build_rejection_layers(rejection_map: dict, ref_H: int, ref_W: int, n_frames: int,
|
|
2300
|
+
*, comb_threshold_frac: float = 0.02):
|
|
2301
|
+
"""
|
|
2302
|
+
Returns:
|
|
2303
|
+
rej_cnt : (H,W) uint16 (or uint32 if needed)
|
|
2304
|
+
rej_frac : (H,W) float32 in [0..1]
|
|
2305
|
+
rej_any : (H,W) bool (thresholded, not "ever rejected")
|
|
2306
|
+
"""
|
|
2307
|
+
if n_frames <= 0:
|
|
2308
|
+
raise ValueError("n_frames must be > 0")
|
|
2309
|
+
|
|
2310
|
+
# Use uint32 if you might exceed 65535 rejections (rare)
|
|
2311
|
+
rej_cnt = np.zeros((ref_H, ref_W), dtype=np.uint32)
|
|
2312
|
+
|
|
2313
|
+
# rejection_map keys are per-file, values are coords in REGISTERED space
|
|
2314
|
+
for _fpath, coords in (rejection_map or {}).items():
|
|
2315
|
+
for (x, y) in coords:
|
|
2316
|
+
xi = int(x); yi = int(y)
|
|
2317
|
+
if 0 <= xi < ref_W and 0 <= yi < ref_H:
|
|
2318
|
+
rej_cnt[yi, xi] += 1
|
|
2319
|
+
|
|
2320
|
+
rej_frac = (rej_cnt.astype(np.float32) / float(n_frames))
|
|
2321
|
+
|
|
2322
|
+
# “combined mask” becomes “rejected in >= threshold% of frames”
|
|
2323
|
+
rej_any = (rej_frac >= float(comb_threshold_frac))
|
|
2324
|
+
|
|
2325
|
+
return rej_cnt, rej_frac, rej_any
|
|
2326
|
+
|
|
2327
|
+
|
|
2298
2328
|
class _Responder(QObject):
|
|
2299
2329
|
finished = pyqtSignal(object) # emits the edited dict or None
|
|
2300
2330
|
|
|
@@ -2472,36 +2502,38 @@ def _save_master_with_rejection_layers(
|
|
|
2472
2502
|
hdr: "fits.Header",
|
|
2473
2503
|
out_path: str,
|
|
2474
2504
|
*,
|
|
2475
|
-
rej_any: "np.ndarray | None" = None, # 2D bool
|
|
2505
|
+
rej_any: "np.ndarray | None" = None, # 2D bool (recommended: thresholded)
|
|
2476
2506
|
rej_frac: "np.ndarray | None" = None, # 2D float32 [0..1]
|
|
2507
|
+
rej_cnt: "np.ndarray | None" = None, # 2D uint16/uint32 counts
|
|
2477
2508
|
):
|
|
2478
2509
|
"""
|
|
2479
2510
|
Writes a MEF (multi-extension FITS) file:
|
|
2480
2511
|
- Primary HDU: the master image (2D or 3D) as float32
|
|
2481
2512
|
* Mono: (H, W)
|
|
2482
|
-
* Color: (
|
|
2483
|
-
- Optional EXTNAME=REJ_COMB: uint8 (0/1)
|
|
2484
|
-
- Optional EXTNAME=REJ_FRAC: float32 fraction-of-frames rejected per pixel
|
|
2513
|
+
* Color: (C, H, W) channels-first for FITS
|
|
2514
|
+
- Optional EXTNAME=REJ_COMB: uint8 (0/1) thresholded rejection mask
|
|
2515
|
+
- Optional EXTNAME=REJ_FRAC: float32 fraction-of-frames rejected per pixel [0..1]
|
|
2516
|
+
- Optional EXTNAME=REJ_CNT : uint16/uint32 count of frames rejected per pixel
|
|
2485
2517
|
"""
|
|
2518
|
+
|
|
2519
|
+
from astropy.io import fits
|
|
2520
|
+
from datetime import datetime
|
|
2521
|
+
|
|
2486
2522
|
# --- sanitize/shape primary data ---
|
|
2487
2523
|
data = np.asarray(img_array, dtype=np.float32, order="C")
|
|
2488
2524
|
|
|
2489
2525
|
# If channels-last, move to channels-first for FITS
|
|
2490
2526
|
if data.ndim == 3:
|
|
2491
|
-
# squeeze accidental singleton channels
|
|
2492
2527
|
if data.shape[-1] == 1:
|
|
2493
|
-
data = np.squeeze(data, axis=-1)
|
|
2494
|
-
elif data.shape[-1] in (3, 4):
|
|
2495
|
-
data = np.transpose(data, (2, 0, 1))
|
|
2496
|
-
# If already (C, H, W) leave it as-is.
|
|
2528
|
+
data = np.squeeze(data, axis=-1) # (H, W)
|
|
2529
|
+
elif data.shape[-1] in (3, 4):
|
|
2530
|
+
data = np.transpose(data, (2, 0, 1)) # (C, H, W)
|
|
2497
2531
|
|
|
2498
|
-
# After squeeze/transpose, re-evaluate dims
|
|
2499
2532
|
if data.ndim not in (2, 3):
|
|
2500
2533
|
raise ValueError(f"Unsupported master image shape for FITS: {data.shape}")
|
|
2501
2534
|
|
|
2502
2535
|
# --- clone + annotate header, and align NAXIS* with 'data' ---
|
|
2503
2536
|
H = (hdr.copy() if hdr is not None else fits.Header())
|
|
2504
|
-
# purge prior NAXIS keys to avoid conflicts after transpose/squeeze
|
|
2505
2537
|
for k in ("NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "NAXIS4"):
|
|
2506
2538
|
if k in H:
|
|
2507
2539
|
del H[k]
|
|
@@ -2512,41 +2544,50 @@ def _save_master_with_rejection_layers(
|
|
|
2512
2544
|
H["CREATOR"] = "SetiAstroSuite"
|
|
2513
2545
|
H["DATE-OBS"] = datetime.utcnow().isoformat()
|
|
2514
2546
|
|
|
2515
|
-
# Fill NAXIS* to match data (optional; Astropy will infer if omitted)
|
|
2516
2547
|
if data.ndim == 2:
|
|
2517
2548
|
H["NAXIS"] = 2
|
|
2518
|
-
H["NAXIS1"] = int(data.shape[1])
|
|
2519
|
-
H["NAXIS2"] = int(data.shape[0])
|
|
2549
|
+
H["NAXIS1"] = int(data.shape[1])
|
|
2550
|
+
H["NAXIS2"] = int(data.shape[0])
|
|
2520
2551
|
else:
|
|
2521
|
-
# data.shape == (C, H, W)
|
|
2522
2552
|
C, Hh, Ww = data.shape
|
|
2523
2553
|
H["NAXIS"] = 3
|
|
2524
|
-
H["NAXIS1"] = int(Ww)
|
|
2525
|
-
H["NAXIS2"] = int(Hh)
|
|
2526
|
-
H["NAXIS3"] = int(C)
|
|
2554
|
+
H["NAXIS1"] = int(Ww)
|
|
2555
|
+
H["NAXIS2"] = int(Hh)
|
|
2556
|
+
H["NAXIS3"] = int(C)
|
|
2527
2557
|
|
|
2528
|
-
# --- build HDU list ---
|
|
2529
2558
|
prim = fits.PrimaryHDU(data=data, header=H)
|
|
2530
2559
|
hdul = [prim]
|
|
2531
2560
|
|
|
2532
|
-
# Optional layers: must be 2D (H, W)
|
|
2561
|
+
# --- Optional layers: all must be 2D (H, W) in REGISTERED space ---
|
|
2533
2562
|
if rej_any is not None:
|
|
2534
2563
|
rej_any_2d = np.asarray(rej_any, dtype=bool)
|
|
2535
2564
|
if rej_any_2d.ndim != 2:
|
|
2536
2565
|
raise ValueError(f"REJ_COMB must be 2D, got {rej_any_2d.shape}")
|
|
2537
2566
|
h = fits.Header()
|
|
2538
2567
|
h["EXTNAME"] = "REJ_COMB"
|
|
2539
|
-
h["COMMENT"] = "
|
|
2568
|
+
h["COMMENT"] = "Thresholded rejection mask (1=frequently rejected, not merely 'ever')"
|
|
2540
2569
|
hdul.append(fits.ImageHDU(data=rej_any_2d.astype(np.uint8, copy=False), header=h))
|
|
2541
2570
|
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2571
|
+
if rej_frac is not None:
|
|
2572
|
+
rej_frac_2d = np.asarray(rej_frac, dtype=np.float32)
|
|
2573
|
+
if rej_frac_2d.ndim != 2:
|
|
2574
|
+
raise ValueError(f"REJ_FRAC must be 2D, got {rej_frac_2d.shape}")
|
|
2575
|
+
h = fits.Header()
|
|
2576
|
+
h["EXTNAME"] = "REJ_FRAC"
|
|
2577
|
+
h["COMMENT"] = "Per-pixel fraction of frames rejected [0..1]"
|
|
2578
|
+
hdul.append(fits.ImageHDU(data=rej_frac_2d, header=h))
|
|
2579
|
+
|
|
2580
|
+
if rej_cnt is not None:
|
|
2581
|
+
rej_cnt_2d = np.asarray(rej_cnt)
|
|
2582
|
+
if rej_cnt_2d.ndim != 2:
|
|
2583
|
+
raise ValueError(f"REJ_CNT must be 2D, got {rej_cnt_2d.shape}")
|
|
2584
|
+
# keep integer type; clamp to uint16 if you prefer smaller files
|
|
2585
|
+
if rej_cnt_2d.dtype not in (np.uint16, np.uint32, np.int32, np.int64):
|
|
2586
|
+
rej_cnt_2d = rej_cnt_2d.astype(np.uint32, copy=False)
|
|
2587
|
+
h = fits.Header()
|
|
2588
|
+
h["EXTNAME"] = "REJ_CNT"
|
|
2589
|
+
h["COMMENT"] = "Per-pixel count of frames rejected"
|
|
2590
|
+
hdul.append(fits.ImageHDU(data=rej_cnt_2d, header=h))
|
|
2550
2591
|
|
|
2551
2592
|
fits.HDUList(hdul).writeto(out_path, overwrite=True)
|
|
2552
2593
|
|
|
@@ -17139,16 +17180,29 @@ class StackingSuiteDialog(QDialog):
|
|
|
17139
17180
|
maps = getattr(self, "_rej_maps", {}).get(group_key)
|
|
17140
17181
|
save_layers = self.settings.value("stacking/save_rejection_layers", True, type=bool)
|
|
17141
17182
|
|
|
17142
|
-
|
|
17183
|
+
save_layers = self.settings.value("stacking/save_rejection_layers", True, type=bool)
|
|
17184
|
+
|
|
17185
|
+
if rejection_map and save_layers:
|
|
17143
17186
|
try:
|
|
17187
|
+
# integrated_image is already normalized above; rejection coords are assumed in this (H,W) space
|
|
17188
|
+
rej_cnt, rej_frac, rej_any = build_rejection_layers(
|
|
17189
|
+
rejection_map=rejection_map,
|
|
17190
|
+
ref_H=H,
|
|
17191
|
+
ref_W=W,
|
|
17192
|
+
n_frames=n_frames_group,
|
|
17193
|
+
comb_threshold_frac=0.02, # tweakable
|
|
17194
|
+
)
|
|
17195
|
+
|
|
17144
17196
|
_save_master_with_rejection_layers(
|
|
17145
|
-
integrated_image,
|
|
17146
|
-
hdr_orig,
|
|
17147
|
-
out_path_orig,
|
|
17148
|
-
rej_any
|
|
17149
|
-
rej_frac=
|
|
17197
|
+
img_array=integrated_image,
|
|
17198
|
+
hdr=hdr_orig,
|
|
17199
|
+
out_path=out_path_orig,
|
|
17200
|
+
rej_any=rej_any,
|
|
17201
|
+
rej_frac=rej_frac,
|
|
17202
|
+
rej_cnt=rej_cnt,
|
|
17150
17203
|
)
|
|
17151
17204
|
log(f"✅ Saved integrated image (with rejection layers) for '{group_key}': {out_path_orig}")
|
|
17205
|
+
|
|
17152
17206
|
except Exception as e:
|
|
17153
17207
|
log(f"⚠️ MEF save failed ({e}); falling back to single-HDU save.")
|
|
17154
17208
|
save_image(
|
|
@@ -17160,8 +17214,8 @@ class StackingSuiteDialog(QDialog):
|
|
|
17160
17214
|
is_mono=is_mono_orig
|
|
17161
17215
|
)
|
|
17162
17216
|
log(f"✅ Saved integrated image (single-HDU) for '{group_key}': {out_path_orig}")
|
|
17217
|
+
|
|
17163
17218
|
else:
|
|
17164
|
-
# No maps available or feature disabled → single-HDU save
|
|
17165
17219
|
save_image(
|
|
17166
17220
|
img_array=integrated_image,
|
|
17167
17221
|
filename=out_path_orig,
|
|
@@ -17481,19 +17535,14 @@ class StackingSuiteDialog(QDialog):
|
|
|
17481
17535
|
sasr_path = os.path.join(self.stacking_directory, f"{group_key}_rejections.sasr")
|
|
17482
17536
|
self.save_rejection_map_sasr(rejection_map, sasr_path)
|
|
17483
17537
|
log(f"✅ Saved rejection map to {sasr_path}")
|
|
17484
|
-
|
|
17485
|
-
|
|
17486
|
-
|
|
17487
|
-
|
|
17488
|
-
|
|
17489
|
-
|
|
17490
|
-
|
|
17491
|
-
|
|
17492
|
-
"integrated_image": integrated_image,
|
|
17493
|
-
"rejection_map": None,
|
|
17494
|
-
"n_frames": n_frames_group,
|
|
17495
|
-
"drizzled": False
|
|
17496
|
-
}
|
|
17538
|
+
|
|
17539
|
+
group_integration_data[group_key] = {
|
|
17540
|
+
"integrated_image": integrated_image,
|
|
17541
|
+
"rejection_map": rejection_map if drizzle_enabled_global else None,
|
|
17542
|
+
"n_frames": n_frames_group,
|
|
17543
|
+
"drizzled": False, # <-- ALWAYS false here
|
|
17544
|
+
}
|
|
17545
|
+
if not drizzle_enabled_global:
|
|
17497
17546
|
log(f"ℹ️ Skipping rejection map save for '{group_key}' (drizzle disabled).")
|
|
17498
17547
|
|
|
17499
17548
|
QApplication.processEvents()
|
|
@@ -17549,19 +17598,27 @@ class StackingSuiteDialog(QDialog):
|
|
|
17549
17598
|
|
|
17550
17599
|
log(f"📐 Drizzle for '{group_key}' at {scale_factor}× (drop={drop_shrink}) using {n_frames_group} frame(s).")
|
|
17551
17600
|
|
|
17552
|
-
|
|
17553
|
-
|
|
17554
|
-
|
|
17555
|
-
|
|
17556
|
-
|
|
17557
|
-
|
|
17558
|
-
|
|
17559
|
-
|
|
17560
|
-
|
|
17561
|
-
|
|
17562
|
-
|
|
17563
|
-
|
|
17564
|
-
|
|
17601
|
+
ok = False
|
|
17602
|
+
try:
|
|
17603
|
+
ok = bool(self.drizzle_stack_one_group(
|
|
17604
|
+
group_key=group_key,
|
|
17605
|
+
file_list=file_list,
|
|
17606
|
+
entries=entries_by_group.get(group_key, []),
|
|
17607
|
+
transforms_dict=transforms_dict,
|
|
17608
|
+
frame_weights=frame_weights,
|
|
17609
|
+
scale_factor=scale_factor,
|
|
17610
|
+
drop_shrink=drop_shrink,
|
|
17611
|
+
rejection_map=rejections_for_group,
|
|
17612
|
+
autocrop_enabled=autocrop_enabled,
|
|
17613
|
+
rect_override=global_rect,
|
|
17614
|
+
status_cb=log
|
|
17615
|
+
))
|
|
17616
|
+
except Exception as e:
|
|
17617
|
+
log(f"⚠️ Drizzle failed for '{group_key}': {e!r}")
|
|
17618
|
+
ok = False
|
|
17619
|
+
|
|
17620
|
+
if ok:
|
|
17621
|
+
group_integration_data[group_key]["drizzled"] = True
|
|
17565
17622
|
|
|
17566
17623
|
|
|
17567
17624
|
# Build summary lines
|
|
@@ -18823,7 +18880,7 @@ class StackingSuiteDialog(QDialog):
|
|
|
18823
18880
|
# ---- sanity: entries must exist ----
|
|
18824
18881
|
if not entries:
|
|
18825
18882
|
log(f"⚠️ Group '{group_key}' has no drizzle entries – skipping.")
|
|
18826
|
-
return
|
|
18883
|
+
return False
|
|
18827
18884
|
|
|
18828
18885
|
# Debug (first few)
|
|
18829
18886
|
try:
|
|
@@ -18856,14 +18913,14 @@ class StackingSuiteDialog(QDialog):
|
|
|
18856
18913
|
# Need at least 2 frames to be worth it
|
|
18857
18914
|
if len(file_list) < 2:
|
|
18858
18915
|
log(f"⚠️ Group '{group_key}' does not have enough frames to drizzle.")
|
|
18859
|
-
return
|
|
18916
|
+
return False
|
|
18860
18917
|
|
|
18861
18918
|
# ---- establish header + default dims from first aligned file (metadata only) ----
|
|
18862
18919
|
first_file = file_list[0]
|
|
18863
18920
|
first_img, hdr, _, _ = load_image(first_file)
|
|
18864
18921
|
if first_img is None:
|
|
18865
18922
|
log(f"⚠️ Could not load {first_file} to determine drizzle shape!")
|
|
18866
|
-
return
|
|
18923
|
+
return False
|
|
18867
18924
|
|
|
18868
18925
|
# Force mono if any entry is channel-split (dual-band drizzle output should be mono)
|
|
18869
18926
|
force_mono = any((e.get("chan") in ("R", "G", "B")) for e in entries)
|
|
@@ -19139,6 +19196,7 @@ class StackingSuiteDialog(QDialog):
|
|
|
19139
19196
|
self._autocrop_outputs = []
|
|
19140
19197
|
self._autocrop_outputs.append((group_key, out_path_crop))
|
|
19141
19198
|
log(f"✂️ Drizzle (auto-cropped) saved: {out_path_crop}")
|
|
19199
|
+
return True
|
|
19142
19200
|
|
|
19143
19201
|
def _load_sasd_v2(self, path: str):
|
|
19144
19202
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: setiastrosuitepro
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.5
|
|
4
4
|
Summary: Seti Astro Suite Pro - Advanced astrophotography toolkit for image calibration, stacking, registration, photometry, and visualization
|
|
5
5
|
License: GPL-3.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -35,6 +35,7 @@ setiastro/images/big_moon_stabilizer_timeline_clean.png,sha256=DN5D66pcMs5ir9VPe
|
|
|
35
35
|
setiastro/images/blaster.png,sha256=lSVcl98Su3wXEL-QVoY_G1GKTRGwsck70jlVLUJ_nv8,25242
|
|
36
36
|
setiastro/images/blink.png,sha256=kV2J74CfkMMqn4WqhHHXWQEvq7Y1zXW21RXaJL29eGI,57285
|
|
37
37
|
setiastro/images/clahe.png,sha256=oobMQzfW-LDCKhaz7JeTqqtxYL0Jp30KbEvk6-oowqw,179866
|
|
38
|
+
setiastro/images/clonestamp.png,sha256=EfWXZb9lYa4AyzlHSWVVXURY6uCOUWUWCegxfqUegxI,29006
|
|
38
39
|
setiastro/images/collage.png,sha256=muJZ5NbhwHXyPL1YcMtMmacTr29hYEc8h7fb0iZ2dzk,5324
|
|
39
40
|
setiastro/images/colorwheel.png,sha256=CuD6SjrLw6ENqPfTtAX5xRfSQ9vd08RjuFuFsmZNooE,225785
|
|
40
41
|
setiastro/images/colorwheel.svg,sha256=QUUnQ9-hGf3hOd_rNZ1efYq2dZLBZ7g390Le_MJ3zsk,89976
|
|
@@ -182,7 +183,7 @@ setiastro/qml/ResourceMonitor.qml,sha256=k9_qXKAZLi8vj-5BffJTJu_UkRnxunZKn53Hthd
|
|
|
182
183
|
setiastro/saspro/__init__.py,sha256=6o8orhytzBWyJWBdG37xPcT6IVPZOG8d22FBVzn0Kac,902
|
|
183
184
|
setiastro/saspro/__main__.py,sha256=_zAHsw4SNWYzMXlswBuiMGgTUGYrU5p5-dsxhQ3_GWk,40789
|
|
184
185
|
setiastro/saspro/_generated/__init__.py,sha256=HbruQfKNbbVL4kh_t4oVG3UeUieaW8MUaqIcDCmnTvA,197
|
|
185
|
-
setiastro/saspro/_generated/build_info.py,sha256=
|
|
186
|
+
setiastro/saspro/_generated/build_info.py,sha256=RdKarljxfwZqtbmi2IOSxR1gufhzVpP3aN5pC-MNLsA,111
|
|
186
187
|
setiastro/saspro/abe.py,sha256=l6o0klT-TqJppzEjCBb9isyIvtuf_UsuMZV8JvqbgPI,59779
|
|
187
188
|
setiastro/saspro/abe_preset.py,sha256=u9t16yTb9v98tLjhvh496Fsp3Z-dNiBSs5itnAaJwh8,7750
|
|
188
189
|
setiastro/saspro/aberration_ai.py,sha256=8LtDu_KuqvL-W0MTUIJq9KguMjJPiUEQjMUibY16H-4,41673
|
|
@@ -198,12 +199,13 @@ setiastro/saspro/autostretch.py,sha256=mSDXjbKjip7BrB19QZhniBxlsf8C_X49IVlc8-0bN
|
|
|
198
199
|
setiastro/saspro/backgroundneutral.py,sha256=MFlBEwR65ASNXIY0S-0ZUaS89FCmNV_qafPs-OxUlO4,26383
|
|
199
200
|
setiastro/saspro/batch_convert.py,sha256=O46KyB-DBZiTHokaWt_op1GDRr3juSaDSeFzP3pv1Zs,12377
|
|
200
201
|
setiastro/saspro/batch_renamer.py,sha256=PZe2MEwjg0n055UETIbwnwdfAux4heTVx5gr3qnNW5g,21900
|
|
201
|
-
setiastro/saspro/blemish_blaster.py,sha256=
|
|
202
|
+
setiastro/saspro/blemish_blaster.py,sha256=aiIpr-qpDmfaLZErUGUtwQgSTnaBHz3l14JvDWRfL3w,22983
|
|
202
203
|
setiastro/saspro/blink_comparator_pro.py,sha256=XwXR-0Xj6Z3R3IYheTRwdyhpnGMiwXh3lpziAZzWRdE,132085
|
|
203
204
|
setiastro/saspro/bundles.py,sha256=jWMYpalOcaMQ1QgikseOOmF7bBzDz9aykhRddaTsReQ,1892
|
|
204
205
|
setiastro/saspro/bundles_dock.py,sha256=IEAJEOEEe7V4NvsmClMygTf55LXN3SkV4e23K1erzkc,4441
|
|
205
206
|
setiastro/saspro/cheat_sheet.py,sha256=0bLmwY3GBWGcb4M7BBYzzrB_03XfqvZWAhQ2cBPavQ8,7852
|
|
206
207
|
setiastro/saspro/clahe.py,sha256=o2cmgYhYS_Z3vo-RR_iw8iZd_7JpelNnub7LvIkFMNA,15259
|
|
208
|
+
setiastro/saspro/clone_stamp.py,sha256=wPICwRoRXtvKgnmtg-06y_kfoF8-n4KD0ZdsO4nO1Nc,28455
|
|
207
209
|
setiastro/saspro/comet_stacking.py,sha256=68aBVCmthgGQcYMZkZ850DAIBlHaMUOFX6bhUenY6Uk,59485
|
|
208
210
|
setiastro/saspro/common_tr.py,sha256=cYFLFP2d8cwXk_phdYbAdVovEOK-7niBCdrUrXCKn-Q,3581
|
|
209
211
|
setiastro/saspro/config.py,sha256=FhvWG-efVdzuEPef-eOezem-YaFgnGNCl86XmkruGYE,1319
|
|
@@ -236,16 +238,16 @@ setiastro/saspro/ghs_preset.py,sha256=Zw3MJH5rEz7nqLdlmRBm3vYXgyopoupyDGAhM-PRXq
|
|
|
236
238
|
setiastro/saspro/graxpert.py,sha256=XSLeBhlAY2DkYUw93j2OEOuPLOPfzWYcT5Dz3JhhpW8,22579
|
|
237
239
|
setiastro/saspro/graxpert_preset.py,sha256=ESds2NPMPfsBHWNBfyYZ1rFpQxZ9gPOtxwz8enHfFfc,10719
|
|
238
240
|
setiastro/saspro/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
239
|
-
setiastro/saspro/gui/main_window.py,sha256=
|
|
241
|
+
setiastro/saspro/gui/main_window.py,sha256=ImF8JUE8PAjj1IHBgpyWq4pVFdbQ2G2WompGpUMfR5E,375703
|
|
240
242
|
setiastro/saspro/gui/mixins/__init__.py,sha256=ubdTIri0xoRs6MwDnEaVsAHbMxuPqz0CZcYcue3Mkcc,836
|
|
241
243
|
setiastro/saspro/gui/mixins/dock_mixin.py,sha256=C6IWKM-uP2ekqO7ozVQQcTnEgAkUL_OaRZbncIdrh8g,23893
|
|
242
244
|
setiastro/saspro/gui/mixins/file_mixin.py,sha256=p6gKJrDTKovK4YzowDQy1Z5nNsspQe7B9ICRXvCUvxc,17893
|
|
243
245
|
setiastro/saspro/gui/mixins/geometry_mixin.py,sha256=x-HyLXGFhEs8SJuXT8EF6tS3XJaH8IhAP-ZFJ2BS2Lw,19038
|
|
244
246
|
setiastro/saspro/gui/mixins/header_mixin.py,sha256=kfZUJviB61c8NBXFB3MVBEcRPX_36ZV8tUZNJkDmMJ0,17253
|
|
245
247
|
setiastro/saspro/gui/mixins/mask_mixin.py,sha256=hrC5jLWxUZgQiYUXp7nvECo2uiarK7_HKgawG4HGB9w,15711
|
|
246
|
-
setiastro/saspro/gui/mixins/menu_mixin.py,sha256=
|
|
248
|
+
setiastro/saspro/gui/mixins/menu_mixin.py,sha256=syal2SUpmR2LkjYaKZqSELmUoGQ27fsqxXwacKl8Zso,16797
|
|
247
249
|
setiastro/saspro/gui/mixins/theme_mixin.py,sha256=td6hYnZn17lXlFxQOXgK7-qfKo6CIJACF8_zrULlEkc,20740
|
|
248
|
-
setiastro/saspro/gui/mixins/toolbar_mixin.py,sha256=
|
|
250
|
+
setiastro/saspro/gui/mixins/toolbar_mixin.py,sha256=LF-obbMkRtz69_NmBsyDPvcOBZlLKN94O15Kw1I0V80,92814
|
|
249
251
|
setiastro/saspro/gui/mixins/update_mixin.py,sha256=rq7J0Pcn_gO8UehCGVtcSvf-MDbmeGf796h0JBGr0sM,16545
|
|
250
252
|
setiastro/saspro/gui/mixins/view_mixin.py,sha256=EacXxtoAvkectGgLiahf3rHv2AaqKDQUteocVV_P834,17850
|
|
251
253
|
setiastro/saspro/gui/statistics_dialog.py,sha256=celhcsHcp3lNpox_X0ByAiYE80qFaF-dYyP8StzgrcU,1955
|
|
@@ -315,7 +317,7 @@ setiastro/saspro/pyi_rthook_astroquery.py,sha256=Omt8U0gMI3efw3y-GUUJtBYUylwT1OM
|
|
|
315
317
|
setiastro/saspro/remove_green.py,sha256=bd_4xLe-tEYPncOSVUHh-fVIrPWG6K0ESKgAygrwj_M,12082
|
|
316
318
|
setiastro/saspro/remove_stars.py,sha256=tv61y58xdm5g5Gu5T3I7jUqSmVVguJ9BWOKBy_X8ub8,60967
|
|
317
319
|
setiastro/saspro/remove_stars_preset.py,sha256=HcR50bRXqiJSx4jMyle389g8b6SIulXHxgSysk07vdk,19195
|
|
318
|
-
setiastro/saspro/resources.py,sha256=
|
|
320
|
+
setiastro/saspro/resources.py,sha256=6C5XA5MvoKfGOY84xcO1QhI2fUSk9OqnHHEkTuugbWI,27698
|
|
319
321
|
setiastro/saspro/rgb_combination.py,sha256=8MKlxsbn5Pmiq3Vh28fMVYgYG4EIsvOYrwSJisJGyds,8765
|
|
320
322
|
setiastro/saspro/rgb_extract.py,sha256=FZRA2qac8LR2u-jNcQP-1xFeiOYVBo06-Q3Xl0KAlZM,624
|
|
321
323
|
setiastro/saspro/rgbalign.py,sha256=XwBDj5t_zj9Uh-PaX1SNlzix9PftnQBIoTMtRPvYK80,46528
|
|
@@ -331,7 +333,7 @@ setiastro/saspro/serviewer.py,sha256=QvPtJky2IzrywXaOYjeSZSNY0I64TSrzfgH7vRgGk7M
|
|
|
331
333
|
setiastro/saspro/sfcc.py,sha256=4wgXqH-E0OoSl_Ox4tMoExUHSnjosx2EnjjKeOXyZgo,89133
|
|
332
334
|
setiastro/saspro/shortcuts.py,sha256=QvFBXN_S8jqEwaP9m4pJMLVqzBmxo5HrjWhVCV9etQg,138256
|
|
333
335
|
setiastro/saspro/signature_insert.py,sha256=pWDxUO1Rxm27_fHSo2Y99bdOD2iG9q4AUjGR20x6TiA,70401
|
|
334
|
-
setiastro/saspro/stacking_suite.py,sha256
|
|
336
|
+
setiastro/saspro/stacking_suite.py,sha256=-eOQN43IngMo4ywpdSyFLn8nfaoxX4KTfks32djr9u4,880749
|
|
335
337
|
setiastro/saspro/star_alignment.py,sha256=lRpDFvDKsMluu2P_kotQ9OumlnGtEWwGE7Gm-ZYccm8,326894
|
|
336
338
|
setiastro/saspro/star_alignment_preset.py,sha256=1QLRRUsVGr3-iX4kKvV9s-NuDRJVnRQat8qdTIs1nao,13164
|
|
337
339
|
setiastro/saspro/star_metrics.py,sha256=LRfBdqjwJJ9iz_paY-CkhPIqt2MxoXARLUy73ZKTA_0,1503
|
|
@@ -407,9 +409,9 @@ setiastro/saspro/wimi.py,sha256=CNo833Pur9P-A1DUSntlAaQWekf6gzWIvetOMvLDrOw,3146
|
|
|
407
409
|
setiastro/saspro/wims.py,sha256=HDfVI3Ckf5OJEJLH8NI36pFc2USZnETpb4UDIvweNX4,27450
|
|
408
410
|
setiastro/saspro/window_shelf.py,sha256=hpId8uRPq7-UZ3dWW5YzH_v4TchQokGFoPGM8dyaIZA,9448
|
|
409
411
|
setiastro/saspro/xisf.py,sha256=Ah1CXDAohN__ej1Lq7LPU8vGLnDz8fluLQTGE71aUoc,52669
|
|
410
|
-
setiastrosuitepro-1.7.
|
|
411
|
-
setiastrosuitepro-1.7.
|
|
412
|
-
setiastrosuitepro-1.7.
|
|
413
|
-
setiastrosuitepro-1.7.
|
|
414
|
-
setiastrosuitepro-1.7.
|
|
415
|
-
setiastrosuitepro-1.7.
|
|
412
|
+
setiastrosuitepro-1.7.5.dist-info/entry_points.txt,sha256=g7cHWhUSiIP7mkyByG9JXGWWlHKeVC2vL7zvB9U-vEU,236
|
|
413
|
+
setiastrosuitepro-1.7.5.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
414
|
+
setiastrosuitepro-1.7.5.dist-info/licenses/license.txt,sha256=KCwYZ9VpVwmzjelDq1BzzWqpBvt9nbbapa-woz47hfQ,123930
|
|
415
|
+
setiastrosuitepro-1.7.5.dist-info/METADATA,sha256=4chMNL04rErx-qeV865sObWFXcS1VWxrXd_FjaW8-m4,9919
|
|
416
|
+
setiastrosuitepro-1.7.5.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
417
|
+
setiastrosuitepro-1.7.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{setiastrosuitepro-1.7.4.dist-info → setiastrosuitepro-1.7.5.dist-info}/licenses/license.txt
RENAMED
|
File without changes
|