24__all__ = [
"assertImagesAlmostEqual",
"assertImagesEqual",
"assertMasksEqual",
25 "assertMaskedImagesAlmostEqual",
"assertMaskedImagesEqual"]
30from .image
import ImageF
31from .basicUtils
import makeMaskedImageFromArrays
35 """Make a gaussian noise MaskedImageF
38 - dimensions: dimensions of output array (cols, rows)
39 - sigma; sigma of image plane's noise distribution
40 - variance: constant value for variance plane
42 npSize = (dimensions[1], dimensions[0])
43 image = np.random.normal(loc=0.0, scale=sigma,
44 size=npSize).astype(np.float32)
45 mask = np.zeros(npSize, dtype=np.int32)
46 variance = np.zeros(npSize, dtype=np.float32) + variance
52 """!Make an image whose values are a linear ramp
55 @param[
in] start starting ramp value, inclusive
56 @param[
in] stop ending ramp value, inclusive;
if None, increase by integer values
57 @param[
in] imageClass type of image (e.g. lsst.afw.image.ImageF)
60 imDim = im.getDimensions()
61 numPix = imDim[0]*imDim[1]
65 stop = start + numPix - 1
66 rampArr = np.linspace(start=start, stop=stop,
67 endpoint=
True, num=numPix, dtype=imArr.dtype)
69 imArr[:] = np.reshape(rampArr, (imDim[1], imDim[0]))
73@lsst.utils.tests.inTestCase
75 rtol=1.0e-05, atol=1e-08, msg="Images differ"):
76 """!Assert that two images are almost equal, including non-finite values
78 @param[
in] testCase unittest.TestCase instance the test
is part of;
79 an object supporting one method: fail(self, msgStr)
81 or transposed numpy array (see warning)
83 or transposed numpy array (see warning)
84 @param[
in] skipMask mask of pixels to skip,
or None to compare all pixels;
86 all non-zero pixels are skipped
87 @param[
in] rtol maximum allowed relative tolerance; more info below
88 @param[
in] atol maximum allowed absolute tolerance; more info below
89 @param[
in] msg exception message prefix; details of the error are appended after
": "
91 The images are nearly equal
if all pixels obey:
92 |val1 - val0| <= rtol*|val1| + atol
93 or,
for float types,
if nan/inf/-inf pixels match.
95 @warning the comparison equation
is not symmetric, so
in rare cases the assertion
96 may give different results depending on which image comes first.
98 @warning the axes of numpy arrays are transposed
with respect to Image
and Mask data.
99 Thus
for example
if image0
and image1 are both lsst.afw.image.ImageD
with dimensions (2, 3)
100 and skipMask
is a numpy array, then skipMask must have shape (3, 2).
102 @throw self.failureException (usually AssertionError)
if any of the following are true
103 for un-skipped pixels:
104 - non-finite values differ
in any way (e.g. one
is "nan" and another
is not)
105 - finite values differ by too much,
as defined by atol
and rtol
107 @throw TypeError
if the dimensions of image0, image1
and skipMask do
not match,
108 or any are
not of a numeric data type.
111 image0, image1, skipMask=skipMask, rtol=rtol, atol=atol)
113 testCase.fail(f
"{msg}: {errStr}")
116@lsst.utils.tests.inTestCase
118 """!Assert that two images are exactly equal, including non-finite values.
120 All arguments are forwarded to assertAnglesAlmostEqual aside from atol
and rtol,
121 which are set to zero.
126@lsst.utils.tests.inTestCase
128 """!Assert that two masks are equal
130 @param[
in] testCase unittest.TestCase instance the test
is part of;
131 an object supporting one method: fail(self, msgStr)
133 or transposed numpy array (see warning)
135 or transposed numpy array (see warning)
136 @param[
in] skipMask mask of pixels to skip,
or None to compare all pixels;
138 all non-zero pixels are skipped
139 @param[
in] msg exception message prefix; details of the error are appended after
": "
141 @warning the axes of numpy arrays are transposed
with respect to Mask
and Image.
143 and skipMask
is a numpy array, then skipMask must have shape (3, 2).
145 @throw self.failureException (usually AssertionError)
if any any un-skipped pixels differ
147 @throw TypeError
if the dimensions of mask0, mask1
and skipMask do
not match,
148 or any are
not of a numeric data type.
150 errStr = imagesDiffer(mask0, mask1, skipMask=skipMask, rtol=0, atol=0)
152 testCase.fail(f
"{msg}: {errStr}")
155@lsst.utils.tests.inTestCase
157 testCase, maskedImage0, maskedImage1,
158 doImage=True, doMask=True, doVariance=True, skipMask=None,
159 rtol=1.0e-05, atol=1e-08, msg="Masked images differ",
161 """!Assert that two masked images are nearly equal, including non-finite values
163 @param[
in] testCase unittest.TestCase instance the test
is part of;
164 an object supporting one method: fail(self, msgStr)
166 collection of three transposed numpy arrays: image, mask, variance)
168 collection of three transposed numpy arrays: image, mask, variance)
169 @param[
in] doImage compare image planes
if True
170 @param[
in] doMask compare mask planes
if True
171 @param[
in] doVariance compare variance planes
if True
172 @param[
in] skipMask mask of pixels to skip,
or None to compare all pixels;
174 all non-zero pixels are skipped
175 @param[
in] rtol maximum allowed relative tolerance; more info below
176 @param[
in] atol maximum allowed absolute tolerance; more info below
177 @param[
in] msg exception message prefix; details of the error are appended after
": "
179 The mask planes must match exactly. The image
and variance planes are nearly equal
if all pixels obey:
180 |val1 - val0| <= rtol*|val1| + atol
181 or,
for float types,
if nan/inf/-inf pixels match.
183 @warning the comparison equation
is not symmetric, so
in rare cases the assertion
184 may give different results depending on which masked image comes first.
186 @warning the axes of numpy arrays are transposed
with respect to MaskedImage data.
187 Thus
for example
if maskedImage0
and maskedImage1 are both lsst.afw.image.MaskedImageD
188 with dimensions (2, 3)
and skipMask
is a numpy array, then skipMask must have shape (3, 2).
190 @throw self.failureException (usually AssertionError)
if any of the following are true
191 for un-skipped pixels:
192 - non-finite image
or variance values differ
in any way (e.g. one
is "nan" and another
is not)
193 - finite values differ by too much,
as defined by atol
and rtol
194 - mask pixels differ at all
196 @throw TypeError
if the dimensions of maskedImage0, maskedImage1
and skipMask do
not match,
197 either image
or variance plane
is not of a numeric data type,
198 either mask plane
is not of an integer type (unsigned
or signed),
199 or skipMask
is not of a numeric data type.
201 maskedImageArrList0 = maskedImage0.getArrays() if hasattr(
202 maskedImage0,
"getArrays")
else maskedImage0
203 maskedImageArrList1 = maskedImage1.getArrays()
if hasattr(
204 maskedImage1,
"getArrays")
else maskedImage1
206 for arrList, arg, name
in (
207 (maskedImageArrList0, maskedImage0,
"maskedImage0"),
208 (maskedImageArrList1, maskedImage1,
"maskedImage1"),
211 assert len(arrList) == 3
216 assert arrList[i].shape == arrList[1].shape
217 assert arrList[i].dtype.kind
in (
"b",
"i",
"u",
"f",
"c")
218 assert arrList[1].dtype.kind
in (
"b",
"i",
"u")
220 raise TypeError(f
"{name}={arg!r} is not a supported type")
223 for ind, (doPlane, planeName)
in enumerate(((doImage,
"image"),
225 (doVariance,
"variance"))):
229 if planeName ==
"mask":
230 errStr =
imagesDiffer(maskedImageArrList0[ind], maskedImageArrList1[ind], skipMask=skipMask,
233 errStrList.append(errStr)
235 errStr =
imagesDiffer(maskedImageArrList0[ind], maskedImageArrList1[ind],
236 skipMask=skipMask, rtol=rtol, atol=atol)
238 errStrList.append(f
"{planeName} planes differ: {errStr}")
241 errStr =
"; ".join(errStrList)
242 testCase.fail(f
"{msg}: {errStr}")
245@lsst.utils.tests.inTestCase
247 """!Assert that two masked images are exactly equal, including non-finite values.
249 All arguments are forwarded to assertMaskedImagesAlmostEqual aside from atol
and rtol,
250 which are set to zero.
255def imagesDiffer(image0, image1, skipMask=None, rtol=1.0e-05, atol=1e-08):
256 """!Compare the pixels of two image or mask arrays; return True if close, False otherwise
259 or transposed numpy array (see warning)
261 or transposed numpy array (see warning)
262 @param[
in] skipMask mask of pixels to skip,
or None to compare all pixels;
264 all non-zero pixels are skipped
265 @param[
in] rtol maximum allowed relative tolerance; more info below
266 @param[
in] atol maximum allowed absolute tolerance; more info below
268 The images are nearly equal
if all pixels obey:
269 |val1 - val0| <= rtol*|val1| + atol
270 or,
for float types,
if nan/inf/-inf pixels match.
272 @warning the comparison equation
is not symmetric, so
in rare cases the assertion
273 may give different results depending on which image comes first.
275 @warning the axes of numpy arrays are transposed
with respect to Image
and Mask data.
276 Thus
for example
if image0
and image1 are both lsst.afw.image.ImageD
with dimensions (2, 3)
277 and skipMask
is a numpy array, then skipMask must have shape (3, 2).
279 @return a string which
is non-empty
if the images differ
281 @throw TypeError
if the dimensions of image0, image1
and skipMask do
not match,
282 or any are
not of a numeric data type.
285 imageArr0 = image0.getArray() if hasattr(image0,
"getArray")
else image0
286 imageArr1 = image1.getArray()
if hasattr(image1,
"getArray")
else image1
287 skipMaskArr = skipMask.getArray()
if hasattr(skipMask,
"getArray")
else skipMask
291 (imageArr0, image0,
"image0"),
292 (imageArr1, image1,
"image1"),
294 if skipMask
is not None:
295 arrArgNameList.append((skipMaskArr, skipMask,
"skipMask"))
296 for i, (arr, arg, name)
in enumerate(arrArgNameList):
298 assert arr.dtype.kind
in (
"b",
"i",
"u",
"f",
"c")
300 raise TypeError(f
"{name!r}={arg!r} is not a supported type")
302 if arr.shape != imageArr0.shape:
303 raise TypeError(f
"{name} shape = {arr.shape} != {imageArr0.shape} = image0 shape")
309 if imageArr0.dtype.kind ==
"u":
310 imageArr0 = imageArr0.astype(
311 np.promote_types(imageArr0.dtype, np.int8))
312 if imageArr1.dtype.kind ==
"u":
313 imageArr1 = imageArr1.astype(
314 np.promote_types(imageArr1.dtype, np.int8))
316 if skipMaskArr
is not None:
317 skipMaskArr = np.array(skipMaskArr, dtype=bool)
318 maskedArr0 = np.ma.array(imageArr0, copy=
False, mask=skipMaskArr)
319 maskedArr1 = np.ma.array(imageArr1, copy=
False, mask=skipMaskArr)
320 filledArr0 = maskedArr0.filled(0.0)
321 filledArr1 = maskedArr1.filled(0.0)
324 filledArr0 = imageArr0
325 filledArr1 = imageArr1
328 np.array([np.nan], dtype=imageArr0.dtype)
329 np.array([np.nan], dtype=imageArr1.dtype)
333 valSkipMaskArr = skipMaskArr
337 nan0 = np.isnan(filledArr0)
338 nan1 = np.isnan(filledArr1)
339 if np.any(nan0 != nan1):
340 errStrList.append(
"NaNs differ")
342 posinf0 = np.isposinf(filledArr0)
343 posinf1 = np.isposinf(filledArr1)
344 if np.any(posinf0 != posinf1):
345 errStrList.append(
"+infs differ")
347 neginf0 = np.isneginf(filledArr0)
348 neginf1 = np.isneginf(filledArr1)
349 if np.any(neginf0 != neginf1):
350 errStrList.append(
"-infs differ")
352 valSkipMaskArr = nan0 | nan1 | posinf0 | posinf1 | neginf0 | neginf1
353 if skipMaskArr
is not None:
354 valSkipMaskArr |= skipMaskArr
357 valMaskedArr1 = np.ma.array(imageArr0, copy=
False, mask=valSkipMaskArr)
358 valMaskedArr2 = np.ma.array(imageArr1, copy=
False, mask=valSkipMaskArr)
359 valFilledArr1 = valMaskedArr1.filled(0.0)
360 valFilledArr2 = valMaskedArr2.filled(0.0)
362 if not np.allclose(valFilledArr1, valFilledArr2, rtol=rtol, atol=atol):
363 errArr = np.abs(valFilledArr1 - valFilledArr2)
364 maxErr = errArr.max()
365 maxPosInd = np.where(errArr == maxErr)
366 maxPosTuple = (maxPosInd[1][0], maxPosInd[0][0])
367 errStr = f
"maxDiff={maxErr} at position {maxPosTuple}; " \
368 f
"value={valFilledArr1[maxPosInd][0]} vs. {valFilledArr2[maxPosInd][0]}"
369 errStrList.insert(0, errStr)
371 return "; ".join(errStrList)
A class to represent a 2-dimensional array of pixels.
Represent a 2-dimensional array of bitmask pixels.
A class to manipulate images, masks, and variance as a single object.
def makeMaskedImageFromArrays(image, mask=None, variance=None)
def imagesDiffer(image0, image1, skipMask=None, rtol=1.0e-05, atol=1e-08)
Compare the pixels of two image or mask arrays; return True if close, False otherwise.
def assertImagesAlmostEqual(testCase, image0, image1, skipMask=None, rtol=1.0e-05, atol=1e-08, msg="Images differ")
Assert that two images are almost equal, including non-finite values.
def assertMasksEqual(testCase, mask0, mask1, skipMask=None, msg="Masks differ")
Assert that two masks are equal.
def assertMaskedImagesAlmostEqual(testCase, maskedImage0, maskedImage1, doImage=True, doMask=True, doVariance=True, skipMask=None, rtol=1.0e-05, atol=1e-08, msg="Masked images differ")
Assert that two masked images are nearly equal, including non-finite values.
def makeRampImage(bbox, start=0, stop=None, imageClass=ImageF)
Make an image whose values are a linear ramp.
def assertMaskedImagesEqual(*args, **kwds)
Assert that two masked images are exactly equal, including non-finite values.
def assertImagesEqual(*args, **kwds)
Assert that two images are exactly equal, including non-finite values.
def makeGaussianNoiseMaskedImage(dimensions, sigma, variance=1.0)