docstr: This test checks the ability of different image formats to consistently save
  meta-data information. Especially if a conversion between formats is required,
  that involves different 3rd party modules, this is not always guaranteed.
  
  The images are saved in one format, loaded and then saved in another format.
  Subsequently the differences in the meta-data is checked.
  
  Currently this test can only check:
  - voxel spacing
  - image offset
  
  Note that some other test are inherently performed by the
  loadsave.TestIOFacilities class:
  - data type
  - shape
  - content
  
  With the verboose switches, a comprehensive list of the results can be obtianed.
docstr_tok: ["This","test","checks","the","ability","of","different","image","formats","to","consistently","save","meta-data","information",".","Especially","if","a","conversion","between","formats","is","required",",","that","involves","different","3rd","party","modules",",","this","is","not","always","guaranteed",".","The","images","are","saved","in","one","format",",","loaded","and","then","saved","in","another","format",".","Subsequently","the","differences","in","the","meta-data","is","checked",".","Currently","this","test","can","only","check",":","-","voxel","spacing","-","image","offset","Note","that","some","other","test","are","inherently","performed","by","the","loadsave.TestIOFacilities","class",":","-","data","type","-","shape","-","content","With","the","verboose","switches",",","a","comprehensive","list","of","the","results","can","be","obtianed","."]
code: def test_MetadataConsistency(self):
      """
      This test checks the ability of different image formats to consistently save
      meta-data information. Especially if a conversion between formats is required,
      that involves different 3rd party modules, this is not always guaranteed.
  
      The images are saved in one format, loaded and then saved in another format.
      Subsequently the differences in the meta-data is checked.
  
      Currently this test can only check:
      - voxel spacing
      - image offset
  
      Note that some other test are inherently performed by the
      loadsave.TestIOFacilities class:
      - data type
      - shape
      - content
  
      With the verboose switches, a comprehensive list of the results can be obtianed.
      """
      ####
      # VERBOOSE SETTINGS
      # The following are two variables that can be used to print some nicely
      # formatted additional output. When one of them is set to True, this unittest
      # should be run stand-alone.
      ####
      # Print a list of format to format conversion which preserve meta-data
      consistent = True
      # Print a list of format to format conversion which do not preserve meta-data
      inconsistent = False
      # Print a list of formats that failed conversion in general
      unsupported = False
  
      ####
      # OTHER SETTINGS
      ####
      # debug settings
      logger = Logger.getInstance()
      # logger.setLevel(logging.DEBUG)
  
      # run test either for most important formats or for all (see loadsave.TestIOFacilities)
      # __suffixes = self.__important # (choice 1)
      __suffixes = self.__important + self.__itk  # (choice 2)
  
      # dimensions and dtypes to check
      __suffixes = list(set(__suffixes))
      __ndims = [1, 2, 3, 4, 5]
      __dtypes = [
          scipy.bool_,
          scipy.int8,
          scipy.int16,
          scipy.int32,
          scipy.int64,
          scipy.uint8,
          scipy.uint16,
          scipy.uint32,
          scipy.uint64,
          scipy.float32,
          scipy.float64,  # scipy.float128, # last one removed, as not present on every machine
          scipy.complex64,
          scipy.complex128,
      ]  # scipy.complex256 ## removed, as not present on every machine
  
      # prepare struct to save settings that passed the test
      consistent_types = dict.fromkeys(__suffixes)
      for k0 in consistent_types:
          consistent_types[k0] = dict.fromkeys(__suffixes)
          for k1 in consistent_types[k0]:
              consistent_types[k0][k1] = dict.fromkeys(__ndims)
              for k2 in consistent_types[k0][k1]:
                  consistent_types[k0][k1][k2] = []
  
      # prepare struct to save settings that did not
      inconsistent_types = dict.fromkeys(__suffixes)
      for k0 in inconsistent_types:
          inconsistent_types[k0] = dict.fromkeys(__suffixes)
          for k1 in inconsistent_types[k0]:
              inconsistent_types[k0][k1] = dict.fromkeys(__ndims)
              for k2 in inconsistent_types[k0][k1]:
                  inconsistent_types[k0][k1][k2] = dict.fromkeys(
                      __dtypes
                  )
  
      # prepare struct to save settings that did not pass the data integrity test
      unsupported_types = dict.fromkeys(__suffixes)
      for k0 in consistent_types:
          unsupported_types[k0] = dict.fromkeys(__suffixes)
          for k1 in unsupported_types[k0]:
              unsupported_types[k0][k1] = dict.fromkeys(__ndims)
              for k2 in unsupported_types[k0][k1]:
                  unsupported_types[k0][k1][k2] = dict.fromkeys(
                      __dtypes
                  )
  
      # create artifical images, save them, load them again and compare them
      path = tempfile.mkdtemp()
      try:
          for ndim in __ndims:
              logger.debug("Testing for dimension {}...".format(ndim))
              arr_base = scipy.random.randint(
                  0, 10, list(range(10, ndim + 10))
              )
              for dtype in __dtypes:
                  arr_save = arr_base.astype(dtype)
                  for suffix_from in __suffixes:
                      # do not run test, if in avoid array
                      if (
                          ndim in self.__avoid
                          and suffix_from in self.__avoid[ndim]
                      ):
                          unsupported_types[suffix_from][suffix_from][
                              ndim
                          ][
                              dtype
                          ] = "Test skipped, as combination in the tests __avoid array."
                          continue
  
                      # save array as file, load again to obtain header and set the meta-data
                      image_from = "{}/img{}".format(path, suffix_from)
                      try:
                          save(arr_save, image_from, None, True)
                          if not os.path.exists(image_from):
                              raise Exception(
                                  "Image of type {} with shape={}/dtype={} has been saved without exception, but the file does not exist.".format(
                                      suffix_from, arr_save.shape, dtype
                                  )
                              )
                      except Exception as e:
                          unsupported_types[suffix_from][suffix_from][
                              ndim
                          ][dtype] = (
                              e.message
                              if hasattr(e, "message")
                              else str(e.args)
                          )
                          continue
  
                      try:
                          img_from, hdr_from = load(image_from)
                          img_from = img_from.astype(
                              dtype
                          )  # change dtype of loaded image again, as sometimes the type is higher (e.g. int64 instead of int32) after loading!
                      except Exception as e:
                          _message = (
                              e.message
                              if hasattr(e, "message")
                              else str(e.args)
                          )
                          unsupported_types[suffix_from][suffix_from][
                              ndim
                          ][
                              dtype
                          ] = "Saved reference image of type {} with shape={}/dtype={} could not be loaded. Reason: {}".format(
                              suffix_from,
                              arr_save.shape,
                              dtype,
                              _message,
                          )
                          continue
  
                      header.set_pixel_spacing(
                          hdr_from,
                          [
                              scipy.random.rand()
                              * scipy.random.randint(1, 10)
                              for _ in range(img_from.ndim)
                          ],
                      )
                      try:
                          header.set_pixel_spacing(
                              hdr_from,
                              [
                                  scipy.random.rand()
                                  * scipy.random.randint(1, 10)
                                  for _ in range(img_from.ndim)
                              ],
                          )
                          header.set_offset(
                              hdr_from,
                              [
                                  scipy.random.rand()
                                  * scipy.random.randint(1, 10)
                                  for _ in range(img_from.ndim)
                              ],
                          )
                      except Exception as e:
                          logger.error(
                              "Could not set the header meta-data for image of type {} with shape={}/dtype={}. This should not happen and hints to a bug in the code. Signaled reason is: {}".format(
                                  suffix_from, arr_save.shape, dtype, e
                              )
                          )
                          unsupported_types[suffix_from][suffix_from][
                              ndim
                          ][dtype] = (
                              e.message
                              if hasattr(e, "message")
                              else str(e.args)
                          )
                          continue
  
                      for suffix_to in __suffixes:
                          # do not run test, if in avoid array
                          if (
                              ndim in self.__avoid
                              and suffix_to in self.__avoid[ndim]
                          ):
                              unsupported_types[suffix_from][suffix_to][
                                  ndim
                              ][
                                  dtype
                              ] = "Test skipped, as combination in the tests __avoid array."
                              continue
  
                          # for each other format, try format to format conversion an check if the meta-data is consistent
                          image_to = "{}/img_to{}".format(
                              path, suffix_to
                          )
                          try:
                              save(img_from, image_to, hdr_from, True)
                              if not os.path.exists(image_to):
                                  raise Exception(
                                      "Image of type {} with shape={}/dtype={} has been saved without exception, but the file does not exist.".format(
                                          suffix_to,
                                          arr_save.shape,
                                          dtype,
                                      )
                                  )
                          except Exception as e:
                              unsupported_types[suffix_from][
                                  suffix_from
                              ][ndim][dtype] = (
                                  e.message
                                  if hasattr(e, "message")
                                  else str(e.args)
                              )
                              continue
  
                          try:
                              _, hdr_to = load(image_to)
                          except Exception as e:
                              _message = (
                                  e.message
                                  if hasattr(e, "message")
                                  else str(e.args)
                              )
                              unsupported_types[suffix_from][suffix_to][
                                  ndim
                              ][
                                  dtype
                              ] = "Saved testing image of type {} with shape={}/dtype={} could not be loaded. Reason: {}".format(
                                  suffix_to,
                                  arr_save.shape,
                                  dtype,
                                  _message,
                              )
                              continue
  
                          msg = self.__diff(hdr_from, hdr_to)
                          if msg:
                              inconsistent_types[suffix_from][
                                  suffix_to
                              ][ndim][dtype] = msg
                          else:
                              consistent_types[suffix_from][suffix_to][
                                  ndim
                              ].append(dtype)
  
                          # remove testing image
                          if os.path.exists(image_to):
                              os.remove(image_to)
  
                      # remove reference image
                      if os.path.exists(image_to):
                          os.remove(image_to)
  
      except Exception:
          if not os.listdir(path):
              os.rmdir(path)
          else:
              logger.debug(
                  "Could not delete temporary directory {}. Is not empty.".format(
                      path
                  )
              )
          raise
  
      if consistent:
          print(
              "\nthe following format conversions are meta-data consistent:"
          )
          print("from\tto\tndim\tdtypes")
          for suffix_from in consistent_types:
              for suffix_to in consistent_types[suffix_from]:
                  for ndim, dtypes in list(
                      consistent_types[suffix_from][suffix_to].items()
                  ):
                      if list == type(dtypes) and not 0 == len(dtypes):
                          print(
                              (
                                  "{}\t{}\t{}D\t{}".format(
                                      suffix_from,
                                      suffix_to,
                                      ndim,
                                      [
                                          str(x).split(".")[-1][:-2]
                                          for x in dtypes
                                      ],
                                  )
                              )
                          )
      if inconsistent:
          print(
              "\nthe following form conversions are not meta-data consistent:"
          )
          print("from\tto\tndim\tdtype\t\terror")
          for suffix_from in inconsistent_types:
              for suffix_to in inconsistent_types[suffix_from]:
                  for ndim in inconsistent_types[suffix_from][
                      suffix_to
                  ]:
                      for dtype, msg in list(
                          inconsistent_types[suffix_from][suffix_to][
                              ndim
                          ].items()
                      ):
                          if msg:
                              print(
                                  (
                                      "{}\t{}\t{}D\t{}\t\t{}".format(
                                          suffix_from,
                                          suffix_to,
                                          ndim,
                                          str(dtype).split(".")[-1][
                                              :-2
                                          ],
                                          msg,
                                      )
                                  )
                              )
  
      if unsupported:
          print(
              "\nthe following form conversions could not be tested due to errors:"
          )
          print("from\tto\tndim\tdtype\t\terror")
          for suffix_from in unsupported_types:
              for suffix_to in unsupported_types[suffix_from]:
                  for ndim in unsupported_types[suffix_from][suffix_to]:
                      for dtype, msg in list(
                          unsupported_types[suffix_from][suffix_to][
                              ndim
                          ].items()
                      ):
                          if msg:
                              print(
                                  (
                                      "{}\t{}\t{}D\t{}\t\t{}".format(
                                          suffix_from,
                                          suffix_to,
                                          ndim,
                                          str(dtype).split(".")[-1][
                                              :-2
                                          ],
                                          msg,
                                      )
                                  )
                              )
  
file_path: medpy/tests/io_/metadata.py
imports: import unittest
  import tempfile
  import os
  import scipy
  from medpy.io import load, save, header
  from medpy.core.logger import Logger
label_desc: Use image node_id 1 for calling the TestMetadataConsistency obj's underlying member method code with example usage: obj.test_MetadataConsistency() without return types