TestCenter Reference
HelperFunctions.py
Go to the documentation of this file.
1 # **InsertLicense** code author="Immanuel Vallei"
2 
3 from mevis import MLAB
4 from TestSupport import Base
5 from TestSupport.Macros import INFO, ERROR, ASSERT_TRUE
6 import numpy
7 import sys
8 import six
9 
10 LOGVOXEL = False #trigger for logging voxel comparison
11 LOGONSUCCESS = False #trigger for logging voxel comparison if success (gLogVoxel needs to be True)
12 EPSILON = 0.0001 #absolute value to compensate floating point imprecisions with voxel comparison
13 
14 INTTYPES = {
15  "int8": (-128,127),
16  "int16": (-32768,32767),
17  "int32": (-2147483648,2147483647),
18  "int64": (-9223372036854775808,9223372036854775807),
19  "uint8": (0,255),
20  "uint16": (0,65535),
21  "uint32": (0,4294967295),
22  "uint64": (0,18446744073709551615)
23 }
24 
25 
26 
30 def generateTestDictionary(function, testDictionaries):
31  if isinstance(testDictionaries, (list, tuple)):
32  testDict = {}
33  for d in testDictionaries:
34  testDict.update(d)
35  else:
36  testDict = testDictionaries
37  for key in testDict:
38  testDict[key][0] = function
39  return testDict
40 
41 
42 
50 def initModuleFromTemplate( targetModuleName, templateModuleName=None, fieldValueOverrides=None,
51  alsoInitNonEditableFields=False ):
52  context = Base.getTestCaseContext()
53  ASSERT_TRUE( context.hasModule( targetModuleName ),
54  msg="Target module {} not found!".format( targetModuleName ), logOnSuccess=False )
55  if templateModuleName is None:
56  templateModuleName = "{}_ParameterTemplate".format( targetModuleName )
57  #
58  def ___applyTemplateParameters():
59  ASSERT_TRUE( context.hasModule( templateModuleName ),
60  msg="Template module {} not found!".format( templateModuleName ), logOnSuccess=False )
61  for f in context.module( templateModuleName ).parameterFields():
62  if f.name != "instanceName" and f.type != "Trigger" and ( alsoInitNonEditableFields or f.isEditable() ):
63  value = f.vectorValue() if "vec" in f.type.lower() else f.value
64  context.field( "{}.{}".format( targetModuleName, f.name ) ).updateValue( value )
65  #
66  def ___applyOverrideParameters():
67  targetModule = context.module( targetModuleName )
68  for fieldName, fieldValue in fieldValueOverrides.items():
69  ASSERT_TRUE( targetModule.hasField( fieldName ),
70  msg = "Invalid field value override: Field {} does not exist.".format( fieldName ), logOnSuccess=False )
71  targetModule.field( fieldName ).updateValue( fieldValue )
72  #
73  ___applyTemplateParameters()
74  if fieldValueOverrides:
75  ___applyOverrideParameters()
76 
77 
78 
83 def getMinMax(fv1, fv2 = None):
84  """Determines max/min value of given fill value(s)."""
85  if isinstance(fv1, six.string_types):
86  fv1 = sorted([float(comp) for comp in fv1.split()])
87  else:
88  fv1 = sorted(fv1)
89  max1 = fv1[len(fv1)-1]
90  min1 = fv1[0]
91 
92  if fv2:
93  if isinstance(fv2, six.string_types):
94  fv2 = sorted([float(comp) for comp in fv2.split()])
95  else:
96  fv2 = sorted(fv2)
97  max2 = fv2[len(fv2)-1]
98  min2 = fv2[0]
99  min = min1 if min1 < min2 else min2
100  max = max1 if max1 > max2 else max2
101  return (min, max)
102  else:
103  return (min1, max1)
104 
105 
106 
110  """Returns the number of components of the image data type."""
111  mlType = imageField.image().dataType()
112  return MLAB.ML.MLTypeGetNumComponents(mlType)
113 
114 
115 
119  if "int" in dataType or "float" in dataType or "double" in dataType:
120  num = 1
121  elif "complex" in dataType:
122  num = 2
123  elif "quaternion" in dataType:
124  num = 4
125  else:
126  num = ""
127  for i in dataType:
128  if i in "0123456789":
129  num += i
130  elif num != "":
131  break
132  #Matrix data types: num*num
133  num = int(num)**2 if "mat" in dataType else int(num)
134  return num
135 
136 
137 
141 def getNumpyTypeFromImage(imageField, context = None):
142  """Returns a NumPy data type object used with the image."""
143  #Std data types
144  if MLAB.ML.MLIsStandardType(imageField.image().dataType()):
145  return type(imageField.image().getTile((0, 0, 0), (1, 1, 1))[0][0][0])
146  #Adv data types
147  else:
148  try:
149  td = context.addModule(MLAB.moduleLiteral("TypeDecomposer64"))
150  except:
151  raise Exception("Use parameter \"context = ctx\" with \"getNumpyType()\" because the image uses an adv. data type!")
152  td.field("input0").connectFrom(imageField)
153  npType = type(td.field("output0").image().getTile((0, 0, 0), (1, 1, 1))[0][0][0])
154  td.remove()
155  return npType
156 
157 
158 
162  if "unsigned" in dataType:
163  if "8" in dataType:
164  npType = numpy.uint8
165  elif "16" in dataType:
166  npType = numpy.uint16
167  elif "32" in dataType:
168  npType = numpy.uint32
169  elif "64" in dataType:
170  npType = numpy.uint64
171 
172  elif "int8" in dataType or "i8" in dataType:
173  npType = numpy.int8
174  elif "int16" in dataType or "i16" in dataType:
175  npType = numpy.int16
176  elif "int32" in dataType or "i32" in dataType:
177  npType = numpy.int32
178  elif "int64" in dataType or "i64" in dataType:
179  npType = numpy.int64
180 
181  elif dataType in ("float", "complexf", "quaternionf") or "vecf" in dataType or "matf" in dataType:
182  npType = numpy.float32
183  elif dataType in ("double", "complexd", "quaterniond") or "vec" in dataType or "mat" in dataType:
184  npType = numpy.float64
185  elif dataType in ("long double", "complexld", "quaternionld"):
186  npType = numpy.float64
187 
188  elif "stringuchar" in dataType:
189  npType = numpy.uint8
190 
191  return npType
192 
193 
194 
200 def castToNumpy(value, numpyType):
201  """Returns the value casted to the given NumPy data type."""
202  typeName = numpyType.__name__
203  #if convert to int and outside int32 or to uint and outside uint32
204  if ("uint" in typeName and (value < INTTYPES["uint32"][0] or value > INTTYPES["uint32"][1])) or \
205  ("uint" not in typeName and "int" in typeName and (value < INTTYPES["int32"][0] or value > INTTYPES["int32"][1])):
206  if sys.version_info.major == 2:
207  value = long(value)
208  else:
209  value = int(value)
210  #newValue = value % (intMax + 1) + intMin * (value / (intMax + 1) % 2)
211  value = value % (INTTYPES[typeName][1]+1) + \
212  INTTYPES[typeName][0] * (value // (INTTYPES[typeName][1]+1) % 2)
213  return numpyType(value)
214 
215 
216 
221 def convertVoxelType(voxel, oldType, newType):
222  """Coverts fill values of any input data type to std output data type."""
223  try:
224  numComp = len(voxel)
225  except:
226  numComp = 1
227  voxel = tuple(voxel)
228 
229  numpyType = getNumpyTypeFromString(newType)
230  newVoxel = voxel[0] if "quaternion" not in oldType else voxel[3]
231  newVoxel = castToNumpy(newVoxel, numpyType)
232  return newVoxel
233 
234 
235 
240 def getImage3D(imageField, imageStart = (0, 0, 0), imageSize = None, context = None):
241  image = []
242  if not imageSize:
243  imageSize = imageField.size3D()
244  numComp = getNumComponentsFromImage(imageField)
245  #adv. data type
246  if numComp > 1:
247  try:
248  td = context.addModule(MLAB.moduleLiteral("TypeDecomposer64"))
249  except:
250  raise Exception("Use parameter \"context = ctx\" with \"getImage3D()\" because the image uses an adv. data type!")
251 
252  td.field("input0").connectFrom(imageField)
253  for comp in range(numComp):
254  image.append(td.field("output{0}".format(comp)).image().getTile(imageStart, imageSize))
255  td.remove()
256  #std. data type
257  else:
258  image.append(imageField.image().getTile(imageStart, imageSize))
259  return image
260 
261 
262 
267 def getImage6D(imageField, imageStart = (0, 0, 0, 0, 0, 0), imageSize = None, context = None):
268  image = []
269  if not imageSize:
270  imageSize = imageField.size6D()
271  numComp = getNumComponentsFromImage(imageField)
272  #adv. data type
273  if numComp > 1:
274  try:
275  td = context.addModule(MLAB.moduleLiteral("TypeDecomposer64"))
276  except:
277  raise Exception("Use parameter \"context = ctx\" with \"getImage6D()\" because the image uses an adv. data type!")
278 
279  td.field("input0").connectFrom(imageField)
280  for comp in range(numComp):
281  image.append(td.field("output{0}".format(comp)).image().getTile(imageStart, imageSize))
282  td.remove()
283  #std. data type
284  else:
285  image.append(imageField.image().getTile(imageStart, imageSize))
286  return image
287 
288 
289 
293 def compareImages3D(resultImage, expectedImage):
294  isEqual = True
295  numErrors = 0
296  #get # of components and extent of expected image; changes shape to [component][z][y][x] if it is not
297  try:
298  numComp = len(expectedImage)
299  expectedSize = [len(expectedImage[0][0][0]),
300  len(expectedImage[0][0]),
301  len(expectedImage[0])]
302  except TypeError:
303  numComp = 1
304  expectedImage = [expectedImage]
305  expectedSize = [len(expectedImage[0][0][0]),
306  len(expectedImage[0][0]),
307  len(expectedImage[0])]
308  #get extent of result image; changes shape to [component][z][y][x] if it is not
309  try:
310  resultSize = [len(resultImage[0][0][0]),
311  len(resultImage[0][0]),
312  len(resultImage[0])]
313  except TypeError:
314  resultImage = [resultImage]
315  resultSize = [len(resultImage[0][0][0]),
316  len(resultImage[0][0]),
317  len(resultImage[0])]
318 
319  if resultSize != expectedSize:
320  ERROR("Image compare failed: expected and output image are not not of the same size >> " \
321  + "result {0} != expected {1}".format(resultSize, expectedSize))
322  return False
323 
324  elif numComp != len(resultImage):
325  ERROR("Image compare failed: expected and output image have different number of data type components >> " \
326  + "result {0} != expected {1}".format(numComp, len(resultImage)))
327  return False
328 
329  if not numpy.allclose(expectedImage, resultImage, 0, EPSILON) or LOGONSUCCESS:
330  for z in range(resultSize[2]):
331  for y in range(resultSize[1]):
332  for x in range(resultSize[0]):
333  for comp in range(numComp):
334  #get expected and out voxel are a number or inf/nan as bool
335  finiteExp = numpy.isfinite(expectedImage[comp][z][y][x])
336  finiteOut = numpy.isfinite(resultImage[comp][z][y][x])
337  #check for difference between voxel
338  if finiteExp != finiteOut \
339  or (finiteExp, finiteOut) == (True, True) \
340  and abs(resultImage[comp][z][y][x] - expectedImage[comp][z][y][x]) > EPSILON:
341  if LOGVOXEL:
342  ERROR("""Voxel ({0},{1},{2}) of Result and Expected image are not equal (Epsilon = {3}):
343  >> {4} == {5} : False""".format(x, y, z, EPSILON, \
344  [resultImage[comp][z][y][x] for comp in range(numComp)], \
345  [expectedImage[comp][z][y][x] for comp in range(numComp)]))
346  isEqual = False
347  numErrors += 1
348  break
349  #voxel are equal
350  elif LOGVOXEL and LOGONSUCCESS and comp == numComp-1:
351  INFO("""Voxel ({0},{1},{2}) of Result and Expected image are equal (Epsilon = {3}):
352  >> {4} == {5} : True""".format(x, y, z, EPSILON, \
353  [resultImage[comp][z][y][x] for comp in range(numComp)], \
354  [expectedImage[comp][z][y][x] for comp in range(numComp)]))
355 
356  if isEqual:
357  INFO("Image compare passed: expected and out image are equal")
358  else:
359  ERROR("Image compare failed: expected and output image are not equal >> " \
360  + "{0} of {1} voxels are different".format(numErrors, resultSize[0] * resultSize[1] * resultSize[2]))
361  return isEqual
362 
363 
364 
368 def compareImages6D(resultImage, expectedImage):
369  isEqual = True
370  numErrors = 0
371  #get # of components and extent of expected image; changes shape to [component][u][t][c][z][y][x] if it is not
372  try:
373  numComp = len(expectedImage)
374  expectedSize = [len(expectedImage[0][0][0][0][0][0]),
375  len(expectedImage[0][0][0][0][0]),
376  len(expectedImage[0][0][0][0]),
377  len(expectedImage[0][0][0]),
378  len(expectedImage[0][0]),
379  len(expectedImage[0])]
380  except TypeError:
381  numComp = 1
382  expectedImage = [expectedImage]
383  expectedSize = [len(expectedImage[0][0][0][0][0][0]),
384  len(expectedImage[0][0][0][0][0]),
385  len(expectedImage[0][0][0][0]),
386  len(expectedImage[0][0][0]),
387  len(expectedImage[0][0]),
388  len(expectedImage[0])]
389  #get extent of result image; changes shape to [component][u][t][c][z][y][x] if it is not
390  try:
391  resultSize = [len(resultImage[0][0][0][0][0][0]),
392  len(resultImage[0][0][0][0][0]),
393  len(resultImage[0][0][0][0]),
394  len(resultImage[0][0][0]),
395  len(resultImage[0][0]),
396  len(resultImage[0])]
397  except TypeError:
398  numComp = 1
399  resultImage = [resultImage]
400  resultSize = [len(resultImage[0][0][0][0][0][0]),
401  len(resultImage[0][0][0][0][0]),
402  len(resultImage[0][0][0][0]),
403  len(resultImage[0][0][0]),
404  len(resultImage[0][0]),
405  len(resultImage[0])]
406 
407  if resultSize != expectedSize:
408  ERROR("Image compare failed: expected and output image are not not of the same size >> " \
409  + "result {0} != expected {1}".format(resultSize, expectedSize))
410  return False
411 
412  elif numComp != len(resultImage):
413  ERROR("Image compare failed: expected and output image have different number of data type components >> " \
414  + "result {0} != expected {1}".format(numComp, len(resultImage)))
415  return False
416 
417  if not numpy.allclose(expectedImage, resultImage, 0, EPSILON) or LOGONSUCCESS:
418  for u in range(resultSize[5]):
419  for t in range(resultSize[4]):
420  for c in range(resultSize[3]):
421  for z in range(resultSize[2]):
422  for y in range(resultSize[1]):
423  for x in range(resultSize[0]):
424  for comp in range(numComp):
425  #get expected and out voxel are a number or inf/nan as bool
426  finiteExp = numpy.isfinite(expectedImage[comp][u][t][c][z][y][x])
427  finiteOut = numpy.isfinite(resultImage[comp][u][t][c][z][y][x])
428  #check for difference between voxel
429  if finiteExp != finiteOut \
430  or (finiteExp, finiteOut) == (True, True) \
431  and abs(resultImage[comp][u][t][c][z][y][x] - expectedImage[comp][u][t][c][z][y][x]) > EPSILON:
432  if LOGVOXEL:
433  ERROR("""Voxel ({0},{1},{2},{3},{4},{5}) of Result and Expected image are not equal (Epsilon = {6}):
434  >> {7} == {8} : False""".format(x, y, z, c, t, u, EPSILON, \
435  [resultImage[comp][u][t][c][z][y][x] for comp in range(numComp)], \
436  [expectedImage[comp][u][t][c][z][y][x] for comp in range(numComp)]))
437  isEqual = False
438  numErrors += 1
439  break
440  #voxel are equal
441  elif LOGVOXEL and LOGONSUCCESS and comp == numComp-1:
442  INFO("""Voxel ({0},{1},{2},{3},{4},{5}) of Result and Expected image are equal (Epsilon = {6}):
443  >> {7} == {8} : True""".format(x, y, z, c, t, u, EPSILON, \
444  [resultImage[comp][u][t][c][z][y][x] for comp in range(numComp)], \
445  [expectedImage[comp][u][t][c][z][y][x] for comp in range(numComp)]))
446 
447  if isEqual:
448  INFO("Image compare passed: expected and out image are equal")
449  else:
450  ERROR("Image compare failed: expected and output image are not equal >> " \
451  + "{0} of {1} voxels are different".format(numErrors, resultSize[0] * resultSize[1] * resultSize[2] * resultSize[3] * resultSize[4] * resultSize[5]))
452  return isEqual
def getNumComponentsFromString(dataType)
Returns the number of components of a data type given as string.
def castToNumpy(value, numpyType)
Casts a value to a NumPy type.
def getNumpyTypeFromString(dataType)
Returns the NumPy data type appropriate to a data type given as string.
def getNumpyTypeFromImage(imageField, context=None)
Returns the NumPy data type of an ML-Image.
def convertVoxelType(voxel, oldType, newType)
Converts a voxel to a new data type.
def getMinMax(fv1, fv2=None)
Determines max/min value of given fill value(s).
def compareImages3D(resultImage, expectedImage)
Compares voxels of a result image with an expected image in 3 dimensions.
def getNumComponentsFromImage(imageField)
Returns the number of components of an ML-image data type.
def generateTestDictionary(function, testDictionaries)
Concatenates test dictionaries and sets a function that needs to be tested in the final dictionary...
def ASSERT_TRUE(expr, msg=None, logOnSuccess=None)
Throw exception if given expression does not evaluate to true.
Definition: Macros.py:60
def getImage6D(imageField, imageStart=(0, 0, 0, 0, 0, 0), imageSize=None, context=None)
Returns ML image output of given module in 6 dimensions.
def getImage3D(imageField, imageStart=(0, 0, 0), imageSize=None, context=None)
Returns ML image output of given module in 3 dimensions.
Adds GoogleTest like methods.
Definition: Macros.py:1
def initModuleFromTemplate(targetModuleName, templateModuleName=None, fieldValueOverrides=None, alsoInitNonEditableFields=False)
Copies all values from a reference module&#39;s editable parameter fields to the target (&#39;test&#39;) module...
def INFO(msg)
Create an info message.
Definition: Macros.py:504
def ERROR(msg)
Create an error message.
Definition: Macros.py:518
def compareImages6D(resultImage, expectedImage)
Compares voxels of a result image with an expected image in 6 dimensions.