Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
itkKspaceImageFilter.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 #ifndef __itkKspaceImageFilter_txx
18 #define __itkKspaceImageFilter_txx
19 
20 #include <time.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 
24 #include "itkKspaceImageFilter.h"
25 #include <itkImageRegionConstIterator.h>
26 #include <itkImageRegionConstIteratorWithIndex.h>
27 #include <itkImageRegionIterator.h>
28 #include <itkImageFileWriter.h>
29 #include <mitkSingleShotEpi.h>
30 #include <mitkCartesianReadout.h>
31 
32 #define _USE_MATH_DEFINES
33 #include <math.h>
34 
35 namespace itk {
36 
37  template< class TPixelType >
40  : m_Z(0)
41  , m_UseConstantRandSeed(false)
42  , m_SpikesPerSlice(0)
43  , m_IsBaseline(true)
44  {
45  m_DiffusionGradientDirection.Fill(0.0);
46 
47  m_CoilPosition.Fill(0.0);
48  }
49 
50  template< class TPixelType >
53  {
54  m_Spike = vcl_complex<double>(0,0);
55  m_SpikeLog = "";
56 
57  typename OutputImageType::Pointer outputImage = OutputImageType::New();
58  itk::ImageRegion<2> region;
59  region.SetSize(0, m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(0));
60  region.SetSize(1, m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(1));
61  outputImage->SetLargestPossibleRegion( region );
62  outputImage->SetBufferedRegion( region );
63  outputImage->SetRequestedRegion( region );
64  outputImage->Allocate();
65  outputImage->FillBuffer(m_Spike);
66 
67  m_KSpaceImage = InputImageType::New();
68  m_KSpaceImage->SetLargestPossibleRegion( region );
69  m_KSpaceImage->SetBufferedRegion( region );
70  m_KSpaceImage->SetRequestedRegion( region );
71  m_KSpaceImage->Allocate();
72  m_KSpaceImage->FillBuffer(0.0);
73 
74  m_Gamma = 42576000; // Gyromagnetic ratio in Hz/T (1.5T)
75  if ( m_Parameters->m_SignalGen.m_EddyStrength>0 && m_DiffusionGradientDirection.GetNorm()>0.001)
76  {
77  m_DiffusionGradientDirection.Normalize();
78  m_DiffusionGradientDirection = m_DiffusionGradientDirection * m_Parameters->m_SignalGen.m_EddyStrength/1000 * m_Gamma;
79  m_IsBaseline = false;
80  }
81 
82  this->SetNthOutput(0, outputImage);
83 
84  m_Transform = m_Parameters->m_SignalGen.m_ImageDirection;
85  for (int i=0; i<3; i++)
86  {
87  for (int j=0; j<3; j++)
88  {
89  m_Transform[i][j] *= m_Parameters->m_SignalGen.m_ImageSpacing[j];
90  }
91  }
92  double a = m_Parameters->m_SignalGen.m_ImageRegion.GetSize(0)*m_Parameters->m_SignalGen.m_ImageSpacing[0];
93  double b = m_Parameters->m_SignalGen.m_ImageRegion.GetSize(1)*m_Parameters->m_SignalGen.m_ImageSpacing[1];
94  double diagonal = sqrt(a*a+b*b)/1000; // image diagonal in m
95 
96  switch (m_Parameters->m_SignalGen.m_CoilSensitivityProfile)
97  {
98  case SignalGenerationParameters::COIL_CONSTANT:
99  {
100  m_CoilSensitivityFactor = 1; // same signal everywhere
101  break;
102  }
103  case SignalGenerationParameters::COIL_LINEAR:
104  {
105  m_CoilSensitivityFactor = -1/diagonal; // about 50% of the signal in the image center remaining
106  break;
107  }
108  case SignalGenerationParameters::COIL_EXPONENTIAL:
109  {
110  m_CoilSensitivityFactor = -log(0.1)/diagonal; // about 32% of the signal in the image center remaining
111  break;
112  }
113  }
114 
115  switch (m_Parameters->m_SignalGen.m_AcquisitionType)
116  {
117  case SignalGenerationParameters::SingleShotEpi:
118  m_ReadoutScheme = new mitk::SingleShotEpi(m_Parameters);
119  break;
120  case SignalGenerationParameters::SpinEcho:
121  m_ReadoutScheme = new mitk::CartesianReadout(m_Parameters);
122  break;
123  default:
124  m_ReadoutScheme = new mitk::SingleShotEpi(m_Parameters);
125  }
126 
127  m_ReadoutScheme->AdjustEchoTime();
128  }
129 
130  template< class TPixelType >
132  {
133  // *************************************************************************
134  // Coil ring is moving with excited slice (FIX THIS SOMETIME)
135  m_CoilPosition[2] = pos[2];
136  // *************************************************************************
137 
138  switch (m_Parameters->m_SignalGen.m_CoilSensitivityProfile)
139  {
140  case SignalGenerationParameters::COIL_CONSTANT:
141  return 1;
142  case SignalGenerationParameters::COIL_LINEAR:
143  {
144  DoubleVectorType diff = pos-m_CoilPosition;
145  double sens = diff.GetNorm()*m_CoilSensitivityFactor + 1;
146  if (sens<0)
147  sens = 0;
148  return sens;
149  }
150  case SignalGenerationParameters::COIL_EXPONENTIAL:
151  {
152  DoubleVectorType diff = pos-m_CoilPosition;
153  double dist = diff.GetNorm();
154  return exp(-dist*m_CoilSensitivityFactor);
155  }
156  default:
157  return 1;
158  }
159  }
160 
161  template< class TPixelType >
163  ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType threadID)
164  {
166  randGen->SetSeed();
167  if (m_UseConstantRandSeed) // always generate the same random numbers?
168  {
169  randGen->SetSeed(0);
170  }
171  else
172  {
173  randGen->SetSeed();
174  }
175 
176  typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0));
177 
178  ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread);
179 
180  typedef ImageRegionConstIterator< InputImageType > InputIteratorType;
181 
182  double kxMax = m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(0);
183  double kyMax = m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(1);
184  double xMax = m_CompartmentImages.at(0)->GetLargestPossibleRegion().GetSize(0); // scanner coverage in x-direction
185  double yMax = m_CompartmentImages.at(0)->GetLargestPossibleRegion().GetSize(1); // scanner coverage in y-direction
186  double yMaxFov = yMax*m_Parameters->m_SignalGen.m_CroppingFactor; // actual FOV in y-direction (in x-direction FOV=xMax)
187 
188  double numPix = kxMax*kyMax;
189  // Adjust noise variance since it is the intended variance in physical space and not in k-space:
190  double noiseVar = m_Parameters->m_SignalGen.m_PartialFourier*m_Parameters->m_SignalGen.m_NoiseVariance/(kyMax*kxMax);
191 
192  while( !oit.IsAtEnd() )
193  {
194  // time from maximum echo
195  double t= m_ReadoutScheme->GetTimeFromMaxEcho(oit.GetIndex());
196 
197  // time passed since k-space readout started
198  double tRead = m_ReadoutScheme->GetRedoutTime(oit.GetIndex());
199 
200  // time passes since application of the RF pulse
201  double tRf = m_Parameters->m_SignalGen.m_tEcho+t;
202 
203  // calculate eddy current decay factor
204  // (TODO: vielleicht umbauen dass hier die zeit vom letzten diffusionsgradienten an genommen wird. doku dann auch entsprechend anpassen.)
205  double eddyDecay = 0;
206  if ( m_Parameters->m_Misc.m_CheckAddEddyCurrentsBox && m_Parameters->m_SignalGen.m_EddyStrength>0)
207  {
208  eddyDecay = exp(-tRead/m_Parameters->m_SignalGen.m_Tau );
209  }
210 
211  // calcualte signal relaxation factors
212  std::vector< double > relaxFactor;
213  if ( m_Parameters->m_SignalGen.m_DoSimulateRelaxation)
214  {
215  for (unsigned int i=0; i<m_CompartmentImages.size(); i++)
216  {
217  relaxFactor.push_back( exp(-tRf/m_T2.at(i) -fabs(t)/ m_Parameters->m_SignalGen.m_tInhom)
218  * (1.0-exp(-(m_Parameters->m_SignalGen.m_tRep + tRf)/m_T1.at(i))) );
219  }
220  }
221  // get current k-space index (depends on the chosen k-space readout scheme)
222  itk::Index< 2 > kIdx = m_ReadoutScheme->GetActualKspaceIndex(oit.GetIndex());
223 
224  // partial fourier
225  bool pf = false;
226  if (kIdx[1]>kyMax*m_Parameters->m_SignalGen.m_PartialFourier)
227  {
228  pf = true;
229  }
230 
231  if (!pf)
232  {
233  // shift k for DFT: (0 -- N) --> (-N/2 -- N/2)
234  double kx = kIdx[0];
235  double ky = kIdx[1];
236  if ((int)kxMax%2==1){ kx -= (kxMax-1)/2; }
237  else{ kx -= kxMax/2; }
238 
239  if ((int)kyMax%2==1){ ky -= (kyMax-1)/2; }
240  else{ ky -= kyMax/2; }
241 
242  // add ghosting
243  if (oit.GetIndex()[1]%2 == 1)
244  {
245  kx -= m_Parameters->m_SignalGen.m_KspaceLineOffset; // add gradient delay induced offset
246  }
247  else
248  {
249  kx += m_Parameters->m_SignalGen.m_KspaceLineOffset; // add gradient delay induced offset
250  }
251 
252  vcl_complex<double> s(0,0);
253  InputIteratorType it(m_CompartmentImages.at(0), m_CompartmentImages.at(0)->GetLargestPossibleRegion() );
254  while( !it.IsAtEnd() )
255  {
256  double x = it.GetIndex()[0];
257  double y = it.GetIndex()[1];
258  if ((int)xMax%2==1){ x -= (xMax-1)/2; }
259  else{ x -= xMax/2; }
260  if ((int)yMax%2==1){ y -= (yMax-1)/2; }
261  else{ y -= yMax/2; }
262 
263  DoubleVectorType pos; pos[0] = x; pos[1] = y; pos[2] = m_Z;
264  pos = m_Transform*pos/1000; // vector from image center to current position (in meter)
265 
266  vcl_complex<double> f(0, 0);
267 
268  // sum compartment signals and simulate relaxation
269  for (unsigned int i=0; i<m_CompartmentImages.size(); i++)
270  if ( m_Parameters->m_SignalGen.m_DoSimulateRelaxation)
271  f += std::complex<double>( m_CompartmentImages.at(i)->GetPixel(it.GetIndex()) * relaxFactor.at(i) * m_Parameters->m_SignalGen.m_SignalScale, 0);
272  else
273  f += std::complex<double>( m_CompartmentImages.at(i)->GetPixel(it.GetIndex()) * m_Parameters->m_SignalGen.m_SignalScale );
274 
275  if (m_Parameters->m_SignalGen.m_CoilSensitivityProfile!=SignalGenerationParameters::COIL_CONSTANT)
276  f *= CoilSensitivity(pos);
277 
278  // simulate eddy currents and other distortions
279  double omega = 0; // frequency offset
280  if ( m_Parameters->m_SignalGen.m_EddyStrength>0 && m_Parameters->m_Misc.m_CheckAddEddyCurrentsBox && !m_IsBaseline)
281  {
282  omega += (m_DiffusionGradientDirection[0]*pos[0]+m_DiffusionGradientDirection[1]*pos[1]+m_DiffusionGradientDirection[2]*pos[2]) * eddyDecay;
283  }
284 
285  if (m_Parameters->m_SignalGen.m_FrequencyMap.IsNotNull()) // simulate distortions
286  {
287  itk::Point<double, 3> point3D;
288  ItkDoubleImgType::IndexType index; index[0] = it.GetIndex()[0]; index[1] = it.GetIndex()[1]; index[2] = m_Zidx;
289  if (m_Parameters->m_SignalGen.m_DoAddMotion) // we have to account for the head motion since this also moves our frequency map
290  {
291  m_Parameters->m_SignalGen.m_FrequencyMap->TransformIndexToPhysicalPoint(index, point3D);
292  point3D = m_FiberBundle->TransformPoint( point3D.GetVnlVector(),
293  -m_Rotation[0], -m_Rotation[1], -m_Rotation[2],
294  -m_Translation[0], -m_Translation[1], -m_Translation[2] );
295  omega += InterpolateFmapValue(point3D);
296  }
297  else
298  {
299  omega += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(index);
300 
301  }
302  }
303 
304  // if signal comes from outside FOV, mirror it back (wrap-around artifact - aliasing)
305  if (y<-yMaxFov/2){ y += yMaxFov; }
306  else if (y>=yMaxFov/2) { y -= yMaxFov; }
307 
308  // actual DFT term
309  s += f * exp( std::complex<double>(0, 2 * M_PI * (kx*x/xMax + ky*y/yMaxFov + omega*t/1000 )) );
310 
311  ++it;
312  }
313  s /= numPix;
314 
315  if (m_SpikesPerSlice>0 && sqrt(s.imag()*s.imag()+s.real()*s.real()) > sqrt(m_Spike.imag()*m_Spike.imag()+m_Spike.real()*m_Spike.real()) )
316  {
317  m_Spike = s;
318 
319  }
320 
321  if (m_Parameters->m_SignalGen.m_NoiseVariance>0 && m_Parameters->m_Misc.m_CheckAddNoiseBox)
322  {
323  s = vcl_complex<double>(s.real()+randGen->GetNormalVariate(0,noiseVar), s.imag()+randGen->GetNormalVariate(0,noiseVar));
324  }
325 
326  outputImage->SetPixel(kIdx, s);
327  m_KSpaceImage->SetPixel(kIdx, sqrt(s.imag()*s.imag()+s.real()*s.real()) );
328  }
329 
330  ++oit;
331  }
332  }
333 
334  template< class TPixelType >
337  {
338  delete m_ReadoutScheme;
339 
340  typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0));
341  double kxMax = outputImage->GetLargestPossibleRegion().GetSize(0); // k-space size in x-direction
342  double kyMax = outputImage->GetLargestPossibleRegion().GetSize(1); // k-space size in y-direction
343 
344  ImageRegionIterator< OutputImageType > oit(outputImage, outputImage->GetLargestPossibleRegion());
345  while( !oit.IsAtEnd() ) // use hermitian k-space symmetry to fill empty k-space parts resulting from partial fourier acquisition
346  {
347  itk::Index< 2 > kIdx;
348  kIdx[0] = oit.GetIndex()[0];
349  kIdx[1] = oit.GetIndex()[1];
350 
351  // reverse phase
352  if (!m_Parameters->m_SignalGen.m_ReversePhase)
353  kIdx[1] = kyMax-1-kIdx[1];
354 
355  if (kIdx[1]>kyMax*m_Parameters->m_SignalGen.m_PartialFourier)
356  {
357  // reverse readout direction
358  if (oit.GetIndex()[1]%2 == 1)
359  kIdx[0] = kxMax-kIdx[0]-1;
360 
361  // calculate symmetric index
362  itk::Index< 2 > kIdx2;
363  kIdx2[0] = (int)(kxMax-kIdx[0]-(int)kxMax%2)%(int)kxMax;
364  kIdx2[1] = (int)(kyMax-kIdx[1]-(int)kyMax%2)%(int)kyMax;
365 
366  // use complex conjugate of symmetric index value at current index
367  vcl_complex<double> s = outputImage->GetPixel(kIdx2);
368  s = vcl_complex<double>(s.real(), -s.imag());
369  outputImage->SetPixel(kIdx, s);
370 
371  m_KSpaceImage->SetPixel(kIdx, sqrt(s.imag()*s.imag()+s.real()*s.real()) );
372  }
373  ++oit;
374  }
375 
377  randGen->SetSeed();
378  if (m_UseConstantRandSeed) // always generate the same random numbers?
379  randGen->SetSeed(0);
380  else
381  randGen->SetSeed();
382 
383  m_Spike *= m_Parameters->m_SignalGen.m_SpikeAmplitude;
384  itk::Index< 2 > spikeIdx;
385  for (unsigned int i=0; i<m_SpikesPerSlice; i++)
386  {
387  spikeIdx[0] = randGen->GetIntegerVariate()%(int)kxMax;
388  spikeIdx[1] = randGen->GetIntegerVariate()%(int)kyMax;
389  outputImage->SetPixel(spikeIdx, m_Spike);
390  m_SpikeLog += "[" + boost::lexical_cast<std::string>(spikeIdx[0]) + "," + boost::lexical_cast<std::string>(spikeIdx[1]) + "," + boost::lexical_cast<std::string>(m_Zidx) + "] Magnitude: " + boost::lexical_cast<std::string>(m_Spike.real()) + "+" + boost::lexical_cast<std::string>(m_Spike.imag()) + "i\n";
391  }
392  }
393 
394 
395 
396  template< class TPixelType >
398  {
399  itk::Index<3> idx;
400  itk::ContinuousIndex< double, 3> cIdx;
401  m_Parameters->m_SignalGen.m_FrequencyMap->TransformPhysicalPointToIndex(itkP, idx);
402  m_Parameters->m_SignalGen.m_FrequencyMap->TransformPhysicalPointToContinuousIndex(itkP, cIdx);
403 
404  double pix = 0;
405  if ( m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().IsInside(idx) )
406  pix = m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(idx);
407  else
408  return pix;
409 
410  double frac_x = cIdx[0] - idx[0];
411  double frac_y = cIdx[1] - idx[1];
412  double frac_z = cIdx[2] - idx[2];
413  if (frac_x<0)
414  {
415  idx[0] -= 1;
416  frac_x += 1;
417  }
418  if (frac_y<0)
419  {
420  idx[1] -= 1;
421  frac_y += 1;
422  }
423  if (frac_z<0)
424  {
425  idx[2] -= 1;
426  frac_z += 1;
427  }
428  frac_x = 1-frac_x;
429  frac_y = 1-frac_y;
430  frac_z = 1-frac_z;
431 
432  // int coordinates inside image?
433  if (idx[0] >= 0 && idx[0] < m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().GetSize(0)-1 &&
434  idx[1] >= 0 && idx[1] < m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().GetSize(1)-1 &&
435  idx[2] >= 0 && idx[2] < m_Parameters->m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion().GetSize(2)-1)
436  {
437  vnl_vector_fixed<double, 8> interpWeights;
438  interpWeights[0] = ( frac_x)*( frac_y)*( frac_z);
439  interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z);
440  interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z);
441  interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z);
442  interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z);
443  interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z);
444  interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z);
445  interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z);
446 
447  pix = m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(idx) * interpWeights[0];
448  ItkDoubleImgType::IndexType tmpIdx = idx; tmpIdx[0]++;
449  pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[1];
450  tmpIdx = idx; tmpIdx[1]++;
451  pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[2];
452  tmpIdx = idx; tmpIdx[2]++;
453  pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[3];
454  tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++;
455  pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[4];
456  tmpIdx = idx; tmpIdx[1]++; tmpIdx[2]++;
457  pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[5];
458  tmpIdx = idx; tmpIdx[2]++; tmpIdx[0]++;
459  pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[6];
460  tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++;
461  pix += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(tmpIdx) * interpWeights[7];
462  }
463 
464  return pix;
465  }
466 
467 }
468 #endif
double CoilSensitivity(DoubleVectorType &pos)
itk::SmartPointer< Self > Pointer
double InterpolateFmapValue(itk::Point< float, 3 > itkP)
void BeforeThreadedGenerateData()
If an imaging filter needs to perform processing after the buffer has been allocated but before threa...
Superclass::OutputImageRegionType OutputImageRegionType
Realizes EPI readout: one echo, maximum intensity in the k-space center, zig-zag trajectory.
Image class for storing images.
Definition: mitkImage.h:76
itk::Vector< double, 3 > DoubleVectorType
Realizes EPI readout: one echo, maximum intensity in the k-space center, zig-zag trajectory.
void AfterThreadedGenerateData()
If an imaging filter needs to perform processing after all processing threads have completed...
void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, ThreadIdType threadID)
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.