File: eddy.cpp

package info (click to toggle)
fsl 5.0.7-4
  • links: PTS, VCS
  • area: non-free
  • in suites: jessie, jessie-kfreebsd
  • size: 23,384 kB
  • ctags: 17,864
  • sloc: cpp: 166,701; tcl: 25,434; sh: 14,545; ansic: 9,707; makefile: 1,332; python: 151; xml: 106; csh: 17
file content (372 lines) | stat: -rw-r--r-- 16,824 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
/*! \file eddy.cpp
    \brief Contains main() and some very high level functions for eddy
*/
#pragma clang diagnostic ignored "-Wunknown-pragmas" // Ignore the OpenMP pragmas

#include <cstdlib>
#include <string>
#include <vector>
#include <cmath>
#include <boost/shared_ptr.hpp>
#include "newmat.h"
#include "newimage/newimageall.h"
#include "miscmaths/miscmaths.h"
// Disable stack_dump that upstream labels as "highly non-portable"
// one can still use plan gdb to get the same information
//#include "stack_dump.h"
#include "EddyHelperClasses.h"
#include "ECScanClasses.h"
#include "DiffusionGP.h"
#include "b0Predictor.h"
#include "EddyUtils.h"
#include "EddyCommandLineOptions.h"

using namespace EDDY;

/// Global function that registers a set of scans together
ReplacementManager Register(const EddyCommandLineOptions&  clo,     // Input
			    ScanType                       st,      // Input
			    ECScanManager&                 sm,      // Input/Output
			    unsigned int                   niter,   // Input
			    NEWMAT::Matrix&                msshist, // Output
			    NEWMAT::Matrix&                phist);  // Output

/// Global function that detect outlier slices and replaces them by their expectation
DiffStatsVector DetectAndReplaceOutliers(// Input
					 const EddyCommandLineOptions& clo,
					 ScanType                      st,
					 // Input/Output
					 ECScanManager&                sm,
					 ReplacementManager&           rm);

/// Global function that Loads up the prediction maker with unwarped scans
boost::shared_ptr<DWIPredictionMaker> LoadPredictionMaker(// Input
							  const EddyCommandLineOptions& clo,
							  ScanType                      st,
							  const ECScanManager&          sm,
							  // Output
							  NEWIMAGE::volume<float>&      mask);

/// Global function that Generates diagnostic information for subsequent analysis
void Diagnostics(const EddyCommandLineOptions&  clo,      // Input
		 unsigned int                   iter,     // Input
		 ScanType                       st,       // Input
		 const ECScanManager&           sm,       // Input
                 const double                   *mss_tmp, // Input
                 const DiffStatsVector&         stats,    // Input
		 NEWMAT::Matrix&                mss,      // Output
		 NEWMAT::Matrix&                phist);   // Output

/// The entry point of eddy.
int main(int argc, char *argv[])
{
  //StackDump::Install(); // Gives us informative stack dump if/when program crashes

  // Parse comand line input
  EddyCommandLineOptions clo(argc,argv); // Command Line Options

  // Read all available info
  if (clo.Verbose()) cout << "Reading images" << endl;
  ECScanManager sm(clo.ImaFname(),clo.MaskFname(),clo.AcqpFname(),clo.TopupFname(),
                   clo.BVecsFname(),clo.BValsFname(),clo.FirstLevelModel(),
		   clo.Indicies(),clo.SessionIndicies(),clo.NoOfSessions()); // Scan Manager
  if (clo.Verbose() && clo.FWHM()) cout << "Smoothing images" << endl;
  sm.SetFWHM(clo.FWHM());
  if (clo.ResamplingMethod() == LSR) {
    if (!sm.CanDoLSRResampling()) throw EddyException("These data do not support least-squares resampling");
  }

  // Set initial parameters. This option is only for testing/debugging/personal use
  if (clo.InitFname() != std::string("")) {
    if (clo.RegisterDWI() && clo.Registerb0()) sm.SetParameters(clo.InitFname(),ANY);
    else if (clo.RegisterDWI()) sm.SetParameters(clo.InitFname(),DWI);
    else sm.SetParameters(clo.InitFname(),B0);
  }

  // Do the registration
  NEWMAT::Matrix dwi_mss, b0_mss, dwi_ph, b0_ph;
  ReplacementManager *dwi_rm;
  if (clo.NIter() && clo.RegisterDWI()) {
    cout << "Running Register" << endl;
    dwi_rm = new ReplacementManager(Register(clo,DWI,sm,clo.NIter(),dwi_mss,dwi_ph));
    // Write outlier information
    if (clo.ReplaceOutliers()) {
      cout << "Running sm.GetDwi2GlobalIndexMapping" << endl;
      std::vector<unsigned int> i2i = sm.GetDwi2GlobalIndexMapping();
      cout << "Running dwi_rm->WriteReport" << endl;
      dwi_rm->WriteReport(i2i,clo.OLReportFname());
      if (clo.WriteOutlierFreeData()) {
	cout << "Running sm.WriteOutlierFreeData" << endl;
        sm.WriteOutlierFreeData(clo.OLFreeDataFname());
      }
    }
  }
  if (clo.NIter() && clo.Registerb0()) {
    cout << "Running Register" << endl;
    ReplacementManager b0_rm = Register(clo,B0,sm,clo.NIter(),b0_mss,b0_ph);
  }

  /*
  // Write raw registration parameters (for debugging).
  cout << "Running sm.WriteParameterFile for raw parameters" << endl;
  sm.WriteParameterFile(clo.ParOutFname()+string(".raw"));
  cout << "Running sm.WriteRegisteredImages for raw parameters" << endl;
  sm.WriteRegisteredImages(clo.IOutFname()+string(".raw"),clo.ResamplingMethod());  
  */  

  // Separate field offset from subject movement in PE direction
  // if (clo.RegisterDWI()) { cout << "Running sm.SeparateFieldOffsetFromMovement" << endl; sm.SeparateFieldOffsetFromMovement(); }

  // Set reference for location
  if (clo.RegisterDWI()) { cout << "Running sm.SetDWIReference" << endl; sm.SetDWIReference(); }
  if (clo.Registerb0()) { cout << "Running sm.Setb0Reference" << endl; sm.Setb0Reference(); }

  // Write registration parameters
  cout << "Running sm.WriteParameterFile" << endl;
  if (clo.RegisterDWI() && clo.Registerb0()) sm.WriteParameterFile(clo.ParOutFname());
  else if (clo.RegisterDWI()) sm.WriteParameterFile(clo.ParOutFname(),DWI);
  else sm.WriteParameterFile(clo.ParOutFname(),B0);

  // Write registered images
  cout << "Running sm.WriteRegisteredImages" << endl;
  if (clo.RegisterDWI() && clo.Registerb0()) sm.WriteRegisteredImages(clo.IOutFname(),clo.ResamplingMethod());
  else if (clo.RegisterDWI()) sm.WriteRegisteredImages(clo.IOutFname(),clo.ResamplingMethod(),DWI);
  else sm.WriteRegisteredImages(clo.IOutFname(),clo.ResamplingMethod(),B0);

  // Write EC fields
  cout << "Running sm.WriteECFields" << endl;
  if (clo.WriteFields()) {
    if (clo.RegisterDWI() && clo.Registerb0()) sm.WriteECFields(clo.ECFOutFname());
    else if (clo.RegisterDWI()) sm.WriteECFields(clo.ECFOutFname(),DWI);
    else sm.WriteECFields(clo.ECFOutFname(),B0);
  }

  if (clo.NIter() && clo.History()) { 
    if (clo.RegisterDWI()) {
      MISCMATHS::write_ascii_matrix(clo.DwiMssHistoryFname(),dwi_mss); 
      MISCMATHS::write_ascii_matrix(clo.DwiParHistoryFname(),dwi_ph);
    }
    if (clo.Registerb0()) {
      MISCMATHS::write_ascii_matrix(clo.B0MssHistoryFname(),b0_mss); 
      MISCMATHS::write_ascii_matrix(clo.B0ParHistoryFname(),b0_ph);
    }
  }
  exit(EXIT_SUCCESS);
}

/****************************************************************//**
*  								  
*  A global function that registers the scans in sm.
*  \param[in] clo Carries information about the command line options 
*  that eddy was invoked with.
*  \param[in] st Specifies if we should register the diffusion weighted 
*  images or the b=0 images.
*  \param[in,out] sm Collection of all scans. Will be updated by this call.
*  \param[in] niter Specifies how many iterations should be run
*  \param[out] msshist Returns the history of the mss. msshist(i,j) 
*  contains the mss for the jth scan on the ith iteration.
*  \param[out] phist Returns the history of the estimated parameters. 
*  phist(i,j) contains the jth parameter on the ith iteration
*  \return A ReplacementManager that details which slices in which 
*  scans were replaced by their expectations.
*
********************************************************************/ 

ReplacementManager Register(const EddyCommandLineOptions&  clo,     // Input
			    ScanType                       st,      // Input
			    ECScanManager&                 sm,      // Input/Output
			    unsigned int                   niter,   // Input
			    NEWMAT::Matrix&                msshist, // Output
			    NEWMAT::Matrix&                phist)   // Output
{
  msshist.ReSize(niter,sm.NScans(st));
  phist.ReSize(niter,sm.NScans(st)*sm.Scan(0,st).NParam());
  double *mss_tmp = new double[sm.NScans(st)]; 
  ReplacementManager rm(sm.NScans(st),static_cast<unsigned int>(sm.Scan(0,st).GetIma().zsize()),clo.OLNStdev(),clo.OLNVox());
  NEWIMAGE::volume<float> mask = sm.Scan(0,st).GetIma(); EddyUtils::SetTrilinearInterp(mask); mask = 1.0; // Mask in model space

  for (unsigned int iter=0; iter<niter; iter++) {
    // Detect outliers and replace them
    DiffStatsVector stats(sm.NScans(st));
    if (iter && st==DWI && clo.ReplaceOutliers()) stats = DetectAndReplaceOutliers(clo,st,sm,rm);

    // Load prediction maker in model space
    // printf("Starting to load and evaluate predictionmaker\n");
    // clock_t stime = clock();
    boost::shared_ptr<DWIPredictionMaker> pmp = LoadPredictionMaker(clo,st,sm,mask);
    // printf("It took %f sec to load and evaluate predictionmaker\n",double(stime-clock())/double(CLOCKS_PER_SEC));

    // Calculate the parameter updates
    if (clo.Verbose()) cout << "Calculating parameter updates" << endl;
    // int dbl = clo.DebugLevel();  // Can't check DebugLevel inside parallel section :(
    // bool vv = clo.VeryVerbose(); // Compiler crashes if I test clo.VeryVerbose() inside parallel loop :(
# pragma omp parallel for shared(mss_tmp, pmp)
    for (int s=0; s<int(sm.NScans(st)); s++) {
      // Get prediction in model space 
      NEWIMAGE::volume<float> pred = pmp->Predict(s);
      // Update parameters
      if (clo.DebugLevel()) {
	mss_tmp[s] = EddyUtils::param_update_debug(pred,sm.GetSuscHzOffResField(),mask,ALL,true,s,iter,clo.DebugLevel(),sm.Scan(s,st),NULL);
      }
      else mss_tmp[s] = EddyUtils::MovAndECParamUpdate(pred,sm.GetSuscHzOffResField(),mask,true,sm.Scan(s,st));
      if (clo.VeryVerbose()) printf("Iter: %d, scan: %d, mss = %f\n",iter,s,mss_tmp[s]);
    }

    // Print/collect some information that can be used for diagnostics
    Diagnostics(clo,iter,st,sm,mss_tmp,stats,msshist,phist);

    // Maybe use model based EC parameters
    // if () sm.SetPredictedECParam();
  }

  delete [] mss_tmp;
  return(rm);
}

/****************************************************************//**
*  								  
*  A global function that loads up a prediction maker with all scans 
*  of a given type. It will load it with unwarped scans (given the 
*  current estimates of the warps) as served up by sm.GetUnwarpedScan().
*  \param[in] clo Carries information about the command line options 
*  that eddy was invoked with.
*  \param[in] st Specifies if we should register the diffusion weighted 
*  images or the b=0 images. If it is set to DWI the function will return 
*  an EDDY::DiffusionGP prediction maker and if it is set to b0 it will 
*  return an EDDY::b0Predictor.
*  \param[in] sm Collection of all scans.
*  \param[out] mask Returns a mask that indicates the voxels where data 
*  is present for all input scans in sm.
*  \return A safe pointer to a DWIPredictionMaker that can be used to 
*  make predictions about what the scans should look like in undistorted space.
*
********************************************************************/ 
boost::shared_ptr<DWIPredictionMaker> LoadPredictionMaker(// Input
							  const EddyCommandLineOptions& clo,
							  ScanType                      st,
							  const ECScanManager&          sm,
							  // Output
							  NEWIMAGE::volume<float>&      mask)
{
  boost::shared_ptr<DWIPredictionMaker>  pmp;                                 // Prediction Maker Pointer
  if (st==DWI) pmp = boost::shared_ptr<DWIPredictionMaker>(new DiffusionGP);  // Gaussian Process
  else pmp = boost::shared_ptr<DWIPredictionMaker>(new b0Predictor);          // Silly mean predictor
  pmp->SetNoOfScans(sm.NScans(st));
  mask = sm.Scan(0,st).GetIma(); EddyUtils::SetTrilinearInterp(mask); mask = 1.0;

  if (clo.Verbose()) cout << "Loading prediction maker";
  // bool vv = clo.VeryVerbose(); // Compiler crashes if I test clo.VeryVerbose() inside parallel loop :(
  if (clo.VeryVerbose()) cout << endl << "Scan: ";
#pragma omp parallel for shared(pmp,st)
  for (int s=0; s<int(sm.NScans(st)); s++) {
    if (clo.VeryVerbose()) printf(" %d",s);
    NEWIMAGE::volume<float> tmpmask = sm.Scan(s,st).GetIma(); 
    EddyUtils::SetTrilinearInterp(tmpmask); tmpmask = 1.0;
    pmp->SetScan(sm.GetUnwarpedScan(s,tmpmask,st),sm.Scan(s,st).GetDiffPara(),s);
#pragma omp critical
    {
      mask *= tmpmask;
    }
  }
  if (clo.Verbose()) cout << endl << "Evaluating prediction maker model" << endl;
  pmp->EvaluateModel(sm.Mask()*mask);

  return(pmp);
}

void Diagnostics(const EddyCommandLineOptions&  clo,      // Input
		 unsigned int                   iter,     // Input
		 ScanType                       st,       // Input
		 const ECScanManager&           sm,       // Input
                 const double                   *mss_tmp, // Input
                 const DiffStatsVector&         stats,    // Input
		 NEWMAT::Matrix&                mss,      // Output
		 NEWMAT::Matrix&                phist)    // Output
{
  if (clo.Verbose()) {
    double tss=0.0;
    for (unsigned int s=0; s<sm.NScans(st); s++) tss+=mss_tmp[s]; 
    cout << "Iter: " << iter << ", Total mss = " << tss/sm.NScans(st) << endl;
  }
  if (clo.History()) {
    for (unsigned int s=0; s<sm.NScans(st); s++) {
      mss(iter+1,s+1) = mss_tmp[s];
      phist.SubMatrix(iter+1,iter+1,s*sm.Scan(0,st).NParam()+1,(s+1)*sm.Scan(0,st).NParam()) = sm.Scan(s,st).GetParams().t();
    }
  }
  if (clo.WriteSliceStats()) {
    char istring[256];
    sprintf(istring,"EddySliceStatsIteration%02d",iter);
    stats.Write(string(istring));
  }
}

DiffStatsVector DetectAndReplaceOutliers(// Input
					 const EddyCommandLineOptions& clo,
					 ScanType                      st,
					 // Input/Output
					 ECScanManager&                sm,
					 ReplacementManager&           rm)
{
  // printf("Entering DetectAndReplaceOutliers\n");
  // Load up a prediction maker with unwarped and unsmoothed data
  DWIPredictionMaker  *pmp;            // Prediction maker used for the outlier detection
  if (st==DWI) pmp = new DiffusionGP;  // Gaussian Process
  else pmp = new b0Predictor;          // Silly mean predictor
  pmp->SetNoOfScans(sm.NScans(st));
  NEWIMAGE::volume<float> mask(sm.Scan(0,st).GetIma().xsize(),sm.Scan(0,st).GetIma().ysize(),sm.Scan(0,st).GetIma().zsize());
  NEWIMAGE::copybasicproperties(sm.Scan(0,st).GetIma(),mask); mask = 1.0;
  // printf("Starting to load predictionmaker\n");
  // clock_t stime = clock();
#pragma omp parallel for shared(pmp,st)
  for (int s=0; s<int(sm.NScans(st)); s++) {
    NEWIMAGE::volume<float> tmpmask(sm.Scan(s,st).GetIma().xsize(),sm.Scan(s,st).GetIma().ysize(),sm.Scan(s,st).GetIma().zsize());
    NEWIMAGE::copybasicproperties(sm.Scan(s,st).GetIma(),tmpmask); tmpmask = 1.0;
    pmp->SetScan(sm.GetUnwarpedOrigScan(s,tmpmask,st),sm.Scan(s,st).GetDiffPara(),s);
#pragma omp critical
    { mask *= tmpmask; }
  }
  // printf("It took %f sec to load pm\n",double(clock()-stime)/double(CLOCKS_PER_SEC));
  // printf("Starting to evaluate the pm model\n");
  // stime = clock();
  pmp->EvaluateModel(sm.Mask()*mask);
  // printf("It took %f sec to evaluate pm\n",double(clock()-stime)/double(CLOCKS_PER_SEC));
  // Use these to generate slice-wise stats on difference between observation and prediction
  // printf("Starting to generate slice-wise stats\n");
  // stime = clock();
  DiffStatsVector stats(sm.NScans(st));
#pragma omp parallel for shared(pmp,mask,stats,st)
  for (int s=0; s<int(sm.NScans(st)); s++) {
    NEWIMAGE::volume<float> pred = pmp->Predict(s);
    stats[s] = EddyUtils::GetSliceWiseStats(pred,sm.GetSuscHzOffResField(),mask,sm.Mask(),sm.Scan(s,st));
  }
  // printf("It took %f sec to generate stats\n",double(clock()-stime)/double(CLOCKS_PER_SEC));
  // stats.Write("slice_wise_stats");
  // Detect outliers and update replacement manager
  // printf("Updating replacement manager\n");
  // stime = clock();
  rm.Update(stats);
  // printf("It took %f sec to update\n",double(clock()-stime)/double(CLOCKS_PER_SEC));
  // Replace outlier slices with their predictions
  // printf("Replacing outliers\n");
  // stime = clock();
#pragma omp parallel for shared(pmp,mask,st)
  for (int s=0; s<int(sm.NScans(st)); s++) {
    // printf("Checking scan %d for outliers\n",s);
    std::vector<unsigned int> ol = rm.OutliersInScan(s);
    if (ol.size()) { // If this scan has outlier slices
      // printf("Scan %d has %d outlier slices\n",s,ol.size());
      NEWIMAGE::volume<float> pred = pmp->Predict(s);
      sm.Scan(s,st).ReplaceSlices(pred,sm.GetSuscHzOffResField(),mask,ol);
    }
  }
  // printf("It took %f sec to replace outliers\n",double(clock()-stime)/double(CLOCKS_PER_SEC));
  //  rm.DumpOutlierMap("DebugReplacementManager");
  //  exit(EXIT_SUCCESS);
  delete pmp;
  return(stats);
}

/*! \mainpage
 * Here goes a description of the eddy project
 */