Coverage for tests/test_kernelImagesForRegion.py: 17%
139 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-22 02:38 -0700
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-22 02:38 -0700
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#
22import math
23import unittest
25import numpy as np
27import lsst.utils.tests
28import lsst.geom
29import lsst.afw.image as afwImage
30import lsst.afw.math as afwMath
31import lsst.afw.math.detail as mathDetail
32from lsst.log import Log
34# Change the level to Log.DEBUG to see debug messages
35Log.getLogger("TRACE5.lsst.afw.math.convolve").setLevel(Log.INFO)
37LocNameDict = {
38 mathDetail.KernelImagesForRegion.BOTTOM_LEFT: "BOTTOM_LEFT",
39 mathDetail.KernelImagesForRegion.BOTTOM_RIGHT: "BOTTOM_RIGHT",
40 mathDetail.KernelImagesForRegion.TOP_LEFT: "TOP_LEFT",
41 mathDetail.KernelImagesForRegion.TOP_RIGHT: "TOP_RIGHT",
42}
44NameLocDict = dict((name, loc) for (loc, name) in LocNameDict.items())
47class KernelImagesForRegion(lsst.utils.tests.TestCase):
49 def setUp(self):
50 boxCorner = lsst.geom.Point2I(11, 50)
51 boxExtent = lsst.geom.Extent2I(100, 99)
52 self.bbox = lsst.geom.Box2I(boxCorner, boxExtent)
53 self.xy0 = lsst.geom.Point2I(100, 251)
54 self.kernel = self.makeKernel()
56 def tearDown(self):
57 self.bbox = None
58 self.kernel = None
60 def assertRegionCorrect(self, region):
61 """Assert that a region has correct corner images
63 This test is only relevant for operations that try to reuse the image array data
64 """
65 regionCopy = mathDetail.KernelImagesForRegion(
66 region.getKernel(), region.getBBox(), region.getXY0(), region.getDoNormalize())
68 for location in (
69 region.BOTTOM_LEFT,
70 region.BOTTOM_RIGHT,
71 region.TOP_LEFT,
72 region.TOP_RIGHT,
73 ):
74 actImage = region.getImage(location)
75 actImArr = actImage.getArray().transpose().copy()
76 desImage = regionCopy.getImage(location)
77 desImArr = desImage.getArray().transpose().copy()
78 actImArr -= desImArr
79 if not np.allclose(actImArr, 0):
80 actImage.writeFits(f"actImage{location}.fits")
81 desImage.writeFits(f"desImage{location}.fits")
82 self.fail(f"failed on location {location}")
84 def makeKernel(self):
85 kCols = 7
86 kRows = 6
88 # create spatial model
89 sFunc = afwMath.PolynomialFunction2D(1)
91 minSigma = 0.1
92 maxSigma = 3.0
94 # spatial parameters are a list of entries, one per kernel parameter;
95 # each entry is a list of spatial parameters
96 xSlope = (maxSigma - minSigma) / self.bbox.getWidth()
97 ySlope = (maxSigma - minSigma) / self.bbox.getHeight()
98 xOrigin = minSigma - (self.xy0[0] * xSlope)
99 yOrigin = minSigma - (self.xy0[1] * ySlope)
100 sParams = (
101 (xOrigin, xSlope, 0.0),
102 (yOrigin, 0.0, ySlope),
103 (0.0, 0.0, 0.0),
104 )
106 kFunc = afwMath.GaussianFunction2D(1.0, 1.0, 0.0)
107 kernel = afwMath.AnalyticKernel(kCols, kRows, kFunc, sFunc)
108 kernel.setSpatialParameters(sParams)
109 return kernel
111 def testDoNormalize(self):
112 """Test getDoNormalize
113 """
114 kernel = self.makeKernel()
115 for doNormalize in (False, True):
116 region = mathDetail.KernelImagesForRegion(
117 kernel, self.bbox, self.xy0, doNormalize)
118 self.assertEqual(region.getDoNormalize(), doNormalize)
120 def testGetPixelIndex(self):
121 """Test getPixelIndex method
122 """
123 region = mathDetail.KernelImagesForRegion(
124 self.kernel, self.bbox, self.xy0, False)
125 leftInd = self.bbox.getMinX()
126 rightInd = self.bbox.getMaxX() + 1
127 bottomInd = self.bbox.getMinY()
128 topInd = self.bbox.getMaxY() + 1
129 int(round((leftInd + rightInd) / 2.0))
130 int(round((bottomInd + topInd) / 2.0))
132 for location, desIndex in (
133 (region.BOTTOM_LEFT, (leftInd, bottomInd)),
134 (region.BOTTOM_RIGHT, (rightInd, bottomInd)),
135 (region.TOP_LEFT, (leftInd, topInd)),
136 (region.TOP_RIGHT, (rightInd, topInd)),
137 ):
138 desPixIndex = lsst.geom.Point2I(desIndex[0], desIndex[1])
139 self.assertEqual(region.getPixelIndex(location), desPixIndex,
140 f"getPixelIndex({LocNameDict[location]}) = {region.getPixelIndex(location)} "
141 f"!= desPixIndex")
143 def testComputeNextRow(self):
144 """Test computeNextRow method and the resulting RowOfKernelImagesForRegion
145 """
146 nx = 6
147 ny = 5
148 regionRow = mathDetail.RowOfKernelImagesForRegion(nx, ny)
149 self.assertFalse(regionRow.hasData())
150 self.assertFalse(regionRow.isLastRow())
151 self.assertEqual(regionRow.getYInd(), -1)
153 region = mathDetail.KernelImagesForRegion(
154 self.kernel, self.bbox, self.xy0, False)
155 floatWidth = self.bbox.getWidth() / float(nx)
156 validWidths = (int(math.floor(floatWidth)), int(math.ceil(floatWidth)))
157 floatHeight = self.bbox.getHeight() / float(ny)
158 validHeights = (int(math.floor(floatHeight)),
159 int(math.ceil(floatHeight)))
161 totalHeight = 0
162 prevBBox = None
163 prevFirstBBox = None
164 for yInd in range(ny):
165 rowWidth = 0
166 isOK = region.computeNextRow(regionRow)
167 self.assertTrue(isOK)
168 self.assertTrue(regionRow.hasData())
169 self.assertEqual(regionRow.isLastRow(), (yInd + 1 >= ny))
170 self.assertEqual(regionRow.getYInd(), yInd)
171 firstBBox = regionRow.getRegion(0).getBBox()
172 self.assertEqual(firstBBox.getMinX(), self.bbox.getMinX())
173 if yInd == 0:
174 self.assertEqual(firstBBox.getMinY(), self.bbox.getMinY())
175 firstBBoxHeight = firstBBox.getHeight()
176 self.assertTrue(firstBBoxHeight in validHeights)
177 totalHeight += firstBBoxHeight
178 if yInd > 0:
179 self.assertEqual(firstBBox.getMinY(),
180 prevFirstBBox.getMaxY() + 1)
181 if yInd == ny - 1:
182 self.assertEqual(firstBBox.getMaxY(), self.bbox.getMaxY())
183 prevFirstBBox = firstBBox
184 for xInd in range(nx):
185 subregion = regionRow.getRegion(xInd)
186 try:
187 self.assertRegionCorrect(subregion)
188 except Exception:
189 print(f"failed on xInd={xInd}, yInd={yInd}")
190 raise
191 bbox = subregion.getBBox()
192 rowWidth += bbox.getWidth()
193 self.assertTrue(bbox.getWidth() in validWidths)
194 self.assertEqual(bbox.getHeight(), firstBBoxHeight)
195 if xInd > 0:
196 self.assertEqual(bbox.getMinX(), prevBBox.getMaxX() + 1)
197 self.assertEqual(bbox.getMinY(), prevBBox.getMinY())
198 self.assertEqual(bbox.getMaxY(), prevBBox.getMaxY())
199 if xInd == nx - 1:
200 self.assertEqual(bbox.getMaxX(), self.bbox.getMaxX())
201 prevBBox = bbox
202 self.assertEqual(rowWidth, self.bbox.getWidth())
203 self.assertEqual(totalHeight, self.bbox.getHeight())
204 self.assertTrue(not region.computeNextRow(regionRow))
206 def testExactImages(self):
207 """Confirm that kernel image at each location is correct
208 """
209 desImage = afwImage.ImageD(lsst.geom.Extent2I(
210 self.kernel.getWidth(), self.kernel.getHeight()))
212 for doNormalize in (False, True):
213 region = mathDetail.KernelImagesForRegion(
214 self.kernel, self.bbox, self.xy0, doNormalize)
215 for location in (
216 region.BOTTOM_LEFT,
217 region.BOTTOM_RIGHT,
218 region.TOP_LEFT,
219 region.TOP_RIGHT,
220 ):
221 pixelIndex = region.getPixelIndex(location)
222 xPos = afwImage.indexToPosition(pixelIndex[0] + self.xy0[0])
223 yPos = afwImage.indexToPosition(pixelIndex[1] + self.xy0[1])
224 self.kernel.computeImage(desImage, doNormalize, xPos, yPos)
226 actImage = region.getImage(location)
227 msg = f"exact image({LocNameDict[location]}) incorrect"
228 self.assertImagesAlmostEqual(actImage, desImage, msg=msg)
231class TestMemory(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()