Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkBeamformingFilterTest.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 (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 #include <mitkTestFixture.h>
14 #include <mitkTestingMacros.h>
15 #include <mitkImage.h>
16 #include <mitkImageReadAccessor.h>
17 #include <mitkBeamformingFilter.h>
18 #include <mitkIOUtil.h>
19 #include <usModuleResource.h>
20 #include <random>
21 
22 class SyntheticPAImageData
23 {
24 public:
25  SyntheticPAImageData(float spacing_x, float spacing_y, unsigned int samples, unsigned int num_transducers, float speedOfSound)
26  {
27  m_Spacing_x = spacing_x;
28  m_Spacing_y = spacing_y;
29  m_TransducerElements = num_transducers;
30  m_Samples = samples;
31  m_SpeedOfSound = speedOfSound;
32  m_Data = new float[m_Samples*m_TransducerElements];
33 
34  for (size_t i = 0; i < m_Samples * m_TransducerElements; ++i)
35  {
36  m_Data[i] = 0;
37  }
38  }
39 
40  ~SyntheticPAImageData()
41  {
42  delete[] m_Data;
43  }
44 
45  void AddWave(float origin_depth, float origin_x, float base_value= 10000)
46  {
47  AddLine(origin_depth, origin_x, base_value);
48  AddLine(origin_depth + 0.0001f, origin_x, -base_value);
49  }
50 
51  const float* GetData()
52  {
53  return (const float*)m_Data;
54  }
55 
56 private:
57  void AddLine(float origin_depth, unsigned int origin_x, float base_value = 10000)
58  {
59  for (unsigned int x = 0; x < m_TransducerElements; ++x)
60  {
61  float distance = std::abs((int)x - (int)origin_x);
62  float delay_in_seconds = std::sqrt(std::pow(origin_depth, 2) + std::pow(distance*m_Spacing_x, 2)) / m_SpeedOfSound;
63  int pixels = std::round(delay_in_seconds / m_Spacing_y);
64 
65  for (int index = -4; index < 9; ++index)
66  {
67  if ((int)pixels + index < (int)m_Samples && (int)pixels + index > 0)
68  {
69  m_Data[(size_t)(x + (pixels + index)*m_TransducerElements)] += base_value / std::sqrt(distance + 1);
70  }
71  }
72  }
73  }
74 
75  float* m_Data;
76  float m_Spacing_x;
77  float m_Spacing_y;
78  unsigned int m_TransducerElements;
79  unsigned int m_Samples;
80  float m_SpeedOfSound;
81 };
82 
83 class mitkBeamformingFilterTestSuite : public mitk::TestFixture
84 {
85  CPPUNIT_TEST_SUITE(mitkBeamformingFilterTestSuite);
86  MITK_TEST(testBeamformingCPU_DAS);
87  MITK_TEST(testBeamformingGPU_DAS);
88  MITK_TEST(testBeamformingCPU_DMAS);
89  MITK_TEST(testBeamformingGPU_DMAS);
90  MITK_TEST(testBeamformingCPU_sDMAS);
91  MITK_TEST(testBeamformingGPU_sDMAS);
92  CPPUNIT_TEST_SUITE_END();
93 
94 private:
95 
96  mitk::BeamformingFilter::Pointer m_BeamformingFilter;
97  const unsigned int NUM_ITERATIONS = 15;
98  const unsigned int SAMPLES = 5000 * 2;
99  const unsigned int RECONSTRUCTED_SAMPLES = 2048;
100  const unsigned int ELEMENTS = 128;
101  const unsigned int RECONSTRUCTED_LINES = 128;
102  const float SPEED_OF_SOUND = 1540; // m/s
103  const float SPACING_X = 0.3; // mm
104  const float SPACING_Y = 0.00625 / 2; // us
105  const unsigned int GPU_BATCH_SIZE = 16;
106 
107 public:
108 
109  void setUp() override
110  {
111 
112  }
113 
114  void test()
115  {
116  std::random_device r;
117  std::default_random_engine randGen(r());
118  float maxDepthInMeters = SAMPLES * (SPACING_Y / 1000000) * SPEED_OF_SOUND / 2;
119  std::uniform_real_distribution<float> randDistrDepth(maxDepthInMeters*0.1, maxDepthInMeters*0.8);
120 
121  for (unsigned int iteration = 0; iteration < NUM_ITERATIONS; ++iteration)
122  {
123  // create some synthetic input data
124  float depth_in_meters1 = randDistrDepth(randGen);
125  float depth_in_meters2 = randDistrDepth(randGen);
126  float depth_in_meters3 = randDistrDepth(randGen);
127 
128  unsigned int element1 = 29;
129  unsigned int element2 = 63;
130  unsigned int element3 = 98;
131 
132  SyntheticPAImageData image(SPACING_X / 1000.f, SPACING_Y / 1000000.f, SAMPLES, ELEMENTS, SPEED_OF_SOUND);
133  image.AddWave(depth_in_meters1, 29);
134  image.AddWave(depth_in_meters2, 63);
135  image.AddWave(depth_in_meters3, 98);
136 
137  mitk::Image::Pointer inputImage = mitk::Image::New();
138  unsigned int dimension[3]{ ELEMENTS, SAMPLES, 1 };
139  inputImage->Initialize(mitk::MakeScalarPixelType<float>(), 3, dimension);
140  mitk::Vector3D spacing;
141  spacing[0] = SPACING_X;
142  spacing[1] = SPACING_Y;
143  spacing[2] = 1;
144  inputImage->SetSpacing(spacing);
145  inputImage->SetImportVolume((const void*)image.GetData(), mitk::Image::CopyMemory);
146 
147  // setup the beamforming filter
148  m_BeamformingFilter->SetInput(inputImage);
149  m_BeamformingFilter->Update();
150 
151  mitk::Image::Pointer outputImage = m_BeamformingFilter->GetOutput();
152  mitk::ImageReadAccessor readAccess(outputImage);
153  const float* outputData = (const float*)readAccess.GetData();
154 
155  unsigned int pos1[3] = { element1, (unsigned int)std::round(depth_in_meters1 * 1000.f / outputImage->GetGeometry()->GetSpacing()[1]), 0 };
156  unsigned int pos2[3] = { element2, (unsigned int)std::round(depth_in_meters2 * 1000.f / outputImage->GetGeometry()->GetSpacing()[1]), 0 };
157  unsigned int pos3[3] = { element3, (unsigned int)std::round(depth_in_meters3 * 1000.f / outputImage->GetGeometry()->GetSpacing()[1]), 0 };
158 
159  double average = 0;
160 
161  for (unsigned int i = 0; i < RECONSTRUCTED_LINES*RECONSTRUCTED_SAMPLES; ++i)
162  {
163  average += outputData[i] / (RECONSTRUCTED_LINES*RECONSTRUCTED_SAMPLES);
164  }
165 
166  CPPUNIT_ASSERT_MESSAGE(std::string("Iteration " + std::to_string(iteration) + ": first point source incorrectly reconstructed; should be > average*100, is " +
167  std::to_string(abs(outputData[pos1[0] + pos1[1] * RECONSTRUCTED_LINES]))) + " < " + std::to_string(average) + "*100"
168  , abs(outputData[pos1[0] + pos1[1]*RECONSTRUCTED_LINES] / average) > 100);
169  CPPUNIT_ASSERT_MESSAGE(std::string("Iteration " + std::to_string(iteration) + ": second point source incorrectly reconstructed; should be > average*100, is " +
170  std::to_string(abs(outputData[pos2[0] + pos2[1] * RECONSTRUCTED_LINES]))) + " < " + std::to_string(average) + "*100"
171  , abs(outputData[pos2[0] + pos2[1] * RECONSTRUCTED_LINES] / average) > 100);
172  CPPUNIT_ASSERT_MESSAGE(std::string("Iteration " + std::to_string(iteration) + ": third point source incorrectly reconstructed; should be > average*100, is " +
173  std::to_string(abs(outputData[pos3[0] + pos3[1] * RECONSTRUCTED_LINES]))) + " < " + std::to_string(average) + "*100"
174  , abs(outputData[pos3[0] + pos3[1] * RECONSTRUCTED_LINES] / average) > 100);
175  }
176  }
177 
178  mitk::BeamformingSettings::Pointer createConfig(bool UseGPU, unsigned int* inputDim, mitk::BeamformingSettings::BeamformingAlgorithm alg)
179  {
180  return mitk::BeamformingSettings::New(SPACING_X / 1000,
181  SPEED_OF_SOUND,
182  SPACING_Y / 1000000,
183  27.f,
184  true,
185  RECONSTRUCTED_SAMPLES,
186  RECONSTRUCTED_LINES,
187  inputDim,
188  SPEED_OF_SOUND * (SPACING_Y / 1000000) * SAMPLES,
189  UseGPU,
190  GPU_BATCH_SIZE,
191  mitk::BeamformingSettings::DelayCalc::Spherical,
192  mitk::BeamformingSettings::Apodization::Box,
193  ELEMENTS * 2,
194  alg);
195  }
196 
197  void testBeamformingCPU_DAS()
198  {
199  MITK_INFO << "Started DAS test on CPU";
200  unsigned int* inputDim = new unsigned int[3];
201  inputDim[0] = ELEMENTS;
202  inputDim[1] = SAMPLES;
203  inputDim[2] = 1;
204  m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(false, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::DAS));
205 
206  test();
207  delete[] inputDim;
208  }
209 
210  void testBeamformingGPU_DAS()
211  {
212  MITK_INFO << "Started DAS test on GPU";
213  unsigned int* inputDim = new unsigned int[3];
214  inputDim[0] = ELEMENTS;
215  inputDim[1] = SAMPLES;
216  inputDim[2] = 1;
217  m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(true, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::DAS));
218 
219  test();
220  delete[] inputDim;
221  }
222 
223  void testBeamformingCPU_sDMAS()
224  {
225  MITK_INFO << "Started sDMAS test on CPU";
226  unsigned int* inputDim = new unsigned int[3];
227  inputDim[0] = ELEMENTS;
228  inputDim[1] = SAMPLES;
229  inputDim[2] = 1;
230  m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(false, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::sDMAS));
231 
232  test();
233  delete[] inputDim;
234  }
235 
236  void testBeamformingGPU_sDMAS()
237  {
238  MITK_INFO << "Started sDMAS test on GPU";
239  unsigned int* inputDim = new unsigned int[3];
240  inputDim[0] = ELEMENTS;
241  inputDim[1] = SAMPLES;
242  inputDim[2] = 1;
243  m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(true, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::sDMAS));
244 
245  test();
246  delete[] inputDim;
247  }
248 
249  void testBeamformingCPU_DMAS()
250  {
251  MITK_INFO << "Started DMAS test on CPU";
252  unsigned int* inputDim = new unsigned int[3];
253  inputDim[0] = ELEMENTS;
254  inputDim[1] = SAMPLES;
255  inputDim[2] = 1;
256  m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(false, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::DMAS));
257 
258  test();
259  delete[] inputDim;
260  }
261 
262  void testBeamformingGPU_DMAS()
263  {
264  MITK_INFO << "Started DMAS test on GPU";
265  unsigned int* inputDim = new unsigned int[3];
266  inputDim[0] = ELEMENTS;
267  inputDim[1] = SAMPLES;
268  inputDim[2] = 1;
269  m_BeamformingFilter = mitk::BeamformingFilter::New(createConfig(true, inputDim, mitk::BeamformingSettings::BeamformingAlgorithm::DMAS));
270 
271  test();
272  delete[] inputDim;
273  }
274 };
275 
276 MITK_TEST_SUITE_REGISTRATION(mitkBeamformingFilter)
MITK_TEST_SUITE_REGISTRATION(mitkImageToItk)
#define MITK_INFO
Definition: mitkLogMacros.h:18
Follow Up Storage - Class to facilitate loading/accessing structured follow-up data.
Definition: testcase.h:28
#define MITK_TEST(TESTMETHOD)
Adds a test to the current test suite.
static Pointer New()
Test fixture for parameterized tests.
BeamformingAlgorithm
Available beamforming algorithms:
static Pointer New(float pitchInMeters, float speedOfSound, float timeSpacing, float angle, bool isPhotoacousticImage, unsigned int samplesPerLine, unsigned int reconstructionLines, unsigned int *inputDim, float reconstructionDepth, bool useGPU, unsigned int GPUBatchSize, Apodization apod, unsigned int apodizationArraySize, BeamformingAlgorithm algorithm, ProbeGeometry geometry, float probeRadius)
mitk::Image::Pointer image
static Pointer New()
ImageReadAccessor class to get locked read access for a particular image part.