main.py 4.56 KB
Newer Older
1
2
3
4
# mrs_io.py - I/O utilities for FSL_MRS
#
# Author: Saad Jbabdi <saad@fmrib.ox.ac.uk>
#
5
# Copyright (C) 2019 University of Oxford
6
7
# SHBASECOPYRIGHT

8
import os
9
10
from pathlib import Path

11
12
import numpy as np

13
from fsl_mrs.utils.mrs_io import fsl_io as fsl, jmrui_io
14
15
from fsl_mrs.utils.mrs_io import lcm_io as lcm
from fsl_mrs.utils.mrs_io import jmrui_io as jmrui
16
from fsl_mrs.core import nifti_mrs  # import NIFTI_MRS, NotNIFTI_MRS
17
from fsl_mrs.core import basis as bmod
18
import fsl.utils.path as fslpath
19

William Clarke's avatar
William Clarke committed
20

21
22
23
class FileNotRecognisedError(Exception):
    pass

24

25
26
27
28
29
30
31
32
class UnknownBasisFormat(Exception):
    pass


class IncompatibleBasisFormat(Exception):
    pass


33
34
# Data reading functions:
# Load FIDs from NIFTI, .raw (LCMODEL style ) or .txt (jMRUI style) files
35
36
# These read functions should in general take a file name as a string and
# return the data in a numpy array with headers (in dict format) containing
37
# the following mandatory fields:
38
# Reciever bandwidth
39
# Dwell time
40
# central frequency
41
def _check_datatype(filename):
42
    """
43
44
45
    If data isn't NIFTI_MRS then
    identify the file type (.nii(.gz),.RAW/.H2O,.txt)
    Returns one of: 'NIFTI', 'RAW', 'TXT', 'Unknown'
46
    """
47
    ext = filename.split(os.extsep, 1)[-1]
48
49
50
51
52
53
    if 'nii' in ext.lower() or 'nii.gz' in ext.lower():
        return 'NIFTI'
    elif ext.lower() == 'raw' or ext.lower() == 'h2o':
        return 'RAW'
    elif ext.lower() == 'txt':
        return 'TXT'
54
    else:
55
        return 'Unknown'
56

57

58
def read_FID(filename):
59
60
61
62
63
    """
     Read FID file. Tries to detect type automatically

     Parameters
     ----------
64
     filename : str or pathlib.Path
65
66
67
68
69
70

     Returns:
     --------
     array-like (complex)
     dict (header info)
    """
71
72
73
    # Handle pathlib Path objects
    filename = str(filename)

74
    try:
75
76
        return nifti_mrs.NIFTI_MRS(filename)
    except (nifti_mrs.NotNIFTI_MRS, fslpath.PathError):
77
        data_type = _check_datatype(filename)
78
79

    if data_type == 'RAW':
80
        return lcm.read_lcm_raw_h2o(filename)
81
    elif data_type == 'NIFTI':
82
        return fsl.readNIFTI(filename)
83
    elif data_type == 'TXT':
84
        return jmrui.readjMRUItxt_fid(filename)
85
    else:
86
87
        raise FileNotRecognisedError(f'Cannot read data format {data_type} for file {filename}.')

88
89
90

# Basis reading functions
# Formats accepted are .json, .basis/.raw (LCMODEL style) or .txt (jMRUI style)
91
# Now handled by the Basis class methods
92
93
def read_basis(filename):
    """
94
95
96
97
98
99
100
101
102
    Read basis file(s) to generate a Basis object

    Load the basis fids, names and headers for each format handled.
    Ensures similar sorting by name for each type.

    :param filepath: Path to basis file or folder
    :type filepath: str or pathlib.Path
    :return: A Basis class object
    :rtype: fsl_mrs.core.basis.Basis
103
    """
104
105
106
107
108
109
110
111

    # Handle str objects
    if isinstance(filename, str):
        filename = Path(filename)

    # LCModel BASIS format format
    if filename.is_file():
        if filename.suffix.lower() == '.basis':
112
            basis, names, header = lcm.readLCModelBasis(filename)
113
114
            # Sort by name to match sorted filenames of other formats
            so = np.argsort(names)
115
            basis = basis[:, so]
116
117
118
            names = list(np.array(names)[so])
            header = list(np.array(header)[so])

119
120
121
            # Add missing hdr field
            for hdr in header:
                hdr['fwhm'] = None
122
123
        elif filename.suffix.lower() == '.txt':
            basis, names, header = jmrui_io.read_txtBasis_files([filename, ])
124
        else:
125
126
127
128
129
130
            raise UnknownBasisFormat(f'Cannot read data format {filename.suffix}')

    elif filename.is_dir():
        fslfiles = sorted(list(filename.glob('*.json')))
        rawfiles = sorted(list(filename.glob('*.RAW')))
        txtfiles = sorted(list(filename.glob('*.txt')))
131
        if fslfiles:
132
            basis, names, header = fsl.readFSLBasisFiles(filename)
133
134
135
        elif txtfiles:
            basis, names, header = jmrui.read_txtBasis_files(txtfiles)
        elif rawfiles:
136
137
138
            raise IncompatibleBasisFormat("LCModel raw files don't contain enough information"
                                          " to generate a Basis object. Please use fsl_mrs.utils.mrs_io"
                                          ".lcm_io.read_basis_files to load the partial information.")
139
        else:
140
            raise UnknownBasisFormat(f'{filename} contains neither .json, .txt, or .raw basis files!')
141
    else:
142
143
144
145
146
        raise UnknownBasisFormat(f'{filename} is neither a file nor a folder!')

    # Handle single basis spectra
    if basis.ndim == 1:
        basis = basis[:, np.newaxis]
147

148
    return bmod.Basis(basis, names, header)