Coverage for python/lsst/pipe/tasks/warpAndPsfMatch.py: 37%

38 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-07-06 01:42 -0700

1# 

2# LSST Data Management System 

3# Copyright 2008, 2009, 2010, 2011, 2012 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22import lsst.pex.config as pexConfig 

23import lsst.afw.math as afwMath 

24import lsst.geom as geom 

25import lsst.afw.geom as afwGeom 

26import lsst.pipe.base as pipeBase 

27from lsst.ip.diffim import ModelPsfMatchTask 

28from lsst.meas.algorithms import WarpedPsf 

29 

30__all__ = ["WarpAndPsfMatchTask"] 

31 

32 

33class WarpAndPsfMatchConfig(pexConfig.Config): 

34 """Config for WarpAndPsfMatchTask 

35 """ 

36 psfMatch = pexConfig.ConfigurableField( 

37 target=ModelPsfMatchTask, 

38 doc="PSF matching model to model task", 

39 ) 

40 warp = pexConfig.ConfigField( 

41 dtype=afwMath.Warper.ConfigClass, 

42 doc="warper configuration", 

43 ) 

44 

45 

46class WarpAndPsfMatchTask(pipeBase.Task): 

47 """A task to warp and PSF-match an exposure 

48 """ 

49 ConfigClass = WarpAndPsfMatchConfig 

50 

51 def __init__(self, *args, **kwargs): 

52 pipeBase.Task.__init__(self, *args, **kwargs) 

53 self.makeSubtask("psfMatch") 

54 self.warper = afwMath.Warper.fromConfig(self.config.warp) 

55 

56 def run(self, exposure, wcs, modelPsf=None, maxBBox=None, destBBox=None, 

57 makeDirect=True, makePsfMatched=False): 

58 """Warp and optionally PSF-match exposure 

59 

60 Parameters 

61 ---------- 

62 exposure : :cpp:class: `lsst::afw::image::Exposure` 

63 Exposure to preprocess. 

64 wcs : :cpp:class:`lsst::afw::image::Wcs` 

65 Desired WCS of temporary images. 

66 modelPsf : :cpp:class: `lsst::meas::algorithms::KernelPsf` or None 

67 Target PSF to which to match. 

68 maxBBox : :cpp:class:`lsst::afw::geom::Box2I` or None 

69 Maximum allowed parent bbox of warped exposure. 

70 If None then the warped exposure will be just big enough to contain all warped pixels; 

71 if provided then the warped exposure may be smaller, and so missing some warped pixels; 

72 ignored if destBBox is not None. 

73 destBBox: :cpp:class: `lsst::afw::geom::Box2I` or None 

74 Exact parent bbox of warped exposure. 

75 If None then maxBBox is used to determine the bbox, otherwise maxBBox is ignored. 

76 makeDirect : bool 

77 Return an exposure that has been only warped? 

78 makePsfMatched : bool 

79 Return an exposure that has been warped and PSF-matched? 

80 

81 Returns 

82 ------- 

83 An lsst.pipe.base.Struct with the following fields: 

84 

85 direct : :cpp:class:`lsst::afw::image::Exposure` 

86 warped exposure 

87 psfMatched : :cpp:class: `lsst::afw::image::Exposure` 

88 warped and psf-Matched temporary exposure 

89 """ 

90 if makePsfMatched and modelPsf is None: 

91 raise RuntimeError("makePsfMatched=True, but no model PSF was provided") 

92 

93 if not makePsfMatched and not makeDirect: 

94 self.log.warning("Neither makeDirect nor makePsfMatched requested") 

95 

96 # Warp PSF before overwriting exposure 

97 xyTransform = afwGeom.makeWcsPairTransform(exposure.getWcs(), wcs) 

98 psfWarped = WarpedPsf(exposure.getPsf(), xyTransform) 

99 

100 if makePsfMatched and maxBBox is not None: 

101 # grow warped region to provide sufficient area for PSF-matching 

102 pixToGrow = 2 * max(self.psfMatch.kConfig.sizeCellX, 

103 self.psfMatch.kConfig.sizeCellY) 

104 # replace with copy 

105 maxBBox = geom.Box2I(maxBBox) 

106 maxBBox.grow(pixToGrow) 

107 

108 with self.timer("warp"): 

109 exposure = self.warper.warpExposure(wcs, exposure, maxBBox=maxBBox, destBBox=destBBox) 

110 exposure.setPsf(psfWarped) 

111 

112 if makePsfMatched: 

113 try: 

114 exposurePsfMatched = self.psfMatch.run(exposure, modelPsf).psfMatchedExposure 

115 except Exception as e: 

116 exposurePsfMatched = None 

117 self.log.info("Cannot PSF-Match: %s", e) 

118 

119 return pipeBase.Struct( 

120 direct=exposure if makeDirect else None, 

121 psfMatched=exposurePsfMatched if makePsfMatched else None 

122 )