LSST Applications g180d380827+0f66a164bb,g2079a07aa2+86d27d4dc4,g2305ad1205+7d304bc7a0,g29320951ab+500695df56,g2bbee38e9b+0e5473021a,g337abbeb29+0e5473021a,g33d1c0ed96+0e5473021a,g3a166c0a6a+0e5473021a,g3ddfee87b4+e42ea45bea,g48712c4677+36a86eeaa5,g487adcacf7+2dd8f347ac,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+c70619cc9d,g5a732f18d5+53520f316c,g5ea96fc03c+341ea1ce94,g64a986408d+f7cd9c7162,g858d7b2824+f7cd9c7162,g8a8a8dda67+585e252eca,g99cad8db69+469ab8c039,g9ddcbc5298+9a081db1e4,ga1e77700b3+15fc3df1f7,gb0e22166c9+60f28cb32d,gba4ed39666+c2a2e4ac27,gbb8dafda3b+c92fc63c7e,gbd866b1f37+f7cd9c7162,gc120e1dc64+02c66aa596,gc28159a63d+0e5473021a,gc3e9b769f7+b0068a2d9f,gcf0d15dbbd+e42ea45bea,gdaeeff99f8+f9a426f77a,ge6526c86ff+84383d05b3,ge79ae78c31+0e5473021a,gee10cc3b42+585e252eca,gff1a9f87cc+f7cd9c7162,w.2024.17
LSST Data Management Base Package
Loading...
Searching...
No Matches
brightStarStamps.py
Go to the documentation of this file.
1# This file is part of meas_algorithms.
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/>.
21
22"""Collection of small images (stamps), each centered on a bright star."""
23
24__all__ = ["BrightStarStamp", "BrightStarStamps"]
25
26import logging
27from collections.abc import Collection
28from dataclasses import dataclass
29from functools import reduce
30from operator import ior
31
32import numpy as np
33from lsst.afw.geom import SpanSet, Stencil
34from lsst.afw.image import MaskedImageF
35from lsst.afw.math import Property, StatisticsControl, makeStatistics, stringToStatisticsProperty
36from lsst.afw.table.io import Persistable
37from lsst.geom import Point2I
38
39from .stamps import AbstractStamp, Stamps, readFitsWithOptions
40
41logger = logging.getLogger(__name__)
42
43
44@dataclass
46 """Single stamp centered on a bright star, normalized by its annularFlux.
47
48 Parameters
49 ----------
50 stamp_im : `~lsst.afw.image.MaskedImage`
51 Pixel data for this postage stamp
52 gaiaGMag : `float`
53 Gaia G magnitude for the object in this stamp
54 gaiaId : `int`
55 Gaia object identifier
56 position : `~lsst.geom.Point2I`
57 Origin of the stamps in its origin exposure (pixels)
58 archive_element : `~lsst.afw.table.io.Persistable` or None, optional
59 Archive element (e.g. Transform or WCS) associated with this stamp.
60 annularFlux : `float` or None, optional
61 Flux in an annulus around the object
62 """
63
64 stamp_im: MaskedImageF
65 gaiaGMag: float
66 gaiaId: int
67 position: Point2I
68 archive_element: Persistable | None = None
69 annularFlux: float | None = None
70 minValidAnnulusFraction: float = 0.0
71 validAnnulusFraction: float | None = None
72 optimalInnerRadius: int | None = None
73 optimalOuterRadius: int | None = None
74
75 @classmethod
76 def factory(cls, stamp_im, metadata, idx, archive_element=None, minValidAnnulusFraction=0.0):
77 """This method is needed to service the FITS reader. We need a standard
78 interface to construct objects like this. Parameters needed to
79 construct this object are passed in via a metadata dictionary and then
80 passed to the constructor of this class. This particular factory
81 method requires keys: G_MAGS, GAIA_IDS, and ANNULAR_FLUXES. They should
82 each point to lists of values.
83
84 Parameters
85 ----------
86 stamp_im : `~lsst.afw.image.MaskedImage`
87 Pixel data to pass to the constructor
88 metadata : `dict`
89 Dictionary containing the information
90 needed by the constructor.
91 idx : `int`
92 Index into the lists in ``metadata``
93 archive_element : `~lsst.afw.table.io.Persistable` or None, optional
94 Archive element (e.g. Transform or WCS) associated with this stamp.
95 minValidAnnulusFraction : `float`, optional
96 The fraction of valid pixels within the normalization annulus of a
97 star.
98
99 Returns
100 -------
101 brightstarstamp : `BrightStarStamp`
102 An instance of this class
103 """
104 if "X0S" in metadata and "Y0S" in metadata:
105 x0 = metadata.getArray("X0S")[idx]
106 y0 = metadata.getArray("Y0S")[idx]
107 position = Point2I(x0, y0)
108 else:
109 position = None
110 return cls(
111 stamp_im=stamp_im,
112 gaiaGMag=metadata.getArray("G_MAGS")[idx],
113 gaiaId=metadata.getArray("GAIA_IDS")[idx],
114 position=position,
115 archive_element=archive_element,
116 annularFlux=metadata.getArray("ANNULAR_FLUXES")[idx],
117 minValidAnnulusFraction=minValidAnnulusFraction,
118 validAnnulusFraction=metadata.getArray("VALID_PIXELS_FRACTION")[idx],
119 )
120
122 self,
123 annulus: SpanSet,
124 statsControl: StatisticsControl = StatisticsControl(),
125 statsFlag: Property = stringToStatisticsProperty("MEAN"),
126 badMaskPlanes: Collection[str] = ("BAD", "SAT", "NO_DATA"),
127 ):
128 """Compute "annularFlux", the integrated flux within an annulus
129 around an object's center, and normalize it.
130
131 Since the center of bright stars are saturated and/or heavily affected
132 by ghosts, we measure their flux in an annulus with a large enough
133 inner radius to avoid the most severe ghosts and contain enough
134 non-saturated pixels.
135
136 Parameters
137 ----------
138 annulus : `~lsst.afw.geom.spanSet.SpanSet`
139 SpanSet containing the annulus to use for normalization.
140 statsControl : `~lsst.afw.math.statistics.StatisticsControl`, optional
141 StatisticsControl to be used when computing flux over all pixels
142 within the annulus.
143 statsFlag : `~lsst.afw.math.statistics.Property`, optional
144 statsFlag to be passed on to ``afwMath.makeStatistics`` to compute
145 annularFlux. Defaults to a simple MEAN.
146 badMaskPlanes : `collections.abc.Collection` [`str`]
147 Collection of mask planes to ignore when computing annularFlux.
148 """
149 stampSize = self.stamp_imstamp_im.getDimensions()
150 # Create image: science pixel values within annulus, NO_DATA elsewhere
151 maskPlaneDict = self.stamp_imstamp_im.mask.getMaskPlaneDict()
152 annulusImage = MaskedImageF(stampSize, planeDict=maskPlaneDict)
153 annulusMask = annulusImage.mask
154 annulusMask.array[:] = 2 ** maskPlaneDict["NO_DATA"]
155 annulus.copyMaskedImage(self.stamp_imstamp_im, annulusImage)
156 # Set mask planes to be ignored.
157 andMask = reduce(ior, (annulusMask.getPlaneBitMask(bm) for bm in badMaskPlanes))
158 statsControl.setAndMask(andMask)
159
160 annulusStat = makeStatistics(annulusImage, statsFlag, statsControl)
161 # Determine the number of valid (unmasked) pixels within the annulus.
162 unMasked = annulusMask.array.size - np.count_nonzero(annulusMask.array)
163 self.validAnnulusFractionvalidAnnulusFraction = unMasked / annulus.getArea()
164 logger.info(
165 "The Star's annulus contains %s valid pixels and the annulus itself contains %s pixels.",
166 unMasked,
167 annulus.getArea(),
168 )
169 if unMasked > (annulus.getArea() * self.minValidAnnulusFractionminValidAnnulusFraction):
170 # Compute annularFlux.
171 self.annularFluxannularFlux = annulusStat.getValue()
172 logger.info("Annular flux is: %s", self.annularFluxannularFlux)
173 else:
174 raise RuntimeError(
175 f"Less than {self.minValidAnnulusFraction * 100}% of pixels within the annulus are valid."
176 )
177 if np.isnan(self.annularFluxannularFlux):
178 raise RuntimeError("Annular flux computation failed, likely because there are no valid pixels.")
179 if self.annularFluxannularFlux < 0:
180 raise RuntimeError("The annular flux is negative. The stamp can not be normalized!")
181 # Normalize stamps.
182 self.stamp_imstamp_im.image.array /= self.annularFluxannularFlux
183 return None
184
185
187 """Collection of bright star stamps and associated metadata.
188
189 Parameters
190 ----------
191 starStamps : `collections.abc.Sequence` [`BrightStarStamp`]
192 Sequence of star stamps. Cannot contain both normalized and
193 unnormalized stamps.
194 innerRadius : `int`, optional
195 Inner radius value, in pixels. This and ``outerRadius`` define the
196 annulus used to compute the ``"annularFlux"`` values within each
197 ``starStamp``. Must be provided if ``normalize`` is True.
198 outerRadius : `int`, optional
199 Outer radius value, in pixels. This and ``innerRadius`` define the
200 annulus used to compute the ``"annularFlux"`` values within each
201 ``starStamp``. Must be provided if ``normalize`` is True.
202 nb90Rots : `int`, optional
203 Number of 90 degree rotations required to compensate for detector
204 orientation.
205 metadata : `~lsst.daf.base.PropertyList`, optional
206 Metadata associated with the bright stars.
207 use_mask : `bool`
208 If `True` read and write mask data. Default `True`.
209 use_variance : `bool`
210 If ``True`` read and write variance data. Default ``False``.
211 use_archive : `bool`
212 If ``True`` read and write an Archive that contains a Persistable
213 associated with each stamp. In the case of bright stars, this is
214 usually a ``TransformPoint2ToPoint2``, used to warp each stamp
215 to the same pixel grid before stacking.
216
217 Raises
218 ------
219 ValueError
220 Raised if one of the star stamps provided does not contain the
221 required keys.
222 AttributeError
223 Raised if there is a mix-and-match of normalized and unnormalized
224 stamps, stamps normalized with different annulus definitions, or if
225 stamps are to be normalized but annular radii were not provided.
226
227 Notes
228 -----
229 A butler can be used to read only a part of the stamps, specified by a
230 bbox:
231
232 >>> starSubregions = butler.get(
233 "brightStarStamps",
234 dataId,
235 parameters={"bbox": bbox}
236 )
237 """
238
240 self,
241 starStamps,
242 innerRadius=None,
243 outerRadius=None,
244 nb90Rots=None,
245 metadata=None,
246 use_mask=True,
247 use_variance=False,
248 use_archive=False,
249 ):
250 super().__init__(starStamps, metadata, use_mask, use_variance, use_archive)
251 # Ensure stamps contain a flux measure if expected to be normalized.
252 self._checkNormalization(False, innerRadius, outerRadius)
253 self._innerRadius, self._outerRadius = innerRadius, outerRadius
254 if innerRadius is not None and outerRadius is not None:
255 self.normalized = True
256 else:
257 self.normalized = False
258 self.nb90Rots = nb90Rots
259
260 @classmethod
262 cls,
263 starStamps,
264 innerRadius,
265 outerRadius,
266 nb90Rots=None,
267 metadata=None,
268 use_mask=True,
269 use_variance=False,
270 use_archive=False,
271 imCenter=None,
272 discardNanFluxObjects=True,
273 forceFindFlux=False,
274 statsControl=StatisticsControl(),
275 statsFlag=stringToStatisticsProperty("MEAN"),
276 badMaskPlanes=("BAD", "SAT", "NO_DATA"),
277 ):
278 """Normalize a set of bright star stamps and initialize a
279 BrightStarStamps instance.
280
281 Since the center of bright stars are saturated and/or heavily affected
282 by ghosts, we measure their flux in an annulus with a large enough
283 inner radius to avoid the most severe ghosts and contain enough
284 non-saturated pixels.
285
286 Parameters
287 ----------
288 starStamps : `collections.abc.Sequence` [`BrightStarStamp`]
289 Sequence of star stamps. Cannot contain both normalized and
290 unnormalized stamps.
291 innerRadius : `int`
292 Inner radius value, in pixels. This and ``outerRadius`` define the
293 annulus used to compute the ``"annularFlux"`` values within each
294 ``starStamp``.
295 outerRadius : `int`
296 Outer radius value, in pixels. This and ``innerRadius`` define the
297 annulus used to compute the ``"annularFlux"`` values within each
298 ``starStamp``.
299 nb90Rots : `int`, optional
300 Number of 90 degree rotations required to compensate for detector
301 orientation.
302 metadata : `~lsst.daf.base.PropertyList`, optional
303 Metadata associated with the bright stars.
304 use_mask : `bool`
305 If `True` read and write mask data. Default `True`.
306 use_variance : `bool`
307 If ``True`` read and write variance data. Default ``False``.
308 use_archive : `bool`
309 If ``True`` read and write an Archive that contains a Persistable
310 associated with each stamp. In the case of bright stars, this is
311 usually a ``TransformPoint2ToPoint2``, used to warp each stamp
312 to the same pixel grid before stacking.
313 imCenter : `collections.abc.Sequence`, optional
314 Center of the object, in pixels. If not provided, the center of the
315 first stamp's pixel grid will be used.
316 discardNanFluxObjects : `bool`
317 Whether objects with NaN annular flux should be discarded.
318 If False, these objects will not be normalized.
319 forceFindFlux : `bool`
320 Whether to try to find the flux of objects with NaN annular flux
321 at a different annulus.
322 statsControl : `~lsst.afw.math.statistics.StatisticsControl`, optional
323 StatisticsControl to be used when computing flux over all pixels
324 within the annulus.
325 statsFlag : `~lsst.afw.math.statistics.Property`, optional
326 statsFlag to be passed on to ``~lsst.afw.math.makeStatistics`` to
327 compute annularFlux. Defaults to a simple MEAN.
328 badMaskPlanes : `collections.abc.Collection` [`str`]
329 Collection of mask planes to ignore when computing annularFlux.
330
331 Raises
332 ------
333 ValueError
334 Raised if one of the star stamps provided does not contain the
335 required keys.
336 AttributeError
337 Raised if there is a mix-and-match of normalized and unnormalized
338 stamps, stamps normalized with different annulus definitions, or if
339 stamps are to be normalized but annular radii were not provided.
340 """
341 stampSize = starStamps[0].stamp_im.getDimensions()
342 if imCenter is None:
343 imCenter = stampSize[0] // 2, stampSize[1] // 2
344
345 # Create SpanSet of annulus.
346 outerCircle = SpanSet.fromShape(outerRadius, Stencil.CIRCLE, offset=imCenter)
347 innerCircle = SpanSet.fromShape(innerRadius, Stencil.CIRCLE, offset=imCenter)
348 annulusWidth = outerRadius - innerRadius
349 if annulusWidth < 1:
350 raise ValueError("The annulus width must be greater than 1 pixel.")
351 annulus = outerCircle.intersectNot(innerCircle)
352
353 # Initialize (unnormalized) brightStarStamps instance.
354 bss = cls(
355 starStamps,
356 innerRadius=None,
357 outerRadius=None,
358 nb90Rots=nb90Rots,
359 metadata=metadata,
360 use_mask=use_mask,
361 use_variance=use_variance,
362 use_archive=use_archive,
363 )
364
365 # Ensure that no stamps have already been normalized.
366 bss._checkNormalization(True, innerRadius, outerRadius)
367 bss._innerRadius, bss._outerRadius = innerRadius, outerRadius
368
369 # Apply normalization.
370 rejects = []
371 badStamps = []
372 for stamp in bss._stamps:
373 try:
374 stamp.measureAndNormalize(
375 annulus, statsControl=statsControl, statsFlag=statsFlag, badMaskPlanes=badMaskPlanes
376 )
377 # Stars that are missing from input bright star stamps may
378 # still have a flux within the normalization annulus. The
379 # following two lines make sure that these stars are included
380 # in the subtraction process. Failing to assign the optimal
381 # radii values may result in an error in the `createAnnulus`
382 # method of the `SubtractBrightStarsTask` class. An alternative
383 # to handle this is to create two types of stamps that are
384 # missing from the input brightStarStamps object. One for those
385 # that have flux within the normalization annulus and another
386 # for those that do not have a flux within the normalization
387 # annulus.
388 stamp.optimalOuterRadius = outerRadius
389 stamp.optimalInnerRadius = innerRadius
390 except RuntimeError as err:
391 logger.error(err)
392 # Optionally keep NaN flux objects, for bookkeeping purposes,
393 # and to avoid having to re-find and redo the preprocessing
394 # steps needed before bright stars can be subtracted.
395 if discardNanFluxObjects:
396 rejects.append(stamp)
397 elif forceFindFlux:
398 newInnerRadius = innerRadius
399 newOuterRadius = outerRadius
400 while True:
401 newOuterRadius += annulusWidth
402 newInnerRadius += annulusWidth
403 if newOuterRadius > min(imCenter):
404 logger.info("No flux found for the star with Gaia ID of %s", stamp.gaiaId)
405 stamp.annularFlux = None
406 badStamps.append(stamp)
407 break
408 newOuterCircle = SpanSet.fromShape(newOuterRadius, Stencil.CIRCLE, offset=imCenter)
409 newInnerCircle = SpanSet.fromShape(newInnerRadius, Stencil.CIRCLE, offset=imCenter)
410 newAnnulus = newOuterCircle.intersectNot(newInnerCircle)
411 try:
412 stamp.measureAndNormalize(
413 newAnnulus,
414 statsControl=statsControl,
415 statsFlag=statsFlag,
416 badMaskPlanes=badMaskPlanes,
417 )
418
419 except RuntimeError:
420 stamp.annularFlux = np.nan
421 logger.error(
422 "The annular flux was not found for radii %d and %d",
423 newInnerRadius,
424 newOuterRadius,
425 )
426 if stamp.annularFlux and stamp.annularFlux > 0:
427 logger.info("The flux is found within an optimized annulus.")
428 logger.info(
429 "The optimized annulus radii are %d and %d and the flux is %f",
430 newInnerRadius,
431 newOuterRadius,
432 stamp.annularFlux,
433 )
434 stamp.optimalOuterRadius = newOuterRadius
435 stamp.optimalInnerRadius = newInnerRadius
436 break
437 else:
438 stamp.annularFlux = np.nan
439
440 # Remove rejected stamps.
441 bss.normalized = True
442 if discardNanFluxObjects:
443 for reject in rejects:
444 bss._stamps.remove(reject)
445 elif forceFindFlux:
446 for badStamp in badStamps:
447 bss._stamps.remove(badStamp)
448 bss._innerRadius, bss._outerRadius = None, None
449 return bss, badStamps
450 return bss
451
453 """Refresh metadata. Should be called before writing the object out.
454
455 This method adds full lists of positions, Gaia magnitudes, IDs and
456 annular fluxes to the shared metadata.
457 """
458 self._metadata["G_MAGS"] = self.getMagnitudes()
459 self._metadata["GAIA_IDS"] = self.getGaiaIds()
460 positions = self.getPositions()
461 self._metadata["X0S"] = [xy0[0] for xy0 in positions]
462 self._metadata["Y0S"] = [xy0[1] for xy0 in positions]
463 self._metadata["ANNULAR_FLUXES"] = self.getAnnularFluxes()
464 self._metadata["VALID_PIXELS_FRACTION"] = self.getValidPixelsFraction()
465 self._metadata["NORMALIZED"] = self.normalized
466 self._metadata["INNER_RADIUS"] = self._innerRadius
467 self._metadata["OUTER_RADIUS"] = self._outerRadius
468 if self.nb90Rots is not None:
469 self._metadata["NB_90_ROTS"] = self.nb90Rots
470 return None
471
472 @classmethod
473 def readFits(cls, filename):
474 """Build an instance of this class from a file.
475
476 Parameters
477 ----------
478 filename : `str`
479 Name of the file to read.
480 """
482
483 @classmethod
484 def readFitsWithOptions(cls, filename, options):
485 """Build an instance of this class with options.
486
487 Parameters
488 ----------
489 filename : `str`
490 Name of the file to read.
491 options : `PropertyList`
492 Collection of metadata parameters.
493 """
494 stamps, metadata = readFitsWithOptions(filename, BrightStarStamp.factory, options)
495 nb90Rots = metadata["NB_90_ROTS"] if "NB_90_ROTS" in metadata else None
496 if metadata["NORMALIZED"]:
497 return cls(
498 stamps,
499 innerRadius=metadata["INNER_RADIUS"],
500 outerRadius=metadata["OUTER_RADIUS"],
501 nb90Rots=nb90Rots,
502 metadata=metadata,
503 use_mask=metadata["HAS_MASK"],
504 use_variance=metadata["HAS_VARIANCE"],
505 use_archive=metadata["HAS_ARCHIVE"],
506 )
507 else:
508 return cls(
509 stamps,
510 nb90Rots=nb90Rots,
511 metadata=metadata,
512 use_mask=metadata["HAS_MASK"],
513 use_variance=metadata["HAS_VARIANCE"],
514 use_archive=metadata["HAS_ARCHIVE"],
515 )
516
517 def append(self, item, innerRadius=None, outerRadius=None):
518 """Add an additional bright star stamp.
519
520 Parameters
521 ----------
522 item : `BrightStarStamp`
523 Bright star stamp to append.
524 innerRadius : `int`, optional
525 Inner radius value, in pixels. This and ``outerRadius`` define the
526 annulus used to compute the ``"annularFlux"`` values within each
527 ``BrightStarStamp``.
528 outerRadius : `int`, optional
529 Outer radius value, in pixels. This and ``innerRadius`` define the
530 annulus used to compute the ``"annularFlux"`` values within each
531 ``BrightStarStamp``.
532 """
533 if not isinstance(item, BrightStarStamp):
534 raise ValueError(f"Can only add instances of BrightStarStamp, got {type(item)}.")
535 if (item.annularFlux is None) == self.normalized:
536 raise AttributeError(
537 "Trying to append an unnormalized stamp to a normalized BrightStarStamps "
538 "instance, or vice-versa."
539 )
540 else:
541 self._checkRadius(innerRadius, outerRadius)
542 self._stamps.append(item)
543 return None
544
545 def extend(self, bss):
546 """Extend BrightStarStamps instance by appending elements from another
547 instance.
548
549 Parameters
550 ----------
551 bss : `BrightStarStamps`
552 Other instance to concatenate.
553 """
554 if not isinstance(bss, BrightStarStamps):
555 raise ValueError(f"Can only extend with a BrightStarStamps object. Got {type(bss)}.")
556 self._checkRadius(bss._innerRadius, bss._outerRadius)
557 self._stamps += bss._stamps
558
559 def getMagnitudes(self):
560 """Retrieve Gaia G-band magnitudes for each star.
561
562 Returns
563 -------
564 gaiaGMags : `list` [`float`]
565 Gaia G-band magnitudes for each star.
566 """
567 return [stamp.gaiaGMag for stamp in self._stamps]
568
569 def getGaiaIds(self):
570 """Retrieve Gaia IDs for each star.
571
572 Returns
573 -------
574 gaiaIds : `list` [`int`]
575 Gaia IDs for each star.
576 """
577 return [stamp.gaiaId for stamp in self._stamps]
578
580 """Retrieve normalization factor for each star.
581
582 These are computed by integrating the flux in annulus centered on the
583 bright star, far enough from center to be beyond most severe ghosts and
584 saturation.
585 The inner and outer radii that define the annulus can be recovered from
586 the metadata.
587
588 Returns
589 -------
590 annularFluxes : `list` [`float`]
591 Annular fluxes which give the normalization factor for each star.
592 """
593 return [stamp.annularFlux for stamp in self._stamps]
594
596 """Retrieve the fraction of valid pixels within the normalization
597 annulus for each star.
598
599 Returns
600 -------
601 validPixelsFractions : `list` [`float`]
602 Fractions of valid pixels within the normalization annulus for each
603 star.
604 """
605 return [stamp.validAnnulusFraction for stamp in self._stamps]
606
607 def selectByMag(self, magMin=None, magMax=None):
608 """Return the subset of bright star stamps for objects with specified
609 magnitude cuts (in Gaia G).
610
611 Parameters
612 ----------
613 magMin : `float`, optional
614 Keep only stars fainter than this value.
615 magMax : `float`, optional
616 Keep only stars brighter than this value.
617 """
618 subset = [
619 stamp
620 for stamp in self._stamps
621 if (magMin is None or stamp.gaiaGMag > magMin) and (magMax is None or stamp.gaiaGMag < magMax)
622 ]
623 # This saves looping over init when guaranteed to be the correct type.
624 instance = BrightStarStamps(
625 (), innerRadius=self._innerRadius, outerRadius=self._outerRadius, metadata=self._metadata
626 )
627 instance._stamps = subset
628 return instance
629
630 def _checkRadius(self, innerRadius, outerRadius):
631 """Ensure provided annulus radius is consistent with that already
632 present in the instance, or with arguments passed on at initialization.
633 """
634 if innerRadius != self._innerRadius or outerRadius != self._outerRadius:
635 raise AttributeError(
636 f"Trying to mix stamps normalized with annulus radii {innerRadius, outerRadius} with those "
637 "of BrightStarStamp instance\n"
638 f"(computed with annular radii {self._innerRadius, self._outerRadius})."
639 )
640
641 def _checkNormalization(self, normalize, innerRadius, outerRadius):
642 """Ensure there is no mixing of normalized and unnormalized stars, and
643 that, if requested, normalization can be performed.
644 """
645 noneFluxCount = self.getAnnularFluxes().count(None)
646 nStamps = len(self)
647 nFluxVals = nStamps - noneFluxCount
648 if noneFluxCount and noneFluxCount < nStamps:
649 # At least one stamp contains an annularFlux value (i.e. has been
650 # normalized), but not all of them do.
651 raise AttributeError(
652 f"Only {nFluxVals} stamps contain an annularFlux value.\nAll stamps in a BrightStarStamps "
653 "instance must either be normalized with the same annulus definition, or none of them can "
654 "contain an annularFlux value."
655 )
656 elif normalize:
657 # Stamps are to be normalized; ensure annular radii are specified
658 # and they have no annularFlux.
659 if innerRadius is None or outerRadius is None:
660 raise AttributeError(
661 "For stamps to be normalized (normalize=True), please provide a valid value (in pixels) "
662 "for both innerRadius and outerRadius."
663 )
664 elif noneFluxCount < nStamps:
665 raise AttributeError(
666 f"{nFluxVals} stamps already contain an annularFlux value. For stamps to be normalized, "
667 "all their annularFlux must be None."
668 )
669 elif innerRadius is not None and outerRadius is not None:
670 # Radii provided, but normalize=False; check that stamps already
671 # contain annularFluxes.
672 if noneFluxCount:
673 raise AttributeError(
674 f"{noneFluxCount} stamps contain no annularFlux, but annular radius values were provided "
675 "and normalize=False.\nTo normalize stamps, set normalize to True."
676 )
677 else:
678 # At least one radius value is missing; ensure no stamps have
679 # already been normalized.
680 if nFluxVals:
681 raise AttributeError(
682 f"{nFluxVals} stamps contain an annularFlux value. If stamps have been normalized, the "
683 "innerRadius and outerRadius values used must be provided."
684 )
685 return None
int min
Pass parameters to a Statistics object.
Definition Statistics.h:83
factory(cls, stamp_im, metadata, idx, archive_element=None, minValidAnnulusFraction=0.0)
measureAndNormalize(self, SpanSet annulus, StatisticsControl statsControl=StatisticsControl(), Property statsFlag=stringToStatisticsProperty("MEAN"), Collection[str] badMaskPlanes=("BAD", "SAT", "NO_DATA"))
initAndNormalize(cls, starStamps, innerRadius, outerRadius, nb90Rots=None, metadata=None, use_mask=True, use_variance=False, use_archive=False, imCenter=None, discardNanFluxObjects=True, forceFindFlux=False, statsControl=StatisticsControl(), statsFlag=stringToStatisticsProperty("MEAN"), badMaskPlanes=("BAD", "SAT", "NO_DATA"))
append(self, item, innerRadius=None, outerRadius=None)
_checkNormalization(self, normalize, innerRadius, outerRadius)
__init__(self, starStamps, innerRadius=None, outerRadius=None, nb90Rots=None, metadata=None, use_mask=True, use_variance=False, use_archive=False)
readFitsWithOptions(cls, filename, options)
Definition stamps.py:365
readFitsWithOptions(cls, filename, options)
Definition stamps.py:509