setiastrosuitepro 1.6.2.post1__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/data/SASP_data.fits +0 -0
- setiastro/data/catalogs/List_of_Galaxies_with_Distances_Gly.csv +488 -0
- setiastro/data/catalogs/astrobin_filters.csv +890 -0
- setiastro/data/catalogs/astrobin_filters_page1_local.csv +51 -0
- setiastro/data/catalogs/cali2.csv +63 -0
- setiastro/data/catalogs/cali2color.csv +65 -0
- setiastro/data/catalogs/celestial_catalog - original.csv +16471 -0
- setiastro/data/catalogs/celestial_catalog.csv +24031 -0
- setiastro/data/catalogs/detected_stars.csv +24784 -0
- setiastro/data/catalogs/fits_header_data.csv +46 -0
- setiastro/data/catalogs/test.csv +8 -0
- setiastro/data/catalogs/updated_celestial_catalog.csv +16471 -0
- setiastro/images/Astro_Spikes.png +0 -0
- setiastro/images/Background_startup.jpg +0 -0
- setiastro/images/HRDiagram.png +0 -0
- setiastro/images/LExtract.png +0 -0
- setiastro/images/LInsert.png +0 -0
- setiastro/images/Oxygenation-atm-2.svg.png +0 -0
- setiastro/images/RGB080604.png +0 -0
- setiastro/images/abeicon.png +0 -0
- setiastro/images/aberration.png +0 -0
- setiastro/images/andromedatry.png +0 -0
- setiastro/images/andromedatry_satellited.png +0 -0
- setiastro/images/annotated.png +0 -0
- setiastro/images/aperture.png +0 -0
- setiastro/images/astrosuite.ico +0 -0
- setiastro/images/astrosuite.png +0 -0
- setiastro/images/astrosuitepro.icns +0 -0
- setiastro/images/astrosuitepro.ico +0 -0
- setiastro/images/astrosuitepro.png +0 -0
- setiastro/images/background.png +0 -0
- setiastro/images/background2.png +0 -0
- setiastro/images/benchmark.png +0 -0
- setiastro/images/big_moon_stabilizer_timeline.png +0 -0
- setiastro/images/big_moon_stabilizer_timeline_clean.png +0 -0
- setiastro/images/blaster.png +0 -0
- setiastro/images/blink.png +0 -0
- setiastro/images/clahe.png +0 -0
- setiastro/images/collage.png +0 -0
- setiastro/images/colorwheel.png +0 -0
- setiastro/images/contsub.png +0 -0
- setiastro/images/convo.png +0 -0
- setiastro/images/copyslot.png +0 -0
- setiastro/images/cosmic.png +0 -0
- setiastro/images/cosmicsat.png +0 -0
- setiastro/images/crop1.png +0 -0
- setiastro/images/cropicon.png +0 -0
- setiastro/images/curves.png +0 -0
- setiastro/images/cvs.png +0 -0
- setiastro/images/debayer.png +0 -0
- setiastro/images/denoise_cnn_custom.png +0 -0
- setiastro/images/denoise_cnn_graph.png +0 -0
- setiastro/images/disk.png +0 -0
- setiastro/images/dse.png +0 -0
- setiastro/images/exoicon.png +0 -0
- setiastro/images/eye.png +0 -0
- setiastro/images/fliphorizontal.png +0 -0
- setiastro/images/flipvertical.png +0 -0
- setiastro/images/font.png +0 -0
- setiastro/images/freqsep.png +0 -0
- setiastro/images/functionbundle.png +0 -0
- setiastro/images/graxpert.png +0 -0
- setiastro/images/green.png +0 -0
- setiastro/images/gridicon.png +0 -0
- setiastro/images/halo.png +0 -0
- setiastro/images/hdr.png +0 -0
- setiastro/images/histogram.png +0 -0
- setiastro/images/hubble.png +0 -0
- setiastro/images/imagecombine.png +0 -0
- setiastro/images/invert.png +0 -0
- setiastro/images/isophote.png +0 -0
- setiastro/images/isophote_demo_figure.png +0 -0
- setiastro/images/isophote_demo_image.png +0 -0
- setiastro/images/isophote_demo_model.png +0 -0
- setiastro/images/isophote_demo_residual.png +0 -0
- setiastro/images/jwstpupil.png +0 -0
- setiastro/images/linearfit.png +0 -0
- setiastro/images/livestacking.png +0 -0
- setiastro/images/mask.png +0 -0
- setiastro/images/maskapply.png +0 -0
- setiastro/images/maskcreate.png +0 -0
- setiastro/images/maskremove.png +0 -0
- setiastro/images/morpho.png +0 -0
- setiastro/images/mosaic.png +0 -0
- setiastro/images/multiscale_decomp.png +0 -0
- setiastro/images/nbtorgb.png +0 -0
- setiastro/images/neutral.png +0 -0
- setiastro/images/nuke.png +0 -0
- setiastro/images/openfile.png +0 -0
- setiastro/images/pedestal.png +0 -0
- setiastro/images/pen.png +0 -0
- setiastro/images/pixelmath.png +0 -0
- setiastro/images/platesolve.png +0 -0
- setiastro/images/ppp.png +0 -0
- setiastro/images/pro.png +0 -0
- setiastro/images/project.png +0 -0
- setiastro/images/psf.png +0 -0
- setiastro/images/redo.png +0 -0
- setiastro/images/redoicon.png +0 -0
- setiastro/images/rescale.png +0 -0
- setiastro/images/rgbalign.png +0 -0
- setiastro/images/rgbcombo.png +0 -0
- setiastro/images/rgbextract.png +0 -0
- setiastro/images/rotate180.png +0 -0
- setiastro/images/rotateclockwise.png +0 -0
- setiastro/images/rotatecounterclockwise.png +0 -0
- setiastro/images/satellite.png +0 -0
- setiastro/images/script.png +0 -0
- setiastro/images/selectivecolor.png +0 -0
- setiastro/images/simbad.png +0 -0
- setiastro/images/slot0.png +0 -0
- setiastro/images/slot1.png +0 -0
- setiastro/images/slot2.png +0 -0
- setiastro/images/slot3.png +0 -0
- setiastro/images/slot4.png +0 -0
- setiastro/images/slot5.png +0 -0
- setiastro/images/slot6.png +0 -0
- setiastro/images/slot7.png +0 -0
- setiastro/images/slot8.png +0 -0
- setiastro/images/slot9.png +0 -0
- setiastro/images/spcc.png +0 -0
- setiastro/images/spin_precession_vs_lunar_distance.png +0 -0
- setiastro/images/spinner.gif +0 -0
- setiastro/images/stacking.png +0 -0
- setiastro/images/staradd.png +0 -0
- setiastro/images/staralign.png +0 -0
- setiastro/images/starnet.png +0 -0
- setiastro/images/starregistration.png +0 -0
- setiastro/images/starspike.png +0 -0
- setiastro/images/starstretch.png +0 -0
- setiastro/images/statstretch.png +0 -0
- setiastro/images/supernova.png +0 -0
- setiastro/images/uhs.png +0 -0
- setiastro/images/undoicon.png +0 -0
- setiastro/images/upscale.png +0 -0
- setiastro/images/viewbundle.png +0 -0
- setiastro/images/whitebalance.png +0 -0
- setiastro/images/wimi_icon_256x256.png +0 -0
- setiastro/images/wimilogo.png +0 -0
- setiastro/images/wims.png +0 -0
- setiastro/images/wrench_icon.png +0 -0
- setiastro/images/xisfliberator.png +0 -0
- setiastro/qml/ResourceMonitor.qml +126 -0
- setiastro/saspro/__init__.py +20 -0
- setiastro/saspro/__main__.py +945 -0
- setiastro/saspro/_generated/__init__.py +7 -0
- setiastro/saspro/_generated/build_info.py +3 -0
- setiastro/saspro/abe.py +1346 -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 +624 -0
- setiastro/saspro/astrobin_exporter.py +1010 -0
- setiastro/saspro/astrospike.py +153 -0
- setiastro/saspro/astrospike_python.py +1841 -0
- setiastro/saspro/autostretch.py +198 -0
- setiastro/saspro/backgroundneutral.py +602 -0
- setiastro/saspro/batch_convert.py +328 -0
- setiastro/saspro/batch_renamer.py +522 -0
- setiastro/saspro/blemish_blaster.py +491 -0
- setiastro/saspro/blink_comparator_pro.py +2926 -0
- setiastro/saspro/bundles.py +61 -0
- setiastro/saspro/bundles_dock.py +114 -0
- setiastro/saspro/cheat_sheet.py +213 -0
- setiastro/saspro/clahe.py +368 -0
- setiastro/saspro/comet_stacking.py +1442 -0
- setiastro/saspro/common_tr.py +107 -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 +1400 -0
- setiastro/saspro/convo_preset.py +414 -0
- setiastro/saspro/copyastro.py +190 -0
- setiastro/saspro/cosmicclarity.py +1589 -0
- setiastro/saspro/cosmicclarity_preset.py +407 -0
- setiastro/saspro/crop_dialog_pro.py +973 -0
- setiastro/saspro/crop_preset.py +189 -0
- setiastro/saspro/curve_editor_pro.py +2562 -0
- setiastro/saspro/curves_preset.py +375 -0
- setiastro/saspro/debayer.py +673 -0
- setiastro/saspro/debug_utils.py +29 -0
- setiastro/saspro/dnd_mime.py +35 -0
- setiastro/saspro/doc_manager.py +2664 -0
- setiastro/saspro/exoplanet_detector.py +2166 -0
- setiastro/saspro/file_utils.py +284 -0
- setiastro/saspro/fitsmodifier.py +748 -0
- setiastro/saspro/fix_bom.py +32 -0
- setiastro/saspro/free_torch_memory.py +48 -0
- setiastro/saspro/frequency_separation.py +1349 -0
- setiastro/saspro/function_bundle.py +1596 -0
- setiastro/saspro/generate_translations.py +3092 -0
- setiastro/saspro/ghs_dialog_pro.py +663 -0
- setiastro/saspro/ghs_preset.py +284 -0
- setiastro/saspro/graxpert.py +637 -0
- setiastro/saspro/graxpert_preset.py +287 -0
- setiastro/saspro/gui/__init__.py +0 -0
- setiastro/saspro/gui/main_window.py +8810 -0
- setiastro/saspro/gui/mixins/__init__.py +33 -0
- setiastro/saspro/gui/mixins/dock_mixin.py +362 -0
- setiastro/saspro/gui/mixins/file_mixin.py +450 -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 +389 -0
- setiastro/saspro/gui/mixins/theme_mixin.py +367 -0
- setiastro/saspro/gui/mixins/toolbar_mixin.py +1457 -0
- setiastro/saspro/gui/mixins/update_mixin.py +309 -0
- setiastro/saspro/gui/mixins/view_mixin.py +435 -0
- setiastro/saspro/gui/statistics_dialog.py +47 -0
- setiastro/saspro/halobgon.py +488 -0
- setiastro/saspro/header_viewer.py +448 -0
- setiastro/saspro/headless_utils.py +88 -0
- setiastro/saspro/histogram.py +756 -0
- setiastro/saspro/history_explorer.py +941 -0
- setiastro/saspro/i18n.py +168 -0
- setiastro/saspro/image_combine.py +417 -0
- setiastro/saspro/image_peeker_pro.py +1604 -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 +236 -0
- setiastro/saspro/isophote.py +1182 -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 +3676 -0
- setiastro/saspro/legacy/xisf.py +1071 -0
- setiastro/saspro/linear_fit.py +537 -0
- setiastro/saspro/live_stacking.py +1841 -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 +931 -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 +3831 -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 +407 -0
- setiastro/saspro/multiscale_decomp.py +1293 -0
- setiastro/saspro/nbtorgb_stars.py +541 -0
- setiastro/saspro/numba_utils.py +3145 -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 +1473 -0
- setiastro/saspro/ops/settings.py +637 -0
- setiastro/saspro/parallel_utils.py +554 -0
- setiastro/saspro/pedestal.py +121 -0
- setiastro/saspro/perfect_palette_picker.py +1071 -0
- setiastro/saspro/pipeline.py +110 -0
- setiastro/saspro/pixelmath.py +1604 -0
- setiastro/saspro/plate_solver.py +2445 -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 +331 -0
- setiastro/saspro/remove_stars.py +1599 -0
- setiastro/saspro/remove_stars_preset.py +404 -0
- setiastro/saspro/resources.py +501 -0
- setiastro/saspro/rgb_combination.py +208 -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 +73 -0
- setiastro/saspro/selective_color.py +1552 -0
- setiastro/saspro/sfcc.py +1472 -0
- setiastro/saspro/shortcuts.py +3043 -0
- setiastro/saspro/signature_insert.py +1102 -0
- setiastro/saspro/stacking_suite.py +18470 -0
- setiastro/saspro/star_alignment.py +7435 -0
- setiastro/saspro/star_alignment_preset.py +329 -0
- setiastro/saspro/star_metrics.py +49 -0
- setiastro/saspro/star_spikes.py +765 -0
- setiastro/saspro/star_stretch.py +507 -0
- setiastro/saspro/stat_stretch.py +538 -0
- setiastro/saspro/status_log_dock.py +78 -0
- setiastro/saspro/subwindow.py +3328 -0
- setiastro/saspro/supernovaasteroidhunter.py +1719 -0
- setiastro/saspro/swap_manager.py +99 -0
- setiastro/saspro/torch_backend.py +89 -0
- setiastro/saspro/torch_rejection.py +434 -0
- setiastro/saspro/translations/all_source_strings.json +3654 -0
- setiastro/saspro/translations/ar_translations.py +3865 -0
- setiastro/saspro/translations/de_translations.py +3749 -0
- setiastro/saspro/translations/es_translations.py +3939 -0
- setiastro/saspro/translations/fr_translations.py +3858 -0
- setiastro/saspro/translations/hi_translations.py +3571 -0
- setiastro/saspro/translations/integrate_translations.py +270 -0
- setiastro/saspro/translations/it_translations.py +3678 -0
- setiastro/saspro/translations/ja_translations.py +3601 -0
- setiastro/saspro/translations/pt_translations.py +3869 -0
- setiastro/saspro/translations/ru_translations.py +2848 -0
- setiastro/saspro/translations/saspro_ar.qm +0 -0
- setiastro/saspro/translations/saspro_ar.ts +255 -0
- setiastro/saspro/translations/saspro_de.qm +0 -0
- setiastro/saspro/translations/saspro_de.ts +253 -0
- setiastro/saspro/translations/saspro_es.qm +0 -0
- setiastro/saspro/translations/saspro_es.ts +12520 -0
- setiastro/saspro/translations/saspro_fr.qm +0 -0
- setiastro/saspro/translations/saspro_fr.ts +12514 -0
- setiastro/saspro/translations/saspro_hi.qm +0 -0
- setiastro/saspro/translations/saspro_hi.ts +257 -0
- setiastro/saspro/translations/saspro_it.qm +0 -0
- setiastro/saspro/translations/saspro_it.ts +12520 -0
- setiastro/saspro/translations/saspro_ja.qm +0 -0
- setiastro/saspro/translations/saspro_ja.ts +257 -0
- setiastro/saspro/translations/saspro_pt.qm +0 -0
- setiastro/saspro/translations/saspro_pt.ts +257 -0
- setiastro/saspro/translations/saspro_ru.qm +0 -0
- setiastro/saspro/translations/saspro_ru.ts +237 -0
- setiastro/saspro/translations/saspro_sw.qm +0 -0
- setiastro/saspro/translations/saspro_sw.ts +257 -0
- setiastro/saspro/translations/saspro_uk.qm +0 -0
- setiastro/saspro/translations/saspro_uk.ts +10771 -0
- setiastro/saspro/translations/saspro_zh.qm +0 -0
- setiastro/saspro/translations/saspro_zh.ts +12520 -0
- setiastro/saspro/translations/sw_translations.py +3671 -0
- setiastro/saspro/translations/uk_translations.py +3700 -0
- setiastro/saspro/translations/zh_translations.py +3675 -0
- setiastro/saspro/versioning.py +77 -0
- setiastro/saspro/view_bundle.py +1558 -0
- setiastro/saspro/wavescale_hdr.py +645 -0
- setiastro/saspro/wavescale_hdr_preset.py +101 -0
- setiastro/saspro/wavescalede.py +680 -0
- setiastro/saspro/wavescalede_preset.py +230 -0
- setiastro/saspro/wcs_update.py +374 -0
- setiastro/saspro/whitebalance.py +492 -0
- setiastro/saspro/widgets/__init__.py +48 -0
- setiastro/saspro/widgets/common_utilities.py +306 -0
- setiastro/saspro/widgets/graphics_views.py +122 -0
- setiastro/saspro/widgets/image_utils.py +518 -0
- setiastro/saspro/widgets/minigame/game.js +986 -0
- setiastro/saspro/widgets/minigame/index.html +53 -0
- setiastro/saspro/widgets/minigame/style.css +241 -0
- setiastro/saspro/widgets/preview_dialogs.py +280 -0
- setiastro/saspro/widgets/resource_monitor.py +237 -0
- setiastro/saspro/widgets/spinboxes.py +275 -0
- setiastro/saspro/widgets/themed_buttons.py +13 -0
- setiastro/saspro/widgets/wavelet_utils.py +331 -0
- setiastro/saspro/wimi.py +7996 -0
- setiastro/saspro/wims.py +578 -0
- setiastro/saspro/window_shelf.py +185 -0
- setiastro/saspro/xisf.py +1123 -0
- setiastrosuitepro-1.6.2.post1.dist-info/METADATA +278 -0
- setiastrosuitepro-1.6.2.post1.dist-info/RECORD +367 -0
- setiastrosuitepro-1.6.2.post1.dist-info/WHEEL +4 -0
- setiastrosuitepro-1.6.2.post1.dist-info/entry_points.txt +6 -0
- setiastrosuitepro-1.6.2.post1.dist-info/licenses/LICENSE +674 -0
- setiastrosuitepro-1.6.2.post1.dist-info/licenses/license.txt +2580 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# pro/star_alignment_preset.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
import re
|
|
6
|
+
import numpy as np
|
|
7
|
+
import cv2
|
|
8
|
+
import astroalign
|
|
9
|
+
from PyQt6.QtWidgets import (
|
|
10
|
+
QDialog, QFormLayout, QDialogButtonBox, QComboBox, QLineEdit, QCheckBox,
|
|
11
|
+
QSpinBox, QPushButton, QFileDialog, QWidget, QMessageBox
|
|
12
|
+
)
|
|
13
|
+
from PyQt6.QtCore import Qt
|
|
14
|
+
|
|
15
|
+
def _norm_name(s: str) -> str:
|
|
16
|
+
if not isinstance(s, str):
|
|
17
|
+
return ""
|
|
18
|
+
s = s.strip()
|
|
19
|
+
# strip common UI decorations
|
|
20
|
+
s = re.sub(r"^[■●◆▲▪▫•◼◻◾◽]\s*", "", s)
|
|
21
|
+
s = re.sub(r"^Active View:\s*", "", s, flags=re.I)
|
|
22
|
+
# ignore extensions when users type a short name
|
|
23
|
+
base = os.path.splitext(s)[0]
|
|
24
|
+
return base.casefold()
|
|
25
|
+
|
|
26
|
+
def _iter_views_with_titles(app):
|
|
27
|
+
"""Yield (visible_title, doc, subwin). Visible title honors per-view overrides."""
|
|
28
|
+
mdi = getattr(app, "mdi", None)
|
|
29
|
+
if mdi is None:
|
|
30
|
+
return
|
|
31
|
+
for sw in mdi.subWindowList() or []:
|
|
32
|
+
try:
|
|
33
|
+
w = sw.widget()
|
|
34
|
+
doc = getattr(w, "document", None)
|
|
35
|
+
if doc is None:
|
|
36
|
+
continue
|
|
37
|
+
if hasattr(w, "_effective_title"):
|
|
38
|
+
title = str(w._effective_title())
|
|
39
|
+
else:
|
|
40
|
+
title = str(sw.windowTitle() or doc.display_name())
|
|
41
|
+
yield (title, doc, sw)
|
|
42
|
+
except Exception:
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
def _resolve_ref_doc_by_view_name(app, name: str):
|
|
46
|
+
"""Return (doc or None, debug_list)."""
|
|
47
|
+
wanted = _norm_name(name)
|
|
48
|
+
if not wanted:
|
|
49
|
+
return None, []
|
|
50
|
+
|
|
51
|
+
views = list(_iter_views_with_titles(app))
|
|
52
|
+
seen = [t for (t, _, __) in views]
|
|
53
|
+
|
|
54
|
+
# 1) exact title or exact doc name
|
|
55
|
+
for t, d, _ in views:
|
|
56
|
+
if _norm_name(t) == wanted or _norm_name(getattr(d, "display_name")()) == wanted:
|
|
57
|
+
return d, seen
|
|
58
|
+
|
|
59
|
+
# 2) prefix match
|
|
60
|
+
for t, d, _ in views:
|
|
61
|
+
if _norm_name(t).startswith(wanted) or _norm_name(getattr(d, "display_name")()).startswith(wanted):
|
|
62
|
+
return d, seen
|
|
63
|
+
|
|
64
|
+
# 3) substring
|
|
65
|
+
for t, d, _ in views:
|
|
66
|
+
if wanted in _norm_name(t) or wanted in _norm_name(getattr(d, "display_name")()):
|
|
67
|
+
return d, seen
|
|
68
|
+
|
|
69
|
+
return None, seen
|
|
70
|
+
|
|
71
|
+
# ---------------------------
|
|
72
|
+
# Preset dialog (like others)
|
|
73
|
+
# ---------------------------
|
|
74
|
+
class StarAlignmentPresetDialog(QDialog):
|
|
75
|
+
"""
|
|
76
|
+
Preset UI for headless Star Alignment:
|
|
77
|
+
ref_mode: active | view_name | file
|
|
78
|
+
ref_name: (string, used when ref_mode=view_name)
|
|
79
|
+
ref_file: (path, used when ref_mode=file)
|
|
80
|
+
overwrite: bool
|
|
81
|
+
downsample:int (>=1)
|
|
82
|
+
"""
|
|
83
|
+
def __init__(self, parent: QWidget | None = None, initial: dict | None = None):
|
|
84
|
+
super().__init__(parent)
|
|
85
|
+
self.setWindowTitle("Star Alignment — Preset")
|
|
86
|
+
init = dict(initial or {})
|
|
87
|
+
|
|
88
|
+
self.cmb_mode = QComboBox()
|
|
89
|
+
self.cmb_mode.addItems(["active", "view_name", "file"])
|
|
90
|
+
self.cmb_mode.setCurrentText(str(init.get("ref_mode", "active")).lower())
|
|
91
|
+
|
|
92
|
+
self.edt_name = QLineEdit()
|
|
93
|
+
self.edt_name.setPlaceholderText("Exact view title (case-insensitive)")
|
|
94
|
+
self.edt_name.setText(str(init.get("ref_name", "")))
|
|
95
|
+
|
|
96
|
+
self.edt_file = QLineEdit()
|
|
97
|
+
self.edt_file.setPlaceholderText("/absolute/path/to/reference.fits|fit|xisf|tif")
|
|
98
|
+
self.edt_file.setText(str(init.get("ref_file", "")))
|
|
99
|
+
self.btn_browse = QPushButton("Browse…")
|
|
100
|
+
def _pick_file():
|
|
101
|
+
p, _ = QFileDialog.getOpenFileName(self, "Choose reference file", "",
|
|
102
|
+
"Images (*.fits *.fit *.xisf *.tif *.tiff *.png *.jpg);;All Files (*)")
|
|
103
|
+
if p:
|
|
104
|
+
self.edt_file.setText(p)
|
|
105
|
+
self.btn_browse.clicked.connect(_pick_file)
|
|
106
|
+
|
|
107
|
+
self.chk_overwrite = QCheckBox("Overwrite target view")
|
|
108
|
+
self.chk_overwrite.setChecked(bool(init.get("overwrite", False)))
|
|
109
|
+
|
|
110
|
+
self.spin_down = QSpinBox()
|
|
111
|
+
self.spin_down.setRange(1, 8)
|
|
112
|
+
self.spin_down.setValue(int(init.get("downsample", 2)))
|
|
113
|
+
self.spin_down.setToolTip("Downsample factor during transform solve (speed vs precision)")
|
|
114
|
+
|
|
115
|
+
lay = QFormLayout(self)
|
|
116
|
+
lay.addRow("Reference mode:", self.cmb_mode)
|
|
117
|
+
lay.addRow("View name (if view_name):", self.edt_name)
|
|
118
|
+
rowf = QWidget(); rf = QFormLayout(rowf); rf.setContentsMargins(0,0,0,0)
|
|
119
|
+
rf.addRow(self.edt_file, self.btn_browse)
|
|
120
|
+
lay.addRow("File (if file):", rowf)
|
|
121
|
+
lay.addRow(self.chk_overwrite)
|
|
122
|
+
lay.addRow("Downsample factor:", self.spin_down)
|
|
123
|
+
|
|
124
|
+
btns = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, self)
|
|
125
|
+
btns.accepted.connect(self.accept); btns.rejected.connect(self.reject)
|
|
126
|
+
lay.addRow(btns)
|
|
127
|
+
|
|
128
|
+
# enable/disable inputs per mode
|
|
129
|
+
def _upd():
|
|
130
|
+
m = self.cmb_mode.currentText()
|
|
131
|
+
self.edt_name.setEnabled(m == "view_name")
|
|
132
|
+
self.edt_file.setEnabled(m == "file")
|
|
133
|
+
self.btn_browse.setEnabled(m == "file")
|
|
134
|
+
self.cmb_mode.currentTextChanged.connect(_upd); _upd()
|
|
135
|
+
|
|
136
|
+
def result_dict(self) -> dict:
|
|
137
|
+
return {
|
|
138
|
+
"ref_mode": self.cmb_mode.currentText(),
|
|
139
|
+
"ref_name": self.edt_name.text().strip(),
|
|
140
|
+
"ref_file": self.edt_file.text().strip(),
|
|
141
|
+
"overwrite": bool(self.chk_overwrite.isChecked()),
|
|
142
|
+
"downsample": int(self.spin_down.value()),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# ---------------------------
|
|
147
|
+
# Headless runner
|
|
148
|
+
# ---------------------------
|
|
149
|
+
def run_star_alignment_via_preset(app, preset: dict, target_doc) -> None:
|
|
150
|
+
"""
|
|
151
|
+
app: main window (has mdi, docman/doc_manager, _log)
|
|
152
|
+
target_doc: document object for the drop target
|
|
153
|
+
Behavior mirrors other *_via_preset helpers: overwrite or create new view.
|
|
154
|
+
"""
|
|
155
|
+
from setiastro.saspro.legacy.image_manager import load_image # for file ref
|
|
156
|
+
|
|
157
|
+
# ---- resolve target image ----
|
|
158
|
+
if target_doc is None or getattr(target_doc, "image", None) is None:
|
|
159
|
+
raise RuntimeError("Target view has no image.")
|
|
160
|
+
tgt = np.asarray(target_doc.image, dtype=np.float32)
|
|
161
|
+
if tgt.ndim not in (2, 3):
|
|
162
|
+
raise RuntimeError("Unsupported target image shape.")
|
|
163
|
+
|
|
164
|
+
# ---- resolve reference image per preset ----
|
|
165
|
+
pp = dict(preset or {})
|
|
166
|
+
mode = str(pp.get("ref_mode", "active")).lower()
|
|
167
|
+
|
|
168
|
+
def _doc_display_name(d):
|
|
169
|
+
try:
|
|
170
|
+
if callable(getattr(d, "display_name", None)):
|
|
171
|
+
return d.display_name() or ""
|
|
172
|
+
nm = getattr(d, "title", None)
|
|
173
|
+
return nm or ""
|
|
174
|
+
except Exception:
|
|
175
|
+
return ""
|
|
176
|
+
|
|
177
|
+
ref = None
|
|
178
|
+
ref_name = "Reference"
|
|
179
|
+
|
|
180
|
+
if mode == "file":
|
|
181
|
+
path = pp.get("ref_file") or ""
|
|
182
|
+
if not path or not os.path.exists(path):
|
|
183
|
+
raise RuntimeError("Reference file does not exist.")
|
|
184
|
+
ref, _, _, _ = load_image(path)
|
|
185
|
+
if ref is None:
|
|
186
|
+
raise RuntimeError("Failed to load reference file.")
|
|
187
|
+
ref = np.asarray(ref, dtype=np.float32)
|
|
188
|
+
ref_name = os.path.basename(path)
|
|
189
|
+
|
|
190
|
+
elif mode == "view_name":
|
|
191
|
+
# NEW: resolve against visible titles (per-view overrides) w/ normalization
|
|
192
|
+
raw_name = (pp.get("ref_name") or "").strip()
|
|
193
|
+
if not raw_name:
|
|
194
|
+
raise RuntimeError("Reference view name is empty.")
|
|
195
|
+
|
|
196
|
+
ref_doc, seen_titles = _resolve_ref_doc_by_view_name(app, raw_name)
|
|
197
|
+
if ref_doc is None or getattr(ref_doc, "image", None) is None:
|
|
198
|
+
# helpful error includes visible open view titles (no glyphs/exts)
|
|
199
|
+
opts = ", ".join(os.path.splitext(t)[0] for t in seen_titles) if seen_titles else "(no open views)"
|
|
200
|
+
raise RuntimeError(f"Reference view '{raw_name}' not found.\nOpen views: {opts}")
|
|
201
|
+
|
|
202
|
+
ref = np.asarray(ref_doc.image, dtype=np.float32)
|
|
203
|
+
|
|
204
|
+
# Prefer the visible title of the matched subwindow for the label
|
|
205
|
+
ref_name = None
|
|
206
|
+
for t, d, _ in _iter_views_with_titles(app):
|
|
207
|
+
if d is ref_doc:
|
|
208
|
+
ref_name = os.path.splitext(t)[0]
|
|
209
|
+
break
|
|
210
|
+
ref_name = ref_name or (_doc_display_name(ref_doc) or "Reference")
|
|
211
|
+
|
|
212
|
+
else: # "active" (default) → use the app's active subwindow doc
|
|
213
|
+
sw = getattr(app, "mdi", None).activeSubWindow() if getattr(app, "mdi", None) else None
|
|
214
|
+
if sw is None:
|
|
215
|
+
raise RuntimeError("No active view to use as reference.")
|
|
216
|
+
ref_doc = getattr(sw.widget(), "document", None)
|
|
217
|
+
if ref_doc is None or getattr(ref_doc, "image", None) is None:
|
|
218
|
+
raise RuntimeError("Active view has no image to use as reference.")
|
|
219
|
+
ref = np.asarray(ref_doc.image, dtype=np.float32)
|
|
220
|
+
# Prefer visible title if available
|
|
221
|
+
try:
|
|
222
|
+
for t, d, _ in _iter_views_with_titles(app):
|
|
223
|
+
if d is ref_doc:
|
|
224
|
+
ref_name = os.path.splitext(t)[0]
|
|
225
|
+
break
|
|
226
|
+
if ref_name == "Reference":
|
|
227
|
+
ref_name = _doc_display_name(ref_doc) or "Reference"
|
|
228
|
+
except Exception:
|
|
229
|
+
ref_name = _doc_display_name(ref_doc) or "Reference"
|
|
230
|
+
|
|
231
|
+
# ---- grayscale + optional downsample for solve ----
|
|
232
|
+
def _to_gray(a):
|
|
233
|
+
return np.mean(a, axis=2) if a.ndim == 3 else a
|
|
234
|
+
|
|
235
|
+
ds = int(max(1, pp.get("downsample", 2)))
|
|
236
|
+
ref_g = _to_gray(ref); tgt_g = _to_gray(tgt)
|
|
237
|
+
if ds > 1:
|
|
238
|
+
wR, hR = ref_g.shape[1] // ds, ref_g.shape[0] // ds
|
|
239
|
+
wT, hT = tgt_g.shape[1] // ds, tgt_g.shape[0] // ds
|
|
240
|
+
ref_s = cv2.resize(ref_g, (max(1, wR), max(1, hR)), interpolation=cv2.INTER_AREA)
|
|
241
|
+
tgt_s = cv2.resize(tgt_g, (max(1, wT), max(1, hT)), interpolation=cv2.INTER_AREA)
|
|
242
|
+
else:
|
|
243
|
+
ref_s, tgt_s = ref_g, tgt_g
|
|
244
|
+
|
|
245
|
+
# ---- find transform (map target → reference) with gentle backoff ----
|
|
246
|
+
tries = [
|
|
247
|
+
dict(detection_sigma=5, min_area=7, max_control_points=75),
|
|
248
|
+
dict(detection_sigma=12, min_area=9, max_control_points=75),
|
|
249
|
+
dict(detection_sigma=20, min_area=9, max_control_points=75),
|
|
250
|
+
dict(detection_sigma=30, min_area=11, max_control_points=75),
|
|
251
|
+
]
|
|
252
|
+
last = None
|
|
253
|
+
M = None
|
|
254
|
+
for kw in tries:
|
|
255
|
+
try:
|
|
256
|
+
T, _ = astroalign.find_transform(tgt_s.astype(np.float32), ref_s.astype(np.float32), **kw)
|
|
257
|
+
M = T.params[0:2, :].astype(np.float32)
|
|
258
|
+
break
|
|
259
|
+
except Exception as e:
|
|
260
|
+
last = e
|
|
261
|
+
# bump SEP pixstack if that was the issue
|
|
262
|
+
try:
|
|
263
|
+
import sep
|
|
264
|
+
if "pixel buffer full" in str(e).lower():
|
|
265
|
+
sep.set_extract_pixstack(int(sep.get_extract_pixstack() * 2))
|
|
266
|
+
except Exception:
|
|
267
|
+
pass
|
|
268
|
+
if M is None:
|
|
269
|
+
raise RuntimeError(f"Astroalign failed: {last}")
|
|
270
|
+
|
|
271
|
+
if ds > 1: # scale the translation back up
|
|
272
|
+
M = M.copy()
|
|
273
|
+
M[0, 2] *= ds
|
|
274
|
+
M[1, 2] *= ds
|
|
275
|
+
|
|
276
|
+
# ---- warp target to reference geometry ----
|
|
277
|
+
H, W = ref_g.shape[:2]
|
|
278
|
+
if tgt.ndim == 2:
|
|
279
|
+
aligned = cv2.warpAffine(
|
|
280
|
+
tgt, M, (W, H),
|
|
281
|
+
flags=cv2.INTER_LANCZOS4,
|
|
282
|
+
borderMode=cv2.BORDER_CONSTANT, borderValue=0
|
|
283
|
+
)
|
|
284
|
+
else:
|
|
285
|
+
planes = [
|
|
286
|
+
cv2.warpAffine(
|
|
287
|
+
tgt[..., i], M, (W, H),
|
|
288
|
+
flags=cv2.INTER_LANCZOS4,
|
|
289
|
+
borderMode=cv2.BORDER_CONSTANT, borderValue=0
|
|
290
|
+
) for i in range(tgt.shape[2])
|
|
291
|
+
]
|
|
292
|
+
aligned = np.stack(planes, axis=2)
|
|
293
|
+
aligned = aligned.astype(np.float32, copy=False)
|
|
294
|
+
|
|
295
|
+
# ---- write back (overwrite or new view) ----
|
|
296
|
+
overwrite = bool(pp.get("overwrite", False))
|
|
297
|
+
if overwrite:
|
|
298
|
+
if hasattr(target_doc, "set_image"):
|
|
299
|
+
target_doc.set_image(aligned, step_name=f"Star Alignment → {ref_name}")
|
|
300
|
+
elif hasattr(target_doc, "apply_numpy"):
|
|
301
|
+
target_doc.apply_numpy(aligned, step_name=f"Star Alignment → {ref_name}")
|
|
302
|
+
else:
|
|
303
|
+
target_doc.image = aligned
|
|
304
|
+
try:
|
|
305
|
+
if hasattr(target_doc, "changed"):
|
|
306
|
+
target_doc.changed.emit()
|
|
307
|
+
except Exception:
|
|
308
|
+
pass
|
|
309
|
+
else:
|
|
310
|
+
dm = getattr(app, "docman", None) or getattr(app, "doc_manager", None)
|
|
311
|
+
if dm is None:
|
|
312
|
+
raise RuntimeError("Document manager not available.")
|
|
313
|
+
# title
|
|
314
|
+
base = ""
|
|
315
|
+
try:
|
|
316
|
+
base = target_doc.display_name() if callable(getattr(target_doc, "display_name", None)) \
|
|
317
|
+
else (getattr(target_doc, "title", None) or "")
|
|
318
|
+
except Exception:
|
|
319
|
+
pass
|
|
320
|
+
base = base or "Image"
|
|
321
|
+
title = f"{base} [Aligned → {ref_name}]"
|
|
322
|
+
meta = {
|
|
323
|
+
"step_name": "Star Alignment",
|
|
324
|
+
"description": f"Aligned to {ref_name}",
|
|
325
|
+
"is_mono": bool(aligned.ndim == 2 or (aligned.ndim == 3 and aligned.shape[2] == 1)),
|
|
326
|
+
}
|
|
327
|
+
newdoc = dm.open_array(aligned, metadata=meta, title=title)
|
|
328
|
+
if hasattr(app, "_spawn_subwindow_for"):
|
|
329
|
+
app._spawn_subwindow_for(newdoc)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# pro/star_metrics.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import numpy as np
|
|
4
|
+
import sep
|
|
5
|
+
|
|
6
|
+
def measure_stars_sep(
|
|
7
|
+
img_norm_mono: np.ndarray,
|
|
8
|
+
*,
|
|
9
|
+
thresh_sigma: float = 7.0,
|
|
10
|
+
minarea: int = 16,
|
|
11
|
+
deblend_nthresh: int = 32,
|
|
12
|
+
aggregate: str = "median",
|
|
13
|
+
) -> tuple[int, float, float]:
|
|
14
|
+
"""
|
|
15
|
+
img_norm_mono: float32 [0..1] 2D array.
|
|
16
|
+
Returns (star_count, fwhm, ecc) using SEP:
|
|
17
|
+
- FWHM = 2.3548 * sqrt(a*b) (a,b are RMS ellipse axes)
|
|
18
|
+
- ecc = sqrt(1 - (b/a)^2)
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
# background & noise (consistent for both callers)
|
|
22
|
+
bkg = sep.Background(img_norm_mono)
|
|
23
|
+
back = bkg.back()
|
|
24
|
+
grms = bkg.globalrms
|
|
25
|
+
|
|
26
|
+
cat = sep.extract(
|
|
27
|
+
img_norm_mono - back,
|
|
28
|
+
thresh_sigma,
|
|
29
|
+
err=grms,
|
|
30
|
+
minarea=minarea,
|
|
31
|
+
clean=True,
|
|
32
|
+
deblend_nthresh=deblend_nthresh,
|
|
33
|
+
)
|
|
34
|
+
if cat is None or len(cat) == 0:
|
|
35
|
+
return 0, 0.0, 0.0
|
|
36
|
+
|
|
37
|
+
a = np.clip(cat["a"].astype(np.float32), 1e-3, None)
|
|
38
|
+
b = np.clip(cat["b"].astype(np.float32), 1e-3, None)
|
|
39
|
+
|
|
40
|
+
fwhm_vals = 2.3548 * np.sqrt(a * b)
|
|
41
|
+
ratios = np.clip(b / a, 0.0, 1.0)
|
|
42
|
+
ecc_vals = np.sqrt(1.0 - ratios * ratios)
|
|
43
|
+
|
|
44
|
+
agg = np.nanmedian if aggregate == "median" else np.nanmean
|
|
45
|
+
return len(cat), float(agg(fwhm_vals)), float(agg(ecc_vals))
|
|
46
|
+
|
|
47
|
+
except Exception:
|
|
48
|
+
# identical failure shape for both callers
|
|
49
|
+
return 0, 0.0, 0.0
|