Coverage for tests / test_copyGoodPixels.py: 17%
131 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:48 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:48 +0000
1#
2# LSST Data Management System
3# Copyright 2008, 2009, 2010 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#
23"""Test lsst.coadd.utils.copyGoodPixels
24"""
25import unittest
27import numpy as np
29import lsst.utils.tests
30import lsst.geom as geom
31import lsst.afw.image as afwImage
32import lsst.coadd.utils as coaddUtils
34try:
35 display
36except NameError:
37 display = False
40def referenceCopyGoodPixelsImage(destImage, srcImage):
41 """Reference implementation of lsst.coadd.utils.copyGoodPixels for Images
43 Unlike lsst.coadd.utils.copyGoodPixels this one does not update the input destImage,
44 but instead returns the new version
46 Inputs:
47 - destImage: source image before adding srcImage (a MaskedImage)
48 - srcImage: masked image to add to destImage (a MaskedImage)
49 - badPixelMask: mask of bad pixels to ignore (an int)
51 Returns:
52 - destImage: new destImage
53 - numGoodPix: number of good pixels
54 """
55 destImage = destImage.Factory(destImage, True) # make deep copy
57 overlapBBox = destImage.getBBox()
58 overlapBBox.clip(srcImage.getBBox())
60 if overlapBBox.isEmpty():
61 return (destImage, 0)
63 destImageView = destImage.Factory(destImage, overlapBBox, afwImage.PARENT, False)
64 destImageArray = destImageView.array
66 srcImageView = srcImage.Factory(srcImage, overlapBBox, afwImage.PARENT, False)
67 srcImageArray = srcImageView.array
69 isBadArray = np.isnan(srcImageArray)
71 destImageArray[:] = np.where(isBadArray, destImageArray, srcImageArray)
72 numGoodPix = np.sum(np.logical_not(isBadArray))
73 return destImage, numGoodPix
76def referenceCopyGoodPixelsMaskedImage(destImage, srcImage, badPixelMask):
77 """Reference implementation of lsst.coadd.utils.copyGoodPixels for MaskedImages
79 Unlike lsst.coadd.utils.copyGoodPixels this one does not update the input destImage,
80 but instead returns an updated copy
82 @param[in] destImage: source image before adding srcImage (a MaskedImage)
83 @param[in] srcImage: masked image to add to destImage (a MaskedImage)
84 @param[in] badPixelMask: mask of bad pixels to ignore (an int)
86 Returns:
87 - destImage: new destImage
88 - numGoodPix: number of good pixels
89 """
90 destImage = destImage.Factory(destImage, True) # make deep copy
92 overlapBBox = destImage.getBBox()
93 overlapBBox.clip(srcImage.getBBox())
95 if overlapBBox.isEmpty():
96 return (destImage, 0)
98 destImageView = destImage.Factory(destImage, overlapBBox, afwImage.PARENT, False)
99 srcImageView = srcImage.Factory(srcImage, overlapBBox, afwImage.PARENT, False)
101 isBadArray = (srcImageView.mask.array & badPixelMask) != 0
103 destImageView.image.array = np.where(isBadArray, destImageView.image.array, srcImageView.image.array)
104 destImageView.mask.array = np.where(isBadArray, destImageView.mask.array, srcImageView.mask.array)
105 destImageView.variance.array = np.where(isBadArray,
106 destImageView.variance.array,
107 srcImageView.variance.array)
109 numGoodPix = np.sum(np.logical_not(isBadArray))
110 return destImage, numGoodPix
113MaxMask = 0xFFFF
116class CopyGoodPixelsTestCase(lsst.utils.tests.TestCase):
117 """A test case for copyGoodPixels
118 """
120 def getSolidMaskedImage(self, bbox, val, badMask=0):
121 afwDim = bbox.getDimensions()
122 npShape = (afwDim[1], afwDim[0])
124 np.random.seed(0)
125 maskedImage = afwImage.MaskedImageF(bbox)
126 maskedImage.image.array[:] = val
127 maskedImage.variance.array[:] = val * 0.5
128 maskedImage.mask.array[:, 0:npShape[1]/2] = 0
129 maskedImage.mask.array[:, npShape[1]/2:] = badMask
130 return maskedImage
132 def getRandomMaskedImage(self, bbox, excludeMask=0):
133 """Get a randomly generated masked image
134 """
135 if excludeMask > MaxMask:
136 raise RuntimeError("excludeMask = %s > %s = MaxMask" % (excludeMask, MaxMask))
138 afwDim = bbox.getDimensions()
139 npShape = (afwDim[1], afwDim[0])
141 np.random.seed(0)
142 maskedImage = afwImage.MaskedImageF(bbox)
143 maskedImage.image.array[:] = np.random.normal(5000, 5000, npShape)
144 maskedImage.variance.array[:] = np.random.normal(3000, 3000, npShape)
145 maskedImage.mask.array[:] = np.logical_and(np.random.randint(0, 8, npShape), ~excludeMask)
146 return maskedImage
148 def getRandomImage(self, bbox, nanSigma=0):
149 """Get a randomly generated image
150 """
151 afwDim = bbox.getDimensions()
152 npShape = (afwDim[1], afwDim[0])
154 np.random.seed(0)
155 image = afwImage.ImageF(bbox)
156 imageArray = image.array
157 imageArray[:] = np.random.normal(5000, 5000, npShape)
158 if nanSigma > 0:
159 # add NaNs at nanSigma above mean of a test array
160 nanTest = np.random.normal(0, 1, npShape)
161 imageArray[:] = np.where(nanTest > nanSigma, np.nan, imageArray)
162 return image
164 def basicMaskedImageTest(self, srcImage, destImage, badMask):
165 refDestImage, refNumGoodPix = referenceCopyGoodPixelsMaskedImage(destImage, srcImage, badMask)
166 numGoodPix = coaddUtils.copyGoodPixels(destImage, srcImage, badMask)
168 self.assertEqual(numGoodPix, refNumGoodPix)
170 msg = "masked image != reference masked image"
171 try:
172 self.assertMaskedImagesAlmostEqual(destImage, refDestImage, msg=msg)
173 except Exception:
174 destImage.writeFits("destMaskedImage.fits")
175 refDestImage.writeFits("refDestMaskedImage.fits")
176 raise
178 def basicImageTest(self, srcImage, destImage):
179 refDestImage, refNumGoodPix = referenceCopyGoodPixelsImage(destImage, srcImage)
180 numGoodPix = coaddUtils.copyGoodPixels(destImage, srcImage)
182 msg = "image != reference image"
183 try:
184 self.assertImagesAlmostEqual(destImage, refDestImage, msg=msg)
185 except Exception:
186 destImage.writeFits("destImage.fits")
187 refDestImage.writeFits("refDestImage.fits")
188 raise
190 self.assertEqual(numGoodPix, refNumGoodPix)
192 def testMaskedImage(self):
193 """Test image version of copyGoodPixels"""
194 srcBBox = geom.Box2I(geom.Point2I(2, 17), geom.Point2I(100, 101))
195 destBBox = geom.Box2I(geom.Point2I(13, 4), geom.Point2I(95, 130))
196 destXY0 = destBBox.getMin()
198 srcImage = self.getRandomMaskedImage(srcBBox)
199 for badMask in (0, 3, MaxMask):
200 destImage = self.getRandomMaskedImage(destBBox, excludeMask=badMask)
201 destBBox = destImage.getBBox()
202 self.basicMaskedImageTest(srcImage, destImage, badMask)
204 for bboxStart in (destXY0, (50, 51)):
205 for bboxDim in ((25, 36), (200, 200)):
206 destViewBox = geom.Box2I(geom.Point2I(*bboxStart), geom.Extent2I(*bboxDim))
207 destViewBox.clip(destBBox)
208 destView = destImage.Factory(destImage, destViewBox, afwImage.PARENT, False)
209 self.basicMaskedImageTest(srcImage, destView, badMask)
211 def testImage(self):
212 """Test image version of copyGoodPixels"""
213 srcBBox = geom.Box2I(geom.Point2I(2, 17), geom.Point2I(100, 101))
214 destBBox = geom.Box2I(geom.Point2I(13, 4), geom.Point2I(95, 130))
215 destXY0 = destBBox.getMin()
217 srcImage = self.getRandomImage(srcBBox)
218 for nanSigma in (0, 0.7, 2.0):
219 destImage = self.getRandomImage(destBBox, nanSigma=nanSigma)
220 destBBox = destImage.getBBox()
221 self.basicImageTest(srcImage, destImage)
223 for bboxStart in (destXY0, (50, 51)):
224 for bboxDim in ((25, 36), (200, 200)):
225 destViewBox = geom.Box2I(geom.Point2I(*bboxStart), geom.Extent2I(*bboxDim))
226 destViewBox.clip(destBBox)
227 destView = destImage.Factory(destImage, destViewBox, afwImage.PARENT, False)
228 self.basicImageTest(srcImage, destView)
231class MemoryTester(lsst.utils.tests.MemoryTestCase):
232 pass
235def setup_module(module):
236 lsst.utils.tests.init()
239if __name__ == "__main__": 239 ↛ 240line 239 didn't jump to line 240 because the condition on line 239 was never true
240 lsst.utils.tests.init()
241 unittest.main()