Example #1
0
    def fit_water(self, line_broadening=5, zerofill=100,
                 filt_method=None, min_ppm=-5.0, max_ppm=5.0):
        """

        """
        # Get the water spectrum as well:
        f_hz, w_spectra = ana.get_spectra(self.water_fid,
                                          line_broadening=line_broadening,
                                          zerofill=zerofill,
                                          filt_method=filt_method)

        f_ppm = ut.freq_to_ppm(f_hz)
        # Averaging across echos:
        self.water_spectra = np.mean(w_spectra, 1)
        model, signal, params = ana.fit_lorentzian(self.water_spectra,
                                                   self.f_ppm,
                                                   lb=min_ppm,
                                                   ub=max_ppm)

        # Store the params:
        self.water_model = model
        self.water_signal = signal
        self.water_params = params
        self.water_idx = ut.make_idx(self.f_ppm, min_ppm, max_ppm)
        mean_params = stats.nanmean(params, 0)
        self.water_auc = self._calc_auc(ut.lorentzian, params, self.water_idx)
Example #2
0
    def fit_water(self,
                  line_broadening=5,
                  zerofill=100,
                  filt_method=None,
                  min_ppm=-5.0,
                  max_ppm=5.0):
        """

        """
        # Get the water spectrum as well:
        f_hz, w_spectra = ana.get_spectra(self.water_fid,
                                          line_broadening=line_broadening,
                                          zerofill=zerofill,
                                          filt_method=filt_method)

        f_ppm = ut.freq_to_ppm(f_hz)
        # Averaging across echos:
        self.water_spectra = np.mean(w_spectra, 1)
        model, signal, params = ana.fit_lorentzian(self.water_spectra,
                                                   self.f_ppm,
                                                   lb=min_ppm,
                                                   ub=max_ppm)

        # Store the params:
        self.water_model = model
        self.water_signal = signal
        self.water_params = params
        self.water_idx = ut.make_idx(self.f_ppm, min_ppm, max_ppm)
        mean_params = stats.nanmean(params, 0)
        self.water_auc = self._calc_auc(ut.lorentzian, params, self.water_idx)
Example #3
0
    def calc_spectra(self,
                     lbr,
                     cutoff,
                     sampling_rate=5000,
                     min_ppm=-0.7,
                     max_ppm=4.3):
        # Once we've done that, we only care about the water-suppressed data
        self.f, self.spec = ana.get_spectra(nt.TimeSeries(
            self.timeseries, sampling_rate=sampling_rate),
                                            line_broadening=lbr,
                                            filt_method=dict(lb=cutoff,
                                                             filt_order=256))

        # The first echo (off-resonance) is in the first output
        self.echo1 = self.spec[:, 0]
        # The on-resonance is in the second:
        self.echo2 = self.spec[:, 1]
        f_ppm = ut.freq_to_ppm(self.f)
        idx0 = np.argmin(np.abs(f_ppm - min_ppm))
        idx1 = np.argmin(np.abs(f_ppm - max_ppm))
        idx = slice(idx1, idx0)
        # Convert from Hz to ppm and extract the part you are interested in.
        self.f_ppm = f_ppm[idx]

        # Pack it into a dict:
        m_e1 = np.mean(self.echo1[:, idx], 0)
        m_e2 = np.mean(self.echo2[:, idx], 0)
        self.diff = m_e2 - m_e1

        return dict(echo1=m_e1, echo2=m_e2, diff=self.diff), f_ppm
Example #4
0
def test_get_spectra():
    """
    Test the function that does the spectral analysis
    """
    data = np.transpose(nib.load(file_name).get_data(), [1,2,3,4,5,0]).squeeze()
    w_data, w_supp_data = ana.coil_combine(data)

    # XXX Just basic smoke-testing for now:
    f_nonw, nonw_sig1 = ana.get_spectra(nt.TimeSeries(w_supp_data,
                                       sampling_rate=5000))

    f_nonw, nonw_sig2 = ana.get_spectra(nt.TimeSeries(w_supp_data,
                                       sampling_rate=5000),
                                       line_broadening=5)

    f_nonw, nonw_sig3 = ana.get_spectra(nt.TimeSeries(w_supp_data,
                                       sampling_rate=5000),
                                       line_broadening=5,
                                       zerofill=1000)
Example #5
0
    def __init__(self,
                 in_file,
                 line_broadening=5,
                 zerofill=100,
                 filt_method=None,
                 min_ppm=-0.7,
                 max_ppm=4.3):
        """
        Parameters
        ----------

        in_file : str
            Path to a nifti file with SV-PROBE MRS data.

        line_broadening : float
           How much to broaden the spectral line-widths (Hz)
           
        zerofill : int
           How many zeros to add to the spectrum for additional spectral
           resolution

        min_ppm, max_ppm : float
           The limits of the spectra that are represented

        fit_lb, fit_ub : float
           The limits for the part of the spectrum for which we fit the
           creatine and GABA peaks. 
        
        """
        self.raw_data = np.transpose(
            nib.load(in_file).get_data(), [1, 2, 3, 4, 5, 0]).squeeze()

        w_data, w_supp_data = ana.coil_combine(self.raw_data,
                                               w_idx=range(8),
                                               coil_dim=1)
        # We keep these around for reference, as private attrs
        self._water_data = w_data
        self._w_supp_data = w_supp_data
        # This is the time-domain signal of interest, combined over coils:
        self.data = ana.subtract_water(w_data, w_supp_data)

        f_hz, spectra = ana.get_spectra(self.data,
                                        line_broadening=line_broadening,
                                        zerofill=zerofill,
                                        filt_method=filt_method)

        self.f_hz = f_hz
        # Convert from Hz to ppm and extract the part you are interested in.
        f_ppm = ut.freq_to_ppm(self.f_hz)
        idx0 = np.argmin(np.abs(f_ppm - min_ppm))
        idx1 = np.argmin(np.abs(f_ppm - max_ppm))
        self.idx = slice(idx1, idx0)
        self.f_ppm = f_ppm
        self.spectra = spectra[:, self.idx]
Example #6
0
    def __init__(self, in_file, line_broadening=5, zerofill=100,
                 filt_method=None, min_ppm=-0.7, max_ppm=4.3):
        """
        Parameters
        ----------

        in_file : str
            Path to a nifti file with SV-PROBE MRS data.

        line_broadening : float
           How much to broaden the spectral line-widths (Hz)
           
        zerofill : int
           How many zeros to add to the spectrum for additional spectral
           resolution

        min_ppm, max_ppm : float
           The limits of the spectra that are represented

        fit_lb, fit_ub : float
           The limits for the part of the spectrum for which we fit the
           creatine and GABA peaks. 
        
        """
        self.raw_data = np.transpose(nib.load(in_file).get_data(),
                                     [1,2,3,4,5,0]).squeeze()

        w_data, w_supp_data = ana.coil_combine(self.raw_data, w_idx = range(8),
                                               coil_dim=1)
        # We keep these around for reference, as private attrs
        self._water_data = w_data
        self._w_supp_data = w_supp_data
        # This is the time-domain signal of interest, combined over coils:
        self.data = ana.subtract_water(w_data, w_supp_data)

        f_hz, spectra = ana.get_spectra(self.data,
                                        line_broadening=line_broadening,
                                        zerofill=zerofill,
                                        filt_method=filt_method)
                                           
        self.f_hz = f_hz
        # Convert from Hz to ppm and extract the part you are interested in.
        f_ppm = ut.freq_to_ppm(self.f_hz)
        idx0 = np.argmin(np.abs(f_ppm - min_ppm))
        idx1 = np.argmin(np.abs(f_ppm - max_ppm))
        self.idx = slice(idx1, idx0)
        self.f_ppm = f_ppm
        self.spectra = spectra[:,self.idx]
Example #7
0
    def __init__(self,
                 in_data,
                 line_broadening=5,
                 zerofill=100,
                 filt_method=None,
                 min_ppm=-0.7,
                 max_ppm=4.3):
        """
        Parameters
        ----------

        in_data : str
            Path to a nifti file containing MRS data.

        line_broadening : float
           How much to broaden the spectral line-widths (Hz)
           
        zerofill : int
           How many zeros to add to the spectrum for additional spectral
           resolution

        min_ppm, max_ppm : float
           The limits of the spectra that are represented

        fit_lb, fit_ub : float
           The limits for the part of the spectrum for which we fit the
           creatine and GABA peaks. 
        
        """
        if isinstance(in_data, str):
            # The nifti files follow the strange nifti convention, but we want
            # to use our own logic, which is transients on dim 0 and time on
            # dim -1:
            self.raw_data = np.transpose(
                nib.load(in_data).get_data(), [1, 2, 3, 4, 5, 0]).squeeze()
        elif isinstance(in_data, np.ndarray):
            self.raw_data = in_data

        w_data, w_supp_data = ana.coil_combine(self.raw_data)
        f_hz, w_supp_spectra = ana.get_spectra(w_supp_data,
                                               line_broadening=line_broadening,
                                               zerofill=zerofill,
                                               filt_method=filt_method)

        self.w_supp_spectra = w_supp_spectra

        # Often, there will be some small offset from the on-resonance
        # frequency, which we can correct for. We fit a Lorentzian to each of
        # the spectra from the water-suppressed data, so that we can get a
        # phase-corrected estimate of the frequency shift, instead of just
        # relying on the frequency of the maximum:
        self.w_supp_lorentz = np.zeros(w_supp_spectra.shape[:-1] + (6, ))
        for ii in range(self.w_supp_lorentz.shape[0]):
            for jj in range(self.w_supp_lorentz.shape[1]):
                self.w_supp_lorentz[ii,jj]=\
                    ana._do_lorentzian_fit(f_hz, w_supp_spectra[ii,jj])

        # We store the frequency offset for each transient/echo:
        self.freq_offset = self.w_supp_lorentz[..., 0]

        # But for now, we average over all the transients/echos for the
        # correction:
        mean_freq_offset = np.mean(self.w_supp_lorentz[..., 0])
        f_hz = f_hz - mean_freq_offset

        self.water_fid = w_data
        self.w_supp_fid = w_supp_data
        # This is the time-domain signal of interest, combined over coils:
        self.data = ana.subtract_water(w_data, w_supp_data)

        _, spectra = ana.get_spectra(self.data,
                                     line_broadening=line_broadening,
                                     zerofill=zerofill,
                                     filt_method=filt_method)

        self.f_hz = f_hz
        # Convert from Hz to ppm and extract the part you are interested in.
        f_ppm = ut.freq_to_ppm(self.f_hz)
        idx0 = np.argmin(np.abs(f_ppm - min_ppm))
        idx1 = np.argmin(np.abs(f_ppm - max_ppm))
        self.idx = slice(idx1, idx0)
        self.f_ppm = f_ppm

        self.echo_off = spectra[:, 1]
        self.echo_on = spectra[:, 0]

        # Calculate sum and difference:
        self.diff_spectra = self.echo_on - self.echo_off
        self.sum_spectra = self.echo_off + self.echo_on
Example #8
0
    def __init__(self,
                 in_data,
                 w_idx=[1,2,3],
                 line_broadening=5,
                 zerofill=100,
                 filt_method=None,
                 spect_method=dict(NFFT=1024, n_overlap=1023, BW=2),
                 min_ppm=-0.7,
                 max_ppm=4.3,
                 sampling_rate=5000.):
        """
        Parameters
        ----------

        in_data : str
            Path to a nifti file containing MRS data.

        w_idx : list (optional)
            The indices to the non-water-suppressed transients. Per default we
            take the 2nd-4th transients. We dump the first one, because it
            seems to be quite different than the rest of them. 

        line_broadening : float (optional)
           How much to broaden the spectral line-widths (Hz). Default: 5
           
        zerofill : int (optional)
           How many zeros to add to the spectrum for additional spectral
           resolution. Default: 100

        filt_method : dict (optional)
            How/whether to filter the data. Default: None (#nofilter)

        spect_method: dict (optional)
            How to derive spectra. Per default, a simple Fourier transform will
            be derived from apodized time-series, but other methods can also be
            used (see `nitime` documentation for details)
            
        min_ppm, max_ppm : float
           The limits of the spectra that are represented

        sampling_rate : float
           The sampling rate in Hz.
        
        """
        if isinstance(in_data, str):
            # The nifti files follow the strange nifti convention, but we want
            # to use our own logic, which is transients on dim 0 and time on
            # dim -1:
            self.raw_data = np.transpose(nib.load(in_data).get_data(),
                                         [1,2,3,4,5,0]).squeeze()
        elif isinstance(in_data, np.ndarray):
            self.raw_data = in_data

        w_data, w_supp_data = ana.coil_combine(self.raw_data, w_idx=w_idx,
                                               sampling_rate=sampling_rate)
        f_hz, w_supp_spectra = ana.get_spectra(w_supp_data,
                                           line_broadening=line_broadening,
                                           zerofill=zerofill,
                                           filt_method=filt_method,
                                           spect_method=spect_method)

        self.w_supp_spectra = w_supp_spectra

        # Often, there will be some small offset from the on-resonance
        # frequency, which we can correct for. We fit a Lorentzian to each of
        # the spectra from the water-suppressed data, so that we can get a
        # phase-corrected estimate of the frequency shift, instead of just
        # relying on the frequency of the maximum:
        self.w_supp_lorentz = np.zeros(w_supp_spectra.shape[:-1] + (6,))
        for ii in range(self.w_supp_lorentz.shape[0]):
            for jj in range(self.w_supp_lorentz.shape[1]):
                self.w_supp_lorentz[ii,jj]=\
                    ana._do_lorentzian_fit(f_hz, w_supp_spectra[ii,jj])

        # We store the frequency offset for each transient/echo:
        self.freq_offset = self.w_supp_lorentz[..., 0]

        # But for now, we average over all the transients/echos for the
        # correction: 
        mean_freq_offset = np.mean(self.w_supp_lorentz[..., 0])
        f_hz = f_hz - mean_freq_offset
    
        self.water_fid = w_data
        self.w_supp_fid = w_supp_data
        # This is the time-domain signal of interest, combined over coils:
        self.data = ana.subtract_water(w_data, w_supp_data)

        _, spectra = ana.get_spectra(self.data,
                                     line_broadening=line_broadening,
                                     zerofill=zerofill,
                                     filt_method=filt_method)

        self.f_hz = f_hz
        # Convert from Hz to ppm and extract the part you are interested in.
        f_ppm = ut.freq_to_ppm(self.f_hz)
        idx0 = np.argmin(np.abs(f_ppm - min_ppm))
        idx1 = np.argmin(np.abs(f_ppm - max_ppm))
        self.idx = slice(idx1, idx0)
        self.f_ppm = f_ppm
    
        self.echo_off = spectra[:, 1]
        self.echo_on = spectra[:, 0]

        # Calculate sum and difference:
        self.diff_spectra = self.echo_on - self.echo_off
        self.sum_spectra = self.echo_off + self.echo_on
Example #9
0
    def __init__(self,
                 in_data,
                 w_idx=[1,2,3],
                 line_broadening=5,
                 zerofill=100,
                 filt_method=None,
                 spect_method=dict(NFFT=1024, n_overlap=1023, BW=2),
                 min_ppm=-0.7,
                 max_ppm=4.3,
                 sampling_rate=5000.):
        """
        Parameters
        ----------

        in_data : str
            Path to a nifti file containing MRS data.

        w_idx : list (optional)
            The indices to the non-water-suppressed transients. Per default we
            take the 2nd-4th transients. We dump the first one, because it
            seems to be quite different than the rest of them. 

        line_broadening : float (optional)
           How much to broaden the spectral line-widths (Hz). Default: 5
           
        zerofill : int (optional)
           How many zeros to add to the spectrum for additional spectral
           resolution. Default: 100

        filt_method : dict (optional)
            How/whether to filter the data. Default: None (#nofilter)

        spect_method: dict (optional)
            How to derive spectra. Per default, a simple Fourier transform will
            be derived from apodized time-series, but other methods can also be
            used (see `nitime` documentation for details)
            
        min_ppm, max_ppm : float
           The limits of the spectra that are represented

        sampling_rate : float
           The sampling rate in Hz.
        
        """
        if isinstance(in_data, str):
            # The nifti files follow the strange nifti convention, but we want
            # to use our own logic, which is transients on dim 0 and time on
            # dim -1:
            self.raw_data = np.transpose(nib.load(in_data).get_data(),
                                         [1,2,3,4,5,0]).squeeze()
        elif isinstance(in_data, np.ndarray):
            self.raw_data = in_data

        w_data, w_supp_data = ana.coil_combine(self.raw_data, w_idx=w_idx,
                                               sampling_rate=sampling_rate)
        f_hz, w_supp_spectra = ana.get_spectra(w_supp_data,
                                           line_broadening=line_broadening,
                                           zerofill=zerofill,
                                           filt_method=filt_method,
                                           spect_method=spect_method)

        self.w_supp_spectra = w_supp_spectra

        # Often, there will be some small offset from the on-resonance
        # frequency, which we can correct for. We fit a Lorentzian to each of
        # the spectra from the water-suppressed data, so that we can get a
        # phase-corrected estimate of the frequency shift, instead of just
        # relying on the frequency of the maximum:
        self.w_supp_lorentz = np.zeros(w_supp_spectra.shape[:-1] + (6,))
        for ii in range(self.w_supp_lorentz.shape[0]):
            for jj in range(self.w_supp_lorentz.shape[1]):
                self.w_supp_lorentz[ii,jj]=\
                    ana._do_lorentzian_fit(f_hz, w_supp_spectra[ii,jj])

        # We store the frequency offset for each transient/echo:
        self.freq_offset = self.w_supp_lorentz[..., 0]

        # But for now, we average over all the transients/echos for the
        # correction: 
        mean_freq_offset = np.mean(self.w_supp_lorentz[..., 0])
        f_hz = f_hz - mean_freq_offset
    
        self.water_fid = w_data
        self.w_supp_fid = w_supp_data
        # This is the time-domain signal of interest, combined over coils:
        self.data = ana.subtract_water(w_data, w_supp_data)

        _, spectra = ana.get_spectra(self.data,
                                     line_broadening=line_broadening,
                                     zerofill=zerofill,
                                     filt_method=filt_method)

        self.f_hz = f_hz
        # Convert from Hz to ppm and extract the part you are interested in.
        f_ppm = ut.freq_to_ppm(self.f_hz)
        idx0 = np.argmin(np.abs(f_ppm - min_ppm))
        idx1 = np.argmin(np.abs(f_ppm - max_ppm))
        self.idx = slice(idx1, idx0)
        self.f_ppm = f_ppm
    
        self.echo_off = spectra[:, 1]
        self.echo_on = spectra[:, 0]

        # Calculate sum and difference:
        self.diff_spectra = self.echo_on - self.echo_off
        self.sum_spectra = self.echo_off + self.echo_on
Example #10
0
    def __init__(self, in_data, line_broadening=5, zerofill=100,
                 filt_method=None, min_ppm=-0.7, max_ppm=4.3):
        """
        Parameters
        ----------

        in_data : str
            Path to a nifti file containing MRS data.

        line_broadening : float
           How much to broaden the spectral line-widths (Hz)
           
        zerofill : int
           How many zeros to add to the spectrum for additional spectral
           resolution

        min_ppm, max_ppm : float
           The limits of the spectra that are represented

        fit_lb, fit_ub : float
           The limits for the part of the spectrum for which we fit the
           creatine and GABA peaks. 
        
        """
        if isinstance(in_data, str):
            # The nifti files follow the strange nifti convention, but we want
            # to use our own logic, which is transients on dim 0 and time on
            # dim -1:
            self.raw_data = np.transpose(nib.load(in_data).get_data(),
                                         [1,2,3,4,5,0]).squeeze()
        elif isinstance(in_data, np.ndarray):
            self.raw_data = in_data

        w_data, w_supp_data = ana.coil_combine(self.raw_data)
        f_hz, w_supp_spectra = ana.get_spectra(w_supp_data,
                                           line_broadening=line_broadening,
                                           zerofill=zerofill,
                                           filt_method=filt_method)

        self.w_supp_spectra = w_supp_spectra

        # Often, there will be some small offset from the on-resonance
        # frequency, which we can correct for. We fit a Lorentzian to each of
        # the spectra from the water-suppressed data, so that we can get a
        # phase-corrected estimate of the frequency shift, instead of just
        # relying on the frequency of the maximum:
        self.w_supp_lorentz = np.zeros(w_supp_spectra.shape[:-1] + (6,))
        for ii in range(self.w_supp_lorentz.shape[0]):
            for jj in range(self.w_supp_lorentz.shape[1]):
                self.w_supp_lorentz[ii,jj]=\
                    ana._do_lorentzian_fit(f_hz, w_supp_spectra[ii,jj])

        # We store the frequency offset for each transient/echo:
        self.freq_offset = self.w_supp_lorentz[..., 0]

        # But for now, we average over all the transients/echos for the
        # correction: 
        mean_freq_offset = np.mean(self.w_supp_lorentz[..., 0])
        f_hz = f_hz - mean_freq_offset
    
        self.water_fid = w_data
        self.w_supp_fid = w_supp_data
        # This is the time-domain signal of interest, combined over coils:
        self.data = ana.subtract_water(w_data, w_supp_data)

        _, spectra = ana.get_spectra(self.data,
                                     line_broadening=line_broadening,
                                     zerofill=zerofill,
                                     filt_method=filt_method)

        self.f_hz = f_hz
        # Convert from Hz to ppm and extract the part you are interested in.
        f_ppm = ut.freq_to_ppm(self.f_hz)
        idx0 = np.argmin(np.abs(f_ppm - min_ppm))
        idx1 = np.argmin(np.abs(f_ppm - max_ppm))
        self.idx = slice(idx1, idx0)
        self.f_ppm = f_ppm
    
        self.echo_off = spectra[:, 1]
        self.echo_on = spectra[:, 0]

        # Calculate sum and difference:
        self.diff_spectra = self.echo_on - self.echo_off
        self.sum_spectra = self.echo_off + self.echo_on