Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,16 @@ New Features
can significantly speed up deblending, especially for large
sources. [#2409]

- Added a ``contrast_method`` keyword to ``deblend_sources`` and
``SourceFinder`` to select the flux used by the deblending contrast
criterion. The default of `None` currently resolves to ``'basin'``,
which preserves the existing behavior (the fraction is the total
flux in the source's watershed basin, with iterative removal of
below-contrast basins), and may change to ``'saddle'`` in version
4.0. The new ``'saddle'`` method instead measures the flux a source
holds above the saddle level where it separates from its neighbors.
[#2411]

- ``photutils.utils``

- Added a new ``DeblendWarning`` class, a subclass of astropy's
Expand Down
61 changes: 60 additions & 1 deletion benchmarks/bench_deblend.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,63 @@ def bench_threads(*, n_sources=4000, size=1000, n_peaks=25,
print(f'{name:>24}{cells}')


def bench_contrast_method(*, n_sources=4000, size=1000, n_peaks=100,
repeats=3, seed=0):
"""
Benchmark the basin and saddle contrast criteria.

The saddle criterion selects the markers in the component tree
and floods once, while the basin criterion floods and then
iteratively removes below-contrast basins, so the interesting
comparisons are the removal-heavy large-segment cases.

Parameters
----------
n_sources : int, optional
The total number of Gaussian sources for the many-source
scene.

size : int, optional
The image size for the large-segment scene.

n_peaks : int, optional
The number of compact peaks in the large segment.

repeats : int, optional
The number of repeats for each timing (best time is kept).

seed : int, optional
The random number generator seed.
"""
_, data, segm = make_inputs(n_sources, seed=seed)
blend_data, blend_segm = make_blended_inputs(size, n_peaks,
seed=seed)

print('\n== deblend_sources: contrast_method ==')
print(f'{"benchmark":>40}{"basin":>18}{"saddle":>18}')
cases = [
(f'{segm.n_labels} small segments, c=0.001', data, segm,
0.001),
(f'{n_peaks}-peak segment, c=0.001', blend_data, blend_segm,
0.001),
(f'{n_peaks}-peak segment, c=0.01', blend_data, blend_segm,
0.01),
(f'{n_peaks}-peak segment, c=0.03', blend_data, blend_segm,
0.03),
]
for name, case_data, case_segm, contrast in cases:
cells = []
for method in ('basin', 'saddle'):
bench = partial(deblend_sources, case_data, case_segm,
N_PIXELS, contrast=contrast,
contrast_method=method)
result = bench()
t_best = time_best(bench, repeats=repeats)
cells.append(f'{t_best:.4f}s ({result.n_labels})')
row = ''.join(f'{cell:>18}' for cell in cells)
print(f'{name:>40}{row}')


def main():
"""
Run the source deblending benchmarks.
Expand Down Expand Up @@ -563,7 +620,7 @@ def main():
'(default: %(default)s)')
parser.add_argument('--which', default='all',
choices=['all', 'many', 'large', 'peaks',
'stages', 'threads', 'profile'],
'stages', 'threads', 'criterion', 'profile'],
help='which benchmark to run '
'(default: %(default)s)')
args = parser.parse_args()
Expand All @@ -587,6 +644,8 @@ def main():
if args.which in ('all', 'threads'):
bench_threads(thread_counts=args.n_threads,
repeats=args.repeats, seed=args.seed)
if args.which in ('all', 'criterion'):
bench_contrast_method(repeats=args.repeats, seed=args.seed)
if args.which == 'profile':
bench_profile(seed=args.seed)

Expand Down
25 changes: 25 additions & 0 deletions docs/user_guide/segmentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,31 @@ and ``contrast``. ``n_levels`` is the number of multi-thresholding
levels to use. ``contrast`` is the fraction of the total source flux
that a local peak must have to be considered as a separate object.

The ``contrast_method`` keyword selects the flux to which the
``contrast`` fraction refers. With the ``'basin'`` method, it is the
total flux in the source's watershed basin, i.e., everything the
watershed assigns to the source, including its share of any surrounding
envelope. Basins below the contrast are removed iteratively and their
territory is re-flooded. With the ``'saddle'`` method, it is only
the flux the source holds above the saddle level where it separates
from its neighbors. This is the significance criterion that
`SourceExtractor`_ applies with its ``DEBLEND_MINCONT`` parameter,
although the two codes space their threshold levels differently
(SourceExtractor between the detection threshold and the peak,
photutils between the source minimum and maximum) and assign the
remaining pixels differently (SourceExtractor with a profile model,
photutils with a watershed). The saddle flux measures the significance
of the peak itself, independently of how much envelope territory the
source would inherit, which makes it stricter for faint peaks sitting
on bright envelopes. Because it excludes the flux below the saddle, the
same ``contrast`` value selects sources differently between the two
methods. The saddle method is evaluated once during marker construction
and needs only a single watershed pass, so it is never slower than
the basin method and is substantially faster when many below-contrast
basins would otherwise be removed one at a time. The default of
`None` currently resolves to ``'basin'``. The default may change to
``'saddle'`` in version 4.0.

Here's a simple example of source deblending::

>>> from photutils.segmentation import deblend_sources
Expand Down
16 changes: 16 additions & 0 deletions docs/whats_new/3.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,22 @@ concurrently, producing results identical to the single-threaded
computation. The compiled kernels release the GIL, so multithreading can
significantly speed up deblending, especially for large sources.

A new ``contrast_method`` keyword selects the flux used by the
deblending contrast criterion. The ``'basin'`` method preserves the
existing behavior where the contrast fraction is the total flux in
the source's watershed basin, and below-contrast basins are removed
iteratively. The new ``'saddle'`` method instead measures the flux
a source holds above the saddle level where it separates from its
neighbors, the significance criterion that SourceExtractor applies
with its ``DEBLEND_MINCONT`` parameter (the threshold levels and the
assignment of the remaining pixels differ between the two codes). It
measures the significance of the peak itself, independently of how much
envelope territory the source would inherit from the watershed, and
requires only a single watershed pass, so it is never slower and is
substantially faster when many below-contrast basins would otherwise
be removed iteratively. The default of `None` currently resolves to
``'basin'``. The default may change to ``'saddle'`` in version 4.0.


``GriddedPSFModel`` Performance Improvements
============================================
Expand Down
Loading
Loading