LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
testUtils.py
Go to the documentation of this file.
1 from __future__ import absolute_import, division
2 #
3 # LSST Data Management System
4 # Copyright 2008, 2009, 2010 LSST Corporation.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
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 LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <http://www.lsstcorp.org/LegalNotices/>.
22 #
23 
24 ##\file
25 ## \brief Utilities to help write tests, mostly using numpy
26 import numpy
27 
28 import lsst.afw.image as afwImage
29 
30 def makeGaussianNoiseMaskedImage(dimensions, sigma, variance=1.0):
31  """Make a gaussian noise MaskedImageF
32 
33  Inputs:
34  - dimensions: dimensions of output array (cols, rows)
35  - sigma; sigma of image plane's noise distribution
36  - variance: constant value for variance plane
37  """
38  npSize = (dimensions[1], dimensions[0])
39  image = numpy.random.normal(loc=0.0, scale=sigma, size=npSize).astype(numpy.float32)
40  mask = numpy.zeros(npSize, dtype=numpy.uint16)
41  variance = numpy.zeros(npSize, dtype=numpy.float32) + variance
42 
43  return afwImage.makeMaskedImageFromArrays(image, mask, variance)
44 
45 def imagesDiffer(imageArr1, imageArr2, skipMaskArr=None, rtol=1.0e-05, atol=1e-08):
46  """Compare the pixels of two image arrays; return True if close, False otherwise
47 
48  Inputs:
49  - image1: first image to compare
50  - image2: second image to compare
51  - skipMaskArr: pixels to ignore; nonzero values are skipped
52  - rtol: relative tolerance (see below)
53  - atol: absolute tolerance (see below)
54 
55  rtol and atol are positive, typically very small numbers.
56  The relative difference (rtol * abs(b)) and the absolute difference "atol" are added together
57  to compare against the absolute difference between "a" and "b".
58 
59  Return a string describing the error if the images differ significantly, an empty string otherwise
60  """
61  retStrs = []
62  if skipMaskArr is not None:
63  maskedArr1 = numpy.ma.array(imageArr1, copy=False, mask = skipMaskArr)
64  maskedArr2 = numpy.ma.array(imageArr2, copy=False, mask = skipMaskArr)
65  filledArr1 = maskedArr1.filled(0.0)
66  filledArr2 = maskedArr2.filled(0.0)
67  else:
68  filledArr1 = imageArr1
69  filledArr2 = imageArr2
70 
71  nan1 = numpy.isnan(filledArr1)
72  nan2 = numpy.isnan(filledArr2)
73  if numpy.any(nan1 != nan2):
74  retStrs.append("NaNs differ")
75 
76  posinf1 = numpy.isposinf(filledArr1)
77  posinf2 = numpy.isposinf(filledArr2)
78  if numpy.any(posinf1 != posinf2):
79  retStrs.append("+infs differ")
80 
81  neginf1 = numpy.isneginf(filledArr1)
82  neginf2 = numpy.isneginf(filledArr2)
83  if numpy.any(neginf1 != neginf2):
84  retStrs.append("-infs differ")
85 
86  # compare values that should be comparable (are neither infinite, nan nor masked)
87  valSkipMaskArr = nan1 | nan2 | posinf1 | posinf2 | neginf1 | neginf2
88  if skipMaskArr is not None:
89  valSkipMaskArr |= skipMaskArr
90  valMaskedArr1 = numpy.ma.array(imageArr1, copy=False, mask = valSkipMaskArr)
91  valMaskedArr2 = numpy.ma.array(imageArr2, copy=False, mask = valSkipMaskArr)
92  valFilledArr1 = valMaskedArr1.filled(0.0)
93  valFilledArr2 = valMaskedArr2.filled(0.0)
94 
95  if not numpy.allclose(valFilledArr1, valFilledArr2, rtol=rtol, atol=atol):
96  errArr = numpy.abs(valFilledArr1 - valFilledArr2)
97  maxErr = errArr.max()
98  maxPosInd = numpy.where(errArr==maxErr)
99  maxPosTuple = (maxPosInd[1][0], maxPosInd[0][0])
100  errStr = "maxDiff=%s at position %s; value=%s vs. %s" % \
101  (maxErr, maxPosTuple, valFilledArr1[maxPosInd][0], valFilledArr2[maxPosInd][0])
102  retStrs.insert(0, errStr)
103  return "; ".join(retStrs)
104 
105 def masksDiffer(maskArr1, maskArr2, skipMaskArr=None):
106  """Compare the pixels of two mask arrays; return True if they match, False otherwise
107 
108  Inputs:
109  - mask1: first image to compare
110  - mask2: second image to compare
111  - skipMaskArr: pixels to ignore; nonzero values are skipped
112 
113  Return a string describing the error if the images differ significantly, an empty string otherwise
114  """
115  retStr = ""
116  if skipMaskArr is not None:
117  maskedArr1 = numpy.ma.array(maskArr1, copy=False, mask = skipMaskArr)
118  maskedArr2 = numpy.ma.array(maskArr2, copy=False, mask = skipMaskArr)
119  filledArr1 = maskedArr1.filled(0.0)
120  filledArr2 = maskedArr2.filled(0.0)
121  else:
122  filledArr1 = maskArr1
123  filledArr2 = maskArr2
124 
125  if numpy.any(filledArr1 != filledArr2):
126  errArr = numpy.abs(filledArr1 - filledArr2)
127  maxErr = errArr.max()
128  maxPosInd = numpy.where(errArr==maxErr)
129  maxPosTuple = (maxPosInd[1][0], maxPosInd[0][0])
130  retStr = "maxDiff=%s at position %s; value=%s vs. %s" % \
131  (maxErr, maxPosTuple, filledArr1[maxPosInd][0], filledArr2[maxPosInd][0])
132  retStr = "masks differ"
133  return retStr
134 
135 def maskedImagesDiffer(maskedImageArrSet1, maskedImageArrSet2,
136  doImage=True, doMask=True, doVariance=True, skipMaskArr=None, rtol=1.0e-05, atol=1e-08):
137  """Compare pixels from two masked images
138 
139  Inputs:
140  - maskedImageArrSet1: first masked image to compare as (image, mask, variance) arrays
141  - maskedImageArrSet2: second masked image to compare as (image, mask, variance) arrays
142  - doImage: compare image planes if True
143  - doMask: compare mask planes if True
144  - doVariance: compare variance planes if True
145  - skipMaskArr: pixels to ingore on the image, mask and variance arrays; nonzero values are skipped
146  - rtol: relative tolerance (see below)
147  - atol: absolute tolerance (see below)
148 
149  rtol and atol are positive, typically very small numbers.
150  The relative difference (rtol * abs(b)) and the absolute difference "atol" are added together
151  to compare against the absolute difference between "a" and "b".
152 
153  Return a string describing the error if the images differ significantly, an empty string otherwise
154  """
155  retStrs = []
156  for ind, (doPlane, planeName) in enumerate(((doImage, "image"),
157  (doMask, "mask"),
158  (doVariance, "variance"))):
159  if not doPlane:
160  continue
161 
162  if planeName == "mask":
163  errStr = masksDiffer(maskedImageArrSet1[ind], maskedImageArrSet2[ind], skipMaskArr=skipMaskArr)
164  if errStr:
165  retStrs.append(errStr)
166  else:
167  errStr = imagesDiffer(maskedImageArrSet1[ind], maskedImageArrSet2[ind],
168  skipMaskArr=skipMaskArr, rtol=rtol, atol=atol)
169  if errStr:
170  retStrs.append("%s planes differ: %s" % (planeName, errStr))
171  return " | ".join(retStrs)