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.

@@ -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)
@@ -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: (3, H, W) <-- channels-first for FITS
2483
- - Optional EXTNAME=REJ_COMB: uint8 (0/1) combined rejection mask
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) # becomes (H, W)
2494
- elif data.shape[-1] in (3, 4): # RGB or RGBA
2495
- data = np.transpose(data, (2, 0, 1)) # (C, H, W)
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]) # width
2519
- H["NAXIS2"] = int(data.shape[0]) # height
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) # width
2525
- H["NAXIS2"] = int(Hh) # height
2526
- H["NAXIS3"] = int(C) # channels/planes
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). Convert types safely.
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"] = "Combined rejection mask (any algorithm / any frame)"
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
- #if rej_frac is not None:
2543
- # rej_frac_2d = np.asarray(rej_frac, dtype=np.float32)
2544
- # if rej_frac_2d.ndim != 2:
2545
- # raise ValueError(f"REJ_FRAC must be 2D, got {rej_frac_2d.shape}")
2546
- # h = fits.Header()
2547
- # h["EXTNAME"] = "REJ_FRAC"
2548
- # h["COMMENT"] = "Per-pixel fraction of frames rejected [0..1]"
2549
- # hdul.append(fits.ImageHDU(data=rej_frac_2d, header=h))
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
- if maps and save_layers:
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 = maps.get("any"),
17149
- rej_frac= maps.get("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
- group_integration_data[group_key] = {
17485
- "integrated_image": integrated_image,
17486
- "rejection_map": rejection_map,
17487
- "n_frames": n_frames_group,
17488
- "drizzled": True
17489
- }
17490
- else:
17491
- group_integration_data[group_key] = {
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
- self.drizzle_stack_one_group(
17553
- group_key=group_key,
17554
- file_list=file_list,
17555
- entries=entries_by_group.get(group_key, []),
17556
- transforms_dict=transforms_dict,
17557
- frame_weights=frame_weights,
17558
- scale_factor=scale_factor,
17559
- drop_shrink=drop_shrink,
17560
- rejection_map=rejections_for_group,
17561
- autocrop_enabled=autocrop_enabled,
17562
- rect_override=global_rect,
17563
- status_cb=log
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.4
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=VxZWJy-KPNuwmcIbdSYbrwcuCOt5PSUVAua4TWpAV9k,111
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=aqD8v_85SBJJBLHB1XbPM-_YzXEIeQM-g-YGhaIcmGE,21314
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=cR7KPPqLE7FmO55Y0_VO-fhvS-gitFW-x65jfeAckDs,374942
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=eoXZPjMysBXcC2DllHmXwSFv2BnFO-GDUgm-9XpJU0c,16750
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=ci15G3SNmysrkvFpx7bGxCK29uzdqLB6bYJYSKsne6s,92922
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=gqnGhWd9gt4NnADI3fnTSRcs8koigFrihLUj4urLlWc,27559
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=5Ut4ImE8Ey4IAK52Bhwmwq8NvB5M3Cjm3b9156uYh0k,878510
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.4.dist-info/entry_points.txt,sha256=g7cHWhUSiIP7mkyByG9JXGWWlHKeVC2vL7zvB9U-vEU,236
411
- setiastrosuitepro-1.7.4.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
412
- setiastrosuitepro-1.7.4.dist-info/licenses/license.txt,sha256=KCwYZ9VpVwmzjelDq1BzzWqpBvt9nbbapa-woz47hfQ,123930
413
- setiastrosuitepro-1.7.4.dist-info/METADATA,sha256=zXX5RdlvdSRINeX6HMnOxEsUicH2kT781yVQM4hnuww,9919
414
- setiastrosuitepro-1.7.4.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
415
- setiastrosuitepro-1.7.4.dist-info/RECORD,,
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,,