Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.