Coverage for python / lsst / ip / isr / binImageDataTask.py: 33%
49 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 08:30 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-01 08:30 +0000
1# This file is part of ip_isr.
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/>.
22__all__ = ["BinImageDataTask", "BinImageDataConfig", "binImageData"]
24import lsst.afw.image as afwImage
25import lsst.afw.math as afwMath
26import lsst.pex.config as pexConfig
27import lsst.pipe.base as pipeBase
28import lsst.pipe.base.connectionTypes as cT
29from lsst.utils.timer import timeMethod
32class BinImageDataConnections(
33 pipeBase.PipelineTaskConnections,
34 dimensions=("instrument", "exposure", "detector"),
35 defaultTemplates={"inputName": "postISRCCD", "outputName": "postISRCCDBin"},
36):
38 inputData = cT.Input(
39 name="{inputName}",
40 doc="Input image data to bin.",
41 storageClass="ExposureF",
42 dimensions=["instrument", "exposure", "detector"],
43 )
44 outputData = cT.Output(
45 name="{outputName}",
46 doc="Binned image data.",
47 storageClass="ExposureF",
48 dimensions=["instrument", "exposure", "detector"],
49 )
51 def __init__(self, *, config=None):
52 """Customize the connections and storageClass for a specific
53 instance. This enables both to be dynamically set at runtime,
54 allowing BinImageDataTask to work with different types of
55 image and image-like data.
57 Parameters
58 ----------
59 config : `BinExposureConfig`
60 A config for `BinExposureTask`.
61 """
62 super().__init__(config=config)
63 if config and config.inputDimensions != self.inputData.dimensions:
64 self.dimensions.clear()
65 self.dimensions.update(config.inputDimensions)
66 self.inputData = cT.Input(
67 name=self.inputData.name,
68 doc=self.inputData.doc,
69 storageClass=self.inputData.storageClass,
70 dimensions=frozenset(config.inputDimensions),
71 )
72 self.outputData = cT.Output(
73 name=self.outputData.name,
74 doc=self.outputData.doc,
75 storageClass=self.outputData.storageClass,
76 dimensions=frozenset(config.inputDimensions),
77 )
78 if config and config.inputStorageClass != self.inputData.storageClass:
79 self.inputData = cT.Input(
80 name=self.inputData.name,
81 doc=self.inputData.doc,
82 storageClass=config.inputStorageClass,
83 dimensions=self.inputData.dimensions,
84 )
85 self.outputData = cT.Output(
86 name=self.outputData.name,
87 doc=self.outputData.doc,
88 storageClass=config.inputStorageClass,
89 dimensions=self.outputData.dimensions,
90 )
93class BinImageDataConfig(
94 pipeBase.PipelineTaskConfig, pipelineConnections=BinImageDataConnections
95):
96 """Config for BinImageDataTask"""
98 inputDimensions = pexConfig.ListField(
99 # Sort to ensure default order is consistent between runs
100 default=sorted(BinImageDataConnections.dimensions),
101 dtype=str,
102 doc="Override for the dimensions of the input and output data.",
103 )
104 inputStorageClass = pexConfig.Field(
105 default="ExposureF",
106 dtype=str,
107 doc=(
108 "Override the storageClass of the input and output data. "
109 "Must be of type `Image`, `MaskedImage`, or `Exposure`, "
110 "or one of their subtypes."
111 ),
112 )
113 binFactor = pexConfig.Field(
114 dtype=int,
115 doc="Binning factor applied to both spatial dimensions.",
116 default=8,
117 check=lambda x: x > 1,
118 )
121class BinImageDataTask(pipeBase.PipelineTask):
122 """Perform an nxn binning of an image or image-like dataset.
124 The binning factor is the same in both spatial dimensions (i.e.,
125 an nxn binning is performed). In the case of MaskedImages and Exposures,
126 each of the input image planes are binned by the same factor.
127 """
129 ConfigClass = BinImageDataConfig
130 _DefaultName = "binImageData"
132 @timeMethod
133 def run(self, inputData, binFactor=None):
134 """Perform an nxn binning of image and image-like data.
136 Parameters:
137 -----------
138 inputData : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage` or
139 `lsst.afw.image.Exposure` or one of their sub-types.
140 Data to spatially bin
141 binFactor : `int`, optional.
142 nxn binning factor. If not provided then self.config.binFactor
143 is used.
145 Returns:
146 --------
147 result : `lsst.pipe.base.Struct`
148 Results as a struct with attributes:
150 ``outputData``
151 Binned data (`lsst.afw.image.Image` or
152 `lsst.afw.image.MaskedImage` or `lsst.afw.image.Exposure`
153 or one of their sub-types. The type matches that of the input.).
154 """
155 if not binFactor:
156 binFactor = self.config.binFactor
157 return pipeBase.Struct(outputData=binImageData(inputData, binFactor))
160def binImageData(inputData, binFactor=8):
161 """Bin image and image-like data to reduce its spatial dimensions.
163 Performs an nxn binning of the input data, reducing both spatial
164 dimensions of each of the input image data by the provided
165 factor.
167 Parameters:
168 -----------
169 inputData: `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage` or
170 `lsst.afw.image.Exposure` or one of their sub-types.
171 Input data to bin.
172 binFactor: `int`
173 Binning factor to apply to each input exposure's image data.
174 Default 8.
176 Returns:
177 --------
178 binnedImage or binnedExposure: `lsst.afw.image.Image` or
179 `lsst.afw.image.MaskedImage` or `lsst.afw.image.Exposure` or one of
180 their sub-types.
181 Binned version of input image.
183 Raises
184 ------
185 TypeError
186 Raised if either the binning factor is not of type `int`, or if the
187 input data to be binned is not of type `lsst.afw.image.Exposure`
188 or one of its sub-types.
189 """
191 if not isinstance(binFactor, int):
192 raise TypeError("binFactor must be of type int")
194 if isinstance(inputData, afwImage.Exposure):
195 inputImage = inputData.getMaskedImage()
196 isExposure = True
197 elif isinstance(inputData, (afwImage.Image, afwImage.MaskedImage)):
198 inputImage = inputData
199 isExposure = False
200 else:
201 message = (
202 "inputData must be of type `lsst.afw.image.Image`, `lsst.afw.MaskedImage`, "
203 "or `lsst.afw.image.Exposure`, or one of their sub-types."
204 )
205 raise TypeError(message)
207 binnedImage = afwMath.binImage(inputImage, binFactor)
209 if isExposure:
210 binnedExposure = afwImage.makeExposure(binnedImage)
211 binnedExposure.setInfo(inputData.getInfo())
212 return binnedExposure
213 else:
214 return binnedImage