PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusForoughiBoneSurfaceProbability.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 #include "PlusConfigure.h"
8 
10 
11 // VTK includes
12 #include "vtkImageCast.h"
13 #include <vtkIntArray.h>
14 #include <vtkNew.h>
15 #include <vtkObjectFactory.h>
16 #include <vtkImageData.h>
17 #include <vtkDoubleArray.h>
18 #include <vtkTimerLog.h>
19 
20 // STD includes
21 #include <cassert>
22 
23 // Other includes
24 #include "mkl.h"
25 #ifdef NDEBUG
26  #include <omp.h>
27 #endif
28 
29 #define MKL_FREE_IF_NULL(var) \
30  if (var != NULL) \
31  { \
32  mkl_free(var); \
33  var = NULL; \
34  }
35 
37 
38 //----------------------------------------------------------------------------
40 {
41  this->BlurredVSBLoG = 3;
42  this->BoneThreshold = 0.4;
43  this->ShadowSigma = 6.0;
44  this->ShadowVSIntensity = 5;
45  this->SmoothingSigma = 5.0;
46  this->TransducerMargin = 60;
47 
48  this->KernelUpdateRequested = true;
49 
50  this->GaussianKernelSize = 0;
51  this->FrameSize[0] = 0;
52  this->FrameSize[1] = 0;
53  this->FrameSize[2] = 1;
54 
55  this->MklGaussianBuffer = NULL;
56  this->MklLaplacianOfGaussianBuffer = NULL;
57  this->MklGaussianBufferTemp = NULL;
59  this->MklReflectionNumberBuffer = NULL;
60  this->MklShadowValueBuffer = NULL;
61  this->MklShadowModel = NULL;
62  this->MklGaussianKernel = NULL;
63  this->MklLaplacianKernel = NULL;
64 }
65 
66 //----------------------------------------------------------------------------
68 {
69  DeleteKernels();
70  // mkl_free_buffers();
71 }
72 
73 //----------------------------------------------------------------------------
74 void vtkPlusForoughiBoneSurfaceProbability::SimpleExecute(vtkImageData* input, vtkImageData* output)
75 {
76  // Allocate output image
77  output->SetExtent(input->GetExtent());
78 #if (VTK_MAJOR_VERSION < 6)
79  output->SetScalarType(input->GetScalarType());
80  output->SetNumberOfScalarComponents(input->GetNumberOfScalarComponents());
81  output->AllocateScalars();
82 #else
83  output->AllocateScalars(input->GetScalarType(), input->GetNumberOfScalarComponents());
84 #endif
85 
86  int* inputExtent = input->GetExtent();
87  if ((inputExtent[1] - inputExtent[0] + 1) != this->FrameSize[0] || (inputExtent[3] - inputExtent[2] + 1) != this->FrameSize[1])
88  {
89  this->FrameSize[0] = static_cast<unsigned int>(inputExtent[1] - inputExtent[0] + 1);
90  this->FrameSize[1] = static_cast<unsigned int>(inputExtent[3] - inputExtent[2] + 1);
91  this->FrameSize[2] = 1;
92  this->KernelUpdateRequested = true;
93  }
94  if (this->KernelUpdateRequested)
95  {
96  UpdateKernels();
97  this->KernelUpdateRequested = false;
98  }
99 
100  int* dims = input->GetDimensions();
101  unsigned int nx = this->FrameSize[0];
102  unsigned int ny = this->FrameSize[1];
103  unsigned int sliceSize = this->FrameSize[0] * this->FrameSize[1];
104 
105  vtkSmartPointer<vtkTimerLog> timer = vtkSmartPointer<vtkTimerLog>::New();
106 
107  // Loop through each slice
108  for (unsigned int sliceIdx = inputExtent[4]; sliceIdx <= inputExtent[5]; ++sliceIdx)
109  {
110  LOG_INFO("---------------");
111  // Index of slice in buffer
112  double* inputSlicePtr = static_cast<double*>(input->GetScalarPointer(0, 0, sliceIdx));
113  double* outputSlicePtr = static_cast<double*>(output->GetScalarPointer(0, 0, sliceIdx));
114 
115  // If slice has not all zero pixels...
116  //if (GetMaxPixelValue(inputSlicePtr, sliceSize) > 0) // this is expensive and always true (except error cases)
117  {
118  // Convolve with Gaussian kernel and normalize result between zero and one
119  timer->StartTimer();
120  Conv2(inputSlicePtr, this->MklGaussianKernel, this->MklGaussianBufferTemp, this->MklGaussianBuffer, static_cast<int>(this->FrameSize[0]), static_cast<int>(this->FrameSize[1]), this->GaussianKernelSize, this->GaussianKernelSize);
121  timer->StopTimer();
122  LOG_INFO("Conv2 1: " << timer->GetElapsedTime());
123 
124  timer->StartTimer();
125  Normalize(this->MklGaussianBuffer, sliceSize, false);
126  timer->StopTimer();
127  LOG_INFO("Normalize 1: " << timer->GetElapsedTime());
128 
129  // Convolve blurred image with Laplacian kernel
130  timer->StartTimer();
131  Conv2(this->MklGaussianBuffer, this->MklLaplacianKernel, this->MklLaplacianOfGaussianBufferTemp, this->MklLaplacianOfGaussianBuffer, static_cast<int>(this->FrameSize[0]), static_cast<int>(this->FrameSize[1]), 3, 3);
132  timer->StopTimer();
133  LOG_INFO("Conv2 2: " << timer->GetElapsedTime());
134 
135  // Main loop calculating reflection number and shadow value
136  timer->StartTimer();
137  double sumG = 0;
138  double sumGI = 0;
139  double sumHist = 0;
140  int i, pixelIdx, x, y;
141 #ifdef NDEBUG
142  #pragma omp parallel for reduction(+:sumG,sumGI, sumHist), private(i, x, pixelIdx)
143 #endif
144  for (y = 0; y < ny; ++y)
145  {
146  for (x = 0; x < nx; ++x)
147  {
148  pixelIdx = x + y * nx;
149 
150  // Only include pixels with intensity value larger than a specified threshold
151  if (this->MklGaussianBuffer[pixelIdx] >= this->BoneThreshold && pixelIdx > this->TransducerMargin * nx)
152  {
153  // Set outermost border pixels to zero and exclude negative pixels
154  if ((x == nx - 1 || x == 0 || y == ny - 1 || y == 0) || this->MklLaplacianOfGaussianBuffer[pixelIdx] <= 0)
155  {
156  this->MklLaplacianOfGaussianBuffer[pixelIdx] = 0.0;
157  }
158  else
159  {
160  // Divide by small number to increase image intensity (What! :)
161  this->MklLaplacianOfGaussianBuffer[pixelIdx] = this->MklLaplacianOfGaussianBuffer[pixelIdx] / 0.005;
162  }
163 
164  // Calculate reflection number
165  this->MklReflectionNumberBuffer[pixelIdx] = pow(this->MklGaussianBuffer[pixelIdx], this->BlurredVSBLoG) + this->MklLaplacianOfGaussianBuffer[pixelIdx];
166 
167  // Calculate shadow value
168  sumG = 0;
169  sumGI = 0;
170  for (i = y; i < ny; ++i)
171  {
172  sumG += this->MklShadowModel[i - y];
173  sumGI += this->MklShadowModel[i - y] * this->MklGaussianBuffer[x + i * nx];
174  }
175  this->MklShadowValueBuffer[pixelIdx] = sumGI / sumG;
176  }
177  else
178  {
179  this->MklReflectionNumberBuffer[pixelIdx] = 0.0;
180  this->MklShadowValueBuffer[pixelIdx] = 0.0;
181  }
182  }
183  }
184 
185  timer->StopTimer();
186  LOG_INFO("Main processing: " << timer->GetElapsedTime());
187 
188  // Normalize both reflection numbers and shadow values
189  timer->StartTimer();
190  Normalize(this->MklReflectionNumberBuffer, sliceSize, false);
191  Normalize(this->MklShadowValueBuffer, sliceSize, true);
192  timer->StopTimer();
193  LOG_INFO("Normalize 2x: " << timer->GetElapsedTime());
194 
195  // Calculate BSP
196  timer->StartTimer();
197  vdPowx(sliceSize, this->MklShadowValueBuffer, this->ShadowVSIntensity, this->MklShadowValueBuffer);
198  vdMul(sliceSize, this->MklShadowValueBuffer, this->MklReflectionNumberBuffer, outputSlicePtr);
199  timer->StopTimer();
200  LOG_INFO("Non-linear transform: " << timer->GetElapsedTime());
201 
202  // Normalize BSP
203  timer->StartTimer();
204  Normalize(outputSlicePtr, sliceSize, false, 255);
205  timer->StopTimer();
206  LOG_INFO("Normalize 3: " << timer->GetElapsedTime());
207  }
208  }
209 }
210 
211 //-----------------------------------------------------------------------------
213 {
214  DeleteKernels();
215 
216  this->GaussianKernelSize = floor(this->SmoothingSigma * 3) * 2 + 1;
217 
218  // Allocating memory for matrices aligned on 64-byte boundary for better performance
219  this->MklGaussianBuffer = (double*)mkl_malloc(this->FrameSize[0] * this->FrameSize[1] * sizeof(double), 64);
220  this->MklLaplacianOfGaussianBuffer = (double*)mkl_malloc(this->FrameSize[0] * this->FrameSize[1] * sizeof(double), 64);
221  this->MklGaussianBufferTemp = (double*)mkl_malloc((this->FrameSize[0] + GaussianKernelSize - 1) * (this->FrameSize[1] + GaussianKernelSize - 1) * sizeof(double), 64);
222  this->MklLaplacianOfGaussianBufferTemp = (double*)mkl_malloc((this->FrameSize[0] + 2) * (this->FrameSize[1] + 2) * sizeof(double), 64);
223  this->MklReflectionNumberBuffer = (double*)mkl_malloc(this->FrameSize[0] * this->FrameSize[1] * sizeof(double), 64);
224  this->MklShadowValueBuffer = (double*)mkl_malloc(this->FrameSize[0] * this->FrameSize[1] * sizeof(double), 64);
225  this->MklShadowModel = (double*)mkl_malloc(this->FrameSize[1] * sizeof(double), 64);
226  this->MklGaussianKernel = (double*)mkl_malloc(GaussianKernelSize * GaussianKernelSize * sizeof(double), 64);
227  this->MklLaplacianKernel = (double*)mkl_malloc(3 * 3 * sizeof(double), 64);
228 
229  // Calculate shadow model
230  for (int i = 0; i < this->FrameSize[1]; ++i)
231  {
232  if (i < this->FrameSize[1] - 5)
233  {
234  this->MklShadowModel[i] = 1 - exp(- (i * i - 1) / (2 * this->ShadowSigma * this->ShadowSigma));
235  }
236  else
237  {
238  this->MklShadowModel[i] = 0.0;
239  }
240  }
241 
242  // Calculate Gaussian kernel
243  int idx = 0;
244  int intervall = (GaussianKernelSize - 1) / 2;
245  for (double x = -intervall; x <= intervall; ++x)
246  {
247  for (double y = -intervall; y <= intervall; ++y)
248  {
249  this->MklGaussianKernel [idx] = exp(-((x * x) / (2 * this->SmoothingSigma * this->SmoothingSigma) + (y * y) / (2 * this->SmoothingSigma * this->SmoothingSigma)));
250  ++idx;
251  }
252  }
253 
254  // Calculate Laplacian kernel
255  this->MklLaplacianKernel[0] = 0;
256  this->MklLaplacianKernel[1] = -1;
257  this->MklLaplacianKernel[2] = 0;
258  this->MklLaplacianKernel[3] = -1;
259  this->MklLaplacianKernel[4] = 4;
260  this->MklLaplacianKernel[5] = -1;
261  this->MklLaplacianKernel[6] = 0;
262  this->MklLaplacianKernel[7] = -1;
263  this->MklLaplacianKernel[8] = 0;
264 }
265 
266 //-----------------------------------------------------------------------------
268 {
269  // Free memory
279 }
280 
281 //-----------------------------------------------------------------------------
282 // Performs a 2D convolution using Intel MKL defined by the kernel buffer.
283 void vtkPlusForoughiBoneSurfaceProbability::Conv2(const double* inputBuffer, const double* kernelBuffer, double* tempBuffer, double* outputBuffer, int nx, int ny, int kx, int ky)
284 {
285  int inputShape[] = {nx, ny};
286  int kernelShape[] = {kx, ky};
287  int resultShape[] = {nx + kx - 1, ny + ky - 1};
288 
289  VSLConvTaskPtr task;
290  int status = vsldConvNewTask(&task, VSL_CONV_MODE_AUTO, 2, inputShape, kernelShape, resultShape);
291 
292  status = vsldConvExec(task, inputBuffer, NULL, kernelBuffer, NULL, tempBuffer, NULL);
293  vslConvDeleteTask(&task);
294 
295  vtkSmartPointer<vtkTimerLog> timer = vtkSmartPointer<vtkTimerLog>::New();
296  timer->StartTimer();
297  ResizeMatrix(tempBuffer, outputBuffer, kx, ky, nx + kx - 1, ny + ky - 1);
298  timer->StopTimer();
299  LOG_INFO("Resizematrix: " << timer->GetElapsedTime());
300 }
301 
302 //-----------------------------------------------------------------------------
303 void vtkPlusForoughiBoneSurfaceProbability::ResizeMatrix(const double* inputBuffer, double* outputBuffer, int xClipping, int yClipping, int xInputSize, int yInputSize)
304 {
305  int xStart = (xClipping + 2 - 1) / 2 - 1;
306  int yStart = (yClipping + 2 - 1) / 2 - 1;
307  int xStop = xInputSize - xStart;
308  int yStop = yInputSize - yStart;
309 
310  int idx = 0;
311  for (int y = 0; y < yInputSize; ++y)
312  {
313  for (int x = 0; x < xInputSize; ++x)
314  {
315  if (x >= xStart && x < xStop && y >= yStart && y < yStop)
316  {
317  outputBuffer[idx] = inputBuffer[x + y * xInputSize];
318  ++idx;
319  }
320  }
321  }
322 }
323 
324 //-----------------------------------------------------------------------------
325 double vtkPlusForoughiBoneSurfaceProbability::GetMaxPixelValue(const double* buffer, int size)
326 {
327  double maxPixelValue = 0;
328  for (int i = 0; i < size; ++i)
329  {
330  if (buffer[i] > maxPixelValue)
331  {
332  maxPixelValue = buffer[i];
333  }
334  }
335  return maxPixelValue;
336 }
337 
338 //-----------------------------------------------------------------------------
339 void vtkPlusForoughiBoneSurfaceProbability::Normalize(double* buffer, int size, bool doInverse, double maxValue /*=1.0*/)
340 {
341  double maxPixelValue = GetMaxPixelValue(buffer, size) / maxValue;
342 
343  if (!doInverse)
344  {
345  for (int i = 0; i < size; ++i)
346  {
347  buffer[i] = buffer[i] / maxPixelValue;
348  }
349  return;
350  }
351 
352  for (int i = 0; i < size; ++i)
353  {
354  buffer[i] = maxValue - buffer[i] / maxPixelValue;
355  }
356 }
double GetMaxPixelValue(const double *buffer, int size)
void Normalize(double *buffer, int size, bool doInverse, double maxValue=1.0)
for i
This class computes bone surface probability by dynamic programming.
void ResizeMatrix(const double *inputBuffer, double *outputBuffer, int xClipping, int yClipping, int xInputSize, int yInputSize)
int x
Definition: phidget22.h:4265
virtual void SimpleExecute(vtkImageData *input, vtkImageData *output)
void Conv2(const double *inputBuffer, const double *kernelBuffer, double *tempBuffer, double *outputBuffer, int nx, int ny, int kx, int ky)
Direction vectors of rods y
Definition: algo3.m:15
#define MKL_FREE_IF_NULL(var)
vtkStandardNewMacro(vtkPlusForoughiBoneSurfaceProbability)