LSST Applications g00274db5b6+edbf708997,g00d0e8bbd7+edbf708997,g199a45376c+5137f08352,g1fd858c14a+1d4b6db739,g262e1987ae+f4d9505c4f,g29ae962dfc+7156fb1a53,g2cef7863aa+73c82f25e4,g35bb328faa+edbf708997,g3e17d7035e+5b3adc59f5,g3fd5ace14f+852fa6fbcb,g47891489e3+6dc8069a4c,g53246c7159+edbf708997,g64539dfbff+9f17e571f4,g67b6fd64d1+6dc8069a4c,g74acd417e5+ae494d68d9,g786e29fd12+af89c03590,g7ae74a0b1c+a25e60b391,g7aefaa3e3d+536efcc10a,g7cc15d900a+d121454f8d,g87389fa792+a4172ec7da,g89139ef638+6dc8069a4c,g8d7436a09f+28c28d8d6d,g8ea07a8fe4+db21c37724,g92c671f44c+9f17e571f4,g98df359435+b2e6376b13,g99af87f6a8+b0f4ad7b8d,gac66b60396+966efe6077,gb88ae4c679+7dec8f19df,gbaa8f7a6c5+38b34f4976,gbf99507273+edbf708997,gc24b5d6ed1+9f17e571f4,gca7fc764a6+6dc8069a4c,gcc769fe2a4+97d0256649,gd7ef33dd92+6dc8069a4c,gdab6d2f7ff+ae494d68d9,gdbb4c4dda9+9f17e571f4,ge410e46f29+6dc8069a4c,geaed405ab2+e194be0d2b,w.2025.47
LSST Data Management Base Package
Loading...
Searching...
No Matches
blend.py
Go to the documentation of this file.
1from __future__ import annotations
2
3import logging
4from dataclasses import dataclass
5from functools import cached_property
6from typing import Any
7
8import numpy as np
9from numpy.typing import DTypeLike
10
11from ..bbox import Box
12from ..blend import Blend
13from ..observation import Observation
14from .blend_base import ScarletBlendBaseData
15from .migration import PRE_SCHEMA, MigrationRegistry, migration
16from .source import ScarletSourceBaseData
17from .utils import decode_metadata, encode_metadata, extract_from_metadata
18
19__all__ = ["ScarletBlendData"]
20
21CURRENT_SCHEMA = "1.0.0"
22BLEND_TYPE = "blend"
23MigrationRegistry.set_current(BLEND_TYPE, CURRENT_SCHEMA)
24logger = logging.getLogger(__name__)
25
26
27@dataclass(kw_only=True)
29 """Data for an entire blend.
30
31 Attributes
32 ----------
33 blend_type :
34 The type of blend being stored.
35 metadata :
36 Metadata associated with the blend,
37 for example the order of bands, the PSF, etc.
38 origin :
39 The lower bound of the blend's bounding box.
40 shape :
41 The shape of the blend's bounding box.
42 sources :
43 Data for the sources contained in the blend,
44 indexed by the source id.
45 version :
46 The schema version of the stored data.
47 """
48
49 blend_type: str = BLEND_TYPE
50 origin: tuple[int, int]
51 shape: tuple[int, int]
52 sources: dict[Any, ScarletSourceBaseData]
53 version: str = CURRENT_SCHEMA
54
55 @cached_property
56 def bbox(self) -> Box:
57 """The bounding box of the blend"""
58 return Box(self.shape, origin=self.origin)
59
60 def as_dict(self) -> dict:
61 """Return the object encoded into a dict for JSON serialization
62
63 Returns
64 -------
65 result :
66 The object encoded as a JSON compatible dict
67 """
68 result: dict[str, Any] = {
69 "blend_type": self.blend_type,
70 "origin": self.origin,
71 "shape": self.shape,
72 "sources": {bid: source.as_dict() for bid, source in self.sources.items()},
73 "version": self.version,
74 }
75 if self.metadata is not None:
76 result["metadata"] = encode_metadata(self.metadata)
77 return result
78
79 @classmethod
80 def from_dict(cls, data: dict, dtype: DTypeLike = np.float32) -> ScarletBlendData:
81 """Reconstruct `ScarletBlendData` from JSON compatible
82 dict.
83
84 Parameters
85 ----------
86 data :
87 Dictionary representation of the object
88 dtype :
89 Datatype of the resulting model.
90
91 Returns
92 -------
93 result :
94 The reconstructed object
95 """
96 data = MigrationRegistry.migrate(BLEND_TYPE, data)
97 metadata = data.get("metadata", None)
98
99 return cls(
100 origin=tuple(data["origin"]), # type: ignore
101 shape=tuple(data["shape"]), # type: ignore
102 sources={
103 bid: ScarletSourceBaseData.from_dict(source, dtype=dtype)
104 for bid, source in data["sources"].items()
105 },
106 metadata=decode_metadata(metadata),
107 )
108
110 self,
111 model_psf: np.ndarray | None = None,
112 psf: np.ndarray | None = None,
113 bands: tuple[str] | None = None,
114 dtype: DTypeLike = np.float32,
115 ) -> Blend:
116 """Convert the storage data model into a scarlet lite blend
117
118 Parameters
119 ----------
120 model_psf :
121 PSF in model space (usually a nyquist sampled circular Gaussian).
122 psf :
123 The PSF of the observation.
124 If not provided, the PSF stored in the blend data is used.
125 bands :
126 The bands in the blend model.
127 If not provided, the bands stored in the blend data are used.
128 dtype :
129 The data type of the model that is generated.
130
131 Returns
132 -------
133 blend :
134 A scarlet blend model extracted from persisted data.
135 """
136
137 _model_psf: np.ndarray = extract_from_metadata(model_psf, self.metadata, "model_psf")
138 _psf: np.ndarray = extract_from_metadata(psf, self.metadata, "psf")
139 _bands: tuple[str] = extract_from_metadata(bands, self.metadata, "bands")
140 model_box = self.bbox
141 observation = Observation.empty(
142 bands=_bands,
143 psfs=_psf,
144 model_psf=_model_psf,
145 bbox=model_box,
146 dtype=dtype,
147 )
148 return self.to_blend(observation)
149
150 def to_blend(self, observation: Observation) -> Blend:
151 """Convert the storage data model into a scarlet lite blend
152
153 Parameters
154 ----------
155 observation :
156 The observation that contains the blend.
157 If `observation` is ``None`` then an `Observation` containing
158 no image data is initialized.
159
160 Returns
161 -------
162 blend :
163 A scarlet blend model extracted from persisted data.
164 """
165 sources = []
166 for source_data in self.sources.values():
167 source = source_data.to_source(observation)
168 sources.append(source)
169
170 return Blend(sources=sources, observation=observation, metadata=self.metadata)
171
172 @staticmethod
173 def from_blend(blend: Blend) -> ScarletBlendData:
174 """Deprecated: Convert a scarlet lite blend into a storage data model.
175
176 Parameters
177 ----------
178 blend :
179 The blend to convert.
180 Returns
181 -------
182 result :
183 The storage data model representing the blend.
184 """
185 logger.warning("ScarletBlendData.from_blend is deprecated. Use blend.to_data() instead.")
186 return blend.to_data()
187
188
189ScarletBlendData.register()
190
191
192@migration(BLEND_TYPE, PRE_SCHEMA)
193def _to_1_0_0(data: dict) -> dict:
194 """Migrate a pre-schema blend to schema version 1.0.0
195
196 Parameters
197 ----------
198 data :
199 The data to migrate.
200
201 Returns
202 -------
203 result :
204 The migrated data.
205 """
206 # Support legacy models before metadata was used
207 if "metadata" not in data and "psf" in data:
208 data["metadata"] = {
209 "psf": data["psf"],
210 "psf_shape": data["psf_shape"],
211 "bands": tuple(data["bands"]),
212 "array" "_keys": ["psf"],
213 }
214 data["version"] = "1.0.0"
215 return data
ScarletBlendData from_blend(Blend blend)
Definition blend.py:173
Blend minimal_data_to_blend(self, np.ndarray|None model_psf=None, np.ndarray|None psf=None, tuple[str]|None bands=None, DTypeLike dtype=np.float32)
Definition blend.py:115
ScarletBlendData from_dict(cls, dict data, DTypeLike dtype=np.float32)
Definition blend.py:80
Blend to_blend(self, Observation observation)
Definition blend.py:150
dict _to_1_0_0(dict data)
Definition blend.py:193