function varargout = pm_unwrap(varargin) % Unwrapping of phasemap % When measuring phase one cannot easily distinguish between e.g. a phase % of 182 degrees, and one of -178 degrees. One tries to distinguish these % cases by using neighbourhood information. So in the example above, if we % find that a neighbouring voxel has a phase of 150 degres it seems much % more likely that the "true" phase is 182 degrees than -178 degrees. It's % trickier than it sounds. % FORMAT: [upm,(angvar),(mask),(opm)] = pm_unwrap(ci,pxs,method) % or % FORMAT: [upm,(angvar),(mask),(opm)] = pm_unwrap(ci,pxs) % or % FORMAT: [upm,(angvar),(mask),(opm)] = pm_unwrap(P,method) % or % FORMAT: [upm,(angvar),(mask),(opm)] = pm_unwrap(P) % % Input: % ci : Complex image volume corresponding % to abs(te2).*exp(i*angle(te2))./exp(i*angle(te1)); % where te1 and te2 corresponds to the complex % images obtained with the short and the long % echo-time respectively, and i denotes sqrt(-1). % pxs : 3x1 (or 2x1) array with pixel sizes. % % or % % P : File structure (from) spm_vol, containing complex % image volume as per above. % % method : Determines which method should be used % for phase-unwrapping. The options are % 'Huttonish', 'Mark2D', 'Mark3D' and 'hybrid'. % 'Huttonish' : Loosely (hence -ish) based on method described % in Hutton et al. Gets an estimate of the % uncertainty of the phase angle at each point % and unwraps in a "watershed" fashion from % a high certainty seed towards more uncertain % areas. % 'Mark2D' : Method suggested for high-res data in % Jenkinssons MRM paper. % 'Mark3D' : Method suggested for low-res data in % Jenkinssons MRM paper. % % Output: % upm : Phasemap (corresponding to angle(ci)) % after unwrapping of phase jumps. % angvar : Map of the variance of the phase-angle % estimates. This is used internally to % guide the unwrapping procedure, and % can also be used if one whishes to % do a weighted fitting of some smooth % basis set to the unwrapped phasemap. % mask : Binary mask indicating what voxels % have been unwrapped. % opm : angle(ci) % % Light reading: % % Examples of water-shed/flood-fill based unwrapping % algorithms: % % Hutton C, Bork A, Josephs O, Deichmann R, Ashburner J, % Turner R. 2002. Image distortion correction in fMRI: A % quantitative evaluation. NeuroImage 16:217-240. % Cusack R & Papadakis N. 2002. New robust 3-D phase unwrapping % algorithms: Application to magnetic field mapping and % undistorting echoplanar images. NeuroImage 16:754-764. % % Region-merging based unwrapping algorithm. % % Jenkinson M. 2003. Fast, automated, N-dimensional phase- % unwrapping algorithm. MRM 49:193-197. %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jesper Andersson % $Id: pm_unwrap.m 5659 2013-09-27 12:38:24Z guillaume $ % % The following are a set of parameters that % guide the phase unwrapping. Some are general % (i.e. apply to all methods) and some are specific. % % % These general parameters appear fine for a wide % variety of phase-map data. % % General % mthres = (pi^2)/6; % Angular uncertainty threshold for mask. ndil = 1; % No. of erode->dilate when creating mask. rr = 0; % Should we remove linear ramps prior to unwrapping? % % These parameters guide the progression of the % unwrapping waterfront. We have found it very % difficult to find a set of parameters that work % for "all" data sets. Sometimes it is better to % "pour" quickly in the beginning (large fthres) % and very slowly towards the end (large nthres). % Other times it is better to pour at an even % pace (fthres=0). % This difficulty of finding a general set of % parameters have lead us to choose 'Mark3D' % as the default method of choice. % % Huttonish % fthres = 0; % First (safe) threshold lthres = (pi^2)/6; % Last threshold (when to stop) nthres = 100; % No. of steps in between % % This, single, parameter guides the unwrapping % using Mark J's method, and has been found to be % useful for a large variety of data sets. % % Mark (2 and 3D) nstep = 8; % No. of angular steps for initial regions. % % Fiddle about with input a bit to make sure it's OK. % if ~isnumeric(varargin{1}) if ischar(varargin{1}) && isstruct(spm_vol(varargin{1})) P = spm_vol(varargin{1}); elseif isstruct(varargin{1}) && isfield(varargin{1},'dim') P = spm_vol(varargin{1}); else error(''); end ci = spm_read_vols(P(1)); pxs = sqrt(sum(P(1).mat(1:3,1:3).^2)); if nargin < 2 method = 'Mark3D'; else method = varargin{2}; end else ci = varargin{1}; pxs = varargin{2}; if length(size(ci)) ~= length(pxs) error(''); end if nargin < 3 method = 'Mark3D'; else method = varargin{3}; end end % % Generate (potentially wrapped) phase-map % opm = angle(ci); % % Try to estimate noise properties % angvar = pm_angvar(ci); % % Create mask % mask = pm_mask(angvar,mthres,ndil); % % Remove linear ramps in data prior to unwrapping % (and restore them after unwrapping). This may or may % not be a good idea. % if rr [ramps,opm] = pm_estimate_ramp(opm,mask); end % % Here comes unwrapping per se. % switch lower(method) case 'huttonish' % % Get seed point % seed = pm_seed(angvar,mask,pxs); % % Get series of thresholds that guide % evolution of unwrapping front. % thres = linspace(fthres,lthres,nthres); % % Do unwrapping % upm = opm; wmap = zeros(size(opm)); wmap(seed(1),seed(2),seed(3)) = 1; spm_progress_bar('Init',length(thres),'Unwrapping phase','Watershed step'); for i=1:length(thres) spm_progress_bar('Set',i); [upm,wmap] = pm_ff_unwrap(upm,angvar,wmap,mask,thres(i)); end spm_progress_bar('Clear'); case 'mark3d' % % Do initial division into connected regions with % limited angular span. % [irima,cn] = pm_initial_regions(opm,mask,nstep); % % Added this little bug fix which prevents pm_merge_regions crashing % because it has too many regions to merge. while cn>1800 && nstep > 2 nstep=nstep-1; [irima,cn] = pm_initial_regions(opm,mask,nstep); end % Get connectogram % [ii,jj,nn,pp] = pm_create_connectogram(irima,opm); % % Merge regions while updating connectogram % rs = histc(irima(:),[0:max(irima(:))]+0.5); rs = rs(1:end-1); upm = pm_merge_regions(opm,irima,ii,jj,nn,pp,rs); wmap = mask; case 'mark2d' upm = zeros(size(opm)); vrima = zeros(size(opm)); for sl=1:size(opm,3) % % Make intial (slicewise) regions. % [srima,cn] = pm_initial_regions(opm(:,:,sl),mask(:,:,sl),nstep); % % Get and merge slicewise connectograms % [ii,jj,nn,pp] = pm_create_connectogram(srima,opm(:,:,sl)); % % Merge regions while updating connectogram % rs = histc(srima(:),[0:max(srima(:))]+0.5); rs = rs(1:end-1); upm(:,:,sl) = pm_merge_regions(opm(:,:,sl),srima,ii,jj,nn,pp,rs); % % Get connected regions after merging (can be more than % one if e.g. the temporal lobes are disconnected in the % present slice). % [tmp,num] = spm_bwlabel(srima,6); if sl == 1 vrima(:,:,sl) = tmp; else tmp(tmp(:)>0) = tmp(tmp(:)>0) + max(max(vrima(:,:,sl-1))); vrima(:,:,sl) = tmp; end end % % Get and merge volumewise connectogram % [ii,jj,nn,pp] = pm_create_connectogram(vrima,upm); rs = histc(vrima(:),[0:max(vrima(:))]+0.5); rs = rs(1:end-1); upm = pm_merge_regions(upm,vrima,ii,jj,nn,pp,rs); wmap = mask; otherwise error('Unknown method %s passed to my_unwrap',method); end % % Restore any ramps we may have removed. % if rr upm = pm_restore_ramp(upm,mask,ramps); end mask = wmap; if nargout > 0, varargout{1} = upm; end if nargout > 1, varargout{2} = angvar; end if nargout > 2, varargout{3} = mask; end if nargout > 3, varargout{4} = opm; end