Photutils
Photutils is an Astropy package for detecting and performing photometry on astronomical sources in image data. It provides tools for source detection, background estimation, aperture and PSF photometry, and image segmentation. Photutils is actively maintained, with new minor versions typically released every 1-3 months, following Astropy's release cycle. The current stable version is 2.3.0.
Common errors
-
AttributeError: 'module' object has no attribute 'Gaussian2D'
cause Attempting to import PSF models (like Gaussian2D) from `photutils.psf` after the v2.0.0 release.fixReplace `from photutils.psf import Gaussian2D` with `from astropy.modeling.models import Gaussian2D`. -
NameError: name 'SourceProperties' is not defined
cause Using the `SourceProperties` or `SourceCatalog` classes, which were removed in `photutils` v2.0.0.fixImport and use `from photutils.segmentation import PropertiesCatalog` instead, adapting your code to its API. -
TypeError: 'astropy.units.quantity.Quantity' object cannot be interpreted as an integer
cause Passing an `astropy.units.Quantity` object (data with units) directly to a `photutils` function that expects a plain numerical array.fixExtract the numerical value from the `Quantity` using `.value` before passing it to the function (e.g., `data_quantity.value`). Ensure you handle units appropriately in subsequent calculations. -
ImportError: cannot import name 'PixelAperture' from 'photutils.aperture'
cause Attempting to import the generic `PixelAperture` class after its removal in `photutils` v2.0.0.fixFor geometrically shaped source apertures, use specific classes like `CircularAperture`, `RectangularAperture`, or `EllipticalAperture`. For pixel-based *background* apertures, use `photutils.aperture.BkgPixelAperture`.
Warnings
- breaking PSF models (e.g., `Gaussian2D`, `Moffat2D`) were removed from `photutils.psf` in v2.0.0.
- breaking The `SourceProperties` and `SourceCatalog` classes were removed in v2.0.0.
- breaking The generic `photutils.aperture.PixelAperture` class was removed in v2.0.0.
- gotcha `photutils` functions often expect plain NumPy arrays for image data, even if your data is an `astropy.units.Quantity` object.
- gotcha Star-finding algorithms like `DAOStarFinder` and `IRAFStarFinder` return `None` if no sources are found in the image data.
Install
-
pip install photutils
Imports
- DAOStarFinder
from photutils.detection import DAOStarFinder
- IRAFStarFinder
from photutils.detection import IRAFStarFinder
- Background2D
from photutils.background import Background2D
- MedianBackground
from photutils.background import MedianBackground
- detect_sources
from photutils.segmentation import detect_sources
- deblend_sources
from photutils.segmentation import deblend_sources
- CircularAperture
from photutils.aperture import CircularAperture
- aperture_photometry
from photutils.aperture import aperture_photometry
- PropertiesCatalog
from photutils.segmentation import SourceProperties
from photutils.segmentation import PropertiesCatalog
- Gaussian2D
from photutils.psf import Gaussian2D
from astropy.modeling.models import Gaussian2D
Quickstart
import numpy as np
from astropy.stats import sigma_clipped_stats
from photutils.detection import DAOStarFinder
from photutils.background import Background2D, MedianBackground
from photutils.aperture import CircularAperture, aperture_photometry
# Create dummy data (replace with your actual FITS data)
data = np.random.rand(100, 100) * 100
y, x = np.mgrid[0:100, 0:100]
data += 500 * np.exp(-((x - 20)**2 + (y - 20)**2) / (2 * 5**2)) # Add a star
data += 700 * np.exp(-((x - 70)**2 + (y - 80)**2) / (2 * 7**2)) # Add another star
# 1. Estimate background
mean_bkg, median_bkg, std_bkg = sigma_clipped_stats(data, sigma=3.0)
bkg = Background2D(data, (50, 50), filter_size=(3, 3),
bkg_estimator=MedianBackground())
data_subtracted = data - bkg.background
# 2. Detect sources
finder = DAOStarFinder(fwhm=5.0, threshold=3.0 * std_bkg)
sources = finder(data_subtracted)
if sources is None:
print("No sources found. Adjust DAOStarFinder parameters.")
else:
print(f"Detected {len(sources)} sources:")
for col in sources.colnames:
sources[col].info.format = '%.8g' # for consistent output
print(sources)
# 3. Perform aperture photometry
positions = (sources['xcentroid'], sources['ycentroid'])
aperture = CircularAperture(positions, r=8.0)
phot_table = aperture_photometry(data_subtracted, aperture)
print("\nAperture Photometry:")
for col in phot_table.colnames:
phot_table[col].info.format = '%.8g' # for consistent output
print(phot_table)