LSST Applications g07dc498a13+cb17356775,g1409bbee79+cb17356775,g1a7e361dbc+cb17356775,g1fd858c14a+d1a2a640a9,g33399d78f5+fe6948661d,g35bb328faa+e55fef2c71,g3bd4b5ce2c+cac9e18807,g3c79e8cd92+2359a18b76,g43bc871e57+a58ba40925,g53246c7159+e55fef2c71,g60b5630c4e+8133a3545f,g78460c75b0+8427c4cc8f,g78619a8342+55305cb8f0,g786e29fd12+307f82e6af,g8534526c7b+8e1c6b434f,g89139ef638+cb17356775,g8b49a6ea8e+8133a3545f,g8ffcb69f3d+818ab6c36e,g9125e01d80+e55fef2c71,g97b8272a79+98425f45f6,g989de1cb63+cb17356775,g9f33ca652e+4245ceb508,gaaedd4e678+cb17356775,gabe3b4be73+9c0c3c7524,gb1101e3267+3e5ef1d639,gb58c049af0+28045f66fd,gc1fe0db326+8133a3545f,gca43fec769+e55fef2c71,gcf25f946ba+fe6948661d,gd397e13551+64039f84ea,gd6cbbdb0b4+f6e5445f66,gde0f65d7ad+7eb368c542,ge278dab8ac+b4c2c8faf7,geab183fbe5+8133a3545f,gecb8035dfe+1f480bec5e,gefa07fa684+e7bc33f3ea,gf58bf46354+e55fef2c71,gfe7187db8c+e55afb4430,w.2025.03
LSST Data Management Base Package
Loading...
Searching...
No Matches
visualizeVisit.py
Go to the documentation of this file.
1# This file is part of pipe_tasks.
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__all__ = [
23 "VisualizeBinExpConfig",
24 "VisualizeBinExpTask",
25 "VisualizeMosaicExpConfig",
26 "VisualizeMosaicExpTask",
27 "VisualizeBinCalibConfig",
28 "VisualizeBinCalibTask",
29 "VisualizeMosaicCalibConfig",
30 "VisualizeMosaicCalibTask",
31 "VisualizeBinCalibFilterConfig",
32 "VisualizeBinCalibFilterTask",
33 "VisualizeMosaicCalibFilterConfig",
34 "VisualizeMosaicCalibFilterTask",
35]
36
37import dataclasses
38
39import lsst.afw.cameraGeom.utils as afwUtils
40import lsst.afw.image as afwImage
41import lsst.afw.math as afwMath
42import lsst.pex.config as pexConfig
43import lsst.pipe.base as pipeBase
44import lsst.pipe.base.connectionTypes as cT
45import numpy as np
46
47
48# VisualizeBinExp (here) & VisualizeMosaicExp (below):
50 pipeBase.PipelineTaskConnections, dimensions=("instrument", "visit", "detector")
51):
52 camera = cT.PrerequisiteInput(
53 name="camera",
54 doc="Input camera to use for mosaic geometry.",
55 storageClass="Camera",
56 dimensions=("instrument",),
57 isCalibration=True,
58 )
59 inputExp = cT.Input(
60 name="calexp",
61 doc="Input exposure data to mosaic.",
62 storageClass="ExposureF",
63 dimensions=("instrument", "visit", "detector"),
64 )
65 outputExp = cT.Output(
66 name="calexpBin",
67 doc="Output binned image.",
68 storageClass="ExposureF",
69 dimensions=("instrument", "visit", "detector"),
70 )
71
72 def __init__(self, *, config=None):
73 """Customize connections for a specific instance.
74
75 This customization enables for dynamic setup at runtime,
76 allowing VisualizeBinExpTask to work with different types of
77 Exposures such as postISRCCDs and calexps.
78
79 Parameters
80 ----------
81 config : `VisualizeBinExpConfig`
82 A config for `VisualizeBinExpTask`.
83 """
84 super().__init__(config=config)
85 if config:
86 # Update the dimensions of the task
87 self.dimensions.clear()
88 self.dimensions.update(config.dimensions)
89 # Update the storage classes and dimensions for inputs/outputs
90 self.inputExp = dataclasses.replace(
91 self.inputExp,
92 storageClass=config.storageClass,
93 dimensions=config.dimensions,
94 )
95 self.outputExp = dataclasses.replace(
96 self.outputExp,
97 storageClass=config.storageClass,
98 dimensions=config.dimensions,
99 )
100
101
102class VisualizeBinExpConfig(pipeBase.PipelineTaskConfig, pipelineConnections=VisualizeBinExpConnections):
103 """Configuration for focal plane visualization."""
104
105 storageClass = pexConfig.Field(
106 default=VisualizeBinExpConnections.inputExp.storageClass,
107 dtype=str,
108 doc=(
109 "The storageClasses of the input and output exposures. "
110 "Must be of type lsst.afw.Image.Exposure, or one of its subtypes."
111 ),
112 )
113 dimensions = pexConfig.ListField(
114 # Sort to ensure default order is consistent between runs
115 default=sorted(VisualizeBinExpConnections.dimensions),
116 dtype=str,
117 doc="The task dimensions, also applied to the input/output exposures. ",
118 )
119 binning = pexConfig.Field(
120 dtype=int,
121 default=8,
122 doc="Binning factor to apply to each input exposure's image data.",
123 )
124 detectorKeyword = pexConfig.Field(
125 dtype=str,
126 default="DET-ID",
127 doc="Metadata keyword to use to find detector if not available from input.",
128 )
129
130
131class VisualizeBinExpTask(pipeBase.PipelineTask):
132 """Bin the detectors of an exposure.
133
134 The outputs of this task should be passed to
135 VisualizeMosaicExpTask to be mosaicked into a full focal plane
136 visualization image.
137 """
138
139 ConfigClass = VisualizeBinExpConfig
140 _DefaultName = "VisualizeBinExp"
141
142 def run(self, inputExp, camera):
143 """Bin input image, attach associated detector.
144
145 Parameters
146 ----------
147 inputExp : `lsst.afw.image.Exposure`
148 Input exposure data to bin.
149 camera : `lsst.afw.cameraGeom.Camera`
150 Input camera to use for mosaic geometry.
151
152 Returns
153 -------
154 output : `lsst.pipe.base.Struct`
155 Results struct with attribute:
156
157 ``outputExp``
158 Binned version of input image (`lsst.afw.image.Exposure`).
159 """
160 if inputExp.getDetector() is None:
161 detectorId = inputExp.getMetadata().get(self.config.detectorKeyword)
162 if detectorId is not None:
163 inputExp.setDetector(camera[detectorId])
164
165 binned = inputExp.getMaskedImage()
166 binned = afwMath.binImage(binned, self.config.binning)
167 outputExp = afwImage.makeExposure(binned)
168
169 outputExp.setInfo(inputExp.getInfo())
170
171 return pipeBase.Struct(outputExp=outputExp)
172
173
174# VisualizeBinExp (above) & VisualizeMosaicExp (here):
175class VisualizeMosaicExpConnections(pipeBase.PipelineTaskConnections, dimensions=("instrument", "visit")):
176 camera = cT.PrerequisiteInput(
177 name="camera",
178 doc="Input camera to use for mosaic geometry.",
179 storageClass="Camera",
180 dimensions=("instrument",),
181 isCalibration=True,
182 )
183 inputExps = cT.Input(
184 name="calexpBin",
185 doc="Input binned images to mosaic.",
186 storageClass="ExposureF",
187 dimensions=("instrument", "visit", "detector"),
188 multiple=True,
189 )
190 outputData = cT.Output(
191 name="calexpFocalPlane",
192 doc="Output binned mosaicked frame.",
193 storageClass="ImageF",
194 dimensions=("instrument", "visit"),
195 )
196
197 def __init__(self, *, config=None):
198 """Customize connections for a specific instance.
199
200 This customization enables for dynamic setup at runtime,
201 allowing VisualizeMosaicExpTask to work with different types of
202 Exposures such as postISRCCDs and calexps.
203
204 Parameters
205 ----------
206 config : `VisualizeMosaicExpTask`
207 A config for `VisualizeMosaicExpTask`.
208 """
209 super().__init__(config=config)
210 if config:
211 # Update the dimensions of the task
212 self.dimensions.clear()
213 self.dimensions.update(config.dimensions)
214 # Update the storage classes and dimensions for inputs/outputs
215 inputExpsDimensions = list(config.dimensions) + ["detector"]
216 self.inputExps = dataclasses.replace(
217 self.inputExps,
218 storageClass=config.storageClass,
219 dimensions=inputExpsDimensions,
220 )
221 self.outputData = dataclasses.replace(
222 self.outputData,
223 dimensions=config.dimensions,
224 )
225
226
228 pipeBase.PipelineTaskConfig, pipelineConnections=VisualizeMosaicExpConnections
229):
230 """Configuration for focal plane visualization."""
231
232 storageClass = pexConfig.Field(
233 default=VisualizeMosaicExpConnections.inputExps.storageClass,
234 dtype=str,
235 doc=(
236 "The storageClass of the input exposures. "
237 "Must be of type lsst.afw.Image.Exposure, or one of its subtypes."
238 ),
239 )
240 dimensions = pexConfig.ListField(
241 # Sort to ensure default order is consistent between runs
242 default=sorted(VisualizeMosaicExpConnections.dimensions),
243 dtype=str,
244 doc="The task dimensions, also applied to the input/output exposures. "
245 "Note: the input exposure will have 'detector' appended by default.",
246 )
247 binning = pexConfig.Field(
248 dtype=int,
249 default=8,
250 doc="Binning factor previously applied to input exposures.",
251 )
252
253
254class VisualizeMosaicExpTask(pipeBase.PipelineTask):
255 """Task to mosaic binned products.
256
257 The config.binning parameter must match that used in the
258 VisualizeBinExpTask. Otherwise there will be a mismatch between
259 the input image size and the expected size of that image in the
260 full focal plane frame.
261 """
262
263 ConfigClass = VisualizeMosaicExpConfig
264 _DefaultName = "VisualizeMosaicExp"
265
266 def makeCameraImage(self, inputExps, camera, binning):
267 """Make an image of an entire focal plane.
268
269 Parameters
270 ----------
271 exposures: `dict` [`int`, `lsst.afw.image.Exposure`]
272 CCD exposures, binned by `binning`. The keys are the
273 detectorIDs, with the values the binned image exposure.
274
275 Returns
276 -------
277 image : `lsst.afw.image.Image`
278 Image mosaicked from the individual binned images for each
279 detector.
280 """
281 image = afwUtils.makeImageFromCamera(
282 camera, imageSource=ImageSource(inputExps), imageFactory=afwImage.ImageF, binSize=binning
283 )
284 return image
285
286 def run(self, inputExps, camera, inputIds=None):
287 """Mosaic inputs together to create focal plane image.
288
289 Parameters
290 ----------
291 inputExps : `list` [`lsst.afw.image.Exposure`]
292 Input exposure data to bin.
293 camera : `lsst.afw.cameraGeom.Camera`
294 Input camera to use for mosaic geometry.
295 inputIds : `list` [`int`], optional
296 Optional list providing exposure IDs corresponding to input
297 exposures. Will be generated via the exposure data `getDetector`
298 method if not provided.
299
300 Returns
301 -------
302 output : `lsst.pipe.base.Struct`
303 Results struct with attribute:
304
305 ``outputExp``
306 Binned version of input image (`lsst.afw.image.Exposure`).
307 """
308 if not inputIds:
309 inputIds = [exp.getDetector().getId() for exp in inputExps]
310 expDict = {id: exp for id, exp in zip(inputIds, inputExps)}
311 image = self.makeCameraImage(expDict, camera, self.config.binning)
312
313 return pipeBase.Struct(outputData=image)
314
315
317 """Source of images for makeImageFromCamera"""
318
319 def __init__(self, exposures):
320 self.exposures = exposures
321 self.isTrimmed = True
322 self.background = np.nan
323
324 def getCcdImage(self, detector, imageFactory, binSize):
325 """Provide image of CCD to makeImageFromCamera
326
327 Parameters
328 ----------
329 detector : `int`
330 Detector ID to get image data for.
331 imageFactory : `lsst.afw.image.Image`
332 Type of image to construct.
333 binSize : `int`
334 Binsize to use to recompute dimensions.
335
336 Returns
337 -------
338 image : `lsst.afw.image.Image`
339 Appropriately rotated, binned, and transformed
340 image to be mosaicked.
341 detector : `lsst.afw.cameraGeom.Detector`
342 Camera detector that the returned image data
343 belongs to.
344 """
345 detId = detector.getId()
346
347 if detId not in self.exposures:
348 dims = detector.getBBox().getDimensions() / binSize
349 image = imageFactory(*[int(xx) for xx in dims])
350 image.set(self.background)
351 else:
352 image = self.exposures[detector.getId()]
353 if hasattr(image, "getMaskedImage"):
354 image = image.getMaskedImage()
355 if hasattr(image, "getMask"):
356 mask = image.getMask()
357 isBad = mask.getArray() & mask.getPlaneBitMask("NO_DATA") > 0
358 image = image.clone()
359 image.getImage().getArray()[isBad] = self.background
360 if hasattr(image, "getImage"):
361 image = image.getImage()
362
363 # afwMath.rotateImageBy90 checks NQuarter values,
364 # so we don't need to here.
365 image = afwMath.rotateImageBy90(image, detector.getOrientation().getNQuarter())
366 return image, detector
367
368
369# VisualizeBinCalib (here) & VisualizeMosaicCalib (below):
370# Inputs to bin task have dimensions: {instrument, detector}
371# Output of the mosaic task have: {instrument, }
372class VisualizeBinCalibConnections(pipeBase.PipelineTaskConnections, dimensions=("instrument", "detector")):
373 inputExp = cT.Input(
374 name="bias",
375 doc="Input exposure data to mosaic.",
376 storageClass="ExposureF",
377 dimensions=("instrument", "detector"),
378 isCalibration=True,
379 )
380 camera = cT.PrerequisiteInput(
381 name="camera",
382 doc="Input camera to use for mosaic geometry.",
383 storageClass="Camera",
384 dimensions=("instrument",),
385 isCalibration=True,
386 )
387
388 outputExp = cT.Output(
389 name="biasBin",
390 doc="Output binned image.",
391 storageClass="ExposureF",
392 dimensions=("instrument", "detector"),
393 )
394
395
396class VisualizeBinCalibConfig(VisualizeBinExpConfig, pipelineConnections=VisualizeBinCalibConnections):
397 pass
398
399
401 """Bin the detectors of an calibration.
402
403 The outputs of this task should be passed to
404 VisualizeMosaicCalibTask to be mosaicked into a full focal plane
405 visualization image.
406 """
407
408 ConfigClass = VisualizeBinCalibConfig
409 _DefaultName = "VisualizeBinCalib"
410
411 pass
412
413
414# VisualizeBinCalib (above) & VisualizeMosaicCalib (here):
415# Inputs to bin task have dimensions: {instrument, detector}
416# Output of the mosaic task have: {instrument, }
417class VisualizeMosaicCalibConnections(pipeBase.PipelineTaskConnections, dimensions=("instrument",)):
418 inputExps = cT.Input(
419 name="biasBin",
420 doc="Input binned images mosaic.",
421 storageClass="ExposureF",
422 dimensions=("instrument", "detector"),
423 multiple=True,
424 )
425 camera = cT.PrerequisiteInput(
426 name="camera",
427 doc="Input camera to use for mosaic geometry.",
428 storageClass="Camera",
429 dimensions=("instrument",),
430 isCalibration=True,
431 )
432
433 outputData = cT.Output(
434 name="biasFocalPlane",
435 doc="Output binned mosaicked frame.",
436 storageClass="ImageF",
437 dimensions=("instrument",),
438 )
439
440
442 VisualizeMosaicExpConfig, pipelineConnections=VisualizeMosaicCalibConnections
443):
444 pass
445
446
448 """Task to mosaic binned products.
449
450 The config.binning parameter must match that used in the
451 VisualizeBinCalibTask. Otherwise there will be a mismatch between
452 the input image size and the expected size of that image in the
453 full focal plane frame.
454 """
455
456 ConfigClass = VisualizeMosaicCalibConfig
457 _DefaultName = "VisualizeMosaicCalib"
458
459 pass
460
461
462# VisualizeBinCalibFilter (here) & VisualizeMosaicCalibFilter (below):
463# Inputs to bin task have dimensions: {instrument, detector, physical_filter}
464# Output of the mosaic task have: {instrument, physical_filter}
466 pipeBase.PipelineTaskConnections, dimensions=("instrument", "detector", "physical_filter")
467):
468 inputExp = cT.Input(
469 name="flat",
470 doc="Input exposure data to mosaic.",
471 storageClass="ExposureF",
472 dimensions=("instrument", "detector", "physical_filter"),
473 isCalibration=True,
474 )
475 camera = cT.PrerequisiteInput(
476 name="camera",
477 doc="Input camera to use for mosaic geometry.",
478 storageClass="Camera",
479 dimensions=("instrument",),
480 isCalibration=True,
481 )
482
483 outputExp = cT.Output(
484 name="flatBin",
485 doc="Output binned image.",
486 storageClass="ExposureF",
487 dimensions=("instrument", "detector", "physical_filter"),
488 )
489
490
492 VisualizeBinExpConfig, pipelineConnections=VisualizeBinCalibFilterConnections
493):
494 pass
495
496
498 """Bin the detectors of an calibration.
499
500 The outputs of this task should be passed to
501 VisualizeMosaicCalibTask to be mosaicked into a full focal plane
502 visualization image.
503 """
504
505 ConfigClass = VisualizeBinCalibFilterConfig
506 _DefaultName = "VisualizeBinCalibFilter"
507
508 pass
509
510
511# VisualizeBinCalibFilter (above) & VisualizeMosaicCalibFilter (here):
512# Inputs to bin task have dimensions: {instrument, detector, physical_filter}
513# Output of the mosaic task have: {instrument, physical_filter}
515 pipeBase.PipelineTaskConnections,
516 dimensions=("instrument", "physical_filter"),
517):
518 inputExps = cT.Input(
519 name="flatBin",
520 doc="Input binned images mosaic.",
521 storageClass="ExposureF",
522 dimensions=("instrument", "detector", "physical_filter"),
523 multiple=True,
524 )
525 camera = cT.PrerequisiteInput(
526 name="camera",
527 doc="Input camera to use for mosaic geometry.",
528 storageClass="Camera",
529 dimensions=("instrument",),
530 isCalibration=True,
531 )
532
533 outputData = cT.Output(
534 name="flatFocalPlane",
535 doc="Output binned mosaicked frame.",
536 storageClass="ImageF",
537 dimensions=("instrument", "physical_filter"),
538 )
539
540
542 VisualizeMosaicExpConfig, pipelineConnections=VisualizeMosaicCalibFilterConnections
543):
544 pass
545
546
548 """Task to mosaic binned products.
549
550 The config.binning parameter must match that used in the
551 VisualizeBinCalibFilterTask. Otherwise there will be a mismatch between
552 the input image size and the expected size of that image in the
553 full focal plane frame.
554 """
555
556 ConfigClass = VisualizeMosaicCalibFilterConfig
557 _DefaultName = "VisualizeMosaicCalibFilter"
558
559 pass
getCcdImage(self, detector, imageFactory, binSize)
makeCameraImage(self, inputExps, camera, binning)
run(self, inputExps, camera, inputIds=None)
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
A function to return an Exposure of the correct type (cf.
Definition Exposure.h:484
std::shared_ptr< ImageT > rotateImageBy90(ImageT const &image, int nQuarter)
Rotate an image by an integral number of quarter turns.
std::shared_ptr< ImageT > binImage(ImageT const &inImage, int const binX, int const binY, lsst::afw::math::Property const flags=lsst::afw::math::MEAN)
Definition binImage.cc:44