def test_fit_FSLModel_on_invivo_sim(): FIDs, hdr, trueconcs = syntheticFromBasisFile(basis_path, noisecovariance=[[1E-3]], broadening=(9.0, 9.0), concentrations={'Mac': 2.0}) basis, names, header = mrs_io.read_basis(basis_path) mrs = MRS(FID=FIDs, header=hdr, basis=basis, basis_hdr=header[0], names=names) mrs.processForFitting() metab_groups = [0] * mrs.numBasis Fitargs = { 'ppmlim': [0.2, 4.2], 'method': 'MH', 'baseline_order': -1, 'metab_groups': metab_groups, 'MHSamples': 100 } res = fit_FSLModel(mrs, **Fitargs) fittedRelconcs = res.getConc(scaling='internal', metab=mrs.names) answers = np.asarray(trueconcs) answers /= (answers[names.index('Cr')] + trueconcs[names.index('PCr')]) assert np.allclose(fittedRelconcs, answers, atol=5E-2)
def data(): noiseCov = 0.01 amplitude = np.asarray([0.5, 0.5, 1.0]) * 10 chemshift = np.asarray([3.0, 3.05, 2.0]) - 4.65 lw = [10, 10, 10] phases = [0, 0, 0] g = [0, 0, 0] basisNames = ['Cr', 'PCr', 'NAA'] basisFIDs = [] for idx, _ in enumerate(amplitude): tmp, basisHdr = syntheticFID(noisecovariance=[[0.0]], chemicalshift=[chemshift[idx]], amplitude=[1.0], linewidth=[lw[idx] / 5], phase=[phases[idx]], g=[g[idx]]) basisFIDs.append(tmp[0]) basisFIDs = np.asarray(basisFIDs) synFID, synHdr = syntheticFID(noisecovariance=[[noiseCov]], chemicalshift=chemshift, amplitude=amplitude, linewidth=lw, phase=phases, g=g) synMRS = MRS(FID=synFID[0], header=synHdr, basis=basisFIDs, basis_hdr=basisHdr, names=basisNames) return synMRS, amplitude
def shiftToRef(FID, target, bw, cf, ppmlim=(2.8, 3.2), shift=True): #Find maximum of absolute spectrum in ppm limit padFID = pad(FID, FID.size * 3) MRSargs = {'FID': padFID, 'bw': bw, 'cf': cf} mrs = MRS(**MRSargs) spec = extract_spectrum(mrs, padFID, ppmlim=ppmlim, shift=shift) if shift: extractedAxis = mrs.getAxes(ppmlim=ppmlim) else: extractedAxis = mrs.getAxes(ppmlim=ppmlim, axis='ppm') maxIndex = np.argmax(np.abs(spec)) shiftAmount = extractedAxis[maxIndex] - target shiftAmountHz = shiftAmount * mrs.centralFrequency / 1E6 return freqshift(FID, 1 / bw, -shiftAmountHz), shiftAmount
def phase_freq_align_diff(FIDlist0, FIDlist1, bandwidth, centralFrequency, diffType='add', ppmlim=None, shift=True, target=None): """ Align subspectra from difference methods. Only spectra in FIDlist0 are shifted. Parameters: ----------- FIDlist0 : list - shifted FIDlist1 : list - fixed bandwidth : float (unit=Hz) centralFrequency : float (unit=Hz) diffType : string - add or subtract ppmlim : tuple shift : apply H20 shift to ppm limit ref : reference data to align to Returns: -------- two lists of FID aligned to each other, phase and shift applied to first list. """ # Process target if target is not None: tgt_FID = target else: diffFIDList = [] for fid0, fid1 in zip(FIDlist0, FIDlist1): if diffType.lower() == 'add': diffFIDList.append(add(fid1, fid0)) elif diffType.lower() == 'sub': diffFIDList.append(subtract(fid1, fid0)) else: raise ValueError('diffType must be add or sub.') tgt_FID = get_target_FID(diffFIDList, target='nearest_to_mean') # Pass to phase_freq_align mrs = MRS(FID=FIDlist0[0], cf=centralFrequency, bw=bandwidth) phiOut, epsOut = [], [] alignedFIDs0 = [] for fid0, fid1 in zip(FIDlist0, FIDlist1): # breakpoint() out = align_FID_diff(mrs, fid0, fid1, tgt_FID, diffType=diffType, ppmlim=ppmlim, shift=shift) alignedFIDs0.append(out[0]) phiOut.append(out[1]) epsOut.append(out[2]) return alignedFIDs0, FIDlist1, phiOut, epsOut
def mrs_by_index(self,index): mrs_out = MRS(FID=self.data[index[0],index[1],index[2],:], header=self.header, basis=self.basis, names=self.names, basis_hdr=self.basis_hdr, H2O=self.H2O[index[0],index[1],index[2],:]) self._process_mrs(mrs_out) return mrs_out
def identifyUnlikeFIDs(FIDList,bandwidth,centralFrequency,sdlimit = 1.96,iterations=2,ppmlim=None,shift=True): """ Identify FIDs in a list that are unlike the others Args: FIDList (list of ndarray): Time domain data bandwidth (float) : Bandwidth in Hz centralFrequency (float) : Central frequency in Hz sdlimit (float,optional) : Exclusion limit (number of stnadard deviations). Default = 3. iterations (int,optional): Number of iterations to use. ppmlim (tuple,optional) : Limit to this ppm range shift (bool,optional) : Apply H20 shft Returns: goodFIDS (list of ndarray): FIDs that passed the criteria badFIDS (list of ndarray): FIDs that failed the likeness critera rmIndicies (list of int): Indicies of those FIDs that have been removed metric (list of floats): Likeness metric of each FID """ # Calculate the FID to compare to target = get_target_FID(FIDList,target='median') if ppmlim is not None: MRSargs = {'FID':target,'bw':bandwidth,'cf':centralFrequency} mrs = MRS(**MRSargs) target = extract_spectrum(mrs,target,ppmlim=ppmlim,shift=shift) compareList = [extract_spectrum(mrs,f,ppmlim=ppmlim,shift=shift) for f in FIDList] else: compareList = [FIDToSpec(f) for f in FIDList] target = FIDToSpec(target) # Do the comparison for idx in range(iterations): metric = [] for data in compareList: metric.append(np.linalg.norm(data-target)) metric = np.asarray(metric) metric_avg = np.mean(metric) metric_std = np.std(metric) goodFIDs,badFIDs,rmIndicies,keepIndicies = [],[],[],[] for iDx,(data,m) in enumerate(zip(FIDList,metric)): if m > ((sdlimit*metric_std)+metric_avg) or m < (-(sdlimit*metric_std)+metric_avg): badFIDs.append(data) rmIndicies.append(iDx) else: goodFIDs.append(data) keepIndicies.append(iDx) target = get_target_FID(goodFIDs,target='median') if ppmlim is not None: target = extract_spectrum(mrs,target,ppmlim=ppmlim,shift=shift) else: target = FIDToSpec(target) return goodFIDs,badFIDs,keepIndicies,rmIndicies,metric.tolist()
def test_shiftToRef(): testFIDs, testHdrs = syn.syntheticFID(amplitude=[1, 0], chemicalshift=[-2.1, 0], phase=[0, 0], points=1024, noisecovariance=[[1E-3]]) shiftFID, _ = preproc.shiftToRef(testFIDs[0], -2.0, testHdrs['bandwidth'], testHdrs['centralFrequency'], ppmlim=(-2.2, -2.0), shift=False) mrs = MRS(FID=shiftFID, header=testHdrs) maxindex = np.argmax(mrs.getSpectrum(shift=False)) position = mrs.getAxes(axis='ppm')[maxindex] assert np.isclose(position, -2.0, atol=1E-1)
def test_align_diff(): shift0 = np.random.randn(10) * 0.15 phs = np.random.randn(10) * 0.1 * np.pi shiftedFIDs0 = [] shiftedFIDs1 = [] for s0, p in zip(shift0, phs): testFIDs, testHdrs = syn.syntheticFID(amplitude=[1, 1], chemicalshift=[-2 + s0, 3 + s0], phase=[p + np.pi, p], damping=[100, 100], points=2048, noisecovariance=[[1E-6]]) shiftedFIDs0.append(testFIDs[0]) testFIDs, testHdrs = syn.syntheticFID(amplitude=[1, 1], chemicalshift=[-2, 3], phase=[0, 0], damping=[100, 100], points=2048, noisecovariance=[[1E-6]]) shiftedFIDs1.append(testFIDs[0]) testFIDs0, _ = syn.syntheticFID(amplitude=[2, 1], chemicalshift=[-2, 3], phase=[np.pi, 0], damping=[100, 100], points=2048, noisecovariance=[[1E-6]]) testFIDs1, _ = syn.syntheticFID(amplitude=[2, 1], chemicalshift=[-2, 3], phase=[0, 0], damping=[100, 100], points=2048, noisecovariance=[[1E-6]]) tgt = testFIDs0[0] + testFIDs1[0] mrs = MRS(FID=tgt, header=testHdrs) sub0_aa, sub1_aa, phi, eps = preproc.phase_freq_align_diff( shiftedFIDs0, shiftedFIDs1, testHdrs['bandwidth'], testHdrs['centralFrequency'], target=tgt, shift=False, ppmlim=(-5, 5)) # align.phase_freq_align_diff_report(shiftedFIDs0,shiftedFIDs1,sub0_aa,sub1_aa,testHdrs,eps,phi,shift=False,ppmlim=(-5,5)) shiftInHz = shift0 * testHdrs['centralFrequency'] assert np.allclose(eps, shiftInHz, atol=1E-0) assert np.allclose(phi, phs, atol=1E-0)
def mrs_from_average(self): FID = misc.volume_to_list(self.data,self.mask) H2O = misc.volume_to_list(self.H2O,self.mask) FID = sum(FID)/len(FID) H2O = sum(H2O)/len(H2O) mrs_out = MRS(FID=FID, header=self.header, basis=self.basis, names=self.names, basis_hdr=self.basis_hdr, H2O=H2O) self._process_mrs(mrs_out) return mrs_out
def data(): noiseCov = 0.001 amplitude = np.asarray([0.5, 0.5, 1.0]) * 10 chemshift = np.asarray([3.0, 3.05, 2.0]) - 4.65 lw = [10, 10, 10] phases = [0, 0, 0] g = [0, 0, 0] basisNames = ['Cr', 'PCr', 'NAA'] begintime = 0.00005 basisFIDs = [] for idx, _ in enumerate(amplitude): tmp, basisHdr = syntheticFID(noisecovariance=[[0.0]], chemicalshift=[chemshift[idx] + 0.1], amplitude=[1.0], linewidth=[lw[idx] / 5], phase=[phases[idx]], g=[g[idx]], begintime=0) basisFIDs.append(tmp[0]) basisFIDs = np.asarray(basisFIDs) synFID, synHdr = syntheticFID(noisecovariance=[[noiseCov]], chemicalshift=chemshift, amplitude=amplitude, linewidth=lw, phase=phases, g=g, begintime=begintime) synMRS = MRS(FID=synFID[0], header=synHdr, basis=basisFIDs, basis_hdr=basisHdr, names=basisNames) metab_groups = [0] * synMRS.numBasis Fitargs = { 'ppmlim': [0.2, 4.2], 'method': 'MH', 'baseline_order': -1, 'metab_groups': metab_groups, 'MHSamples': 100, 'disable_mh_priors': True } res = fit_FSLModel(synMRS, **Fitargs) return res, amplitude
def generateBasisFromRes(mrs, res, resparams): """ Return mrs objects for each fitted basis spectrum""" mrsFits = [] for metab in mrs.names: pred = models.getFittedModel(res.model, resparams, res.base_poly, res.metab_groups, mrs, basisSelect=metab, noBaseline=True) pred = SpecToFID(pred) # predict FID not Spec mrsOut = MRS(pred, cf=mrs.centralFrequency, bw=mrs.bandwidth) mrsFits.append(mrsOut) return mrsFits
def test_quantifyWater(): basis, names, headerb = mrsio.read_basis(basisfile) crIndex = names.index('Cr') data, header = mrsio.read_FID(metabfile) dataw, headerw = mrsio.read_FID(h2ofile) mrs = MRS(FID=data, header=header, basis=basis[:, crIndex], names=['Cr'], basis_hdr=headerb[crIndex], H2O=dataw) mrs.check_FID(repair=True) mrs.check_Basis(repair=True) Fitargs = { 'ppmlim': [0.2, 5.2], 'method': 'MH', 'baseline_order': -1, 'metab_groups': [0] } res = fit_FSLModel(mrs, **Fitargs) tissueFractions = {'GM': 0.6, 'WM': 0.4, 'CSF': 0.0} TE = 0.03 T2dict = { 'H2O_GM': 0.110, 'H2O_WM': 0.080, 'H2O_CSF': 2.55, 'METAB': 0.160 } res.calculateConcScaling(mrs, referenceMetab=['Cr'], waterRefFID=mrs.H2O, tissueFractions=tissueFractions, TE=TE, T2=T2dict, waterReferenceMetab='Cr', wRefMetabProtons=5, reflimits=(2, 5), verbose=False) print(res.getConc(scaling='raw')) print(res.getConc(scaling='internal')) print(res.getConc(scaling='molality')) print(res.getConc(scaling='molarity')) assert np.allclose(res.getConc(scaling='internal'), 1.0) assert np.allclose(res.getConc(scaling='molarity'), 10.59, atol=1E-1)
def phaseCorrect(FID, bw, cf, ppmlim=(2.8, 3.2), shift=True, hlsvd=False): """ Phase correction based on the phase of a maximum point. HLSVD is used to remove peaks outside the limits to flatten baseline first. Args: FID (ndarray): Time domain data bw (float): bandwidth cf (float): central frequency in Hz ppmlim (tuple,optional) : Limit to this ppm range shift (bool,optional) : Apply H20 shft hlsvd (bool,optional) : Enable hlsvd step Returns: FID (ndarray): Phase corrected FID """ if hlsvd: # Run HLSVD to remove peaks outside limits try: fid_hlsvd = hlsvd(FID, 1 / bw, cf, (ppmlim[1] + 0.5, ppmlim[1] + 3.0), limitUnits='ppm+shift') fid_hlsvd = hlsvd(fid_hlsvd, 1 / bw, cf, (ppmlim[0] - 3.0, ppmlim[0] - 0.5), limitUnits='ppm+shift') except: fid_hlsvd = FID print('HLSVD in phaseCorrect failed, proceeding to phasing.') else: fid_hlsvd = FID # Find maximum of absolute spectrum in ppm limit padFID = pad(fid_hlsvd, FID.size * 3) MRSargs = {'FID': padFID, 'bw': bw, 'cf': cf} mrs = MRS(**MRSargs) spec = extract_spectrum(mrs, padFID, ppmlim=ppmlim, shift=shift) maxIndex = np.argmax(np.abs(spec)) phaseAngle = -np.angle(spec[maxIndex]) return applyPhase(FID, phaseAngle), phaseAngle, int(np.round(maxIndex / 4))
def __iter__(self): shape = self.data.shape self._store_scalings = [] for idx in np.ndindex(shape[:3]): if self.mask[idx]: mrs_out = MRS(FID=self.data[idx], header=self.header, basis=self.basis, names=self.names, basis_hdr=self.basis_hdr, H2O=self.H2O[idx]) self._process_mrs(mrs_out) self._store_scalings.append(mrs_out.scaling) if self.tissue_seg_loaded: tissue_seg = {'CSF':self.csf[idx],'WM':self.wm[idx],'GM':self.gm[idx]} else: tissue_seg = None yield mrs_out,idx,tissue_seg
def apodize_report(inFID, outFID, hdr, plotlim=(0.2, 6), html=None): """ Generate report """ # from matplotlib import pyplot as plt from fsl_mrs.core import MRS import plotly.graph_objects as go from fsl_mrs.utils.preproc.reporting import plotStyles, plotAxesStyle # Turn input FIDs into mrs objects toMRSobj = lambda fid: MRS(FID=fid, header=hdr) plotIn = toMRSobj(inFID) plotOut = toMRSobj(outFID) # Fetch line styles lines, colors, _ = plotStyles() # Make a new figure fig = go.Figure() # Add lines to figure def addline(fig, mrs, lim, name, linestyle): trace = go.Scatter(x=mrs.getAxes(ppmlim=lim), y=np.real(mrs.getSpectrum(ppmlim=lim)), mode='lines', name=name, line=linestyle) return fig.add_trace(trace) fig = addline(fig, plotIn, plotlim, 'Uncorrected', lines['in']) fig = addline(fig, plotOut, plotlim, 'Corrected', lines['out']) # Axes layout plotAxesStyle(fig, plotlim, title='Apodization summary') # Generate report if html is not None: from plotly.offline import plot from fsl_mrs.utils.preproc.reporting import figgroup, singleReport from datetime import datetime import os.path as op if op.isdir(html): filename = 'report_' + datetime.now().strftime( "%Y%m%d_%H%M%S%f")[:-3] + '.html' htmlfile = op.join(html, filename) elif op.isdir(op.dirname(html)) and op.splitext(html)[1] == '.html': htmlfile = html else: raise ValueError('Report html path must be file or directory. ') opName = 'Apodization' timestr = datetime.now().strftime("%H:%M:%S") datestr = datetime.now().strftime("%d/%m/%Y") headerinfo = 'Report for fsl_mrs.utils.preproc.filtering.apodize.\n'\ + f'Generated at {timestr} on {datestr}.' # Figures div = plot(fig, output_type='div', include_plotlyjs='cdn') figurelist = [ figgroup(fig=div, name='', foretext=f'Apodization of spectra.', afttext=f'') ] singleReport(htmlfile, opName, headerinfo, figurelist) return fig else: return fig
def combine_FIDs_report(inFIDs, outFID, hdr, ncha=2, ppmlim=(0.0, 6.0), method='not specified', html=None): """ Take list of FIDs that are passed to combine and output If uncombined data it will display ncha channels (default 2). """ from fsl_mrs.core import MRS import plotly.graph_objects as go from fsl_mrs.utils.preproc.reporting import plotStyles, plotAxesStyle from matplotlib.pyplot import cm toMRSobj = lambda fid: MRS(FID=fid, header=hdr) # Assemble data to plot toPlotIn = [] colourVecIn = [] legendIn = [] if isinstance(inFIDs, list): for idx, fid in enumerate(inFIDs): if inFIDs[0].ndim > 1: toPlotIn.extend([toMRSobj(f) for f in fid[:, :ncha].T]) colourVecIn.extend([idx / len(inFIDs)] * ncha) legendIn.extend( [f'FID #{idx}: CHA #{jdx}' for jdx in range(ncha)]) else: toPlotIn.append(toMRSobj(fid)) colourVecIn.append(idx / len(inFIDs)) legendIn.append(f'FID #{idx}') elif inFIDs.ndim > 1: toPlotIn.extend([toMRSobj(f) for f in inFIDs[:, :ncha].T]) colourVecIn.extend([float(jdx) / ncha for jdx in range(ncha)]) legendIn.extend([f'FID #0: CHA #{jdx}' for jdx in range(ncha)]) toPlotOut = [] legendOut = [] if outFID.ndim > 1: toPlotOut.extend([toMRSobj(f) for f in outFID[:, :ncha].T]) legendOut.extend([f'Combined: CHA #{jdx}' for jdx in range(ncha)]) else: toPlotOut.append(toMRSobj(outFID)) legendOut.append('Combined') def addline(fig, mrs, lim, name, linestyle): trace = go.Scatter(x=mrs.getAxes(ppmlim=lim), y=np.real(mrs.getSpectrum(ppmlim=lim)), mode='lines', name=name, line=linestyle) return fig.add_trace(trace) lines, colors, _ = plotStyles() colors = cm.Spectral(np.array(colourVecIn).ravel()) fig = go.Figure() for idx, fid in enumerate(toPlotIn): cval = np.round(255 * colors[idx, :]) linetmp = {'color': f'rgb({cval[0]},{cval[1]},{cval[2]})', 'width': 1} fig = addline(fig, fid, ppmlim, legendIn[idx], linetmp) for idx, fid in enumerate(toPlotOut): fig = addline(fig, fid, ppmlim, legendOut[idx], lines['blk']) plotAxesStyle(fig, ppmlim, 'Combined') # Generate report if html is not None: from plotly.offline import plot from fsl_mrs.utils.preproc.reporting import figgroup, singleReport from datetime import datetime import os.path as op if op.isdir(html): filename = 'report_' + datetime.now().strftime( "%Y%m%d_%H%M%S%f")[:-3] + '.html' htmlfile = op.join(html, filename) elif op.isdir(op.dirname(html)) and op.splitext(html)[1] == '.html': htmlfile = html else: raise ValueError('Report html path must be file or directory. ') opName = 'Combination' timestr = datetime.now().strftime("%H:%M:%S") datestr = datetime.now().strftime("%d/%m/%Y") headerinfo = 'Report for fsl_mrs.utils.preproc.combine.combine_FIDs.\n'\ + f'Generated at {timestr} on {datestr}.' # Figures div = plot(fig, output_type='div', include_plotlyjs='cdn') figurelist = [ figgroup(fig=div, name='', foretext=f'Combination of spectra. Method = {method}', afttext=f'') ] singleReport(htmlfile, opName, headerinfo, figurelist) return fig else: return fig
def shift_report(inFID, outFID, inHdr, outHdr, ppmlim=(0.2, 4.2), html=None, function='shift'): """ Generate report """ import plotly.graph_objects as go from plotly.subplots import make_subplots from fsl_mrs.utils.preproc.reporting import plotStyles, plotAxesStyle plotIn = MRS(FID=inFID, header=inHdr) plotOut = MRS(FID=outFID, header=outHdr) # Fetch line styles lines, colors, _ = plotStyles() # Make a new figure fig = make_subplots(rows=1, cols=2, subplot_titles=['Spectra', 'FID']) # Add lines to figure trace1 = go.Scatter(x=plotIn.getAxes(ppmlim=ppmlim), y=np.real(plotIn.getSpectrum(ppmlim=ppmlim)), mode='lines', name='Original', line=lines['in']) trace2 = go.Scatter(x=plotOut.getAxes(ppmlim=ppmlim), y=np.real(plotOut.getSpectrum(ppmlim=ppmlim)), mode='lines', name='Shifted', line=lines['out']) fig.add_trace(trace1, row=1, col=1) fig.add_trace(trace2, row=1, col=1) # Add lines to figure trace3 = go.Scatter(x=plotIn.getAxes(axis='time'), y=np.real(plotIn.FID), mode='lines', name='Original', line=lines['emph']) trace4 = go.Scatter(x=plotOut.getAxes(axis='time'), y=np.real(plotOut.FID), mode='lines', name='Shifted', line=lines['diff']) fig.add_trace(trace3, row=1, col=2) fig.add_trace(trace4, row=1, col=2) # Axes layout plotAxesStyle(fig, ppmlim, title='Shift summary') fig.layout.xaxis2.update(title_text='Time (s)') fig.layout.yaxis2.update(zeroline=True, zerolinewidth=1, zerolinecolor='Gray', showgrid=False, showticklabels=False) if html is not None: from plotly.offline import plot from fsl_mrs.utils.preproc.reporting import figgroup, singleReport from datetime import datetime import os.path as op if op.isdir(html): filename = 'report_' + datetime.now().strftime( "%Y%m%d_%H%M%S%f")[:-3] + '.html' htmlfile = op.join(html, filename) elif op.isdir(op.dirname(html)) and op.splitext(html)[1] == '.html': htmlfile = html else: raise ValueError('Report html path must be file or directory. ') operation, function, description = reportStrings(function) opName = operation timestr = datetime.now().strftime("%H:%M:%S") datestr = datetime.now().strftime("%d/%m/%Y") headerinfo = f'Report for fsl_mrs.utils.preproc.shifting.{function}.\n'\ + f'Generated at {timestr} on {datestr}.' # Figures div = plot(fig, output_type='div', include_plotlyjs='cdn') figurelist = [ figgroup(fig=div, name='', foretext=f'{description}', afttext=f'') ] singleReport(htmlfile, opName, headerinfo, figurelist) return fig else: return fig
def phaseCorrect_report(inFID, outFID, hdr, position, ppmlim=(2.8, 3.2), html=None): """ Generate report for phaseCorrect """ # from matplotlib import pyplot as plt from fsl_mrs.core import MRS import plotly.graph_objects as go from fsl_mrs.utils.preproc.reporting import plotStyles, plotAxesStyle # Turn input FIDs into mrs objects toMRSobj = lambda fid: MRS(FID=fid, header=hdr) plotIn = toMRSobj(inFID) plotOut = toMRSobj(outFID) widelimit = (0, 6) # Fetch line styles lines, colors, _ = plotStyles() # Make a new figure fig = go.Figure() # Add lines to figure def addline(fig, mrs, lim, name, linestyle): trace = go.Scatter(x=mrs.getAxes(ppmlim=lim), y=np.real(mrs.getSpectrum(ppmlim=lim)), mode='lines', name=name, line=linestyle) return fig.add_trace(trace) fig = addline(fig, plotIn, widelimit, 'Unphased', lines['in']) fig = addline(fig, plotIn, ppmlim, 'Search region', lines['emph']) if position is None: # re-estimate here. position = np.argmax(np.abs(plotIn.getSpectrum(ppmlim=ppmlim))) axis = [plotIn.getAxes(ppmlim=ppmlim)[position]] y_data = [np.real(plotIn.getSpectrum(ppmlim=ppmlim))[position]] trace = go.Scatter(x=axis, y=y_data, mode='markers', name='max point', marker=dict(color=colors['emph'], symbol='x', size=8)) fig.add_trace(trace) fig = addline(fig, plotOut, widelimit, 'Phased', lines['out']) # Axes layout plotAxesStyle(fig, widelimit, title='Phase correction summary') # Axes if html is not None: from plotly.offline import plot from fsl_mrs.utils.preproc.reporting import figgroup, singleReport from datetime import datetime import os.path as op if op.isdir(html): filename = 'report_' + datetime.now().strftime( "%Y%m%d_%H%M%S%f")[:-3] + '.html' htmlfile = op.join(html, filename) elif op.isdir(op.dirname(html)) and op.splitext(html)[1] == '.html': htmlfile = html else: raise ValueError('Report html path must be file or directory. ') opName = 'Phase correction' timestr = datetime.now().strftime("%H:%M:%S") datestr = datetime.now().strftime("%d/%m/%Y") headerinfo = 'Report for fsl_mrs.utils.preproc.phasing.phaseCorrect.\n'\ + f'Generated at {timestr} on {datestr}.' # Figures div = plot(fig, output_type='div', include_plotlyjs='cdn') figurelist = [ figgroup( fig=div, name='', foretext= f'Phase correction of spectra based on maximum in the range {ppmlim[0]} to {ppmlim[1]} ppm.', afttext=f'') ] singleReport(htmlfile, opName, headerinfo, figurelist) return fig else: return fig
def toMRSobj(fid): return MRS(FID=fid, header=hdr)
def identifyUnlikeFIDs_report(goodFIDs,badFIDs,hdr,keepIndicies,rmIndicies,metric,ppmlim=(0.2,4.2),sdlimit = 1.96,html=None): from fsl_mrs.utils.preproc.combine import combine_FIDs import plotly.graph_objects as go from fsl_mrs.utils.preproc.reporting import plotStyles,plotAxesStyle metricGd = np.array(metric)[keepIndicies] metricBd = np.array(metric)[rmIndicies] metric_avg = np.mean(metric) metric_std = np.std(metric) metricGd_SD = np.abs(metricGd-metric_avg)/metric_std metricBd_SD = np.abs(metricBd-metric_avg)/metric_std gdIndex = np.argsort(metricGd_SD) bdIndex = np.argsort(metricBd_SD) plotGood,plotBad = [],[] gdLegend,bdLegend = [],[] toMRSobj = lambda fid : MRS(FID=fid,header=hdr) for idx in gdIndex: fid = goodFIDs[idx] plotGood.append(toMRSobj(fid)) gdLegend.append(f'Kept (SD={metricGd_SD[idx]:0.2f})') for idx in bdIndex: fid = badFIDs[idx] plotBad.append(toMRSobj(fid)) bdLegend.append(f'Removed (SD={metricBd_SD[idx]:0.2f})') target = get_target_FID(goodFIDs,target='median') tgtmrs = toMRSobj(target) # Fetch line styles lines,colors,_ = plotStyles() # Make a new figure fig = go.Figure() # Add lines to figure def addline(fig,mrs,lim,name,linestyle): trace = go.Scatter(x=mrs.getAxes(ppmlim=lim), y=np.real(mrs.getSpectrum(ppmlim=lim)), mode='lines', name=name, line=linestyle) return fig.add_trace(trace) for fid,leg in zip(plotGood,gdLegend): fig = addline(fig,fid,ppmlim,leg,lines['out']) for fid,leg in zip(plotBad,bdLegend): fig = addline(fig,fid,ppmlim,leg,lines['emph']) fig = addline(fig,tgtmrs,ppmlim,'Target',lines['blk']) plotAxesStyle(fig,ppmlim,title = 'Bad average removal summary') # Generate report if html is not None: from plotly.offline import plot from fsl_mrs.utils.preproc.reporting import figgroup, singleReport from datetime import datetime import os.path as op if op.isdir(html): filename = 'report_' + datetime.now().strftime("%Y%m%d_%H%M%S%f")[:-3]+'.html' htmlfile=op.join(html,filename) elif op.isdir(op.dirname(html)) and op.splitext(html)[1]=='.html': htmlfile = html else: raise ValueError('Report html path must be file or directory. ') opName = 'BadAverageRemoval' timestr = datetime.now().strftime("%H:%M:%S") datestr = datetime.now().strftime("%d/%m/%Y") headerinfo = 'Report for fsl_mrs.utils.preproc.unlike.identifyUnlikeFIDs.\n'\ + f'Generated at {timestr} on {datestr}.' # Figures div = plot(fig, output_type='div',include_plotlyjs='cdn') figurelist = [figgroup(fig = div, name= '', foretext= f'Identification of FIDs unlike others. SD limit = {sdlimit:0.2f}', afttext= f'')] singleReport(htmlfile,opName,headerinfo,figurelist) return fig else: return fig
def phase_freq_align(FIDlist, bandwidth, centralFrequency, ppmlim=None, niter=2, apodize=10, verbose=False, shift=True, target=None): """ Algorithm: Average spectra Loop over all spectra and find best phase/frequency shifts Iterate TODO: test the code add dedrifting? Parameters: ----------- FIDlist : list bandwidth : float (unit=Hz) centralFrequency : float (unit=Hz) ppmlim : tuple niter : int apodize : float (unit=Hz) verbose : bool shift : apply H20 shift to ppm limit ref : reference data to align to Returns: -------- list of FID aligned to each other """ all_FIDs = FIDlist.copy() phiOut, epsOut = np.zeros(len(FIDlist)), np.zeros(len(FIDlist)) for iter in range(niter): if verbose: print(' ---- iteration {} ----\n'.format(iter)) if target is None: target = get_target_FID(all_FIDs, target='nearest_to_mean') MRSargs = {'FID': target, 'bw': bandwidth, 'cf': centralFrequency} mrs = MRS(**MRSargs) if apodize > 0: target = apod(target, mrs.dwellTime, [apodize]) for idx, FID in enumerate(all_FIDs): if verbose: print(f'... aligning FID number {idx}\r') if apodize > 0: FID_apod = apod(FID.copy(), mrs.dwellTime, [apodize]) else: FID_apod = FID phi, eps = align_FID(mrs, FID_apod, target, ppmlim=ppmlim, shift=shift) all_FIDs[idx] = np.exp(-1j * phi) * shift_FID(mrs, FID, eps) phiOut[idx] += phi epsOut[idx] += eps if verbose: print('\n') return all_FIDs, phiOut, epsOut
def phase_freq_align_diff_report(inFIDs0, inFIDs1, outFIDs0, outFIDs1, hdr, eps, phi, ppmlim=None, diffType='add', shift=True, html=None): from fsl_mrs.utils.preproc.combine import combine_FIDs import plotly.graph_objects as go from fsl_mrs.utils.preproc.reporting import plotStyles, plotAxesStyle from plotly.subplots import make_subplots # Fetch line styles lines, _, _ = plotStyles() # Make a new figure fig = make_subplots(rows=1, cols=2, subplot_titles=['Phase', 'Shift']) trace1 = go.Scatter(x=np.arange(1, len(phi) + 1), y=np.array(phi) * (180.0 / np.pi), mode='lines', name='Phase', line=lines['out']) fig.add_trace(trace1, row=1, col=1) fig.layout.xaxis.update(title_text='Transient #') fig.layout.yaxis.update(title_text=r'$\phi$ (degrees)') trace2 = go.Scatter(x=np.arange(1, len(eps) + 1), y=eps, mode='lines', name='Shift', line=lines['diff']) fig.add_trace(trace2, row=1, col=2) fig.layout.yaxis2.update(title_text='Shift (Hz)') fig.layout.xaxis2.update(title_text='Transient #') diffFIDListIn = [] diffFIDListOut = [] for fid0i, fid1i, fid0o, fid1o in zip(inFIDs0, inFIDs1, outFIDs0, outFIDs1): if diffType.lower() == 'add': diffFIDListIn.append(add(fid1i, fid0i)) diffFIDListOut.append(add(fid1o, fid0o)) elif diffType.lower() == 'sub': diffFIDListIn.append(subtract(fid1i, fid0i)) diffFIDListOut.append(subtract(fid1o, fid0o)) else: raise ValueError('diffType must be add or sub.') meanIn = combine_FIDs(diffFIDListIn, 'mean') meanOut = combine_FIDs(diffFIDListOut, 'mean') toMRSobj = lambda fid: MRS(FID=fid, header=hdr) meanIn = toMRSobj(meanIn) meanOut = toMRSobj(meanOut) if shift: axis = 'ppmshift' else: axis = 'ppm' toPlotIn, toPlotOut = [], [] for fid in diffFIDListIn: toPlotIn.append(toMRSobj(fid)) for fid in diffFIDListOut: toPlotOut.append(toMRSobj(fid)) def addline(fig, mrs, lim, name, linestyle): trace = go.Scatter(x=mrs.getAxes(ppmlim=lim, axis=axis), y=np.real(mrs.getSpectrum(ppmlim=lim, shift=shift)), mode='lines', name=name, line=linestyle) return fig.add_trace(trace) fig2 = go.Figure() for idx, fid in enumerate(toPlotIn): cval = np.round(255 * idx / len(toPlotIn)) linetmp = {'color': f'rgb(0,{cval},{cval})', 'width': 1} fig2 = addline(fig2, fid, ppmlim, f'#{idx}', linetmp) fig2 = addline(fig2, meanIn, ppmlim, f'Mean - Unligned', lines['blk']) plotAxesStyle(fig2, ppmlim, 'Unaligned') fig3 = go.Figure() for idx, fid in enumerate(toPlotOut): cval = np.round(255 * idx / len(toPlotIn)) linetmp = {'color': f'rgb(0,{cval},{cval})', 'width': 1} fig3 = addline(fig3, fid, ppmlim, f'#{idx}', linetmp) fig3 = addline(fig3, meanIn, ppmlim, f'Mean - Unligned', lines['out']) fig3 = addline(fig3, meanOut, ppmlim, f'Mean - Aligned', lines['blk']) plotAxesStyle(fig3, ppmlim, 'Aligned') if html is not None: from plotly.offline import plot from fsl_mrs.utils.preproc.reporting import figgroup, singleReport from datetime import datetime import os.path as op if op.isdir(html): filename = 'report_' + datetime.now().strftime( "%Y%m%d_%H%M%S%f")[:-3] + '.html' htmlfile = op.join(html, filename) elif op.isdir(op.dirname(html)) and op.splitext(html)[1] == '.html': htmlfile = html else: raise ValueError('Report html path must be file or directory. ') opName = 'AlignDiff' timestr = datetime.now().strftime("%H:%M:%S") datestr = datetime.now().strftime("%d/%m/%Y") headerinfo = 'Report for fsl_mrs.utils.align.phase_freq_align_diff.\n'\ + f'Generated at {timestr} on {datestr}.' # Figures div = plot(fig, output_type='div', include_plotlyjs='cdn') figurelist = [ figgroup(fig=div, name='', foretext=f'Alignment parameters.', afttext=f'') ] div2 = plot(fig2, output_type='div', include_plotlyjs='cdn') figurelist.append( figgroup(fig=div2, name='', foretext=f'Transients before alignment.', afttext=f'')) div3 = plot(fig3, output_type='div', include_plotlyjs='cdn') figurelist.append( figgroup(fig=div3, name='', foretext=f'Transients after alignment.', afttext=f'')) singleReport(htmlfile, opName, headerinfo, figurelist) return fig, fig2, fig3 else: return fig, fig2, fig3
def test_calcQC(): # Syntetic data synFID, synHdr = syntheticFID(noisecovariance=[[0.1]], points=2 * 2048, chemicalshift=[0], amplitude=[6.0], linewidth=[10]) synFIDNoise, synHdrNoise = syntheticFID(noisecovariance=[[0.1]], points=2 * 2048, chemicalshift=[0], amplitude=[0], linewidth=[10]) basisFID, basisHdr = syntheticFID(noisecovariance=[[0.0]], points=2 * 2048, chemicalshift=[0], amplitude=[0.1], linewidth=[2]) synMRS = MRS(FID=synFID[0], header=synHdr) synMRSNoise = MRS(FID=synFIDNoise[0], header=synHdrNoise) synMRSNoNoise = MRS(FID=synHdr['noiseless'], header=synHdr) synMRS_basis = MRS(FID=synFID[0], header=synHdr, basis=basisFID[0], basis_hdr=basisHdr, names=['Peak1']) truenoiseSD = np.sqrt(synHdrNoise['cov'][0, 0]) pureNoiseMeasured = np.std(synMRSNoise.getSpectrum()) realnoise = np.std(np.real(synMRSNoise.getSpectrum())) imagNoise = np.std(np.imag(synMRSNoise.getSpectrum())) print( f'True cmplx noise = {truenoiseSD:0.3f}, pure noise measured = {pureNoiseMeasured:0.3f} (real/imag = {realnoise:0.3f}/{imagNoise:0.3f})' ) # Calc SNR without apodisation from the no noise and pure noise spectra truePeakHeight = np.max(np.real(synMRSNoNoise.getSpectrum())) SNR_noApod = truePeakHeight / pureNoiseMeasured print( f'SNR no apod: {SNR_noApod:0.1f} ({truePeakHeight:0.2e}/{pureNoiseMeasured:0.2e})' ) # Calc SNR with apodisation from the no noise and pure noise spectra trueLW = synHdr['inputopts']['linewidth'][0] trueApodSpec_Noise = specApodise(synMRSNoise, trueLW) apodNoise = np.std(trueApodSpec_Noise) trueApodSpec_noNoise = specApodise(synMRSNoNoise, trueLW) peakHeigtApod = np.max(np.real(trueApodSpec_noNoise)) SNR = peakHeigtApod / apodNoise print(f'SNR w. apod: {SNR:0.1f} ({peakHeigtApod:0.2e}/{apodNoise:0.2e})') metab_groups = [0] Fitargs = { 'ppmlim': [2.65, 6.65], 'method': 'Newton', 'baseline_order': -1, 'metab_groups': [0] } res = fit_FSLModel(synMRS_basis, **Fitargs) fwhm_test, SNRObj = calcQC(synMRS_basis, res, ppmlim=[2.65, 6.65]) print(f'Measured FWHM: {fwhm_test.mean().to_numpy()[0]:0.1f}') print(f'Measured spec SNR: {SNRObj.spectrum:0.1f}') print(f'Measured peak SNR: {SNRObj.peaks.mean().to_numpy()[0]:0.1f}') assert np.isclose(fwhm_test.mean().to_numpy(), trueLW, atol=1E0) assert np.isclose(SNRObj.spectrum, SNR_noApod, atol=1E1) assert np.isclose(SNRObj.peaks.mean().to_numpy(), SNR, atol=2E1)