Coverage for python / lsst / meas / extensions / scarlet / source.py: 42%
39 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-12 01:32 -0800
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-12 01:32 -0800
1# This file is part of meas_extensions_scarlet.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22from __future__ import annotations
24from typing import TYPE_CHECKING
26import numpy as np
27from numpy.typing import DTypeLike
29from lsst.afw.detection import Footprint
30from lsst.afw.image import MultibandExposure
31import lsst.scarlet.lite as scl
33if TYPE_CHECKING:
34 from .io import IsolatedSourceData
37__all__ = ["IsolatedSource"]
40class IsolatedSource(scl.source.SourceBase):
41 """A scarlet source representation for isolated sources.
42 """
43 def __init__(self, model: scl.Image, peak: tuple[int, int], metadata: dict | None = None):
44 """Construct an IsolatedSource.
46 Parameters
47 ----------
48 model :
49 The 3D (band, y, x) model of the source.
50 center :
51 The (y, x) coordinates of the peak pixel within the model.
52 metadata :
53 Optional metadata to store with the source.
54 """
55 self.model = model
56 self.peak = peak
57 self.metadata = metadata
59 @staticmethod
60 def from_footprint(
61 footprint: Footprint,
62 mCoadd: MultibandExposure,
63 dtype: DTypeLike,
64 metadata: dict | None = None,
65 ) -> IsolatedSource:
66 """Create an IsolatedSource from a footprint in a multiband coadd.
68 Parameters
69 ----------
70 footprint :
71 The footprint of the source in the multiband coadd.
72 mCoadd :
73 The multiband coadd containing the source.
74 dtype :
75 The desired data type for the source model.
76 metadata :
77 Optional metadata to store with the source.
79 Returns
80 -------
81 source :
82 The isolated source represented as a ScarletSource.
83 """
84 if len(footprint.peaks) != 1:
85 raise ValueError(
86 "Footprint must have exactly one peak to create an IsolatedSource, "
87 f"found {len(footprint.peaks)}"
88 )
89 peak = (footprint.peaks[0].getIy(), footprint.peaks[0].getIx())
90 bbox = footprint.getBBox()
91 x0, y0 = bbox.getMin()
92 width, height = bbox.getDimensions()
93 # Convert the footprint into a boolean array
94 footprint_array = footprint.spans.asArray((height, width), (x0, y0))
95 # Create the 3D model array by multiplying the footprint by each band
96 # of the multiband coadd.
97 model_array = np.ndarray((len(mCoadd.bands), height, width), dtype=dtype)
98 for bidx, band in enumerate(mCoadd.bands):
99 model_array[bidx] = mCoadd[band, bbox].image.array * footprint_array
100 # Create the model
101 model = scl.Image(model_array, bands=mCoadd.bands, yx0=(y0, x0))
102 return IsolatedSource(model=model, peak=peak, metadata=metadata)
104 @property
105 def bbox(self) -> scl.Box:
106 """The bounding box of the source in the full Blend."""
107 return self.model.bbox
109 @property
110 def bands(self) -> list[str]:
111 """The ordered list of bands in the full source model."""
112 return self.model.bands
114 def get_model(self) -> scl.Image:
115 """Get the full 3D (band, y, x) model of the source.
117 Returns
118 -------
119 model :
120 The 3D (band, y, x) model of the source.
121 """
122 return self.model
124 def to_data(self) -> IsolatedSourceData:
125 """Convert to a ScarletSourceData representation.
127 Returns
128 -------
129 source_data :
130 The source represented as a ScarletSourceData.
131 """
132 from .io import IsolatedSourceData
134 span_array = np.any(self.model.data != 0, axis=0)
135 return IsolatedSourceData(
136 span_array=span_array,
137 origin=self.bbox.origin,
138 peak=self.peak,
139 )