Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added flag to BasicPSFPhotometry to preserve ID order #746

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ Bug Fixes
API changes
^^^^^^^^^^^

-
- ``photutils.psf``

- Added ``preserve_id_order`` as input parameter to ``BasicPSFPhotometry``,
allowing for the return of source table in ``id`` rather than ``group_id``
order. [#746]

Other Changes and Additions
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
12 changes: 11 additions & 1 deletion photutils/psf/photometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ class BasicPSFPhotometry:
The radius (in units of pixels) used to compute initial
estimates for the fluxes of sources. If ``None``, one FWHM will
be used if it can be determined from the ``psf_model``.
preserve_id_order : bool, optional
Flag indicating whether to present final output table in ascending
ID order. Default is ``False``, and thus sources may be presented
in differing order to any input as ``init_guesses``, with output
being arranged by ``group_id``.

Notes
-----
Expand All @@ -113,7 +118,8 @@ class BasicPSFPhotometry:
"""

def __init__(self, group_maker, bkg_estimator, psf_model, fitshape,
finder=None, fitter=LevMarLSQFitter(), aperture_radius=None):
finder=None, fitter=LevMarLSQFitter(), aperture_radius=None,
preserve_id_order=False):
self.group_maker = group_maker
self.bkg_estimator = bkg_estimator
self.psf_model = psf_model
Expand All @@ -124,6 +130,7 @@ def __init__(self, group_maker, bkg_estimator, psf_model, fitshape,
self._pars_to_set = None
self._pars_to_output = None
self._residual_image = None
self._preserve_id_order = preserve_id_order

@property
def fitshape(self):
Expand Down Expand Up @@ -292,6 +299,9 @@ def do_photometry(self, image, init_guesses=None):
star_groups = star_groups.group_by('group_id')
output_tab = hstack([star_groups, output_tab])

if self._preserve_id_order:
output_tab = output_tab.group_by('id')

return output_tab

def nstar(self, image, star_groups):
Expand Down
51 changes: 51 additions & 0 deletions photutils/psf/tests/test_photometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ def make_psf_photometry_objs(std=1, sigma_psf=1):
sources3['group_id'] = [1] * 2
sources3['iter_detected'] = [1, 2]

sigma_psfs.append(2)
sources4 = Table()
sources4['flux'] = [10000, 5000, 3000]
sources4['x_mean'] = [20, 8, 13]
sources4['y_mean'] = [9, 19, 21]
sources4['x_stddev'] = [sigma_psfs[-1]] * len(sources4['x_mean'])
sources4['y_stddev'] = [sigma_psfs[-1]] * len(sources4['y_mean'])


@pytest.mark.skipif('not HAS_SCIPY')
@pytest.mark.parametrize("sigma_psf, sources", [(sigma_psfs[2], sources3)])
Expand Down Expand Up @@ -518,6 +526,49 @@ def test_psf_boundary_gaussian():
assert_allclose(f['flux_fit'], 0, atol=1e-8)


@pytest.mark.xfail('not HAS_SCIPY')
@pytest.mark.parametrize("sigma_psf, sources", [(sigma_psfs[0], sources4)])
def test_psf_preserve_order(sigma_psf, sources):
"""
Test the order preservation of BasicPSFPhotometry.
"""

psf_model = IntegratedGaussianPRF(sigma=sigma_psf)

img_shape = (32, 32)
# generate image with read-out noise (Gaussian) and
# background noise (Poisson)
image = (make_gaussian_prf_sources_image(img_shape, sources) +
make_noise_image(img_shape, type='poisson', mean=6.,
random_state=1) +
make_noise_image(img_shape, type='gaussian', mean=0.,
stddev=2., random_state=1))

psf_model.sigma.fixed = False
mmm_bkg = MMMBackground()
daogroup = DAOGroup(crit_separation=8)
basic_phot = BasicPSFPhotometry(group_maker=daogroup, bkg_estimator=mmm_bkg,
psf_model=psf_model, fitshape=(11, 11))
phot_results = basic_phot(image, init_guesses=Table(names=['x_0', 'y_0', 'flux_0'],
data=[[7.8, 20.5, 13.1],
[19.4, 8.8, 21.4],
[4500, 9000, 2950]]))
# If preserver ID order is off -- by default -- we get group as [1, 1, 2]
assert np.all(phot_results['group_id'] == [1, 1, 2])
assert np.all(phot_results['id'] == [1, 3, 2])

basic_phot = BasicPSFPhotometry(group_maker=daogroup, bkg_estimator=mmm_bkg,
psf_model=psf_model, fitshape=(11, 11),
preserve_id_order=True)
phot_results = basic_phot(image, init_guesses=Table(names=['x_0', 'y_0', 'flux_0'],
data=[[7.8, 20.5, 13.1],
[19.4, 8.8, 21.4],
[4500, 9000, 2950]]))

assert np.all(phot_results['group_id'] == [1, 2, 1])
assert np.all(phot_results['id'] == [1, 2, 3])


@pytest.mark.skipif('not HAS_SCIPY')
def test_psf_photometry_gaussian():
"""
Expand Down