25 __all__ = [
"assertImagesAlmostEqual",
"assertImagesEqual",
"assertMasksEqual",
26 "assertMaskedImagesAlmostEqual",
"assertMaskedImagesEqual"]
31 from .image
import ImageF
32 from .basicUtils
import makeMaskedImageFromArrays
36 """Make a gaussian noise MaskedImageF
39 - dimensions: dimensions of output array (cols, rows)
40 - sigma; sigma of image plane's noise distribution
41 - variance: constant value for variance plane
43 npSize = (dimensions[1], dimensions[0])
44 image = np.random.normal(loc=0.0, scale=sigma,
45 size=npSize).astype(np.float32)
46 mask = np.zeros(npSize, dtype=np.int32)
47 variance = np.zeros(npSize, dtype=np.float32) + variance
53 """!Make an image whose values are a linear ramp
55 @param[in] bbox bounding box of image (an lsst.geom.Box2I)
56 @param[in] start starting ramp value, inclusive
57 @param[in] stop ending ramp value, inclusive; if None, increase by integer values
58 @param[in] imageClass type of image (e.g. lsst.afw.image.ImageF)
61 imDim = im.getDimensions()
62 numPix = imDim[0]*imDim[1]
66 stop = start + numPix - 1
67 rampArr = np.linspace(start=start, stop=stop,
68 endpoint=
True, num=numPix, dtype=imArr.dtype)
70 imArr[:] = np.reshape(rampArr, (imDim[1], imDim[0]))
74 @lsst.utils.tests.inTestCase
76 rtol=1.0e-05, atol=1e-08, msg="Images differ"):
77 """!Assert that two images are almost equal, including non-finite values
79 @param[in] testCase unittest.TestCase instance the test is part of;
80 an object supporting one method: fail(self, msgStr)
81 @param[in] image0 image 0, an lsst.afw.image.Image, lsst.afw.image.Mask,
82 or transposed numpy array (see warning)
83 @param[in] image1 image 1, an lsst.afw.image.Image, lsst.afw.image.Mask,
84 or transposed numpy array (see warning)
85 @param[in] skipMask mask of pixels to skip, or None to compare all pixels;
86 an lsst.afw.image.Mask, lsst.afw.image.Image, or transposed numpy array (see warning);
87 all non-zero pixels are skipped
88 @param[in] rtol maximum allowed relative tolerance; more info below
89 @param[in] atol maximum allowed absolute tolerance; more info below
90 @param[in] msg exception message prefix; details of the error are appended after ": "
92 The images are nearly equal if all pixels obey:
93 |val1 - val0| <= rtol*|val1| + atol
94 or, for float types, if nan/inf/-inf pixels match.
96 @warning the comparison equation is not symmetric, so in rare cases the assertion
97 may give different results depending on which image comes first.
99 @warning the axes of numpy arrays are transposed with respect to Image and Mask data.
100 Thus for example if image0 and image1 are both lsst.afw.image.ImageD with dimensions (2, 3)
101 and skipMask is a numpy array, then skipMask must have shape (3, 2).
103 @throw self.failureException (usually AssertionError) if any of the following are true
104 for un-skipped pixels:
105 - non-finite values differ in any way (e.g. one is "nan" and another is not)
106 - finite values differ by too much, as defined by atol and rtol
108 @throw TypeError if the dimensions of image0, image1 and skipMask do not match,
109 or any are not of a numeric data type.
112 image0, image1, skipMask=skipMask, rtol=rtol, atol=atol)
114 testCase.fail(f
"{msg}: {errStr}")
117 @lsst.utils.tests.inTestCase
119 """!Assert that two images are exactly equal, including non-finite values.
121 All arguments are forwarded to assertAnglesAlmostEqual aside from atol and rtol,
122 which are set to zero.
127 @lsst.utils.tests.inTestCase
129 """!Assert that two masks are equal
131 @param[in] testCase unittest.TestCase instance the test is part of;
132 an object supporting one method: fail(self, msgStr)
133 @param[in] mask0 mask 0, an lsst.afw.image.Mask, lsst.afw.image.Image,
134 or transposed numpy array (see warning)
135 @param[in] mask1 mask 1, an lsst.afw.image.Mask, lsst.afw.image.Image,
136 or transposed numpy array (see warning)
137 @param[in] skipMask mask of pixels to skip, or None to compare all pixels;
138 an lsst.afw.image.Mask, lsst.afw.image.Image, or transposed numpy array (see warning);
139 all non-zero pixels are skipped
140 @param[in] msg exception message prefix; details of the error are appended after ": "
142 @warning the axes of numpy arrays are transposed with respect to Mask and Image.
143 Thus for example if mask0 and mask1 are both lsst.afw.image.Mask with dimensions (2, 3)
144 and skipMask is a numpy array, then skipMask must have shape (3, 2).
146 @throw self.failureException (usually AssertionError) if any any un-skipped pixels differ
148 @throw TypeError if the dimensions of mask0, mask1 and skipMask do not match,
149 or any are not of a numeric data type.
151 errStr =
imagesDiffer(mask0, mask1, skipMask=skipMask, rtol=0, atol=0)
153 testCase.fail(f
"{msg}: {errStr}")
156 @lsst.utils.tests.inTestCase
158 testCase, maskedImage0, maskedImage1,
159 doImage=True, doMask=True, doVariance=True, skipMask=None,
160 rtol=1.0e-05, atol=1e-08, msg="Masked images differ",
162 """!Assert that two masked images are nearly equal, including non-finite values
164 @param[in] testCase unittest.TestCase instance the test is part of;
165 an object supporting one method: fail(self, msgStr)
166 @param[in] maskedImage0 masked image 0 (an lsst.afw.image.MaskedImage or
167 collection of three transposed numpy arrays: image, mask, variance)
168 @param[in] maskedImage1 masked image 1 (an lsst.afw.image.MaskedImage or
169 collection of three transposed numpy arrays: image, mask, variance)
170 @param[in] doImage compare image planes if True
171 @param[in] doMask compare mask planes if True
172 @param[in] doVariance compare variance planes if True
173 @param[in] skipMask mask of pixels to skip, or None to compare all pixels;
174 an lsst.afw.image.Mask, lsst.afw.image.Image, or transposed numpy array;
175 all non-zero pixels are skipped
176 @param[in] rtol maximum allowed relative tolerance; more info below
177 @param[in] atol maximum allowed absolute tolerance; more info below
178 @param[in] msg exception message prefix; details of the error are appended after ": "
180 The mask planes must match exactly. The image and variance planes are nearly equal if all pixels obey:
181 |val1 - val0| <= rtol*|val1| + atol
182 or, for float types, if nan/inf/-inf pixels match.
184 @warning the comparison equation is not symmetric, so in rare cases the assertion
185 may give different results depending on which masked image comes first.
187 @warning the axes of numpy arrays are transposed with respect to MaskedImage data.
188 Thus for example if maskedImage0 and maskedImage1 are both lsst.afw.image.MaskedImageD
189 with dimensions (2, 3) and skipMask is a numpy array, then skipMask must have shape (3, 2).
191 @throw self.failureException (usually AssertionError) if any of the following are true
192 for un-skipped pixels:
193 - non-finite image or variance values differ in any way (e.g. one is "nan" and another is not)
194 - finite values differ by too much, as defined by atol and rtol
195 - mask pixels differ at all
197 @throw TypeError if the dimensions of maskedImage0, maskedImage1 and skipMask do not match,
198 either image or variance plane is not of a numeric data type,
199 either mask plane is not of an integer type (unsigned or signed),
200 or skipMask is not of a numeric data type.
202 maskedImageArrList0 = maskedImage0.getArrays()
if hasattr(
203 maskedImage0,
"getArrays")
else maskedImage0
204 maskedImageArrList1 = maskedImage1.getArrays()
if hasattr(
205 maskedImage1,
"getArrays")
else maskedImage1
207 for arrList, arg, name
in (
208 (maskedImageArrList0, maskedImage0,
"maskedImage0"),
209 (maskedImageArrList1, maskedImage1,
"maskedImage1"),
212 assert len(arrList) == 3
217 assert arrList[i].shape == arrList[1].shape
218 assert arrList[i].dtype.kind
in (
"b",
"i",
"u",
"f",
"c")
219 assert arrList[1].dtype.kind
in (
"b",
"i",
"u")
221 raise TypeError(f
"{name}={arg!r} is not a supported type")
224 for ind, (doPlane, planeName)
in enumerate(((doImage,
"image"),
226 (doVariance,
"variance"))):
230 if planeName ==
"mask":
231 errStr =
imagesDiffer(maskedImageArrList0[ind], maskedImageArrList1[ind], skipMask=skipMask,
234 errStrList.append(errStr)
236 errStr =
imagesDiffer(maskedImageArrList0[ind], maskedImageArrList1[ind],
237 skipMask=skipMask, rtol=rtol, atol=atol)
239 errStrList.append(f
"{planeName} planes differ: {errStr}")
242 errStr =
"; ".join(errStrList)
243 testCase.fail(f
"{msg}: {errStr}")
246 @lsst.utils.tests.inTestCase
248 """!Assert that two masked images are exactly equal, including non-finite values.
250 All arguments are forwarded to assertMaskedImagesAlmostEqual aside from atol and rtol,
251 which are set to zero.
256 def imagesDiffer(image0, image1, skipMask=None, rtol=1.0e-05, atol=1e-08):
257 """!Compare the pixels of two image or mask arrays; return True if close, False otherwise
259 @param[in] image0 image 0, an lsst.afw.image.Image, lsst.afw.image.Mask,
260 or transposed numpy array (see warning)
261 @param[in] image1 image 1, an lsst.afw.image.Image, lsst.afw.image.Mask,
262 or transposed numpy array (see warning)
263 @param[in] skipMask mask of pixels to skip, or None to compare all pixels;
264 an lsst.afw.image.Mask, lsst.afw.image.Image, or transposed numpy array (see warning);
265 all non-zero pixels are skipped
266 @param[in] rtol maximum allowed relative tolerance; more info below
267 @param[in] atol maximum allowed absolute tolerance; more info below
269 The images are nearly equal if all pixels obey:
270 |val1 - val0| <= rtol*|val1| + atol
271 or, for float types, if nan/inf/-inf pixels match.
273 @warning the comparison equation is not symmetric, so in rare cases the assertion
274 may give different results depending on which image comes first.
276 @warning the axes of numpy arrays are transposed with respect to Image and Mask data.
277 Thus for example if image0 and image1 are both lsst.afw.image.ImageD with dimensions (2, 3)
278 and skipMask is a numpy array, then skipMask must have shape (3, 2).
280 @return a string which is non-empty if the images differ
282 @throw TypeError if the dimensions of image0, image1 and skipMask do not match,
283 or any are not of a numeric data type.
286 imageArr0 = image0.getArray()
if hasattr(image0,
"getArray")
else image0
287 imageArr1 = image1.getArray()
if hasattr(image1,
"getArray")
else image1
288 skipMaskArr = skipMask.getArray()
if hasattr(skipMask,
"getArray")
else skipMask
292 (imageArr0, image0,
"image0"),
293 (imageArr1, image1,
"image1"),
295 if skipMask
is not None:
296 arrArgNameList.append((skipMaskArr, skipMask,
"skipMask"))
297 for i, (arr, arg, name)
in enumerate(arrArgNameList):
299 assert arr.dtype.kind
in (
"b",
"i",
"u",
"f",
"c")
301 raise TypeError(f
"{name!r}={arg!r} is not a supported type")
303 if arr.shape != imageArr0.shape:
304 raise TypeError(f
"{name} shape = {arr.shape} != {imageArr0.shape} = image0 shape")
310 if imageArr0.dtype.kind ==
"u":
311 imageArr0 = imageArr0.astype(
312 np.promote_types(imageArr0.dtype, np.int8))
313 if imageArr1.dtype.kind ==
"u":
314 imageArr1 = imageArr1.astype(
315 np.promote_types(imageArr1.dtype, np.int8))
317 if skipMaskArr
is not None:
318 skipMaskArr = np.array(skipMaskArr, dtype=bool)
319 maskedArr0 = np.ma.array(imageArr0, copy=
False, mask=skipMaskArr)
320 maskedArr1 = np.ma.array(imageArr1, copy=
False, mask=skipMaskArr)
321 filledArr0 = maskedArr0.filled(0.0)
322 filledArr1 = maskedArr1.filled(0.0)
325 filledArr0 = imageArr0
326 filledArr1 = imageArr1
329 np.array([np.nan], dtype=imageArr0.dtype)
330 np.array([np.nan], dtype=imageArr1.dtype)
334 valSkipMaskArr = skipMaskArr
338 nan0 = np.isnan(filledArr0)
339 nan1 = np.isnan(filledArr1)
340 if np.any(nan0 != nan1):
341 errStrList.append(
"NaNs differ")
343 posinf0 = np.isposinf(filledArr0)
344 posinf1 = np.isposinf(filledArr1)
345 if np.any(posinf0 != posinf1):
346 errStrList.append(
"+infs differ")
348 neginf0 = np.isneginf(filledArr0)
349 neginf1 = np.isneginf(filledArr1)
350 if np.any(neginf0 != neginf1):
351 errStrList.append(
"-infs differ")
353 valSkipMaskArr = nan0 | nan1 | posinf0 | posinf1 | neginf0 | neginf1
354 if skipMaskArr
is not None:
355 valSkipMaskArr |= skipMaskArr
358 valMaskedArr1 = np.ma.array(imageArr0, copy=
False, mask=valSkipMaskArr)
359 valMaskedArr2 = np.ma.array(imageArr1, copy=
False, mask=valSkipMaskArr)
360 valFilledArr1 = valMaskedArr1.filled(0.0)
361 valFilledArr2 = valMaskedArr2.filled(0.0)
363 if not np.allclose(valFilledArr1, valFilledArr2, rtol=rtol, atol=atol):
364 errArr = np.abs(valFilledArr1 - valFilledArr2)
365 maxErr = errArr.max()
366 maxPosInd = np.where(errArr == maxErr)
367 maxPosTuple = (maxPosInd[1][0], maxPosInd[0][0])
368 errStr = f
"maxDiff={maxErr} at position {maxPosTuple}; " \
369 f
"value={valFilledArr1[maxPosInd][0]} vs. {valFilledArr2[maxPosInd][0]}"
370 errStrList.insert(0, errStr)
372 return "; ".join(errStrList)