1 function varargout = phzshift(a,b,mode,verbose,phz,thrsh);
2 % [c_out,y_out] = phzshift( a, b, [mode, verbose, phz]);
4 % calculates the coordinate shift between two k-space data sets, a and b.
5 % Using the Phase Correlation Method, each k-space set is transformed
6 % to the image domain, the phase difference is calculated and then
7 % applied to the second set. After this correction, the two data sets
8 % are added together and output.
10 % inputs can be any number of dimensions, but 'a' and 'b' must be the
13 % c_out is the combination of (a+b)/2, after phase correction.
15 % y_out contains the estimated phase shift between 'a' and 'b' for
18 % Advanced (optional) options:
19 % mode - a cell variable containing flags to various settings.
20 % 'nofft' : The input data is already in the spatial domain
21 % 'nocombo' : Returns b with the phase correction applied.
22 % 'nofilter' : use the full data to estimate the phase shift,
23 % instead of the default setting which uses a
24 % low-passed filtered version.
25 % 'center' : center the low-pass filter.
26 % verbose - set to '1' to show debugging information
27 % phz~ - if nonempty, use this matrix instead of estimating the phase
28 % shift. (i.e. y_out from a previous run)
29 % thrsh - the mask threshold for the svd calculation. range: [0:1]
32 % Copyright 2004-2016 W. Scott Hoge (wsh032580 at proton dot me)
34 % Licensed under the terms of the MIT License
35 % (https://opensource.org/licenses/MIT)
38 % 2013/nov/07: jonathan polimeni <jonp at nmr dot mgh dot harvard dot edu>
39 % fixed non-integer index, fixed missing conditional for "nofilter" option
41 % 2014/sep/25: jonathan polimeni <jonp at nmr dot mgh dot harvard dot edu>
42 % replaced phase difference fitting with Local Phase Correction (LPC) method
43 % based on the methods described in:
45 % Feiweier T. Magnetic Resonance Method and Apparatus to Determine Phase
46 % Correction Parameters. U.S. Patent No. 8,497,681, Issued 7/30/2013.
67 phz = reshape(phz,[2 c]);
75 if ( sum(strcmp(mode,'nofilter')) || sum(strcmp(mode,'nofft')) ),
78 % define a low-pass envelope to apply in kspace
82 if (alph > m), alph2 = m; else, alph2 = alph; end;
83 f1 = [ zeros(floor((m-alph2)/2),1); gausswin(alph2); zeros(ceil((m-alph2)/2),1)];
85 if (alph > n), alph2 = n; else, alph2 = alph; end;
86 f2 = [ zeros(floor((n-alph2)/2),1); gausswin(alph2); zeros(ceil((n-alph2)/2),1)];
88 [tmp,maxx] = max(max(sum(abs(a(:,:,:)),3) ));
89 [tmp,maxy] = max(max(sum(abs(a(:,:,:)),3)'));
91 %%% correct the window coordinates to set ontop of DC (which may be off center)
95 if sum( strcmp(mode,'center') )
101 % fprintf('c1: %d c2: %d\n',c1,c2);
104 f1 = f1( [ c1:length(f1) 1:(c1-1)]);
106 f1 = f1( [ length(f1)+(c1:0) 1:(length(f1)+c1-1)]);
110 f2 = f2( [ c2:length(f2) 1:(c2-1)]);
112 f2 = f2( [ length(f2)+(c2:0) 1:(length(f2)+c2-1)]);
118 % convert k-space to image-space
119 if sum(strcmp(mode,'nofft'));
122 elseif sum(strcmp(mode,'nofilter')),
123 A = fftshifthift(ifft2(fftshifthift( reshape(a,[m n c]) )));
124 B = fftshifthift(ifft2(fftshifthift( reshape(b,[m n c]) )));
126 A = fftshifthift(ifft2(fftshifthift( repmat( (f1*f2'),[1 1 c]).*reshape(a,[m n c]) )));
127 B = fftshifthift(ifft2(fftshifthift( repmat( (f1*f2'),[1 1 c]).*reshape(b,[m n c]) )));
130 mask = ( abs(A) > thrsh*max( abs(A(:)) ) );
132 A1 = reshape( permute(A,[2 1 3 4]),[n m*c]).';
133 B1 = reshape( permute(B,[2 1 3 4]),[n m*c]).';
134 M1 = reshape( permute(mask,[2 1 3 4]),[n m*c]).';
138 % calculate the normalized cross-correlation
140 % [JRP] why does next definition of "tt" below use a threshold of 0.1*max?
141 tt = find( abs(A1(:)) > 0.01*max(abs(A1(:))) );
142 C(tt) = A1(tt).*conj(B1(tt))./abs(A1(tt).*conj(A1(tt)));
144 % extract out the dominant x and y shift vectors
145 [u d v]=svds(M1.*C,1);
146 % v = rsvd( mask.*C, 1 );
152 % [JRP] previously the vector "v" was unwrapped after extracting a
153 % subset defined by vector "t2", but since the subset of samples may be
154 % non-contiguous the unwrapping could add erroroneous phase
155 % jumps---better to unwrap the vector first then extract the subset
157 unv = unwrap(angle(v));
159 % calculate the shift from the slope of the phase.
160 % here, we are only concerned with shifts along rows
164 Atmp = [ ones(length(t2),1) t2(:)-size(B,2) ];
166 fprintf(' %d ', Atmp'*Atmp );
169 fprintf(' %f ', Atmp'*unv(t2) );
173 % [JRP] "robustfit" performs better than backslash when outliers are
174 % present and are not properly masked
175 %y2 = ( [ ones(length(t2),1) t2(:)-size(B,2) ] ) \ unv(t2);
176 y2 = robustfit(t2(:)-size(B,2), unv(t2) );
178 % [JRP] based on "Local Phase Correction" of Feiweier
179 dphz = angle(sum(v(2:end) .* conj(v(1:end-1))));
183 figure; plot( t2-size(B,2), unv(t2), 'bx-', t2-size(B,2), [ ones(length(t2),1) t2(:) ]*y2,'k-.' );
185 fprintf(' x = %f %f \n', y2 );
190 end; % if exist('phz')
192 % correct the phase of the second data set to match the first
193 % (i.e. shift the k-space grids to match)
194 if sum(strcmp(mode,'nofft'));
198 A = fftshift(ifft2(fftshift( a,1:2 )), 1:2 );
199 B = fftshift(ifft2(fftshift( b,1:2 )), 1:2 );
202 if sum(strcmp(mode,'nocombo'))
203 B(:,:,cnt) = B(:,:,cnt) * diag( exp( -j*y2(2)*( (1:size(B,2))-size(B,2)/2 ) ) );
205 A(:,:,cnt) = A(:,:,cnt) * diag( exp( +j*y2(2)/2*( (1:size(A,2))-size(A,2)/2 ) ) );
206 B(:,:,cnt) = B(:,:,cnt) * diag( exp( -j*y2(2)/2*( (1:size(B,2))-size(B,2)/2 ) ) );
209 % m1 = sum(mask,2).';
211 % % calculate the shift from the slope of the phase.
212 % % here, we are only concerned with shifts along rows
213 % y1 = ( [ ones(length(t1),1) t1(:) ] ) \ unv(t1);
215 % % correct the phase of the second data set to match the first
216 % % (i.e. shift the k-space grids to match)
217 % B = diag( exp( j*y1(2)*((1:size(B,1))-1) ) ) * B;
219 % calculate the scalar phase difference between the sets ...
223 % [JRP] why does previous definition of "tt" above use a threshold of 0.01*max?
224 tt = find(A(:)>0.1*max(A(:)));
225 P(tt) = A(tt).*conj(B(tt))./abs(A(tt).*conj(A(tt)));
226 indx1 = round((size(P,1) - 10)/2) + (1:10); if ( sum(indx1<=0) > 0 ) indx1 = 1:size(P,1); end;
227 x = mean(vec(P( indx1, (end-10)/2 +(1:10), : ) ));
229 % [JRP] based on "Local Phase Correction" of Feiweier
230 x = sum(vec(A.*conj(B)));
236 % and apply a scalar correction as well.
237 B = B.*exp(j*angle(x));
240 y_out(:,cnt) = [ y2(2); exp(j*angle(x)) ];
242 fprintf(' p = %f %f + j %f\n',y_out(1,cnt), ...
243 real(y_out(2,cnt)),imag(y_out(2,cnt)) );
247 % add the two sets together, and convert back to k-space for output.
248 if sum(strcmp(mode,'nocombo'))
253 if sum(strcmp(mode,'nofft')),
255 c_out = ifftshift(fft2(ifftshift(c_out,1:2)),1:2);
261 c_out = reshape( c_out, size(a) );
263 if ( (nargout > 1) && length(sz)>2),
264 y_out = reshape( y_out, [ 2 sz(3:end) ] );