A Python desktop application for characterizing narrowband astrophotography filters through quantitative image analysis. Load two calibrated images taken through different filters and generate a detailed comparison report covering PSF quality, halo artifacts, ghost images, edge sharpness, spatial frequency content, multi-scale detail preservation, and signal-to-noise ratio.
| Metric | Description | Bandwidth-independent? |
|---|---|---|
| PSF / MTF | Moffat profile fitting, empirical PSF, MTF curve and MTF50 | ✓ Yes |
| Halo analysis | Two-component radial profile fit; halo-to-core ratio | ✓ Yes |
| Ghost detection | Secondary reflection search around bright stars | ✓ Yes |
| Edge analysis (LSF) | Edge Spread Function, 10–90% edge width, Line Spread Function | ✓ Yes (width) / ⚠ (contrast ratio) |
| Power spectrum | Signal-normalised 2D FFT, mid/high spatial frequency ratio | ✓ Normalised |
| Local std maps | Local standard deviation at 3 kernel scales; contrast ratio metric | ✓ Normalised |
| Laplacian of Gaussian | Edge/detail enhancement at 3 spatial scales | ✓ Normalised |
| Wavelet decomposition | 4-level Daubechies-4 decomposition; per-level SNR; detail images | ✓ Normalised |
| Signal / Noise (SNR) | Global sky-σ SNR, median star SNR ± IQR, per-pixel SNR map, pixel percentile table | ✓ Yes |
All analysis runs on linear (unstretched) calibrated image data. Images with different filter bandwidths are handled correctly — metrics are clearly labelled as bandwidth-independent or bandwidth-sensitive, and a warning banner appears in the report when bandwidths differ.
Pre-built binaries are produced automatically by GitHub Actions on every push to main — no Python or conda required to run them.
| Platform | Artifact | How to run |
|---|---|---|
| Windows | AstroImageLab-win64.zip |
Unzip and double-click AstroImageLab.exe |
| macOS | AstroImageLab-macos.zip |
Unzip, then see the macOS note below |
| Linux | AstroImageLab-linux.zip |
Unzip and run ./AstroImageLab from a terminal |
Download the latest artifact from the Actions tab → most recent CI run → Artifacts section at the bottom of the page.
CI-built binaries are not code-signed. macOS Gatekeeper will block the app on first launch. To open it, use either of these methods:
- Right-click the binary → Open → click Open in the security dialog, or
- Run in Terminal:
xattr -dr com.apple.quarantine AstroImageLab
This is a one-time step per download. Code signing for seamless distribution requires an Apple Developer Program membership ($99/year) and is not currently configured.
Python 3.10+ (tested with Anaconda 3.12.7 / Python 3.12)
The easiest way to get a working environment is via the included environment.yml:
conda env create -f environment.yml
conda activate astrolab
python AstroImageLab.pyThis creates a conda environment named astrolab with all required packages. PyQt6 is installed via pip (not conda-forge) because the pip layout is required for PyInstaller to correctly locate Qt DLLs when building a standalone executable.
Install the scientific stack via conda-forge, then add PyQt6 and XISF support via pip:
conda install -c conda-forge numpy scipy matplotlib astropy photutils pywavelets astroalign pillow lz4 zstandard
pip install pyqt6 xisfImportant: Install PyQt6 via
pip, notconda install pyqt6. The conda-forge PyQt6 package uses a different DLL layout that conflicts with PyInstaller's hook discovery and with the pip-installed Qt runtime. Using pip for PyQt6 avoids this conflict.
git clone https://github.com/<your-username>/AstroImageLab.git
cd AstroImageLab
# Option A — recommended (creates an isolated conda environment)
conda env create -f environment.yml
conda activate astrolab
# Option B — manual install into an existing environment
conda install -c conda-forge numpy scipy matplotlib astropy photutils pywavelets astroalign pillow lz4 zstandard
pip install pyqt6 xisfconda activate astrolab
python AstroImageLab.pyRequired — two images of the same sky region:
- Image A and Image B must cover the same field of view, captured through different filters (or different filter configurations you want to compare).
- Images should be calibrated and stacked (bias/dark/flat corrected) but not stretched. Linear data is required for valid metric calculations.
- Supported formats:
.fits,.fit,.fts,.xisf.
Suggested — starless versions of each image:
- Creating starless counterparts (using tools such as Star XTerminator, StarNet++, or equivalent) and loading them alongside the original images significantly improves edge, power spectrum, and spatial detail analysis by removing the PSF contribution of stars from nebula regions.
- Load the starless images when prompted after loading the original image.
For narrowband filter comparisons — enter filter bandwidths:
- If your image headers do not contain filter bandwidth information, enter the bandwidth (in nm) manually in the field provided in each image panel (e.g., 3 nm vs 7 nm Ha filters).
- Several metrics are bandwidth-sensitive. Providing accurate bandwidths ensures the report correctly flags and contextualises these differences. A warning banner appears in the report when bandwidths differ.
Recommended — draw a cross-section line:
- Before running analysis, click Select Line… and draw a line across a region of astrophysical interest (e.g., across a nebula filament or emission edge). This enables per-pixel cross-section profile overlays in the Spatial Detail and Edge Analysis sections of the report.
- Without a cross-section line, edge analysis auto-detects the strongest gradient and spatial detail sections show maps only, with no profile overlays.
- Load images — Click Open FITS / XISF… in each panel to load Image A and Image B.
- Load starless images (optional but recommended) — Click Open Starless… in each panel to load the corresponding starless version. Used to isolate nebula structure for edge and spatial detail analysis.
- Review metadata — Telescope, camera, filter, exposure, date, and pixel scale are read from the file headers and displayed automatically. Enter the filter bandwidth (nm) manually if not present in the header.
- Draw a cross-section line (recommended) — Click Select Line… and drag a line across a region of interest. The line appears overlaid on both images. This drives cross-section profile analysis in the report.
- Select ROI (optional) — Click Select ROI… and draw a rectangle to target a specific nebula region for edge and power spectrum analysis. If no ROI is selected, the app auto-detects the strongest edge and a star-free region automatically.
- Select metrics — Check or uncheck the metrics you want to run in the control panel. Each metric can also have its figures exported as standalone PNG files using the corresponding export checkbox.
- Set output directory and format — Browse to where the report should be saved. Choose HTML (default) or PDF from the format selector. PDF requires WeasyPrint or xhtml2pdf (see Requirements).
- Run — Click Run Analysis. Images are aligned automatically using
astroalignbefore per-pixel comparisons. A progress bar and elapsed timer are shown during analysis. - Review report — The HTML report opens automatically in your default browser when analysis completes.
The report is saved to your chosen output directory as a self-contained HTML file (all plots embedded as base64 PNG) or as a PDF if a renderer is installed. HTML is the default and requires no additional packages. It contains ten sections:
- Image metadata — Side-by-side header info for both filters; bandwidth warning banner if bandwidths differ
- Observation context — Seeing warning if FWHM > 3″; notes on valid comparison conditions
- PSF / MTF — FWHM, Moffat β, ellipticity, MTF50, MTF at Nyquist; overlaid MTF curves; ePSF images
- Halo analysis — Halo-to-core ratio, halo radius; side-by-side semi-log radial profiles
- Ghost detection — Candidate table (separation, intensity ratio, classification); annotated image
- Edge analysis — 10–90% edge width in pixels and arcseconds; ESF and LSF plots; edge contrast ratio (flagged ⚠ if bandwidths differ); cross-section profile overlay if a line was drawn
- Power spectrum — Signal-normalised 2D power spectrum; radial power comparison; mid/high ratio
- Spatial detail — Local σ maps (3 scales), |LoG| maps (3 scales), wavelet detail images and SNR bar chart; cross-section profile overlays if a line was drawn
- Signal / Noise (SNR) — Global sky-σ SNR, median star SNR ± IQR, per-pixel SNR map (side-by-side, plasma colourmap), and a pixel percentile table showing what fraction of the field exceeds 3σ / 5σ / 10σ / 20σ
- Summary table — All scalar metrics side by side; better value highlighted green, worse value highlighted red
| Format | Extension | Notes |
|---|---|---|
| FITS | .fits .fit .fts |
Standard calibrated output from all major acquisition software |
| XISF | .xisf |
PixInsight native format; requires pip install xisf |
This tool is designed for on-sky images, not optical bench tests. Several important caveats apply:
- Seeing is the dominant PSF contribution on most nights. PSF/MTF comparisons between filters are most meaningful when both images were captured on the same night under similar atmospheric conditions.
- The app flags
seeing_dominated = Trueand adds a warning in the report when FWHM exceeds 3″. - Halo, ghost, edge width, and spatial detail metrics are less sensitive to seeing and are more reliably attributable to filter differences.
- Astroalign is used to register Image A onto the coordinate frame of Image B before any per-pixel comparison metrics are computed.
Filters with different bandwidths (e.g., 3 nm vs 7 nm) produce different absolute ADU levels. The app handles this systematically:
Bandwidth-independent metrics (ratio or normalised — valid as-is):
- PSF FWHM and MTF (normalised PSF shape)
- Halo-to-core ratio and ghost-to-parent ratio
- Edge 10–90% width (normalised ESF)
- Local std contrast ratio, LoG maps, wavelet SNR (all mean-signal normalised)
- Power spectrum mid/high ratio (mean-signal normalised before FFT)
- SNR metrics (all expressed as signal / noise ratios, independent of absolute flux)
Bandwidth-sensitive metrics (flagged ⚠ in the report):
- Edge contrast ratio (bright/dark side signal; affected by background level)
When filter bandwidths differ, a banner appears at the top of the report, and each sensitive metric carries an explanatory note.
AstroImageLab.py # Entry point
environment.yml # Conda environment specification
core/
models.py # Constants, AnalysisResult dataclass
astro_image.py # FITS/XISF loading, background estimation, statistical stretch
analysis/
star_catalog.py # DAOStarFinder + isolation filtering
psf_analyzer.py # Moffat fitting, ePSF builder, MTF via FFT
halo_analyzer.py # Radial profile extraction, two-component Moffat fit
ghost_detector.py # Secondary source search in annular regions
edge_analyzer.py # Sobel edge detection, ESF/LSF extraction
power_spectrum.py # Signal-normalised 2D FFT and radial average
image_filters.py # Local std maps, LoG maps, wavelet decomposition
snr_analyzer.py # Global SNR, per-star SNR, local SNR map, percentile table
report/
report_builder.py # Self-contained HTML report generator
gui/
image_panel.py # PyQt6 image display with ROI rubber-band and line selection
control_panel.py # Metric checkboxes, parameters, output directory
analysis_thread.py # QThread orchestrator; runs all engines off the main thread
main_window.py # QMainWindow; assembles panels, menu, signal wiring
| Library | Purpose |
|---|---|
| astropy | FITS I/O, Moffat2D model, Background2D |
| photutils | DAOStarFinder, EPSFBuilder, morphology |
| scipy | Optimisation, FFT, image filters |
| PyWavelets | Daubechies-4 wavelet decomposition |
| astroalign | Image registration |
| xisf | PixInsight XISF format support |
| PyQt6 | GUI framework |
| matplotlib | All plots and figures |
| WeasyPrint (optional) | High-fidelity HTML→PDF rendering |
| xhtml2pdf (optional) | Pure-Python HTML→PDF fallback |
Wavelet noise estimation uses the robust MAD estimator from Donoho & Johnstone (1994).
SNR background estimation uses photutils Background2D with MADStdBackgroundRMS.
MIT License — see LICENSE for details.
