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, and multi-scale detail preservation.
| 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 |
All analysis runs on linear (unstretched) calibrated image data. Display uses an STF-equivalent statistical stretch (SETIAstroSuite MTF method). 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.
(Add a screenshot here after first launch)
Python 3.10+ (tested with Anaconda 3.12.7)
conda install -c conda-forge pyqt6 astropy photutils scipy numpy matplotlib astroalign pillow pywaveletspip install xisfxisf provides support for PixInsight's native .xisf format. All other dependencies are available via conda-forge.
git clone https://github.com/<your-username>/AtroFilterImageComparison.git
cd AtroFilterImageComparison
# Install dependencies (see Requirements above)
conda install -c conda-forge pyqt6 astropy photutils scipy numpy matplotlib astroalign pillow pywavelets
pip install xisfcd AtroFilterImageComparison
python FilterImageCompare.py- Load images — Click Open FITS / XISF… in each panel to load Image A and Image B. Supported formats:
.fits,.fit,.fts,.xisf. - 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.
- Select metrics — Check or uncheck the metrics you want to run in the control panel.
- Select ROI (optional) — Click Select ROI… and draw a rectangle on either image 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.
- Set output directory — Browse to where the HTML report should be saved.
- Run — Click Run Analysis. Images are aligned automatically using
astroalignbefore per-pixel comparisons. Progress is shown in the status bar. - Review report — The HTML report opens automatically in your default browser when analysis completes.
The report is a self-contained HTML file (all plots embedded as base64 PNG) saved to your chosen output directory. It contains nine 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)
- 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
- 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 |
Images should be calibrated and stacked (bias/dark/flat corrected) but not stretched. Linear data is required for valid metric calculations.
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)
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.
FilterImageCompare.py # Entry point
requirements.txt
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
report/
report_builder.py # Self-contained HTML report generator
gui/
image_panel.py # PyQt5 image display with ROI rubber-band 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 |
Statistical stretch algorithm adapted from SETIAstroSuite (MTF-based autostretch, equivalent to PixInsight STF).
Wavelet noise estimation uses the robust MAD estimator from Donoho & Johnstone (1994).
MIT License — see LICENSE for details.