1 /*=========================================================================
2 
3   Program: GDCM (Grassroots DICOM). A DICOM library
4 
5   Copyright (c) 2006-2011 Mathieu Malaterre
6   All rights reserved.
7   See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
8 
9      This software is distributed WITHOUT ANY WARRANTY; without even
10      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11      PURPOSE.  See the above copyright notice for more information.
12 
13 =========================================================================*/
14 /*=========================================================================
15 
16   Portions of this file are subject to the VTK Toolkit Version 3 copyright.
17 
18   Program:   Visualization Toolkit
19   Module:    $RCSfile: vtkImageMapToWindowLevelColors2.cxx,v $
20 
21   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
22   All rights reserved.
23   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
24 
25      This software is distributed WITHOUT ANY WARRANTY; without even
26      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
27      PURPOSE.  See the above copyright notice for more information.
28 
29 =========================================================================*/
30 #include "vtkImageMapToWindowLevelColors2.h"
31 
32 #include "vtkDataArray.h"
33 #include "vtkImageData.h"
34 #include "vtkInformation.h"
35 #include "vtkInformationVector.h"
36 #include "vtkObjectFactory.h"
37 #include "vtkScalarsToColors.h"
38 #include "vtkPointData.h"
39 
40 //vtkCxxRevisionMacro(vtkImageMapToWindowLevelColors2, "$Revision: 1.3 $")
vtkStandardNewMacro(vtkImageMapToWindowLevelColors2)41 vtkStandardNewMacro(vtkImageMapToWindowLevelColors2)
42 
43 // Constructor sets default values
44 vtkImageMapToWindowLevelColors2::vtkImageMapToWindowLevelColors2()
45 {
46   this->Window = 255;
47   this->Level  = 127.5;
48 }
49 
~vtkImageMapToWindowLevelColors2()50 vtkImageMapToWindowLevelColors2::~vtkImageMapToWindowLevelColors2()
51 {
52 }
53 
54 //----------------------------------------------------------------------------
55 // This method checks to see if we can simply reference the input data
RequestData(vtkInformation * request,vtkInformationVector ** inputVector,vtkInformationVector * outputVector)56 int vtkImageMapToWindowLevelColors2::RequestData(
57   vtkInformation *request,
58   vtkInformationVector **inputVector,
59   vtkInformationVector *outputVector)
60 {
61   vtkInformation *outInfo = outputVector->GetInformationObject(0);
62   vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
63 
64   vtkImageData *outData = vtkImageData::SafeDownCast(
65     outInfo->Get(vtkDataObject::DATA_OBJECT()));
66   vtkImageData *inData = vtkImageData::SafeDownCast(
67     inInfo->Get(vtkDataObject::DATA_OBJECT()));
68 
69   // If LookupTable is null and window / level produces no change,
70   // then just pass the data
71   if (this->LookupTable == NULL &&
72       (inData->GetScalarType() == VTK_UNSIGNED_CHAR &&
73        this->Window == 255 && this->Level == 127.5))
74     {
75     vtkDebugMacro("ExecuteData: LookupTable not set, "\
76                   "Window / Level at default, "\
77                   "passing input to output.");
78 
79     outData->SetExtent(inData->GetExtent());
80     outData->GetPointData()->PassData(inData->GetPointData());
81     this->DataWasPassed = 1;
82     }
83   else
84     // normal behaviour - skip up a level since we don't want to
85     // call the superclasses ExecuteData - it would pass the data if there
86     // is no lookup table even if there is a window / level - wrong
87     // behavior.
88     {
89     if (this->DataWasPassed)
90       {
91       outData->GetPointData()->SetScalars(NULL);
92       this->DataWasPassed = 0;
93       }
94 
95     return this->vtkThreadedImageAlgorithm::RequestData(request, inputVector,
96                                                         outputVector);
97     }
98 
99   return 1;
100 }
101 
102 //----------------------------------------------------------------------------
RequestInformation(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)103 int vtkImageMapToWindowLevelColors2::RequestInformation (
104   vtkInformation *vtkNotUsed(request),
105   vtkInformationVector **inputVector,
106   vtkInformationVector *outputVector)
107 {
108   vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
109   vtkInformation *outInfo = outputVector->GetInformationObject(0);
110 
111   vtkInformation *inScalarInfo =
112     vtkDataObject::GetActiveFieldInformation(inInfo,
113     vtkDataObject::FIELD_ASSOCIATION_POINTS, vtkDataSetAttributes::SCALARS);
114   if (!inScalarInfo)
115     {
116     vtkErrorMacro("Missing scalar field on input information!");
117     return 0;
118     }
119 
120   // If LookupTable is null and window / level produces no change,
121   // then the data will be passed
122   if ( this->LookupTable == NULL &&
123        (inScalarInfo->Get(vtkDataObject::FIELD_ARRAY_TYPE()) ==
124         VTK_UNSIGNED_CHAR &&
125         this->Window == 255 && this->Level == 127.5) )
126     {
127     if (inScalarInfo->Get(vtkDataObject::FIELD_ARRAY_TYPE()) !=
128         VTK_UNSIGNED_CHAR)
129       {
130       vtkErrorMacro("ExecuteInformation: No LookupTable was set and input data is not VTK_UNSIGNED_CHAR!");
131       }
132     else
133       {
134       // no lookup table, pass the input if it was UNSIGNED_CHAR
135       vtkDataObject::SetPointDataActiveScalarInfo
136         (outInfo, VTK_UNSIGNED_CHAR,
137          inScalarInfo->Get(vtkDataObject::FIELD_NUMBER_OF_COMPONENTS()));
138       }
139     }
140   else  // the lookup table was set or window / level produces a change
141     {
142     int numComponents = 4;
143     switch (this->OutputFormat)
144       {
145       case VTK_RGBA:
146         numComponents = 4;
147         break;
148       case VTK_RGB:
149         numComponents = 3;
150         break;
151       case VTK_LUMINANCE_ALPHA:
152         numComponents = 2;
153         break;
154       case VTK_LUMINANCE:
155         numComponents = 1;
156         break;
157       default:
158         vtkErrorMacro("ExecuteInformation: Unrecognized color format.");
159         break;
160       }
161     vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_UNSIGNED_CHAR, numComponents);
162     }
163 
164   return 1;
165 }
166 
167 /*
168  * This templated routine calculates effective lower and upper limits
169  * for a window of values of type T, lower and upper.
170  */
171 template <class T>
vtkImageMapToWindowLevelClamps(vtkImageData * data,double w,double l,T & lower,T & upper,unsigned char & lower_val,unsigned char & upper_val)172 void vtkImageMapToWindowLevelClamps ( vtkImageData *data, double w,
173                                       double l, T& lower, T& upper,
174                                       unsigned char &lower_val,
175                                       unsigned char &upper_val)
176 {
177   double f_lower, f_upper, f_lower_val, f_upper_val;
178   double adjustedLower, adjustedUpper;
179   double range[2];
180 
181   data->GetPointData()->GetScalars()->GetDataTypeRange( range );
182 
183   f_lower = l - fabs(w) / 2.0;
184   f_upper = f_lower + fabs(w);
185 
186   // Set the correct lower value
187   if ( f_lower <= range[1])
188     {
189     if (f_lower >= range[0])
190       {
191       lower = (T) f_lower;
192       adjustedLower = f_lower;
193       }
194     else
195       {
196       lower = (T) range[0];
197       adjustedLower = range[0];
198       }
199     }
200   else
201     {
202     lower = (T) range[1];
203     adjustedLower = range[1];
204     }
205 
206   // Set the correct upper value
207   if ( f_upper >= range[0])
208     {
209     if (f_upper <= range[1])
210       {
211       upper = (T) f_upper;
212       adjustedUpper = f_upper;
213       }
214     else
215       {
216       upper = (T) range[1];
217       adjustedUpper = range[1];
218       }
219     }
220   else
221     {
222     upper = (T) range [0];
223     adjustedUpper = range [0];
224     }
225 
226   // now compute the lower and upper values
227   if (w >= 0)
228     {
229     f_lower_val = 255.0*(adjustedLower - f_lower)/w;
230     f_upper_val = 255.0*(adjustedUpper - f_lower)/w;
231     }
232   else
233     {
234     f_lower_val = 255.0 + 255.0*(adjustedLower - f_lower)/w;
235     f_upper_val = 255.0 + 255.0*(adjustedUpper - f_lower)/w;
236     }
237 
238   if (f_upper_val > 255)
239     {
240     upper_val = 255;
241     }
242   else if (f_upper_val < 0)
243     {
244     upper_val = 0;
245     }
246   else
247     {
248     upper_val = (unsigned char)(f_upper_val);
249     }
250 
251   if (f_lower_val > 255)
252     {
253     lower_val = 255;
254     }
255   else if (f_lower_val < 0)
256     {
257     lower_val = 0;
258     }
259   else
260     {
261     lower_val = (unsigned char)(f_lower_val);
262     }
263 }
264 
265 //----------------------------------------------------------------------------
266 // Small helper to do the clamp:
267 template <typename T>
vtkClampHelper1(T * iptr,unsigned char * optr,T lower,T upper,unsigned char lower_val,unsigned char upper_val,double shift,double scale)268 void vtkClampHelper1(T* iptr, unsigned char *optr,
269   T lower, T upper,
270   unsigned char lower_val, unsigned char upper_val,
271   double shift, double scale)
272 {
273   unsigned short ushort_val;
274   if (*iptr <= lower)
275     {
276     ushort_val = lower_val;
277     }
278   else if (*iptr >= upper)
279     {
280     ushort_val = upper_val;
281     }
282   else
283     {
284     ushort_val = (unsigned char) (((double)*iptr + shift)*scale);
285     }
286   *optr = (unsigned char)((*optr * ushort_val) >> 8);
287 }
288 
289 //----------------------------------------------------------------------------
290 template <typename T>
vtkClampHelper2(T * iptr,unsigned char * optr,T lower,T upper,unsigned char lower_val,unsigned char upper_val,double shift,double scale)291 void vtkClampHelper2(T* iptr, unsigned char *optr,
292   T lower, T upper,
293   unsigned char lower_val, unsigned char upper_val,
294   double shift, double scale)
295 {
296   unsigned char result_val;
297   if (*iptr <= lower)
298     {
299     result_val = lower_val;
300     }
301   else if (*iptr >= upper)
302     {
303     result_val = upper_val;
304     }
305   else
306     {
307     result_val = (unsigned char) (((double)*iptr + shift)*scale);
308     }
309   *optr = result_val;
310 }
311 
312 //----------------------------------------------------------------------------
313 // This non-templated function executes the filter for any type of data.
314 template <class T>
vtkImageMapToWindowLevelColors2Execute(vtkImageMapToWindowLevelColors2 * self,vtkImageData * inData,T * inPtr,vtkImageData * outData,unsigned char * outPtr,int outExt[6],int id)315 void vtkImageMapToWindowLevelColors2Execute(
316   vtkImageMapToWindowLevelColors2 *self,
317   vtkImageData *inData, T *inPtr,
318   vtkImageData *outData,
319   unsigned char *outPtr,
320   int outExt[6], int id)
321 {
322   int idxX, idxY, idxZ;
323   int extX, extY, extZ;
324   vtkIdType inIncX, inIncY, inIncZ;
325   vtkIdType outIncX, outIncY, outIncZ;
326   unsigned long count = 0;
327   unsigned long target;
328   int dataType = inData->GetScalarType();
329   int numberOfComponents,numberOfOutputComponents,outputFormat;
330   int rowLength;
331   vtkScalarsToColors *lookupTable = self->GetLookupTable();
332   unsigned char *outPtr1;
333   T *inPtr1;
334   unsigned char *optr;
335   T    *iptr;
336   double shift =  self->GetWindow() / 2.0 - self->GetLevel();
337   double scale = 255.0 / self->GetWindow();
338 
339   T   lower, upper;
340   unsigned char lower_val, upper_val;
341   vtkImageMapToWindowLevelClamps( inData, self->GetWindow(),
342                                   self->GetLevel(),
343                                   lower, upper, lower_val, upper_val );
344 
345   // find the region to loop over
346   extX = outExt[1] - outExt[0] + 1;
347   extY = outExt[3] - outExt[2] + 1;
348   extZ = outExt[5] - outExt[4] + 1;
349 
350   target = (unsigned long)(extZ*extY/50.0);
351   target++;
352 
353   // Get increments to march through data
354   inData->GetContinuousIncrements(outExt, inIncX, inIncY, inIncZ);
355 
356   outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ);
357   numberOfComponents = inData->GetNumberOfScalarComponents();
358   numberOfOutputComponents = outData->GetNumberOfScalarComponents();
359   outputFormat = self->GetOutputFormat();
360 
361   rowLength = extX*numberOfComponents;
362 
363   // Loop through output pixels
364   outPtr1 = outPtr;
365   inPtr1 = inPtr;
366   for (idxZ = 0; idxZ < extZ; idxZ++)
367     {
368     for (idxY = 0; !self->AbortExecute && idxY < extY; idxY++)
369       {
370       if (!id)
371         {
372         if (!(count%target))
373           {
374           self->UpdateProgress((double)count/(50.0*(double)target));
375           }
376         count++;
377         }
378 
379       iptr = inPtr1;
380       optr = outPtr1;
381 
382       if ( lookupTable )
383         {
384         lookupTable->MapScalarsThroughTable2(inPtr1,(unsigned char *)outPtr1,
385                                              dataType,extX,numberOfComponents,
386                                              outputFormat);
387 
388         for (idxX = 0; idxX < extX; idxX++)
389           {
390           vtkClampHelper1<T>(iptr,optr,lower,upper,lower_val,upper_val,shift,scale);
391           switch (outputFormat)
392             {
393             case VTK_RGBA:
394               vtkClampHelper1<T>(iptr+(1%numberOfComponents),optr+1,lower,upper,lower_val,upper_val,shift,scale);
395               vtkClampHelper1<T>(iptr+(2%numberOfComponents),optr+2,lower,upper,lower_val,upper_val,shift,scale);
396               *(optr+3) = 255;
397               break;
398             case VTK_RGB:
399               vtkClampHelper1<T>(iptr+(1%numberOfComponents),optr+1,lower,upper,lower_val,upper_val,shift,scale);
400               vtkClampHelper1<T>(iptr+(2%numberOfComponents),optr+2,lower,upper,lower_val,upper_val,shift,scale);
401               break;
402             case VTK_LUMINANCE_ALPHA:
403               *(optr+1) = 255;
404               break;
405             }
406           iptr += numberOfComponents;
407           optr += numberOfOutputComponents;
408           }
409         }
410       else
411         {
412         for (idxX = 0; idxX < extX; idxX++)
413           {
414           // We want to shift to the right position depending on the numberOfComponents from input
415           // if grayscale we should stay at the same position, otherwise need to shift to r,g,b
416           // (0%numberOfComponents) == 0 ...
417           // (1%numberOfComponents) == 0 or 1
418           vtkClampHelper2<T>(iptr,optr,lower,upper,lower_val,upper_val,shift,scale);
419           switch (outputFormat)
420             {
421             case VTK_RGBA:
422               vtkClampHelper2<T>(iptr+(1%numberOfComponents),optr+1,lower,upper,lower_val,upper_val,shift,scale);
423               vtkClampHelper2<T>(iptr+(2%numberOfComponents),optr+2,lower,upper,lower_val,upper_val,shift,scale);
424               *(optr+3) = 255;
425               break;
426             case VTK_RGB:
427               vtkClampHelper2<T>(iptr+(1%numberOfComponents),optr+1,lower,upper,lower_val,upper_val,shift,scale);
428               vtkClampHelper2<T>(iptr+(2%numberOfComponents),optr+2,lower,upper,lower_val,upper_val,shift,scale);
429               break;
430             case VTK_LUMINANCE_ALPHA:
431               *(optr+1) = 255;
432               break;
433             }
434           iptr += numberOfComponents;
435           optr += numberOfOutputComponents;
436           }
437         }
438       outPtr1 += outIncY + extX*numberOfOutputComponents;
439       inPtr1 += inIncY + rowLength;
440       }
441     outPtr1 += outIncZ;
442     inPtr1 += inIncZ;
443     }
444 }
445 
446 //----------------------------------------------------------------------------
447 // This method is passed a input and output data, and executes the filter
448 // algorithm to fill the output from the input.
449 
ThreadedRequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (inputVector),vtkInformationVector * vtkNotUsed (outputVector),vtkImageData *** inData,vtkImageData ** outData,int outExt[6],int id)450 void vtkImageMapToWindowLevelColors2::ThreadedRequestData(
451   vtkInformation *vtkNotUsed(request),
452   vtkInformationVector **vtkNotUsed(inputVector),
453   vtkInformationVector *vtkNotUsed(outputVector),
454   vtkImageData ***inData,
455   vtkImageData **outData,
456   int outExt[6], int id)
457 {
458   void *inPtr = inData[0][0]->GetScalarPointerForExtent(outExt);
459   void *outPtr = outData[0]->GetScalarPointerForExtent(outExt);
460 
461   switch (inData[0][0]->GetScalarType())
462     {
463     vtkTemplateMacro(
464       vtkImageMapToWindowLevelColors2Execute( this,
465                                              inData[0][0],
466                                              (VTK_TT *)(inPtr),
467                                              outData[0],
468                                              (unsigned char *)(outPtr),
469                                              outExt,
470                                              id));
471     default:
472       vtkErrorMacro(<< "Execute: Unknown ScalarType");
473       return;
474     }
475 }
476 
477 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)478 void vtkImageMapToWindowLevelColors2::PrintSelf(ostream& os, vtkIndent indent)
479 {
480   this->Superclass::PrintSelf(os,indent);
481 
482   os << indent << "Window: " << this->Window << endl;
483   os << indent << "Level: " << this->Level << endl;
484 }
485