PageRenderTime 23ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/ase/se10.m

http://ssparse.googlecode.com/
Objective C | 486 lines | 447 code | 39 blank | 0 comment | 52 complexity | 722410771c866ec17f243a2f8fa78698 MD5 | raw file
  1. function se10(parameter_name,parameter_values,varargin)
  2. %% run PARSE with several different values of a parameter
  3. % examples:
  4. % se10; % tests the code
  5. % se10('freq_off',-52:2:100,'dataset','fourtube_se');
  6. % se10('echo_time',1000:1000:11000,'dataset','fourtube_se');
  7. % se10('dataset','fourtube_se');
  8. % This is the currently the top level script for sePARSE code.
  9. % add your datasets and specific parameters in dataset_load.m
  10. % add new optional parameter try..catch blocks in se_opt.m
  11. % Varargin is a matlab keyword that is used to pull in optional name, value
  12. % pairs which are then parsed by the local function varparser
  13. % separse4 does most of the work
  14. % uses NIfTI toolbox from
  15. % http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=8797&objectType=File
  16. %
  17. % msb 05 Feb '07
  18. % msb 19 Feb '07
  19. % msb 22 Feb '07
  20. %
  21. % dependencies:
  22. % se10
  23. % |- plotter
  24. % |- make_nii, from mathworks user site.
  25. % |- save_nii, from mathworks user site.
  26. % |- dataset_load
  27. % |- se_opts
  28. % |- varparser
  29. % |- load_procpar
  30. % '- query_procpar
  31. seversion=10;
  32. %% init some things...
  33. tic % start the process timer
  34. if exist('parameter_name','var')
  35. disp([parameter_name ' will be evaluated at ']);
  36. disp(parameter_values)
  37. else % if the code is called without parameters then execute test case
  38. disp('running sanity check.')
  39. parameter_name='test';
  40. parameter_values='0';
  41. s=['mv psis.mat psis' num2str(seversion) '.bak'];
  42. system(s);
  43. end
  44. try % flag to show progress or not with frequently updated figs
  45. plot_a.show=varparser('show',varargin);
  46. if plot_a.show
  47. disp('progress display is turned on.')
  48. else
  49. disp('progress display is turned off.')
  50. end
  51. catch
  52. plot_a.show=1;
  53. end
  54. if strcmp(parameter_name,'dataset')
  55. dataset=parameter_values;
  56. disp(['analyzing ' dataset])
  57. parameter_name=parameter_values;
  58. parameter_values=1;
  59. else
  60. try % choose the dataset using input value or default value...
  61. dataset=varparser('dataset',varargin);
  62. disp(['using dataset ' dataset])
  63. catch
  64. dataset='fourtube'; % an real example dataset to see if the code is working.
  65. disp(['using a default dataset of ' dataset])
  66. end
  67. end
  68. %% select dataset =======================================================
  69. % also contains some dataset specific defaults
  70. settings={}; % init with empty cell array that dataset_load fills in
  71. dataset_load % call the dataset and opt parameter loading script
  72. %% make a place to save the data
  73. savedir=['sweepresults_' num2str(seversion) '_' parameter_name]; % where should the results go
  74. try
  75. mkdirresult=mkdir( savedir );
  76. if mkdirresult==1
  77. disp(['running will write to directory ' savedir '. continuing in 5 sec.'])
  78. pause(5)
  79. end
  80. catch
  81. error('couldn not make dir for results')
  82. end
  83. %% run the sweep
  84. sweep_count=0; % parameter value trial counter
  85. figure(2);
  86. figx=size(parameter_values,2); % one little figure for each trial
  87. figy=4;
  88. yres=64;
  89. yrng=1:yres;
  90. for parameter_value=parameter_values % try each parameter value
  91. sweep_count=sweep_count+1;
  92. coefficientmaps=separse4(mr_data,plot_a,parameter_name,parameter_value,varargin{:},settings{:});
  93. paramvolume(:,:,sweep_count)=coefficientmaps;
  94. save([savedir '/' parameter_name num2str(parameter_value)],'coefficientmaps') % save current result
  95. set(0,'CurrentFigure',2);
  96. for idx=1:figy
  97. subplot('Position',[(sweep_count-1)/figx (idx-1)/figy (1/figx) (1/figy)]) % plot them all in a row.
  98. imagesc(coefficientmaps(yrng+((idx-1)*yres),:)) % display the current result
  99. axis off
  100. colormap gray;
  101. end
  102. drawnow
  103. elapsed_time=toc;
  104. disp([ 'elapsed time: ' num2str(elapsed_time)])
  105. end
  106. %% save all the results as a single 3D nifti format volume for inspection
  107. paramvolume(65:128,:,:)=paramvolume(65:128,:,:)*100000; % scaling factor because Mag image is small valued.
  108. save paramvolume paramvolume
  109. try
  110. nii=make_nii(paramvolume,[2 2 2]);
  111. save_nii(nii,[savedir '/sigvolume']);
  112. catch
  113. disp('could not make the nifti volumes. Do you have the nii tools installed?')
  114. end
  115. %% functions ==================================================
  116. function coefficientmaps = separse4(mr_data,plot_a,varargin)
  117. % example: cfm = separse2('fourtube',1)
  118. % mark bolding mbolding@uab.edu
  119. % 29 Nov '06 msb
  120. % 03 Jan '07 msb new trajectories
  121. % 19 Jan '07 msb converting to SE
  122. % 05 Feb '07 msb converting to SE second try, merging pre- and main code
  123. % 19 Feb '07 msb is "try catch" crazy
  124. % 22 Feb '07 msb is adding more parameters... SE third try rolling in
  125. % functions and making p into a structure
  126. % 27 Feb '07 msb SE works I think, now trying to search from echo and not
  127. % first data point.
  128. %
  129. % original:
  130. % D Twieg UAB Biomedical Engineering
  131. % uses hexagonal evaluation grid.
  132. %% constants ===========================================================
  133. % also see the tweakers
  134. plot_a.fig=1; % figure number for plot
  135. maxIter=200; % the maximum number of iterations
  136. sq2=sqrt(2);
  137. twopi=2*pi;
  138. % gam=4258.0;
  139. % gmax=3.643; % low-g
  140. % fov = 12.8; % field of view in cm calibration traj dependent
  141. % nres = 64; % nominal resolution for reconstruction
  142. %% tweakers <<<<<<<<<<<<<<<<<<<<
  143. trimoff=220; % how many initial data points to discard
  144. % trimoff=8280 % echo time for current spin echo data
  145. shift_fft=0; % calibration v. signal time offset
  146. dc_offset=21+1i*32;
  147. hammfilter=0; % hamming filter in k-space 1=on, 0=off
  148. %% look for varargs, optional inputs to the function =====================
  149. disp(['=== loading ' num2str(size(varargin,2)/2) ' optional settings ==='])
  150. se_opts % call the option loading script to parse the varargin
  151. disp('=== end of optional settings ===')
  152. %% try to grab some values from "procpar" ===============================
  153. % Varian stores all of the acquisition parameters in procpar.
  154. % it contains offsets, FOV, thk, powers, etc.
  155. try
  156. % using resto the resonance offset frequency as an example
  157. procpar_vars=load_procpar(mr_data);
  158. showprocvar('thk',procpar_vars)
  159. showprocvar('pss0',procpar_vars)
  160. catch
  161. disp('could not read values from the procpar file...')
  162. end
  163. %% bring all of the plots to the front at the start
  164. if(plot_a.show==1)
  165. figure(plot_a.fig)
  166. end
  167. %% load the calibration trajectory, kss =================================
  168. phi=[];
  169. ktraj=[];
  170. try
  171. load(calibrationdata); % brings in phi and ktraj
  172. catch
  173. error('Could not load the calibration data. Booo.');
  174. end
  175. disp(['processing map ' num2str(whichfid)]);
  176. % gmax=4.6833; % hi-g maximum read gradient used. Not the Varian
  177. % system maximum gradient!
  178. Ta=.068; % duration of acquisition in ms
  179. % Ta=.025; % post echo
  180. % Ta=0.045;
  181. % kf=nres/(2*fov);
  182. % delt=1.0/(gam*gmax*fov); % theoretical time step
  183. delt=1.0/198511.166253; % defined from sw in procpar
  184. numTimePts=floor(Ta/delt); % number of acquisition points
  185. kss=ktraj(shift_fft+trimoff+1:numTimePts+trimoff+shift_fft); % k trajectory with lead in and lead out trimmed off
  186. phi=real(phi)+imag(phi);
  187. phi=phi(shift_fft+trimoff+1:numTimePts+trimoff+shift_fft); % trim phi to the correct length and offset
  188. kss=kss*1.61; % empirical scaling
  189. %% build Hamming filter to correct for sample spacing ====================
  190. if hammfilter==1
  191. kr=abs(kss); % find the radius of each point
  192. kfe=max(kr); % kfe set as max radius
  193. kch=(0.54+0.46*cos(pi*kr/(sq2*kfe))); % circular Hamming, circumscribed
  194. kss=kss.*kch;
  195. end
  196. %% lengths and stopping tolerances for progressive conjugate =============
  197. % gradient search. For different rosettes, the lengths should be
  198. % integer multiples of a single "echo time" -- i.e., the number of
  199. % samples between successive crossings of the k-origin.
  200. % You can determine this length by plotting the magnitude of the signal
  201. % and counting the number of points between peaks. The first number, N1, in
  202. % NLIST should be an integer multiple of the echo time, and it should be
  203. % sufficiently long to give a complete circle of rosette
  204. % lobes, covering k-space more or less symmetrically. Check this by
  205. % plotting kss(1:N1).
  206. swoopsize=121.0; % one pass through the center of k space
  207. N1=floor(5*swoopsize); % one "slice"
  208. NLIST=[(N1*cumsum(ones(1,5)*4))';numTimePts]; % analyze a little data then a little more
  209. NLENGTHS = size(NLIST,1); % how many sections is it divided into
  210. % FLIST=[0.3 ; 0.12 ; 0.1 ; 0.1 ; 0.1]; % tighten the tolerance as more data is added
  211. FLIST=[0.2 ; 0.1 ; 0.05 ; 0.05 ; 0.05 ; 0.05]; % tighten the tolerance as more data is added
  212. whichplot='slice';plotter;fprintf('%s',whichplot)
  213. %% makehex1; % builds numVox, xx, yy, xa, ya, XX, YY hexagonal evaluation grid
  214. % makehex1.m
  215. % generate hexagonal sampling grid
  216. % D. Twieg
  217. fov = 12.8; % field of view in cm calibration traj dependent
  218. nres = 64; % nominal resolution for reconstruction
  219. delxnom=fov/nres;
  220. %% make hexagonal grid
  221. [xx,yy]=hexgrid80(delxnom);
  222. %% count hex grid points and define arrays
  223. RA=1.0*fov/2; % radius
  224. nl=1;
  225. xa=zeros(80*80,1);
  226. ya=xa;
  227. ia=xa;
  228. ja=xa;
  229. for nc=1:80 % number of columns
  230. for nr=1:80 % number of rows
  231. if sqrt(xx(nr,nc).^2+yy(nr,nc).^2) < RA % if it is in the radius
  232. xa(nl)=xx(nr,nc); % x coordinates
  233. ya(nl)=yy(nr,nc); % y coordinates
  234. ia(nl)=nr; % row index
  235. ja(nl)=nc; % col index
  236. nl=nl+1;
  237. end
  238. end
  239. end
  240. [XX,YY]=meshgrid(((1:64)-33)*fov/64); % standard rectilinear grid for data display
  241. %% initialize parameter arrays and structures
  242. numVox = nl-1; % numVox is the number of grid points
  243. Voxels = 1:numVox; % handy array of voxel indicies
  244. p.Mperp = zeros(1,numVox); % parameter estimate array. initial mag
  245. p.fRa = zeros(1,numVox); % parameter estimate array. freq and decay
  246. p.Rb = zeros(1,numVox); % parameter to account for 2*R2 prime
  247. p.echo_time = echo_time;
  248. ExponentA = zeros(size(xx)); % storage for hex grid representation
  249. ExponentB = zeros(size(xx));
  250. Magnitude = zeros(size(xx));
  251. CC = zeros(1,maxIter); % array to store current cost result
  252. %% load the data.=========================================================
  253. disp(['loading ' mr_data]);
  254. %try
  255. for signal_index=whichfid % load the set of wanted signals
  256. [the_data_RE,the_data_IM]=LOAD_FID(mr_data,signal_index);
  257. the_data(:,signal_index)=the_data_RE+1i*the_data_IM + dc_offset; % combine re and im parts and dc offset
  258. end
  259. % catch
  260. % error('could not load the data.')
  261. % end
  262. theSignal=sum(the_data(trimoff+1:numTimePts+trimoff,:),2)'; % trim the signal to the proper size
  263. theSignal=theSignal.*exp(-1i*phi)'; % phi correction
  264. theSignal=theSignal.*exp(-1i*twopi*freq_off*(1:numTimePts)*delt); % frequency offset
  265. theSignal=theSignal*sig_weight*numVox*delt/max(abs(theSignal)); % regularization
  266. clear the_data_RE the_data_IM
  267. %% Load psis, the basis functions representing the effect of k-traj on
  268. % signal. only need calculate psis once for a given traj and grid
  269. psis=psisGen(kss,numVox,numTimePts,xa,ya);
  270. %% prep done. main code.==================================================
  271. disp('setup is finished. beginning search...');
  272. old_timeLength = 0; % the time length from previous iteration
  273. iteration = 1; % record number of steps and estimations in parameter space
  274. CCold = 1e+15; % last "current cost"
  275. CCnew = 1e+14; % current "current cost"
  276. for nlen = 1:NLENGTHS % for each data length, this is the main code loop
  277. timeLength = NLIST(nlen);
  278. timeVec=1:timeLength;
  279. while (abs(CCold-CCnew) > FLIST(nlen)*CCold || timeLength ~= old_timeLength ) % while not converged or new data
  280. sigEstimate = estimateSignal(p,psis,timeVec); % estimate the signal
  281. sigdiff = (theSignal(timeVec)-sigEstimate); % estimate the error
  282. %---------------------parameter gradient evaluation----------------
  283. % define direction of 1-D search
  284. if (timeLength ~= old_timeLength) % more data, reset search direction
  285. g = calcGrad(psis,timeVec,sigdiff,p);
  286. h = g;
  287. whichplot='gradients';plotter;fprintf('%s',whichplot)
  288. else
  289. gold = g;
  290. g = calcGrad(psis,timeVec,sigdiff,p);
  291. ga = max(0,((g-gold)*g')/(gold*gold'));
  292. h = g + ga*h;
  293. whichplot='gradients';plotter;fprintf('%s',whichplot)
  294. end
  295. delpf = NewtonMeth(p,psis,timeVec,sigEstimate,theSignal,h); % how far do we step?
  296. p.Mperp = p.Mperp + delpf*(h(Voxels)); % take a step magnitude
  297. p.fRa = p.fRa + delpf*(h(Voxels+numVox)); % take a step freq and Ra
  298. p.Rb = p.Rb + real(delpf*(h(Voxels+numVox*2))); % take a step Rb
  299. for nl = Voxels % read parameter values into the hex grid locations
  300. Magnitude(ia(nl),ja(nl)) = p.Mperp(nl); % amplitude, complex magnitude image
  301. ExponentA(ia(nl),ja(nl)) = p.fRa(nl); % freq and part of R that may reverse after echo?
  302. ExponentB(ia(nl),ja(nl)) = p.Rb(nl); % R correction after echo, now we can find R2 and R2'
  303. end
  304. M0Rect =griddata(yy,xx,abs(Magnitude),XX,YY,'cubic'); % resample M on a rect grid
  305. frRect =griddata(yy,xx,imag(ExponentA)/(twopi*delt),XX,YY,'cubic'); % resample f on a rect grid
  306. R2Rect =griddata(yy,xx,real(-1*ExponentA)/delt,XX,YY,'cubic'); % resample R2 on a rect grid
  307. R2primeRect =griddata(yy,xx,real(-1*ExponentB)/delt,XX,YY,'cubic');
  308. %----------------------------------------
  309. sigEstimate = estimateSignal(p,psis,timeLength);
  310. sigdif = theSignal(1:timeLength)-sigEstimate;
  311. CC(iteration)= sigdif*sigdif';
  312. whichplot='progress';plotter;fprintf('%s',whichplot)
  313. old_timeLength = timeLength;
  314. CCold = CCnew;
  315. CCnew = CC(iteration);
  316. iteration = iteration + 1;
  317. end
  318. end
  319. %% all done, show results -----------------
  320. coefficientmaps=[frRect;M0Rect;R2Rect;R2primeRect];
  321. return % FIN!
  322. %% sub-functions ====================================
  323. %% Newtons method
  324. function delpf = NewtonMeth(p,psis,timeVec,signalEstimate,theSignal,h)
  325. numVox=size(p.Mperp,2);
  326. Voxels=1:numVox;
  327. delta = 1e-6; % step size for gradient estimation. <<<
  328. % delta = 1e-7;
  329. % delta = 1e-8;
  330. sigdiff = signalEstimate-theSignal(timeVec);
  331. f_0 = sigdiff*sigdiff';
  332. p0.echo_time=p.echo_time;
  333. p0.Mperp = p.Mperp + delta*(h(Voxels)); % M perp
  334. p0.fRa = p.fRa + delta*(h(Voxels+numVox)); % f and R
  335. p0.Rb = p.Rb + delta*(h(Voxels+numVox*2));% R prime
  336. sigdiff = estimateSignal(p0,psis,timeVec) - theSignal(timeVec);
  337. f_a_p = sigdiff*sigdiff';
  338. p0.Mperp = p.Mperp - delta*(h(Voxels)); % M perp
  339. p0.fRa = p.fRa - delta*(h(Voxels+numVox)); % f and R
  340. p0.Rb = p.Rb - delta*(h(Voxels+numVox*2));% R prime
  341. sigdiff = estimateSignal(p0,psis,timeVec) - theSignal(timeVec);
  342. f_a_n = sigdiff*sigdiff';
  343. f_1_0 = (f_a_p-f_a_n)/(2*delta); % First derivative @ alpha = 0
  344. f_1_a_p = (f_a_p-f_0)/delta; % First derivative @ alpha = delta/2
  345. f_1_a_n = (f_0-f_a_n)/delta; % First derivative @ alpha = -delta/2
  346. f_2_0 = (f_1_a_p-f_1_a_n)/delta; % Second derivative @ alpha = 0
  347. delpf = max(0,-f_1_0/f_2_0); % min point
  348. return
  349. %% signal estimation
  350. function sigEst = estimateSignal(p,psis,timeVec)
  351. sigEst=zeros(size(timeVec)); % array to hold the signal
  352. b=p.Mperp; % initial magnetization
  353. preechodecay =exp(p.fRa - real(p.Rb));
  354. postechodecay=exp(p.fRa + real(p.Rb)); % imag part p.fRa is freq, real parts are relaxation
  355. sigEst(1)=b*psis(:,1); % first time point
  356. for t=timeVec(2:end) % iterate over time points
  357. if t < p.echo_time
  358. b=b.*(preechodecay); % phase evolution and decay due to model
  359. else
  360. b=b.*(postechodecay); % after the echo some of the phase changes reverse
  361. end
  362. sigEst(t)=b*psis(:,t); % effect of motion through k-space
  363. end
  364. return
  365. %% gradient calculation
  366. function g = calcGrad(psis,timeVec,sigdiff,p)
  367. numVox=size(p.fRa,2);
  368. Voxels=1:numVox;
  369. CC_conj = conj(-sigdiff);
  370. fR_grad = zeros(numVox,1); % freq and decay
  371. Rp_grad = zeros(numVox,1); % R prime
  372. M = transpose(p.Mperp); % M perp
  373. wfpre = transpose(exp(p.fRa - real(p.Rb))); % before spin echo
  374. wfpost = transpose(exp(p.fRa + real(p.Rb))); % after spin echo
  375. wf_n = ones(numVox,1);
  376. M_grad = psis(:,1)*CC_conj(1);
  377. for t = timeVec
  378. if t < p.echo_time
  379. wf_n = wf_n.*wfpre;
  380. else
  381. wf_n = wf_n.*wfpost;
  382. end
  383. temp = wf_n.*psis(:,t)*CC_conj(t);
  384. M_grad = M_grad + temp;
  385. fR_grad = fR_grad + (t-1)*temp;
  386. if t > p.echo_time
  387. Rp_grad = Rp_grad + (t-p.echo_time)*temp;
  388. end
  389. end
  390. fR_grad = M.*fR_grad;
  391. Rp_grad = M.*Rp_grad;
  392. g(Voxels) = -conj(2*M_grad);
  393. g(Voxels+numVox) = -conj(2*fR_grad);
  394. g(Voxels+numVox*2) = -conj(2*Rp_grad);
  395. return
  396. function psis=psisGen(kss,numVox,numTimePts,xa,ya)
  397. try
  398. disp('looking for cached psis, delete the psis.mat file to recalculate');
  399. disp('psis depends on the k traj and evaluation grid');
  400. load psis
  401. catch
  402. disp('psis could not be loaded. regenerating psis...');
  403. kx=real(kss);
  404. ky=imag(kss);
  405. psis = zeros(numVox,numTimePts);
  406. % here psis is the array of ideal local responses to the encoding gradients for unit-magnitude on-resonance
  407. % magnetization vectors at each pixel (xa,ya) location. These are "basis"
  408. % functions of a sort.
  409. figure(3)
  410. for nloc = 1:numVox % for each location...
  411. psis(nloc,:) = exp(-2i*pi*(kx(1:numTimePts)*xa(nloc)+ky(1:numTimePts)*ya(nloc))); % effect at each Voxel due to k-traj
  412. if mod(nloc,500) == 0 % show calculation progress once every 500 steps
  413. imagesc(real(psis));
  414. drawnow;
  415. end
  416. end
  417. fprintf('%s','saving psis ')
  418. save('psis','psis')
  419. disp('done')
  420. end
  421. return
  422. function [xx,yy]=hexgrid80(delxnom)
  423. w=1.128/(2*delxnom);
  424. delx=1.0/(sqrt(3)*w); % circumscribed hexagon;
  425. % gives minimum bias error
  426. dely=delx*sqrt(3)/2;
  427. xx=zeros(80,80);
  428. yy=xx;
  429. for nc=1:80
  430. for nr=1:2:79
  431. yy(nr,nc)=(nr-41)*dely; % y locations
  432. yy(nr+1,nc)=(nr-40)*dely;
  433. xx(nr,nc)=(nc-33)*delx; % x locations
  434. xx(nr+1,nc)=((nc-33)+0.5)*delx;
  435. end
  436. end
  437. return
  438. %% display a value from procpar
  439. function showprocvar(procvar,procpar_vars)
  440. value=query_procpar(procvar,procpar_vars);
  441. disp([procvar ' was '])
  442. disp(value)
  443. return
  444. %% eq. 8.15, 8.18 from Haacke
  445. % { exp(-t*R2)*exp(-t*R2') | 0 < t < tau
  446. % Mtransverse(t) = Mtransverse(0) * { exp(-t*R2)*exp((t-TE)*R2') | tau < t < 2tau
  447. % { exp(-t*R2)*exp((TE-t)*R2') | 2tau < t