Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkToFCompositeFilter.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 #include <mitkToFCompositeFilter.h>
19 #include "mitkImageReadAccessor.h"
20 
21 #include <itkImage.h>
22 
23 mitk::ToFCompositeFilter::ToFCompositeFilter() : m_SegmentationMask(nullptr), m_ImageWidth(0), m_ImageHeight(0), m_ImageSize(0),
24 m_IplDistanceImage(nullptr), m_IplOutputImage(nullptr), m_ItkInputImage(nullptr), m_ApplyTemporalMedianFilter(false), m_ApplyAverageFilter(false),
25  m_ApplyMedianFilter(false), m_ApplyThresholdFilter(false), m_ApplyMaskSegmentation(false), m_ApplyBilateralFilter(false), m_DataBuffer(nullptr),
26 m_DataBufferCurrentIndex(0), m_DataBufferMaxSize(0), m_TemporalMedianFilterNumOfFrames(10), m_ThresholdFilterMin(1),
27 m_ThresholdFilterMax(7000), m_BilateralFilterDomainSigma(2), m_BilateralFilterRangeSigma(60), m_BilateralFilterKernelRadius(0)
28 {
29 }
30 
32 {
33  cvReleaseImage(&(this->m_IplDistanceImage));
34  cvReleaseImage(&(this->m_IplOutputImage));
35  if (m_DataBuffer!=nullptr)
36  {
37  delete [] m_DataBuffer;
38  }
39 }
40 
42 {
43  this->SetInput(0, distanceImage);
44 }
45 
46 void mitk::ToFCompositeFilter::SetInput( unsigned int idx, mitk::Image* distanceImage )
47 {
48  if ((distanceImage == nullptr) && (idx == this->GetNumberOfInputs() - 1)) // if the last input is set to NULL, reduce the number of inputs by one
49  {
50  this->SetNumberOfInputs(this->GetNumberOfInputs() - 1);
51  }
52  else
53  {
54  if (idx==0) //create IPL image holding distance data
55  {
56  if (!distanceImage->IsEmpty())
57  {
58  this->m_ImageWidth = distanceImage->GetDimension(0);
59  this->m_ImageHeight = distanceImage->GetDimension(1);
60  this->m_ImageSize = this->m_ImageWidth * this->m_ImageHeight * sizeof(float);
61 
62  if (this->m_IplDistanceImage != nullptr)
63  {
64  cvReleaseImage(&(this->m_IplDistanceImage));
65  }
66  ImageReadAccessor distImgAcc(distanceImage, distanceImage->GetSliceData(0,0,0));
67  float* distanceFloatData = (float*) distImgAcc.GetData();
68  this->m_IplDistanceImage = cvCreateImage(cvSize(this->m_ImageWidth, this->m_ImageHeight), IPL_DEPTH_32F, 1);
69  memcpy(this->m_IplDistanceImage->imageData, (void*)distanceFloatData, this->m_ImageSize);
70 
71  if (this->m_IplOutputImage != nullptr)
72  {
73  cvReleaseImage(&(this->m_IplOutputImage));
74  }
75  this->m_IplOutputImage = cvCreateImage(cvSize(this->m_ImageWidth, this->m_ImageHeight), IPL_DEPTH_32F, 1);
76 
77  CreateItkImage(this->m_ItkInputImage);
78  }
79  }
80  this->ProcessObject::SetNthInput(idx, distanceImage); // Process object is not const-correct so the const_cast is required here
81  }
82 
83  this->CreateOutputsForAllInputs();
84 }
85 
87 {
88  return this->GetInput(0);
89 }
90 
92 {
93  if (this->GetNumberOfInputs() < 1)
94  return nullptr; //TODO: geeignete exception werfen
95  return static_cast< mitk::Image*>(this->ProcessObject::GetInput(idx));
96 }
97 
99 {
100  // copy input 1...n to output 1...n
101  for (unsigned int idx=0; idx<this->GetNumberOfOutputs(); idx++)
102  {
103  mitk::Image::Pointer outputImage = this->GetOutput(idx);
104  mitk::Image::Pointer inputImage = this->GetInput(idx);
105  if (outputImage.IsNotNull()&&inputImage.IsNotNull())
106  {
107  ImageReadAccessor inputAcc(inputImage, inputImage->GetSliceData());
108  outputImage->CopyInformation(inputImage);
109  outputImage->Initialize(inputImage->GetPixelType(),inputImage->GetDimension(),inputImage->GetDimensions());
110  outputImage->SetSlice(inputAcc.GetData());
111  }
112  }
113  //mitk::Image::Pointer outputDistanceImage = this->GetOutput();
114  ImageReadAccessor outputAcc(this->GetOutput(), this->GetOutput()->GetSliceData(0, 0, 0) );
115  float* outputDistanceFloatData = (float*) outputAcc.GetData();
116 
117  //mitk::Image::Pointer inputDistanceImage = this->GetInput();
118  ImageReadAccessor inputAcc(this->GetInput(), this->GetInput()->GetSliceData(0, 0, 0) );
119 
120  // copy initial distance image to ipl image
121  float* distanceFloatData = (float*)inputAcc.GetData();
122  memcpy(this->m_IplDistanceImage->imageData, (void*)distanceFloatData, this->m_ImageSize);
123  if (m_ApplyThresholdFilter||m_ApplyMaskSegmentation)
124  {
125  ProcessSegmentation(this->m_IplDistanceImage);
126  }
127  if (this->m_ApplyTemporalMedianFilter||this->m_ApplyAverageFilter)
128  {
129  ProcessStreamedQuickSelectMedianImageFilter(this->m_IplDistanceImage);
130  }
131  if (this->m_ApplyMedianFilter)
132  {
133  ProcessCVMedianFilter(this->m_IplDistanceImage, this->m_IplOutputImage);
134  memcpy( this->m_IplDistanceImage->imageData, this->m_IplOutputImage->imageData, this->m_ImageSize );
135  }
136  if (this->m_ApplyBilateralFilter)
137  {
138  float* itkFloatData = this->m_ItkInputImage->GetBufferPointer();
139  memcpy(itkFloatData, this->m_IplDistanceImage->imageData, this->m_ImageSize );
140  ItkImageType2D::Pointer itkOutputImage = ProcessItkBilateralFilter(this->m_ItkInputImage);
141  memcpy( this->m_IplDistanceImage->imageData, itkOutputImage->GetBufferPointer(), this->m_ImageSize );
142 
143  //ProcessCVBilateralFilter(this->m_IplDistanceImage, this->m_OutputIplImage, domainSigma, rangeSigma, kernelRadius);
144  //memcpy( distanceFloatData, this->m_OutputIplImage->imageData, distanceImageSize );
145  }
146  memcpy( outputDistanceFloatData, this->m_IplDistanceImage->imageData, this->m_ImageSize );
147 }
148 
150 {
151  this->SetNumberOfOutputs(this->GetNumberOfInputs()); // create outputs for all inputs
152  for (unsigned int idx = 0; idx < this->GetNumberOfIndexedInputs(); ++idx)
153  if (this->GetOutput(idx) == nullptr)
154  {
155  DataObjectPointer newOutput = this->MakeOutput(idx);
156  this->SetNthOutput(idx, newOutput);
157  }
158  this->Modified();
159 }
160 
162 {
163  mitk::Image::ConstPointer input = this->GetInput();
164  mitk::Image::Pointer output = this->GetOutput();
165 
166  if (output->IsInitialized())
167  return;
168 
169  itkDebugMacro(<<"GenerateOutputInformation()");
170 
171  output->Initialize(input->GetPixelType(), *input->GetTimeGeometry());
172  output->SetPropertyList(input->GetPropertyList()->Clone());
173 }
174 
176 {
177  char* segmentationMask;
178  if (m_SegmentationMask.IsNotNull())
179  {
180  ImageReadAccessor segMaskAcc(m_SegmentationMask, m_SegmentationMask->GetSliceData(0,0,0));
181  segmentationMask = (char*)segMaskAcc.GetData();
182  }
183  else
184  {
185  segmentationMask = nullptr;
186  }
187  float *f = (float*)inputIplImage->imageData;
188  for(int i=0; i<this->m_ImageWidth*this->m_ImageHeight; i++)
189  {
190  if (this->m_ApplyThresholdFilter)
191  {
192  if (f[i]<=m_ThresholdFilterMin)
193  {
194  f[i] = 0.0;
195  }
196  else if (f[i]>=m_ThresholdFilterMax)
197  {
198  f[i] = 0.0;
199  }
200  }
201  if (this->m_ApplyMaskSegmentation)
202  {
203  if (segmentationMask)
204  {
205  if (segmentationMask[i]==0)
206  {
207  f[i] = 0.0;
208  }
209  }
210  }
211  }
212 }
213 
215 {
216  ItkImageType2D::Pointer outputItkImage;
218  bilateralFilter->SetInput(inputItkImage);
219  bilateralFilter->SetDomainSigma(m_BilateralFilterDomainSigma);
220  bilateralFilter->SetRangeSigma(m_BilateralFilterRangeSigma);
221  //bilateralFilter->SetRadius(m_BilateralFilterKernelRadius);
222  outputItkImage = bilateralFilter->GetOutput();
223  outputItkImage->Update();
224  return outputItkImage;
225 }
226 
227 void mitk::ToFCompositeFilter::ProcessCVBilateralFilter(IplImage* inputIplImage, IplImage* outputIplImage)
228 {
229  int diameter = m_BilateralFilterKernelRadius;
230  double sigmaColor = m_BilateralFilterRangeSigma;
231  double sigmaSpace = m_BilateralFilterDomainSigma;
232  cvSmooth(inputIplImage, outputIplImage, CV_BILATERAL, diameter, 0, sigmaColor, sigmaSpace);
233 }
234 
235 void mitk::ToFCompositeFilter::ProcessCVMedianFilter(IplImage* inputIplImage, IplImage* outputIplImage, int radius)
236 {
237  cvSmooth(inputIplImage, outputIplImage, CV_MEDIAN, radius, 0, 0, 0);
238 }
239 
241 {
242  float* data = (float*)inputIplImage->imageData;
243 
244  int imageSize = inputIplImage->width * inputIplImage->height;
245  float* tmpArray;
246 
247  if (this->m_TemporalMedianFilterNumOfFrames == 0)
248  {
249  return;
250  }
251 
252  if (m_TemporalMedianFilterNumOfFrames != this->m_DataBufferMaxSize) // reset
253  {
254  //delete current buffer
255  for( int i=0; i<this->m_DataBufferMaxSize; i++ ) {
256  delete[] this->m_DataBuffer[i];
257  }
258  if (this->m_DataBuffer != nullptr)
259  {
260  delete[] this->m_DataBuffer;
261  }
262 
263  this->m_DataBufferMaxSize = m_TemporalMedianFilterNumOfFrames;
264 
265  // create new buffer with current size
266  this->m_DataBuffer = new float*[this->m_DataBufferMaxSize];
267  for(int i=0; i<this->m_DataBufferMaxSize; i++)
268  {
269  this->m_DataBuffer[i] = nullptr;
270  }
271  this->m_DataBufferCurrentIndex = 0;
272  }
273 
274  int currentBufferSize = this->m_DataBufferMaxSize;
275  tmpArray = new float[this->m_DataBufferMaxSize];
276 
277  // copy data to buffer
278  if (this->m_DataBuffer[this->m_DataBufferCurrentIndex] == nullptr)
279  {
280  this->m_DataBuffer[this->m_DataBufferCurrentIndex] = new float[imageSize];
281  currentBufferSize = this->m_DataBufferCurrentIndex + 1;
282  }
283 
284  for(int j=0; j<imageSize; j++)
285  {
286  this->m_DataBuffer[this->m_DataBufferCurrentIndex][j] = data[j];
287  }
288 
289  float tmpValue = 0.0f;
290  for(int i=0; i<imageSize; i++)
291  {
292  if (m_ApplyAverageFilter)
293  {
294  tmpValue = 0.0f;
295  for(int j=0; j<currentBufferSize; j++)
296  {
297  tmpValue+=this->m_DataBuffer[j][i];
298  }
299  data[i] = tmpValue/currentBufferSize;
300  }
301  else if (m_ApplyTemporalMedianFilter)
302  {
303  for(int j=0; j<currentBufferSize; j++)
304  {
305  tmpArray[j] = this->m_DataBuffer[j][i];
306  }
307  data[i] = quick_select(tmpArray, currentBufferSize);
308  }
309  }
310 
311  this->m_DataBufferCurrentIndex = (this->m_DataBufferCurrentIndex + 1) % this->m_DataBufferMaxSize;
312 
313  delete[] tmpArray;
314 }
315 
316 #define ELEM_SWAP(a,b) { register float t=(a);(a)=(b);(b)=t; }
317 float mitk::ToFCompositeFilter::quick_select(float arr[], int n)
318 {
319  int low = 0;
320  int high = n-1;
321  int median = (low + high)/2;
322  int middle = 0;
323  int ll = 0;
324  int hh = 0;
325  for (;;) {
326  if (high <= low) /* One element only */
327  return arr[median] ;
328  if (high == low + 1) { /* Two elements only */
329  if (arr[low] > arr[high])
330  ELEM_SWAP(arr[low], arr[high]) ;
331  return arr[median] ;
332  }
333  /* Find median of low, middle and high items; swap into position low */
334  middle = (low + high) / 2;
335  if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]) ;
336  if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]) ;
337  if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ;
338  /* Swap low item (now in position middle) into position (low+1) */
339  ELEM_SWAP(arr[middle], arr[low+1]) ;
340  /* Nibble from each end towards middle, swapping items when stuck */
341  ll = low + 1;
342  hh = high;
343  for (;;) {
344  do ll++; while (arr[low] > arr[ll]) ;
345  do hh--; while (arr[hh] > arr[low]) ;
346  if (hh < ll)
347  break;
348  ELEM_SWAP(arr[ll], arr[hh]) ;
349  }
350  /* Swap middle item (in position low) back into correct position */
351  ELEM_SWAP(arr[low], arr[hh]) ;
352  /* Re-set active partition */
353  if (hh <= median)
354  low = ll;
355  if (hh >= median)
356  high = hh - 1;
357  }
358 }
359 #undef ELEM_SWAP
360 
361 void mitk::ToFCompositeFilter::SetTemporalMedianFilterParameter(int tmporalMedianFilterNumOfFrames)
362 {
363  this->m_TemporalMedianFilterNumOfFrames = tmporalMedianFilterNumOfFrames;
364 }
365 
367 {
368  if (min > max)
369  {
370  min = max;
371  }
372  this->m_ThresholdFilterMin = min;
373  this->m_ThresholdFilterMax = max;
374 }
375 
376 void mitk::ToFCompositeFilter::SetBilateralFilterParameter(double domainSigma, double rangeSigma, int kernelRadius = 0)
377 {
378  this->m_BilateralFilterDomainSigma = domainSigma;
379  this->m_BilateralFilterRangeSigma = rangeSigma;
380  this->m_BilateralFilterKernelRadius = kernelRadius;
381 }
382 
384 {
385  itkInputImage = ItkImageType2D::New();
386  ItkImageType2D::IndexType startIndex;
387  startIndex[0] = 0; // first index on X
388  startIndex[1] = 0; // first index on Y
389  ItkImageType2D::SizeType size;
390  size[0] = this->m_ImageWidth; // size along X
391  size[1] = this->m_ImageHeight; // size along Y
392  ItkImageType2D::RegionType region;
393  region.SetSize( size );
394  region.SetIndex( startIndex );
395  itkInputImage->SetRegions( region );
396  itkInputImage->Allocate();
397 
398 }
itk::SmartPointer< Self > Pointer
virtual void GenerateOutputInformation() override
void CreateOutputsForAllInputs()
Create an output for each input.
const void * GetData() const
Gives const access to the data.
void ProcessCVMedianFilter(IplImage *inputIplImage, IplImage *outputIplImage, int radius=3)
Applies the OpenCV median filter to the input image. See http://opencv.willowgarage.com/documentation/c/image_filtering.html#smooth for more details.
#define ELEM_SWAP(a, b)
virtual ImageDataItemPointer GetSliceData(int s=0, int t=0, int n=0, void *data=nullptr, ImportMemoryManagementType importMemoryManagement=CopyMemory) const
Definition: mitkImage.cpp:247
~ToFCompositeFilter()
standard destructor
void ProcessCVBilateralFilter(IplImage *inputIplImage, IplImage *outputIplImage)
Applies the OpenCV bilateral filter to the input image. See http://opencv.willowgarage.com/documentation/c/image_filtering.html#smooth for more details.
void CreateItkImage(ItkImageType2D::Pointer &itkInputImage)
Initialize and allocate a 2D ITK image of dimension m_ImageWidth*m_ImageHeight.
ToFCompositeFilter()
standard constructor
virtual void GenerateData() override
method generating the output of this filter. Called in the updated process of the pipeline...
Image class for storing images.
Definition: mitkImage.h:76
virtual void SetInput(Image *distanceImage)
sets the input of this filter
void SetThresholdFilterParameter(int min, int max)
Sets the parameters (lower, upper threshold) of the threshold filter.
static T max(T x, T y)
Definition: svm.cpp:70
void SetTemporalMedianFilterParameter(int tmporalMedianFilterNumOfFrames)
Sets the parameter of the temporal median filter.
void ProcessStreamedQuickSelectMedianImageFilter(IplImage *inputIplImage)
Performs temporal median filter on an image given the number of frames to be considered.
void SetBilateralFilterParameter(double domainSigma, double rangeSigma, int kernelRadius)
Sets the parameters (domain sigma, range sigma, kernel radius) of the bilateral filter.
float quick_select(float arr[], int n)
Quickselect algorithm This Quickselect routine is based on the algorithm described in "Numerical reci...
static T min(T x, T y)
Definition: svm.cpp:67
virtual bool IsEmpty() const
Check whether object contains data (at least at one point in time), e.g., a set of points may be empt...
ItkImageType2D::Pointer ProcessItkBilateralFilter(ItkImageType2D::Pointer inputItkImage)
Applies the ITK bilateral filter to the input image See http://www.itk.org/Doxygen320/html/classitk_1...
void ProcessSegmentation(IplImage *inputIplImage)
Applies a mask and/or threshold segmentation to the input image. All pixels with values outside the m...
Image * GetInput()
returns the input of this filter
unsigned int GetDimension() const
Get dimension of the image.
Definition: mitkImage.cpp:110
ImageReadAccessor class to get locked read access for a particular image part.
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.