lsst.pipe.tasks  21.0.0-20-g55224fe4+fb58a89680
calexpCutout.py
Go to the documentation of this file.
1 import lsst.pipe.base as pipeBase
2 import lsst.geom as geom
3 from lsst.pex.config import Field
4 from lsst.meas.algorithms import Stamp, Stamps
5 
6 __all__ = ['CalexpCutoutTaskConfig', 'CalexpCutoutTask']
7 DETECTOR_DIMENSIONS = ('instrument', 'visit', 'detector')
8 
9 
10 class CalexpCutoutTaskConnections(pipeBase.PipelineTaskConnections,
11  dimensions=DETECTOR_DIMENSIONS,
12  defaultTemplates={}):
13  """Connections class for CalexpCutoutTask
14  """
15  in_table = pipeBase.connectionTypes.Input(
16  doc="Locations for cutouts",
17  name="cutout_positions",
18  storageClass="AstropyQTable",
19  dimensions=DETECTOR_DIMENSIONS,
20  )
21  calexp = pipeBase.connectionTypes.Input(
22  doc="Calexp objects",
23  name="calexp",
24  storageClass="ExposureF",
25  dimensions=DETECTOR_DIMENSIONS,
26  )
27  cutouts = pipeBase.connectionTypes.Output(
28  doc="Cutouts",
29  name="calexp_cutouts",
30  storageClass="Stamps",
31  dimensions=DETECTOR_DIMENSIONS,
32  )
33 
34 
35 class CalexpCutoutTaskConfig(pipeBase.PipelineTaskConfig,
36  pipelineConnections=CalexpCutoutTaskConnections):
37  """Configuration for CalexpCutoutTask
38  """
39  max_cutouts = Field(dtype=int, default=100, doc='Maximum number of entries to process. '
40  'The result will be the first N in the input table.')
41  skip_bad = Field(dtype=bool, default=True, doc='Skip cutouts that do not fall completely within'
42  ' the calexp bounding box? If set to False a ValueError'
43  ' is raised instead.')
44 
45 
46 class CalexpCutoutTask(pipeBase.PipelineTask):
47  """Task for computing cutouts on a specific calexp given
48  positions and sizes of the stamps.
49  """
50  ConfigClass = CalexpCutoutTaskConfig
51  _DefaultName = "calexpCutoutTask"
52 
53  def run(self, in_table, calexp):
54  """Compute and return the cutouts.
55 
56  Parameters
57  ----------
58  in_table : `astropy.QTable`
59  A table containing at least the following columns: position, size.
60  The position should be an `astropy.SkyCoord`. The size is
61  the size of the cutout in pixels. All cutouts are square in pixel
62  space.
63  calexp : `lsst.afw.image.ExposureF`
64  The calibrated exposure from which to extract cutouts
65 
66  Returns
67  -------
68  output : `lsst.pipe.base.Struct`
69  A struct containing:
70 
71  * cutouts: an `lsst.meas.algorithms.Stamps` object
72  that wraps a list of masked images of the cutouts and a
73  `PropertyList` containing the metadata to be persisted
74  with the cutouts. The exposure metadata is preserved and,
75  in addition, arrays holding the RA and Dec of each stamp
76  in degrees are added to the metadata. Note: the origin
77  of the output stamps is `lsst.afw.image.PARENT`.
78  * skipped_positions: a `list` of `lsst.geom.SpherePoint` objects for
79  stamps that were skiped for being off the image
80  or partially off the image
81 
82  Raises
83  ------
84  ValueError
85  If the input catalog doesn't have the required columns,
86  a ValueError is raised
87  """
88  if 'position' not in in_table.colnames or 'size' not in in_table.colnames:
89  raise ValueError('Required column missing from the input table. '
90  'Required columns are "position" and "size".'
91  f'The column names are: {in_table.colnames}')
92  max_idx = self.config.max_cutouts
93  cutout_list = []
94  wcs = calexp.getWcs()
95  mim = calexp.getMaskedImage()
96  ras = []
97  decs = []
98  skipped_positions = []
99  for rec in in_table[:max_idx]:
100  ra = rec['position'].ra.degree
101  dec = rec['position'].dec.degree
102  ras.append(ra)
103  decs.append(dec)
104  pt = geom.SpherePoint(geom.Angle(ra, geom.degrees),
105  geom.Angle(dec, geom.degrees))
106  pix = wcs.skyToPixel(pt)
107  size = rec['size'].value
108  # Clamp to LL corner of the LL pixel and draw extent from there
109  box = geom.Box2I(geom.Point2I(int(pix.x-size/2), int(pix.y-size/2)),
110  geom.Extent2I(size, size))
111  if not mim.getBBox().contains(box):
112  if not self.config.skip_bad:
113  raise ValueError(f'Cutout bounding box is not completely contained in the image: {box}')
114  else:
115  skipped_positions.append(pt)
116  continue
117  sub = mim.Factory(mim, box)
118  stamp = Stamp(stamp_im=sub, position=pt)
119  cutout_list.append(stamp)
120  metadata = calexp.getMetadata()
121  metadata['RA_DEG'] = ras
122  metadata['DEC_DEG'] = decs
123  return pipeBase.Struct(cutouts=Stamps(cutout_list, metadata=metadata),
124  skipped_positions=skipped_positions)
lsst.pipe.tasks.calexpCutout.CalexpCutoutTask
Definition: calexpCutout.py:46
lsst.pipe.tasks.calexpCutout.CalexpCutoutTask.run
def run(self, in_table, calexp)
Definition: calexpCutout.py:53
lsst.pipe.tasks.calexpCutout.CalexpCutoutTaskConnections
Definition: calexpCutout.py:12
lsst::pex::config
lsst::geom
Point< int, 2 >
lsst::geom::Angle
lsst::geom::Box2I
lsst::geom::SpherePoint
lsst.pipe::base
lsst::meas::algorithms
Extent< int, 2 >
lsst.pipe.tasks.calexpCutout.CalexpCutoutTaskConfig
Definition: calexpCutout.py:36