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,331 @@
|
|
|
1
|
+
# pro/widgets/wavelet_utils.py
|
|
2
|
+
"""
|
|
3
|
+
Shared wavelet utilities for à-trous decomposition and reconstruction.
|
|
4
|
+
|
|
5
|
+
This module provides centralized implementations for wavelet operations
|
|
6
|
+
used across wavescale_hdr.py, wavescalede.py, and other modules.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
# Convolution helpers (SciPy if available; otherwise a separable fallback)
|
|
14
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
from scipy.ndimage import convolve as _nd_convolve
|
|
18
|
+
from scipy.ndimage import gaussian_filter as _nd_gauss
|
|
19
|
+
_HAVE_SCIPY = True
|
|
20
|
+
except ImportError:
|
|
21
|
+
_HAVE_SCIPY = False
|
|
22
|
+
_nd_convolve = None
|
|
23
|
+
_nd_gauss = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def conv_sep_reflect(image2d: np.ndarray, k1d: np.ndarray, axis: int) -> np.ndarray:
|
|
27
|
+
"""
|
|
28
|
+
Separable 1D convolution along a given axis with reflect padding.
|
|
29
|
+
|
|
30
|
+
Uses scipy.ndimage.convolve if available, otherwise falls back to numpy.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
image2d: 2D input array
|
|
34
|
+
k1d: 1D kernel
|
|
35
|
+
axis: 0 for vertical (y), 1 for horizontal (x)
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Convolved array, same shape as input
|
|
39
|
+
"""
|
|
40
|
+
if _HAVE_SCIPY:
|
|
41
|
+
if axis == 1: # x
|
|
42
|
+
# Use 'reflect' mode to match original behavior
|
|
43
|
+
return _nd_convolve(image2d, k1d.reshape(1, -1), mode="reflect")
|
|
44
|
+
else: # y
|
|
45
|
+
return _nd_convolve(image2d, k1d.reshape(-1, 1), mode="reflect")
|
|
46
|
+
else:
|
|
47
|
+
# Optimization: Use OpenCV if available, falling back to NumPy only if strictly necessary.
|
|
48
|
+
# cv2.sepFilter2D can tackle 1D separable/separable passes easily,
|
|
49
|
+
# but here we need a specific generic 1D convolution.
|
|
50
|
+
# cv2.filter2D is generic 2D but fast.
|
|
51
|
+
try:
|
|
52
|
+
import cv2
|
|
53
|
+
# cv2.filter2D(src, ddepth, kernel, anchor, delta, borderType)
|
|
54
|
+
# kernel needs to be created correctly.
|
|
55
|
+
if axis == 1: # horizontal
|
|
56
|
+
k = k1d.reshape(1, -1)
|
|
57
|
+
else: # vertical
|
|
58
|
+
k = k1d.reshape(-1, 1)
|
|
59
|
+
|
|
60
|
+
# Using ddepth=-1 to keep input depth (float32)
|
|
61
|
+
# borderType=cv2.BORDER_REFLECT to match mode="reflect"
|
|
62
|
+
return cv2.filter2D(image2d, -1, k, borderType=cv2.BORDER_REFLECT)
|
|
63
|
+
except ImportError:
|
|
64
|
+
# Fallback numpy implementation (slow!)
|
|
65
|
+
image2d = np.asarray(image2d, dtype=np.float32)
|
|
66
|
+
k1d = np.asarray(k1d, dtype=np.float32)
|
|
67
|
+
r = len(k1d) // 2
|
|
68
|
+
if axis == 1: # horizontal
|
|
69
|
+
pad = np.pad(image2d, ((0, 0), (r, r)), mode="reflect")
|
|
70
|
+
out = np.empty_like(image2d, dtype=np.float32)
|
|
71
|
+
for i in range(image2d.shape[0]):
|
|
72
|
+
out[i] = np.convolve(pad[i], k1d, mode="valid")
|
|
73
|
+
return out
|
|
74
|
+
else: # vertical
|
|
75
|
+
pad = np.pad(image2d, ((r, r), (0, 0)), mode="reflect")
|
|
76
|
+
out = np.empty_like(image2d, dtype=np.float32)
|
|
77
|
+
for j in range(image2d.shape[1]):
|
|
78
|
+
out[:, j] = np.convolve(pad[:, j], k1d, mode="valid")
|
|
79
|
+
return out
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def gauss1d(sigma: float) -> np.ndarray:
|
|
83
|
+
"""
|
|
84
|
+
Create a 1D Gaussian kernel.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
sigma: Standard deviation of the Gaussian
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Normalized 1D Gaussian kernel
|
|
91
|
+
"""
|
|
92
|
+
if sigma <= 0:
|
|
93
|
+
return np.array([1.0], dtype=np.float32)
|
|
94
|
+
radius = max(1, int(round(3.0 * sigma)))
|
|
95
|
+
x = np.arange(-radius, radius + 1, dtype=np.float32)
|
|
96
|
+
k = np.exp(-0.5 * (x / sigma)**2)
|
|
97
|
+
k /= np.sum(k)
|
|
98
|
+
return k.astype(np.float32)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def gauss_blur(image2d: np.ndarray, sigma: float) -> np.ndarray:
|
|
102
|
+
"""
|
|
103
|
+
Apply Gaussian blur to a 2D image.
|
|
104
|
+
|
|
105
|
+
Uses scipy.ndimage.gaussian_filter if available, otherwise separable convolution.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
image2d: 2D input array
|
|
109
|
+
sigma: Standard deviation of the Gaussian
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Blurred array
|
|
113
|
+
"""
|
|
114
|
+
if _HAVE_SCIPY and _nd_gauss is not None:
|
|
115
|
+
return _nd_gauss(image2d, sigma=sigma, mode="reflect")
|
|
116
|
+
|
|
117
|
+
# Try OpenCV GaussianBlur first -> much faster
|
|
118
|
+
try:
|
|
119
|
+
import cv2
|
|
120
|
+
# kernel size: sigma*3 or auto (0)
|
|
121
|
+
# cv2 uses (ksize_w, ksize_h), ksize must be odd.
|
|
122
|
+
# To strictly match sigma, simple let cv2 compute it with ksize=(0,0)
|
|
123
|
+
# BUT 'reflect' border is not default (BORDER_DEFAULT is Reflect_101).
|
|
124
|
+
# mode="reflect" in scipy is usually dcb a | abcd | dc
|
|
125
|
+
# cv2.BORDER_REFLECT is fedcba | abcdef | gfedcb
|
|
126
|
+
# They are compatible enough for this application.
|
|
127
|
+
return cv2.GaussianBlur(image2d, (0, 0), sigmaX=sigma, sigmaY=sigma, borderType=cv2.BORDER_REFLECT)
|
|
128
|
+
except ImportError:
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
k = gauss1d(float(sigma))
|
|
132
|
+
tmp = conv_sep_reflect(image2d, k, axis=1)
|
|
133
|
+
return conv_sep_reflect(tmp, k, axis=0)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
137
|
+
# À-trous wavelet transform (B3 spline kernel)
|
|
138
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
# Standard B3-spline kernel for à-trous
|
|
141
|
+
B3_KERNEL = np.array([1, 4, 6, 4, 1], dtype=np.float32) / 16.0
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def build_spaced_kernel(kernel: np.ndarray, scale_idx: int) -> np.ndarray:
|
|
145
|
+
"""
|
|
146
|
+
Build a spaced (à-trous) kernel for a given scale.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
kernel: Base 1D kernel
|
|
150
|
+
scale_idx: Scale index (0 = no spacing, 1 = step 2, 2 = step 4, etc.)
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Spaced kernel with zeros inserted
|
|
154
|
+
"""
|
|
155
|
+
if scale_idx == 0:
|
|
156
|
+
return kernel.astype(np.float32, copy=False)
|
|
157
|
+
step = 2 ** scale_idx
|
|
158
|
+
spaced_len = len(kernel) + (len(kernel) - 1) * (step - 1)
|
|
159
|
+
spaced = np.zeros(spaced_len, dtype=np.float32)
|
|
160
|
+
spaced[0::step] = kernel
|
|
161
|
+
return spaced
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def atrous_decompose(img2d: np.ndarray, n_scales: int,
|
|
165
|
+
base_kernel: np.ndarray | None = None) -> list[np.ndarray]:
|
|
166
|
+
"""
|
|
167
|
+
Perform à-trous (undecimated) wavelet decomposition.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
img2d: 2D input image
|
|
171
|
+
n_scales: Number of detail scales to extract
|
|
172
|
+
base_kernel: Base kernel (default: B3 spline)
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
List of [detail_0, detail_1, ..., detail_n-1, residual]
|
|
176
|
+
where detail_i is the wavelet plane at scale i
|
|
177
|
+
"""
|
|
178
|
+
if base_kernel is None:
|
|
179
|
+
base_kernel = B3_KERNEL
|
|
180
|
+
|
|
181
|
+
current = img2d.astype(np.float32, copy=True)
|
|
182
|
+
planes: list[np.ndarray] = []
|
|
183
|
+
|
|
184
|
+
for s in range(n_scales):
|
|
185
|
+
k = build_spaced_kernel(base_kernel, s)
|
|
186
|
+
tmp = conv_sep_reflect(current, k, axis=1)
|
|
187
|
+
smooth = conv_sep_reflect(tmp, k, axis=0)
|
|
188
|
+
planes.append(current - smooth) # detail = current - smoothed
|
|
189
|
+
current = smooth
|
|
190
|
+
|
|
191
|
+
planes.append(current) # residual (lowest frequency)
|
|
192
|
+
return planes
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def atrous_reconstruct(planes: list[np.ndarray]) -> np.ndarray:
|
|
196
|
+
"""
|
|
197
|
+
Reconstruct image from à-trous wavelet planes.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
planes: List of [detail_0, ..., detail_n-1, residual]
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Reconstructed image
|
|
204
|
+
"""
|
|
205
|
+
out = planes[-1].astype(np.float32, copy=True) # start with residual
|
|
206
|
+
for detail in planes[:-1]:
|
|
207
|
+
out += detail
|
|
208
|
+
return out
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
212
|
+
# Color space utilities (with optional Numba acceleration)
|
|
213
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
from setiastro.saspro.legacy.numba_utils import (
|
|
217
|
+
rgb_to_xyz_numba, xyz_to_lab_numba,
|
|
218
|
+
lab_to_xyz_numba, xyz_to_rgb_numba,
|
|
219
|
+
)
|
|
220
|
+
_HAVE_NUMBA = True
|
|
221
|
+
except ImportError:
|
|
222
|
+
_HAVE_NUMBA = False
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# sRGB -> XYZ transformation matrix
|
|
226
|
+
_RGB_TO_XYZ_MATRIX = np.array([
|
|
227
|
+
[0.4124564, 0.3575761, 0.1804375],
|
|
228
|
+
[0.2126729, 0.7151522, 0.0721750],
|
|
229
|
+
[0.0193339, 0.1191920, 0.9503041]
|
|
230
|
+
], dtype=np.float32)
|
|
231
|
+
|
|
232
|
+
# XYZ -> sRGB transformation matrix (inverse)
|
|
233
|
+
_XYZ_TO_RGB_MATRIX = np.array([
|
|
234
|
+
[ 3.2404542, -1.5371385, -0.4985314],
|
|
235
|
+
[-0.9692660, 1.8760108, 0.0415560],
|
|
236
|
+
[ 0.0556434, -0.2040259, 1.0572252]
|
|
237
|
+
], dtype=np.float32)
|
|
238
|
+
|
|
239
|
+
# D65 illuminant reference white
|
|
240
|
+
_D65_WHITE = np.array([0.95047, 1.0, 1.08883], dtype=np.float32)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def rgb_to_lab(rgb: np.ndarray) -> np.ndarray:
|
|
244
|
+
"""
|
|
245
|
+
Convert sRGB image to CIE L*a*b* color space.
|
|
246
|
+
|
|
247
|
+
Uses Numba-accelerated version if available.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
rgb: RGB image (H, W, 3) float32 in [0, 1]
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Lab image (H, W, 3) where L is [0, 100], a/b are roughly [-128, 127]
|
|
254
|
+
"""
|
|
255
|
+
if _HAVE_NUMBA:
|
|
256
|
+
rgb32 = np.ascontiguousarray(rgb.astype(np.float32))
|
|
257
|
+
xyz = rgb_to_xyz_numba(rgb32)
|
|
258
|
+
lab = xyz_to_lab_numba(xyz)
|
|
259
|
+
return lab
|
|
260
|
+
|
|
261
|
+
# Numpy fallback
|
|
262
|
+
rgb = np.asarray(rgb, dtype=np.float32)
|
|
263
|
+
|
|
264
|
+
# sRGB gamma linearization
|
|
265
|
+
linear = np.where(rgb > 0.04045,
|
|
266
|
+
np.power((rgb + 0.055) / 1.055, 2.4),
|
|
267
|
+
rgb / 12.92)
|
|
268
|
+
|
|
269
|
+
# RGB -> XYZ
|
|
270
|
+
xyz = np.einsum('ij,...j->...i', _RGB_TO_XYZ_MATRIX, linear)
|
|
271
|
+
|
|
272
|
+
# XYZ -> Lab
|
|
273
|
+
xyz_n = xyz / _D65_WHITE
|
|
274
|
+
|
|
275
|
+
def f(t):
|
|
276
|
+
return np.where(t > 0.008856,
|
|
277
|
+
np.power(t, 1/3),
|
|
278
|
+
7.787 * t + 16/116)
|
|
279
|
+
|
|
280
|
+
fx, fy, fz = f(xyz_n[..., 0]), f(xyz_n[..., 1]), f(xyz_n[..., 2])
|
|
281
|
+
|
|
282
|
+
L = 116 * fy - 16
|
|
283
|
+
a = 500 * (fx - fy)
|
|
284
|
+
b = 200 * (fy - fz)
|
|
285
|
+
|
|
286
|
+
return np.stack([L, a, b], axis=-1).astype(np.float32)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def lab_to_rgb(lab: np.ndarray) -> np.ndarray:
|
|
290
|
+
"""
|
|
291
|
+
Convert CIE L*a*b* image to sRGB color space.
|
|
292
|
+
|
|
293
|
+
Uses Numba-accelerated version if available.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
lab: Lab image (H, W, 3)
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
RGB image (H, W, 3) float32 in [0, 1]
|
|
300
|
+
"""
|
|
301
|
+
if _HAVE_NUMBA:
|
|
302
|
+
lab32 = np.ascontiguousarray(lab.astype(np.float32))
|
|
303
|
+
xyz = lab_to_xyz_numba(lab32)
|
|
304
|
+
rgb = xyz_to_rgb_numba(xyz)
|
|
305
|
+
return np.clip(rgb, 0.0, 1.0)
|
|
306
|
+
|
|
307
|
+
# Numpy fallback
|
|
308
|
+
lab = np.asarray(lab, dtype=np.float32)
|
|
309
|
+
L, a, b = lab[..., 0], lab[..., 1], lab[..., 2]
|
|
310
|
+
|
|
311
|
+
# Lab -> XYZ
|
|
312
|
+
fy = (L + 16) / 116
|
|
313
|
+
fx = a / 500 + fy
|
|
314
|
+
fz = fy - b / 200
|
|
315
|
+
|
|
316
|
+
def f_inv(t):
|
|
317
|
+
return np.where(t > 0.206893,
|
|
318
|
+
np.power(t, 3),
|
|
319
|
+
(t - 16/116) / 7.787)
|
|
320
|
+
|
|
321
|
+
xyz = np.stack([f_inv(fx), f_inv(fy), f_inv(fz)], axis=-1) * _D65_WHITE
|
|
322
|
+
|
|
323
|
+
# XYZ -> linear RGB
|
|
324
|
+
linear = np.einsum('ij,...j->...i', _XYZ_TO_RGB_MATRIX, xyz)
|
|
325
|
+
|
|
326
|
+
# sRGB gamma correction
|
|
327
|
+
rgb = np.where(linear > 0.0031308,
|
|
328
|
+
1.055 * np.power(np.maximum(linear, 0), 1/2.4) - 0.055,
|
|
329
|
+
12.92 * linear)
|
|
330
|
+
|
|
331
|
+
return np.clip(rgb, 0.0, 1.0).astype(np.float32)
|