Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
vtkMitkLevelWindowFilter.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 
14 #include "vtkObjectFactory.h"
15 #include <vtkColorTransferFunction.h>
16 #include <vtkImageData.h>
17 #include <vtkImageIterator.h>
18 #include <vtkInformation.h>
19 #include <vtkInformationVector.h>
20 #include <vtkLookupTable.h>
21 #include <vtkPiecewiseFunction.h>
22 
23 #include <vtkStreamingDemandDrivenPipeline.h>
24 
25 // used for acos etc.
26 #include <cmath>
27 
28 // used for PI
29 #include <itkMath.h>
30 
31 #include <mitkLogMacros.h>
32 
33 static const double PI = itk::Math::pi;
34 
36 
38  : m_LookupTable(nullptr), m_OpacityFunction(nullptr), m_MinOpacity(0.0), m_MaxOpacity(255.0)
39 {
40  // MITK_INFO << "mitk level/window filter uses " << GetNumberOfThreads() << " thread(s)";
41 }
42 
44 {
45 }
46 
48 {
49  vtkMTimeType mTime = this->vtkObject::GetMTime();
50  vtkMTimeType time;
51 
52  if (this->m_LookupTable != nullptr)
53  {
54  time = this->m_LookupTable->GetMTime();
55  mTime = (time > mTime ? time : mTime);
56  }
57 
58  return mTime;
59 }
60 
61 void vtkMitkLevelWindowFilter::SetLookupTable(vtkScalarsToColors *lookupTable)
62 {
63  if (m_LookupTable != lookupTable)
64  {
65  m_LookupTable = lookupTable;
66  this->Modified();
67  }
68 }
69 
71 {
72  return m_LookupTable;
73 }
74 
75 void vtkMitkLevelWindowFilter::SetOpacityPiecewiseFunction(vtkPiecewiseFunction *opacityFunction)
76 {
77  if (m_OpacityFunction != opacityFunction)
78  {
79  m_OpacityFunction = opacityFunction;
80  this->Modified();
81  }
82 }
83 
84 // This code was copied from the iil. The template works only for float and double.
85 // Internal method which should never be used anywhere else and should not be in th header.
86 // Convert color pixels from (R,G,B) to (H,S,I).
87 // Reference: "Digital Image Processing, 2nd. edition", R. Gonzalez and R. Woods. Prentice Hall, 2002.
88 template <class T>
89 void RGBtoHSI(T *RGB, T *HSI)
90 {
91  T R = RGB[0], G = RGB[1], B = RGB[2], nR = (R < 0 ? 0 : (R > 255 ? 255 : R)) / 255,
92  nG = (G < 0 ? 0 : (G > 255 ? 255 : G)) / 255, nB = (B < 0 ? 0 : (B > 255 ? 255 : B)) / 255,
93  m = nR < nG ? (nR < nB ? nR : nB) : (nG < nB ? nG : nB),
94  theta = (T)(std::acos(0.5f * ((nR - nG) + (nR - nB)) / std::sqrt(std::pow(nR - nG, 2) + (nR - nB) * (nG - nB))) *
95  180 / PI),
96  sum = nR + nG + nB;
97  T H = 0, S = 0, I = 0;
98  if (theta > 0)
99  H = (nB <= nG) ? theta : 360 - theta;
100  if (sum > 0)
101  S = 1 - 3 / sum * m;
102  I = sum / 3;
103  HSI[0] = (T)H;
104  HSI[1] = (T)S;
105  HSI[2] = (T)I;
106 }
107 
108 // This code was copied from the iil. The template works only for float and double.
109 // Internal method which should never be used anywhere else and should not be in th header.
110 // Convert color pixels from (H,S,I) to (R,G,B).
111 template <class T>
112 void HSItoRGB(T *HSI, T *RGB)
113 {
114  T H = (T)HSI[0], S = (T)HSI[1], I = (T)HSI[2], a = I * (1 - S), R = 0, G = 0, B = 0;
115  if (H < 120)
116  {
117  B = a;
118  R = (T)(I * (1 + S * std::cos(H * PI / 180) / std::cos((60 - H) * PI / 180)));
119  G = 3 * I - (R + B);
120  }
121  else if (H < 240)
122  {
123  H -= 120;
124  R = a;
125  G = (T)(I * (1 + S * std::cos(H * PI / 180) / std::cos((60 - H) * PI / 180)));
126  B = 3 * I - (R + G);
127  }
128  else
129  {
130  H -= 240;
131  G = a;
132  B = (T)(I * (1 + S * std::cos(H * PI / 180) / std::cos((60 - H) * PI / 180)));
133  R = 3 * I - (G + B);
134  }
135  R *= 255;
136  G *= 255;
137  B *= 255;
138  RGB[0] = (T)(R < 0 ? 0 : (R > 255 ? 255 : R));
139  RGB[1] = (T)(G < 0 ? 0 : (G > 255 ? 255 : G));
140  RGB[2] = (T)(B < 0 ? 0 : (B > 255 ? 255 : B));
141 }
142 
143 // Internal method which should never be used anywhere else and should not be in th header.
144 //----------------------------------------------------------------------------
145 // This templated function executes the filter for any type of data.
146 template <class T>
148  vtkImageData *inData,
149  vtkImageData *outData,
150  int outExt[6],
151  double *clippingBounds,
152  T *)
153 {
154  vtkImageIterator<T> inputIt(inData, outExt);
155  vtkImageIterator<T> outputIt(outData, outExt);
156  vtkLookupTable *lookupTable;
157  const int maxC = inData->GetNumberOfScalarComponents();
158 
159  double tableRange[2];
160 
161  lookupTable = dynamic_cast<vtkLookupTable *>(self->GetLookupTable());
162 
163  lookupTable->GetTableRange(tableRange);
164 
165  // parameters for RGB level window
166  const double scale = (tableRange[1] - tableRange[0] > 0 ? 255.0 / (tableRange[1] - tableRange[0]) : 0.0);
167  const double bias = tableRange[0] * scale;
168 
169  // parameters for opaque level window
170  const double scaleOpac =
171  (self->GetMaxOpacity() - self->GetMinOpacity() > 0 ? 255.0 / (self->GetMaxOpacity() - self->GetMinOpacity()) : 0.0);
172  const double biasOpac = self->GetMinOpacity() * scaleOpac;
173 
174  int y = outExt[2];
175 
176  // Loop through ouput pixels
177  while (!outputIt.IsAtEnd())
178  {
179  T *inputSI = inputIt.BeginSpan();
180  T *outputSI = outputIt.BeginSpan();
181  T *outputSIEnd = outputIt.EndSpan();
182 
183  if (y >= clippingBounds[2] && y < clippingBounds[3])
184  {
185  int x = outExt[0];
186 
187  while (outputSI != outputSIEnd)
188  {
189  if (x >= clippingBounds[0] && x < clippingBounds[1])
190  {
191  double rgb[3], alpha, hsi[3];
192 
193  // level/window mechanism for intensity in HSI space
194  rgb[0] = static_cast<double>(*inputSI);
195  inputSI++;
196  rgb[1] = static_cast<double>(*inputSI);
197  inputSI++;
198  rgb[2] = static_cast<double>(*inputSI);
199  inputSI++;
200 
201  RGBtoHSI<double>(rgb, hsi);
202  hsi[2] = hsi[2] * 255.0 * scale - bias;
203  hsi[2] = (hsi[2] > 255.0 ? 255 : (hsi[2] < 0.0 ? 0 : hsi[2]));
204  hsi[2] /= 255.0;
205  HSItoRGB<double>(hsi, rgb);
206 
207  *outputSI = static_cast<T>(rgb[0]);
208  outputSI++;
209  *outputSI = static_cast<T>(rgb[1]);
210  outputSI++;
211  *outputSI = static_cast<T>(rgb[2]);
212  outputSI++;
213 
214  unsigned char finalAlpha = 255;
215 
216  // RGBA case
217  if (maxC >= 4)
218  {
219  // level/window mechanism for opacity
220  alpha = static_cast<double>(*inputSI);
221  inputSI++;
222  alpha = alpha * scaleOpac - biasOpac;
223  if (alpha > 255.0)
224  {
225  alpha = 255.0;
226  }
227  else if (alpha < 0.0)
228  {
229  alpha = 0.0;
230  }
231  finalAlpha = static_cast<unsigned char>(alpha);
232 
233  for (int c = 4; c < maxC; c++)
234  inputSI++;
235  }
236 
237  *outputSI = static_cast<T>(finalAlpha);
238  outputSI++;
239  }
240  else
241  {
242  inputSI += maxC;
243  *outputSI = 0;
244  outputSI++;
245  *outputSI = 0;
246  outputSI++;
247  *outputSI = 0;
248  outputSI++;
249  *outputSI = 0;
250  outputSI++;
251  }
252 
253  x++;
254  }
255  }
256  else
257  {
258  while (outputSI != outputSIEnd)
259  {
260  *outputSI = 0;
261  outputSI++;
262  *outputSI = 0;
263  outputSI++;
264  *outputSI = 0;
265  outputSI++;
266  *outputSI = 0;
267  outputSI++;
268  }
269  }
270  inputIt.NextSpan();
271  outputIt.NextSpan();
272  y++;
273  }
274 }
275 
276 // Internal method which should never be used anywhere else and should not be in th header.
277 //----------------------------------------------------------------------------
278 // This templated function executes the filter for any type of data.
279 template <class T>
281  vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], T *)
282 {
283  vtkImageIterator<T> inputIt(inData, outExt);
284  vtkImageIterator<unsigned char> outputIt(outData, outExt);
285 
286  double tableRange[2];
287 
288  // access vtkLookupTable
289  auto *lookupTable = dynamic_cast<vtkLookupTable *>(self->GetLookupTable());
290  lookupTable->GetTableRange(tableRange);
291 
292  // access elements of the vtkLookupTable
293  const int *realLookupTable = reinterpret_cast<int *>(lookupTable->GetTable()->GetPointer(0));
294  int maxIndex = lookupTable->GetNumberOfColors() - 1;
295 
296  const float scale = (tableRange[1] - tableRange[0] > 0 ? (maxIndex + 1) / (tableRange[1] - tableRange[0]) : 0.0);
297  // ensuring that starting point is zero
298  float bias = -tableRange[0] * scale;
299  // due to later conversion to int for rounding
300  bias += 0.5f;
301 
302  // Loop through ouput pixels
303  while (!outputIt.IsAtEnd())
304  {
305  unsigned char *outputSI = outputIt.BeginSpan();
306  unsigned char *outputSIEnd = outputIt.EndSpan();
307 
308  T *inputSI = inputIt.BeginSpan();
309 
310  while (outputSI != outputSIEnd)
311  {
312  // map to an index
313  auto idx = static_cast<int>(*inputSI * scale + bias);
314 
315  if (idx < 0)
316  idx = 0;
317  else if (idx > maxIndex)
318  idx = maxIndex;
319 
320  *reinterpret_cast<int *>(outputSI) = realLookupTable[idx];
321 
322  inputSI++;
323  outputSI += 4;
324  }
325 
326  inputIt.NextSpan();
327  outputIt.NextSpan();
328  }
329 }
330 
331 // Internal method which should never be used anywhere else and should not be in th header.
332 //----------------------------------------------------------------------------
333 // This templated function executes the filter for any type of data.
334 template <class T>
336  vtkImageData *inData,
337  vtkImageData *outData,
338  int outExt[6],
339  double *clippingBounds,
340  T *)
341 {
342  vtkImageIterator<T> inputIt(inData, outExt);
343  vtkImageIterator<unsigned char> outputIt(outData, outExt);
344  vtkScalarsToColors *lookupTable = self->GetLookupTable();
345 
346  int y = outExt[2];
347 
348  // Loop through ouput pixels
349  while (!outputIt.IsAtEnd())
350  {
351  unsigned char *outputSI = outputIt.BeginSpan();
352  unsigned char *outputSIEnd = outputIt.EndSpan();
353 
354  // do we iterate over the inner vertical clipping bounds
355  if (y >= clippingBounds[2] && y < clippingBounds[3])
356  {
357  T *inputSI = inputIt.BeginSpan();
358 
359  int x = outExt[0];
360 
361  while (outputSI != outputSIEnd)
362  {
363  // is this pixel within horizontal clipping bounds
364  if (x >= clippingBounds[0] && x < clippingBounds[1])
365  {
366  // fetching original value
367  auto grayValue = static_cast<double>(*inputSI);
368  // applying lookuptable - copy the 4 (RGBA) chars as a single int
369  *reinterpret_cast<int *>(outputSI) = *reinterpret_cast<int *>(lookupTable->MapValue(grayValue));
370  }
371  else
372  {
373  // outer horizontal clipping bounds - write a transparent RGBA pixel as a single int
374  *reinterpret_cast<int *>(outputSI) = 0;
375  }
376 
377  inputSI++;
378  outputSI += 4;
379  x++;
380  }
381  }
382  else
383  {
384  // outer vertical clipping bounds - write a transparent RGBA line as ints
385  while (outputSI != outputSIEnd)
386  {
387  *reinterpret_cast<int *>(outputSI) = 0;
388  outputSI += 4;
389  }
390  }
391 
392  inputIt.NextSpan();
393  outputIt.NextSpan();
394  y++;
395  }
396 }
397 
398 // Internal method which should never be used anywhere else and should not be in th header.
399 //----------------------------------------------------------------------------
400 // This templated function executes the filter for any type of data.
401 template <class T>
403  vtkImageData *inData,
404  vtkImageData *outData,
405  int outExt[6],
406  double *clippingBounds,
407  T *)
408 {
409  vtkImageIterator<T> inputIt(inData, outExt);
410  vtkImageIterator<unsigned char> outputIt(outData, outExt);
411  auto *lookupTable = dynamic_cast<vtkColorTransferFunction *>(self->GetLookupTable());
412  vtkPiecewiseFunction *opacityFunction = self->GetOpacityPiecewiseFunction();
413 
414  int y = outExt[2];
415 
416  // Loop through ouput pixels
417  while (!outputIt.IsAtEnd())
418  {
419  unsigned char *outputSI = outputIt.BeginSpan();
420  unsigned char *outputSIEnd = outputIt.EndSpan();
421 
422  // do we iterate over the inner vertical clipping bounds
423  if (y >= clippingBounds[2] && y < clippingBounds[3])
424  {
425  T *inputSI = inputIt.BeginSpan();
426 
427  int x = outExt[0];
428 
429  while (outputSI != outputSIEnd)
430  {
431  // is this pixel within horizontal clipping bounds
432  if (x >= clippingBounds[0] && x < clippingBounds[1])
433  {
434  // fetching original value
435  auto grayValue = static_cast<double>(*inputSI);
436 
437  // applying directly colortransferfunction
438  // because vtkColorTransferFunction::MapValue is not threadsafe
439  double rgba[4];
440  lookupTable->GetColor(grayValue, rgba); // RGB mapping
441  rgba[3] = 1.0;
442  if (opacityFunction)
443  rgba[3] = opacityFunction->GetValue(grayValue); // Alpha mapping
444 
445  for (int i = 0; i < 4; ++i)
446  {
447  outputSI[i] = static_cast<unsigned char>(255.0 * rgba[i] + 0.5);
448  }
449  }
450  else
451  {
452  // outer horizontal clipping bounds - write a transparent RGBA pixel as a single int
453  *reinterpret_cast<int *>(outputSI) = 0;
454  }
455 
456  inputSI++;
457  outputSI += 4;
458  x++;
459  }
460  }
461  else
462  {
463  // outer vertical clipping bounds - write a transparent RGBA line as ints
464  while (outputSI != outputSIEnd)
465  {
466  *reinterpret_cast<int *>(outputSI) = 0;
467  outputSI += 4;
468  }
469  }
470 
471  inputIt.NextSpan();
472  outputIt.NextSpan();
473  y++;
474  }
475 }
476 
478  vtkInformationVector **inputVector,
479  vtkInformationVector *outputVector)
480 {
481  vtkInformation *outInfo = outputVector->GetInformationObject(0);
482 
483  // do nothing except copy scalar type info
484  this->CopyInputArrayAttributesToOutput(request, inputVector, outputVector);
485 
486  vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_UNSIGNED_CHAR, 4);
487 
488  return 1;
489 }
490 
491 // Method to run the filter in different threads.
492 void vtkMitkLevelWindowFilter::ThreadedExecute(vtkImageData *inData, vtkImageData *outData, int extent[6], int /*id*/)
493 {
494  if (inData->GetNumberOfScalarComponents() > 2)
495  {
496  switch (inData->GetScalarType())
497  {
498  vtkTemplateMacro(
499  vtkApplyLookupTableOnRGBA(this, inData, outData, extent, m_ClippingBounds, static_cast<VTK_TT *>(nullptr)));
500  default:
501  vtkErrorMacro(<< "Execute: Unknown ScalarType");
502  return;
503  }
504  }
505  else
506  {
507  bool dontClip = extent[2] >= m_ClippingBounds[2] && extent[3] <= m_ClippingBounds[3] &&
508  extent[0] >= m_ClippingBounds[0] && extent[1] <= m_ClippingBounds[1];
509 
510  if (this->GetLookupTable())
511  this->GetLookupTable()->Build();
512 
513  auto *vlt = dynamic_cast<vtkLookupTable *>(this->GetLookupTable());
514  auto *ctf = dynamic_cast<vtkColorTransferFunction *>(this->GetLookupTable());
515 
516  bool linearLookupTable = vlt && vlt->GetScale() == VTK_SCALE_LINEAR;
517 
518  bool useFast = dontClip && linearLookupTable;
519 
520  if (ctf)
521  {
522  switch (inData->GetScalarType())
523  {
524  vtkTemplateMacro(vtkApplyLookupTableOnScalarsCTF(
525  this, inData, outData, extent, m_ClippingBounds, static_cast<VTK_TT *>(nullptr)));
526  default:
527  vtkErrorMacro(<< "Execute: Unknown ScalarType");
528  return;
529  }
530  }
531  else if (useFast)
532  {
533  switch (inData->GetScalarType())
534  {
535  vtkTemplateMacro(
536  vtkApplyLookupTableOnScalarsFast(this, inData, outData, extent, static_cast<VTK_TT *>(nullptr)));
537  default:
538  vtkErrorMacro(<< "Execute: Unknown ScalarType");
539  return;
540  }
541  }
542  else
543  {
544  switch (inData->GetScalarType())
545  {
546  vtkTemplateMacro(vtkApplyLookupTableOnScalars(
547  this, inData, outData, extent, m_ClippingBounds, static_cast<VTK_TT *>(nullptr)));
548  default:
549  vtkErrorMacro(<< "Execute: Unknown ScalarType");
550  return;
551  }
552  }
553  }
554 }
555 
556 // void vtkMitkLevelWindowFilter::ExecuteInformation(
557 // vtkImageData *vtkNotUsed(inData), vtkImageData *vtkNotUsed(outData))
558 //{
559 //}
560 
562 {
563  m_MinOpacity = minOpacity;
564 }
565 
567 {
568  return m_MinOpacity;
569 }
570 
572 {
573  m_MaxOpacity = maxOpacity;
574 }
575 
577 {
578  return m_MaxOpacity;
579 }
580 
581 void vtkMitkLevelWindowFilter::SetClippingBounds(double *bounds) // TODO does double[4] work??
582 {
583  for (unsigned int i = 0; i < 4; ++i)
584  m_ClippingBounds[i] = bounds[i];
585 }
void SetOpacityPiecewiseFunction(vtkPiecewiseFunction *opacityFunction)
Set the piecewise function used to map scalar to alpha component value (only used when the lookupTabl...
void vtkApplyLookupTableOnScalarsFast(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], T *)
vtkMTimeType GetMTime() override
static const double PI
void SetLookupTable(vtkScalarsToColors *lookupTable)
Set the lookup table for the RGB level window.
vtkScalarsToColors * GetLookupTable()
Get the lookup table for the RGB level window.
void HSItoRGB(T *HSI, T *RGB)
void vtkApplyLookupTableOnScalars(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double *clippingBounds, T *)
vtkStandardNewMacro(vtkMitkLevelWindowFilter)
void SetClippingBounds(double *)
Set clipping bounds for the opaque part of the resliced 2d image.
void RGBtoHSI(T *RGB, T *HSI)
void ThreadedExecute(vtkImageData *inData, vtkImageData *outData, int extent[6], int id) override
Method for threaded execution of the filter.
void vtkApplyLookupTableOnRGBA(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double *clippingBounds, T *)
void SetMaxOpacity(double maxOpacity)
Get/Set the upper window opacity for the alpha level window.
Applies the grayvalue or color/opacity level window to scalar or RGB(A) images.
int RequestInformation(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override
void vtkApplyLookupTableOnScalarsCTF(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double *clippingBounds, T *)
void SetMinOpacity(double minOpacity)
Get/Set the lower window opacity for the alpha level window.