8.3.1. Peak finding#

8.3.1.1. Simulated image of galaxies and stars#

In the first part of this notebook we consider an image that includes 100 sources, all with Gaussian profiles, some of which are fairly elongated. We used this image in the previous section about removing the background prior to source detection.

As usual, we begin with some imports.

import warnings

from astropy.stats import sigma_clipped_stats, gaussian_sigma_to_fwhm
from astropy.table import QTable
from astropy.visualization import simple_norm, SqrtStretch
from astropy.visualization.mpl_normalize import ImageNormalize

import matplotlib.pyplot as plt
import numpy as np

from photutils.aperture import CircularAperture, EllipticalAperture
from photutils.centroids import centroid_2dg
from photutils.datasets import make_100gaussians_image, make_gaussian_sources_image
from photutils.detection import find_peaks, DAOStarFinder

plt.style.use('../photutils_notebook_style.mplstyle')

To begin, let’s create the image and subtract the background. Recall that this image contains a mix of star-like object and more extended sources.

data = make_100gaussians_image()
mean, med, std = sigma_clipped_stats(data, sigma=3.0, maxiters=5)
data_subtracted = data - med

8.3.1.2. Source Detection with peak finding#

For more general source detection cases that do not require comparison with models, photutils offers the find_peaks function.

This function simply finds sources by identifying local maxima above a given threshold and separated by a given distance, rather than trying to fit data to a given model. Unlike the previous detection algorithms, find_peaks does not necessarily calculate objects’ centroids. Unless the centroid_func argument is passed a function like photutils.centroids.centroid_2dg that can handle source position centroiding, find_peaks will return just the integer value of the peak pixel for each source.

This algorithm is particularly useful for identifying non-stellar sources or heavily distorted sources in image data.

The centroid-finding, which we do here only so that we can compare positions found by this method to positions found by the star-finding methods, can generate many warnings when an attempt is made to fit a 2D Gaussian to the sources. The warnings are suppressed below to avoid cluttering the output.

Let’s see how it does:

with warnings.catch_warnings(action="ignore"):
    sources_findpeaks = find_peaks(data_subtracted,
                                   threshold=5. * std, box_size=29,
                                   centroid_func=centroid_2dg)
print(sources_findpeaks)
 id x_peak y_peak     peak_value         x_centroid          y_centroid    
--- ------ ------ ------------------ ------------------ -------------------
  1    233      0 22.633606464239687 234.32551060610888 0.38896986412871687
  2    493      6 13.546963965223004  494.1272167404115   5.939571884949902
  3    208      9 17.346874844390104 207.13796151354234  10.355177520263105
  4    259     11 11.248467564978402 257.83238483501776   12.22740506322798
  5    365     11 12.637249593274138 364.55155912596456   9.224483096533689
  6    290     23  28.98909001390059 289.57041897476165   21.47837588946521
  7    379     29 10.906120071732058  380.5885171360037   26.49194761963024
  8    442     31 27.009595861523184 440.63903769793376   31.37637787846706
  9    471     37 18.989486145201486 470.44183656812066   39.73450647456632
 10    358     39 13.519122776424464 358.35138033049554  34.727672725854575
...    ...    ...                ...                ...                 ...
 62    293    245  38.69510036786478  295.1028939224394   241.8094512143153
 63    398    246 18.775975076986878  396.5029989021236  248.07414278626956
 64    446    248 13.906635070560856  446.7652008958186  247.68076682470934
 65     42    249 13.256323329550764 35.157240743452434  243.32985878928932
 66    356    252  30.00969198273592  355.1722928770967  252.69137480544242
 67     98    254 15.847143478097742  98.71781235685901   253.1060400794161
 68     74    260  47.35429493883031  77.76082097397119  262.46985847821963
 69    477    268 17.870494505057362 479.22594977370267   267.4704052654612
 70    141    275 23.127844438211586 138.14700328179325  275.01535057159293
 71    434    281 28.311057338166965  434.1035119677329   286.0635169424726
Length = 71 rows
# Set up the figure with subplots
fig, ax1 = plt.subplots(1, 1, figsize=(8, 8))

norm = ImageNormalize(stretch=SqrtStretch())
fitsplot = ax1.imshow(data, cmap='Greys', origin='lower', norm=norm,
           interpolation='nearest')
ax1.scatter(sources_findpeaks['x_peak'], sources_findpeaks['y_peak'], s=30, marker='o',
            lw=1, alpha=0.7, facecolor='None', edgecolor='r');
../../_images/a0f34d9f3b193c535c5e58cc3b0d054b78b1d1dc63a1670acd052f2e5e59170b.png

8.3.1.3. Comparing Detection Methods#

Let’s compare how peak finding compares to using DAOStarFinder on this image.

detect_fwhm = 5.0
daofind = DAOStarFinder(fwhm=detect_fwhm, threshold=5.*std) 
sources_dao = daofind(data_subtracted)
print(f'''DAOStarFinder: {len(sources_dao)} sources
find_peaks: {len(sources_findpeaks)} sources''') 
DAOStarFinder: 48 sources
find_peaks: 71 sources

Let’s plot both sets of sources on the original image.

fig, ax1 = plt.subplots(1, 1, figsize=(8, 8))

# Plot the data
fitsplot = ax1.imshow(data, norm=norm, cmap='Greys', origin='lower')

marker_size = 60
ax1.scatter(sources_findpeaks['x_peak'], sources_findpeaks['y_peak'], s=marker_size, marker='s',
            lw=1, alpha=1, facecolor='None', edgecolor='r', label='Found by find_peaks')
ax1.scatter(sources_dao['xcentroid'], sources_dao['ycentroid'], s=2 * marker_size, marker='D',
            lw=1, alpha=1, facecolor='None', edgecolor='#0077BB', label='Found by DAOfind')

# Add legend
ax1.legend(ncol=2, loc='lower center', bbox_to_anchor=(0.5, -0.35))


ax1.set_xlabel('X (pixels)')
ax1.set_ylabel('Y (pixels)')
ax1.set_title('Sources Found by Different Methods');
../../_images/7b20d5090c73a2b3359316f08ee20a4b56d67a7c2996cd40153ee72c75dd47d4.png

That peak findging works better on this image that DAOfind shouldn’t be surprising because many of the sources are not round. As we discussed in the section on source detection in a simulated image, the star-finding techniques are not a good match for an image like this with many extended sources.

8.3.1.4. Summary#

There are several more sources found by find_peaks than are found found by the star-finding algorithms, but several are still being missed. In the section on image segmentation with a simulated image we will look at a much different approach that is especially well suited to finding extended objects.