fits_plugin.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. __all__ = ['imread', 'imread_collection']
  2. import skimage.io as io
  3. try:
  4. from astropy.io import fits
  5. except ImportError:
  6. raise ImportError(
  7. "Astropy could not be found. It is needed to read FITS files.\n"
  8. "Please refer to https://www.astropy.org for installation\n"
  9. "instructions."
  10. )
  11. def imread(fname):
  12. """Load an image from a FITS file.
  13. Parameters
  14. ----------
  15. fname : string
  16. Image file name, e.g. ``test.fits``.
  17. Returns
  18. -------
  19. img_array : ndarray
  20. Unlike plugins such as PIL, where different color bands/channels are
  21. stored in the third dimension, FITS images are grayscale-only and can
  22. be N-dimensional, so an array of the native FITS dimensionality is
  23. returned, without color channels.
  24. Currently if no image is found in the file, None will be returned
  25. Notes
  26. -----
  27. Currently FITS ``imread()`` always returns the first image extension when
  28. given a Multi-Extension FITS file; use ``imread_collection()`` (which does
  29. lazy loading) to get all the extensions at once.
  30. """
  31. with fits.open(fname) as hdulist:
  32. # Iterate over FITS image extensions, ignoring any other extension types
  33. # such as binary tables, and get the first image data array:
  34. img_array = None
  35. for hdu in hdulist:
  36. if isinstance(hdu, fits.ImageHDU) or isinstance(hdu, fits.PrimaryHDU):
  37. if hdu.data is not None:
  38. img_array = hdu.data
  39. break
  40. return img_array
  41. def imread_collection(load_pattern, conserve_memory=True):
  42. """Load a collection of images from one or more FITS files
  43. Parameters
  44. ----------
  45. load_pattern : str or list
  46. List of extensions to load. Filename globbing is currently
  47. unsupported.
  48. conserve_memory : bool
  49. If True, never keep more than one in memory at a specific
  50. time. Otherwise, images will be cached once they are loaded.
  51. Returns
  52. -------
  53. ic : ImageCollection
  54. Collection of images.
  55. """
  56. intype = type(load_pattern)
  57. if intype is not list and intype is not str:
  58. raise TypeError("Input must be a filename or list of filenames")
  59. # Ensure we have a list, otherwise we'll end up iterating over the string:
  60. if intype is not list:
  61. load_pattern = [load_pattern]
  62. # Generate a list of filename/extension pairs by opening the list of
  63. # files and finding the image extensions in each one:
  64. ext_list = []
  65. for filename in load_pattern:
  66. with fits.open(filename) as hdulist:
  67. for n, hdu in zip(range(len(hdulist)), hdulist):
  68. if isinstance(hdu, fits.ImageHDU) or isinstance(hdu, fits.PrimaryHDU):
  69. # Ignore (primary) header units with no data (use '.size'
  70. # rather than '.data' to avoid actually loading the image):
  71. try:
  72. data_size = hdu.size # size is int in Astropy 3.1.2
  73. except TypeError:
  74. data_size = hdu.size()
  75. if data_size > 0:
  76. ext_list.append((filename, n))
  77. return io.ImageCollection(
  78. ext_list, load_func=FITSFactory, conserve_memory=conserve_memory
  79. )
  80. def FITSFactory(image_ext):
  81. """Load an image extension from a FITS file and return a NumPy array
  82. Parameters
  83. ----------
  84. image_ext : tuple
  85. FITS extension to load, in the format ``(filename, ext_num)``.
  86. The FITS ``(extname, extver)`` format is unsupported, since this
  87. function is not called directly by the user and
  88. ``imread_collection()`` does the work of figuring out which
  89. extensions need loading.
  90. """
  91. # Expect a length-2 tuple with a filename as the first element:
  92. if not isinstance(image_ext, tuple):
  93. raise TypeError("Expected a tuple")
  94. if len(image_ext) != 2:
  95. raise ValueError("Expected a tuple of length 2")
  96. filename = image_ext[0]
  97. extnum = image_ext[1]
  98. if not (isinstance(filename, str) and isinstance(extnum, int)):
  99. raise ValueError("Expected a (filename, extension) tuple")
  100. with fits.open(filename) as hdulist:
  101. data = hdulist[extnum].data
  102. if data is None:
  103. raise RuntimeError(f"Extension {extnum} of {filename} has no data")
  104. return data