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