1# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
2# vi: set ft=python sts=4 ts=4 sw=4 et:
3### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
4#
5#   See COPYING file distributed along with the NiBabel package for the
6#   copyright and license terms.
7#
8### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
9# module imports
10from .py3k import asbytes
11from .filename_parser import types_filenames, splitext_addext
12from . import volumeutils as vu
13from . import spm2analyze as spm2
14from . import nifti1
15from .freesurfer import MGHImage
16from .fileholders import FileHolderError
17from .spatialimages import ImageFileError
18from .imageclasses import class_map, ext_map
19
20
21def load(filename):
22    ''' Load file given filename, guessing at file type
23
24    Parameters
25    ----------
26    filename : string
27       specification of file to load
28
29    Returns
30    -------
31    img : ``SpatialImage``
32       Image of guessed type
33    '''
34    froot, ext, trailing = splitext_addext(filename, ('.gz', '.bz2'))
35    try:
36        img_type = ext_map[ext]
37    except KeyError:
38        raise ImageFileError('Cannot work out file type of "%s"' %
39                             filename)
40    if ext in ('.nii', '.mnc', '.mgh', '.mgz'):
41        klass = class_map[img_type]['class']
42    else:
43        # might be nifti pair or analyze of some sort
44        files_types = (('image','.img'), ('header','.hdr'))
45        filenames = types_filenames(filename, files_types)
46        hdr = nifti1.Nifti1Header.from_fileobj(
47            vu.allopen(filenames['header']),
48            check=False)
49        if hdr['magic'] in (asbytes('ni1'), asbytes('n+1')):
50            # allow goofy nifti single magic for pair
51            klass = nifti1.Nifti1Pair
52        else:
53            klass =  spm2.Spm2AnalyzeImage
54    return klass.from_filename(filename)
55
56
57def save(img, filename):
58    ''' Save an image to file adapting format to `filename`
59
60    Parameters
61    ----------
62    img : ``SpatialImage``
63       image to save
64    filename : str
65       filename (often implying filenames) to which to save `img`.
66
67    Returns
68    -------
69    None
70    '''
71    try:
72        img.to_filename(filename)
73    except ImageFileError:
74        pass
75    else:
76        return
77    froot, ext, trailing = splitext_addext(filename, ('.gz', '.bz2'))
78    img_type = ext_map[ext]
79    klass = class_map[img_type]['class']
80    converted = klass.from_image(img)
81    converted.to_filename(filename)
82
83
84def read_img_data(img, prefer='scaled'):
85    """ Read data from image associated with files
86
87    Parameters
88    ----------
89    img : ``SpatialImage``
90       Image with valid image file in ``img.file_map``.  Unlike the
91       ``img.get_data()`` method, this function returns the data read
92       from the image file, as specified by the *current* image header
93       and *current* image files.
94    prefer : str, optional
95       Can be 'scaled' - in which case we return the data with the
96       scaling suggested by the format, or 'unscaled', in which case we
97       return, if we can, the raw data from the image file, without the
98       scaling applied.
99
100    Returns
101    -------
102    arr : ndarray
103       array as read from file, given parameters in header
104
105    Notes
106    -----
107    Summary: please use the ``get_data`` method of `img` instead of this
108    function unless you are sure what you are doing.
109
110    In general, you will probably prefer ``prefer='scaled'``, because
111    this gives the data as the image format expects to return it.
112
113    Use `prefer` == 'unscaled' with care; the modified Analyze-type
114    formats such as SPM formats, and nifti1, specify that the image data
115    array is given by the raw data on disk, multiplied by a scalefactor
116    and maybe with the addition of a constant.  This function, with
117    ``unscaled`` returns the data on the disk, without these
118    format-specific scalings applied.  Please use this funciton only if
119    you absolutely need the unscaled data, and the magnitude of the
120    data, as given by the scalefactor, is not relevant to your
121    application.  The Analyze-type formats have a single scalefactor +/-
122    offset per image on disk. If you do not care about the absolute
123    values, and will be removing the mean from the data, then the
124    unscaled values will have preserved intensity ratios compared to the
125    mean-centered scaled data.  However, this is not necessarily true of
126    other formats with more complicated scaling - such as MINC.
127    """
128    image_fileholder = img.file_map['image']
129    try:
130        fileobj = image_fileholder.get_prepare_fileobj()
131    except FileHolderError:
132        raise ImageFileError('No image file specified for this image')
133    if prefer not in ('scaled', 'unscaled'):
134        raise ValueError('Invalid string "%s" for "prefer"' % prefer)
135    hdr = img.get_header()
136    if prefer == 'unscaled':
137        try:
138            return hdr.raw_data_from_fileobj(fileobj)
139        except AttributeError:
140            pass
141    return hdr.data_from_fileobj(fileobj)
142