1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkImplicitImageRepresentation.cxx
5
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE. See the above copyright notice for more information.
13
14 =========================================================================*/
15 #include "vtkImplicitImageRepresentation.h"
16
17 #include "vtkActor.h"
18 #include "vtkAlgorithmOutput.h"
19 #include "vtkCutter.h"
20 #include "vtkFeatureEdges.h"
21 #include "vtkImageData.h"
22 #include "vtkImageMapToColors.h"
23 #include "vtkImageReslice.h"
24 #include "vtkInformation.h"
25 #include "vtkLookupTable.h"
26 #include "vtkMatrix4x4.h"
27 #include "vtkObjectFactory.h"
28 #include "vtkPlaneSource.h"
29 #include "vtkPolyDataMapper.h"
30 #include "vtkProperty.h"
31 #include "vtkRenderWindow.h"
32 #include "vtkRenderWindowInteractor.h"
33 #include "vtkRenderer.h"
34 #include "vtkStreamingDemandDrivenPipeline.h"
35 #include "vtkTexture.h"
36 #include "vtkTextureMapToPlane.h"
37
38 #include <iostream>
39
40 vtkStandardNewMacro(vtkImplicitImageRepresentation);
41 vtkCxxSetObjectMacro(vtkImplicitImageRepresentation, ColorMap, vtkImageMapToColors);
42
43 //------------------------------------------------------------------------------
vtkImplicitImageRepresentation()44 vtkImplicitImageRepresentation::vtkImplicitImageRepresentation()
45 {
46 this->UserControlledLookupTable = false;
47 this->TextureInterpolate = true;
48 this->ResliceInterpolate = VTK_LINEAR_RESLICE;
49 this->OriginalWindow = 1.0;
50 this->OriginalLevel = 0.5;
51
52 this->ImageData = nullptr;
53 this->Reslice = vtkImageReslice::New();
54 this->Reslice->TransformInputSamplingOff();
55 this->ResliceAxes = vtkMatrix4x4::New();
56 this->ColorMap = vtkImageMapToColors::New();
57 this->Texture = vtkTexture::New();
58 this->LookupTable = nullptr;
59
60 // Setup the image / texture pipeline
61 this->GenerateTexturePlane();
62 }
63
64 //------------------------------------------------------------------------------
~vtkImplicitImageRepresentation()65 vtkImplicitImageRepresentation::~vtkImplicitImageRepresentation()
66 {
67 if (this->LookupTable)
68 {
69 this->LookupTable->UnRegister(this);
70 }
71 if (this->ImageData)
72 {
73 this->ImageData = nullptr;
74 }
75 this->Reslice->Delete();
76 this->ResliceAxes->Delete();
77 this->ColorMap->Delete();
78 this->Texture->Delete();
79 this->TextureMapToPlane->Delete();
80 }
81
82 //------------------------------------------------------------------------------
PlaceImage(vtkAlgorithmOutput * aout)83 void vtkImplicitImageRepresentation::PlaceImage(vtkAlgorithmOutput* aout)
84 {
85 vtkImageData* img =
86 vtkImageData::SafeDownCast(aout->GetProducer()->GetOutputDataObject(aout->GetIndex()));
87
88 this->PlaceImage(img);
89 this->Reslice->SetInputConnection(aout);
90 }
91
92 //------------------------------------------------------------------------------
PlaceImage(vtkImageData * img)93 void vtkImplicitImageRepresentation::PlaceImage(vtkImageData* img)
94 {
95 this->ImageData = img;
96 if (!this->ImageData)
97 {
98 // If nullptr is passed, remove any reference that Reslice had
99 // on the old ImageData
100 //
101 this->Reslice->SetInputData(nullptr);
102 return;
103 }
104
105 // Place the widget
106 double bounds[6];
107 img->GetBounds(bounds);
108 this->Superclass::PlaceWidget(bounds);
109
110 // Now update the pipeline
111 double range[2];
112 this->ImageData->GetScalarRange(range);
113
114 if (!this->UserControlledLookupTable)
115 {
116 this->LookupTable->SetTableRange(range[0], range[1]);
117 this->LookupTable->Build();
118 }
119
120 this->OriginalWindow = range[1] - range[0];
121 this->OriginalLevel = 0.5 * (range[0] + range[1]);
122
123 if (fabs(this->OriginalWindow) < 0.001)
124 {
125 this->OriginalWindow = 0.001 * (this->OriginalWindow < 0.0 ? -1 : 1);
126 }
127 if (fabs(this->OriginalLevel) < 0.001)
128 {
129 this->OriginalLevel = 0.001 * (this->OriginalLevel < 0.0 ? -1 : 1);
130 }
131
132 this->Reslice->SetInputData(img);
133 int interpolate = this->ResliceInterpolate;
134 this->ResliceInterpolate = -1; // Force change
135 this->SetResliceInterpolate(interpolate);
136
137 this->ColorMap->SetInputConnection(this->Reslice->GetOutputPort());
138
139 this->Texture->SetInputConnection(this->ColorMap->GetOutputPort());
140 this->Texture->SetInterpolate(this->TextureInterpolate);
141 }
142
143 //------------------------------------------------------------------------------
SetLookupTable(vtkLookupTable * table)144 void vtkImplicitImageRepresentation::SetLookupTable(vtkLookupTable* table)
145 {
146 if (this->LookupTable != table)
147 {
148 // to avoid destructor recursion
149 vtkLookupTable* temp = this->LookupTable;
150 this->LookupTable = table;
151 if (temp != nullptr)
152 {
153 temp->UnRegister(this);
154 }
155 if (this->LookupTable != nullptr)
156 {
157 this->LookupTable->Register(this);
158 }
159 else // create a default lut
160 {
161 this->LookupTable = this->CreateDefaultLookupTable();
162 }
163 }
164
165 this->ColorMap->SetLookupTable(this->LookupTable);
166 this->Texture->SetLookupTable(this->LookupTable);
167
168 if (this->ImageData && !this->UserControlledLookupTable)
169 {
170 double range[2];
171 this->ImageData->GetScalarRange(range);
172
173 this->LookupTable->SetTableRange(range[0], range[1]);
174 this->LookupTable->Build();
175
176 this->OriginalWindow = range[1] - range[0];
177 this->OriginalLevel = 0.5 * (range[0] + range[1]);
178 }
179 }
180
181 //------------------------------------------------------------------------------
SetResliceInterpolate(int i)182 void vtkImplicitImageRepresentation::SetResliceInterpolate(int i)
183 {
184 if (this->ResliceInterpolate == i)
185 {
186 return;
187 }
188 this->ResliceInterpolate = i;
189 this->Modified();
190
191 if (!this->Reslice)
192 {
193 return;
194 }
195
196 if (i == VTK_NEAREST_RESLICE)
197 {
198 this->Reslice->SetInterpolationModeToNearestNeighbor();
199 }
200 else if (i == VTK_LINEAR_RESLICE)
201 {
202 this->Reslice->SetInterpolationModeToLinear();
203 }
204 else
205 {
206 this->Reslice->SetInterpolationModeToCubic();
207 }
208 this->Texture->SetInterpolate(this->TextureInterpolate);
209 }
210
211 //------------------------------------------------------------------------------
CreateDefaultProperties()212 void vtkImplicitImageRepresentation::CreateDefaultProperties()
213 {
214 // Use what's defined in the superclass
215 this->Superclass::CreateDefaultProperties();
216
217 // Plane properties need to modified for best appearance due to texture
218 this->PlaneProperty->SetAmbient(1.0);
219 this->PlaneProperty->SetAmbientColor(1.0, 1.0, 1.0);
220 this->PlaneProperty->SetOpacity(1.0);
221 this->CutActor->SetProperty(this->PlaneProperty);
222
223 this->SelectedPlaneProperty->SetAmbient(1.0);
224 this->SelectedPlaneProperty->SetAmbientColor(0.0, 1.0, 0.0);
225 this->SelectedPlaneProperty->SetOpacity(1.0);
226 }
227
228 //------------------------------------------------------------------------------
CreateDefaultLookupTable()229 vtkLookupTable* vtkImplicitImageRepresentation::CreateDefaultLookupTable()
230 {
231 vtkLookupTable* lut = vtkLookupTable::New();
232 lut->Register(this);
233 lut->Delete();
234 lut->SetNumberOfColors(256);
235 lut->SetHueRange(0, 0);
236 lut->SetSaturationRange(0, 0);
237 lut->SetValueRange(0, 1);
238 lut->SetAlphaRange(1, 1);
239 lut->Build();
240 return lut;
241 }
242
243 //------------------------------------------------------------------------------
GenerateTexturePlane()244 void vtkImplicitImageRepresentation::GenerateTexturePlane()
245 {
246 this->TextureMapToPlane = vtkTextureMapToPlane::New();
247 this->TextureMapToPlane->AutomaticPlaneGenerationOff();
248 this->TextureMapToPlane->SetOrigin(this->PlaneSource->GetOrigin());
249 this->TextureMapToPlane->SetPoint1(this->PlaneSource->GetPoint1());
250 this->TextureMapToPlane->SetPoint2(this->PlaneSource->GetPoint2());
251
252 // Modify superclasses' pipeline to add in texture mapping
253 this->TextureMapToPlane->SetInputConnection(this->Cutter->GetOutputPort());
254 this->CutMapper->SetInputConnection(this->TextureMapToPlane->GetOutputPort());
255 this->Edges->SetInputConnection(this->Cutter->GetOutputPort());
256
257 this->SetResliceInterpolate(this->ResliceInterpolate);
258
259 this->LookupTable = this->CreateDefaultLookupTable();
260
261 this->ColorMap->SetLookupTable(this->LookupTable);
262 this->ColorMap->SetOutputFormatToRGBA();
263 this->ColorMap->PassAlphaToOutputOn();
264
265 this->Texture->SetQualityTo32Bit();
266 this->Texture->SetColorMode(VTK_COLOR_MODE_DEFAULT);
267 this->Texture->SetInterpolate(this->TextureInterpolate);
268 this->Texture->RepeatOff();
269 this->Texture->SetLookupTable(this->LookupTable);
270
271 // Note using the superclasses' actor for texturing, this may mean
272 // modifying the pipeline.
273 this->CutActor->SetTexture(this->Texture);
274 }
275
276 //------------------------------------------------------------------------------
UpdatePlane()277 void vtkImplicitImageRepresentation::UpdatePlane()
278 {
279 if (!this->Reslice || !this->ImageData)
280 {
281 return;
282 }
283
284 // Calculate appropriate pixel spacing for the reslicing
285 //
286 vtkAlgorithm* inpAlg = this->Reslice->GetInputAlgorithm();
287 inpAlg->UpdateInformation();
288 vtkInformation* outInfo = inpAlg->GetOutputInformation(0);
289 double spacing[3];
290 outInfo->Get(vtkDataObject::SPACING(), spacing);
291 double origin[3];
292 outInfo->Get(vtkDataObject::ORIGIN(), origin);
293 int extent[6];
294 outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent);
295
296 int i;
297
298 for (i = 0; i < 3; i++)
299 {
300 if (extent[2 * i] > extent[2 * i + 1])
301 {
302 vtkErrorMacro("Invalid extent [" << extent[0] << ", " << extent[1] << ", " << extent[2]
303 << ", " << extent[3] << ", " << extent[4] << ", "
304 << extent[5] << "]."
305 << " Perhaps the input data is empty?");
306 break;
307 }
308 }
309
310 // Update texture coordinate generation
311 this->TextureMapToPlane->SetOrigin(this->PlaneSource->GetOrigin());
312 this->TextureMapToPlane->SetPoint1(this->PlaneSource->GetPoint1());
313 this->TextureMapToPlane->SetPoint2(this->PlaneSource->GetPoint2());
314
315 // Get the plane axes and related informaion
316 double planeAxis1[3], planeAxis2[3];
317 this->PlaneSource->GetAxis1(planeAxis1);
318 this->PlaneSource->GetAxis2(planeAxis2);
319
320 // The x,y dimensions of the plane
321 //
322 double planeSizeX = vtkMath::Normalize(planeAxis1);
323 double planeSizeY = vtkMath::Normalize(planeAxis2);
324
325 double normal[3];
326 this->PlaneSource->GetNormal(normal);
327
328 // Generate the slicing matrix
329 //
330 this->ResliceAxes->Identity();
331 for (i = 0; i < 3; i++)
332 {
333 this->ResliceAxes->SetElement(0, i, planeAxis1[i]);
334 this->ResliceAxes->SetElement(1, i, planeAxis2[i]);
335 this->ResliceAxes->SetElement(2, i, normal[i]);
336 }
337
338 double planeOrigin[4];
339 this->PlaneSource->GetOrigin(planeOrigin);
340
341 planeOrigin[3] = 1.0;
342
343 this->ResliceAxes->Transpose();
344 this->ResliceAxes->SetElement(0, 3, planeOrigin[0]);
345 this->ResliceAxes->SetElement(1, 3, planeOrigin[1]);
346 this->ResliceAxes->SetElement(2, 3, planeOrigin[2]);
347
348 this->Reslice->SetResliceAxes(this->ResliceAxes);
349
350 double spacingX = fabs(planeAxis1[0] * spacing[0]) + fabs(planeAxis1[1] * spacing[1]) +
351 fabs(planeAxis1[2] * spacing[2]);
352
353 double spacingY = fabs(planeAxis2[0] * spacing[0]) + fabs(planeAxis2[1] * spacing[1]) +
354 fabs(planeAxis2[2] * spacing[2]);
355
356 // Pad extent up to a power of two for efficient texture mapping
357
358 // make sure we're working with valid values
359 double realExtentX = (spacingX == 0) ? VTK_INT_MAX : planeSizeX / spacingX;
360
361 int extentX;
362 // Sanity check the input data:
363 // * if realExtentX is too large, extentX will wrap
364 // * if spacingX is 0, things will blow up.
365 if (realExtentX > (VTK_INT_MAX >> 1))
366 {
367 vtkErrorMacro(<< "Invalid X extent: " << realExtentX);
368 extentX = 0;
369 }
370 else
371 {
372 extentX = 1;
373 while (extentX < realExtentX)
374 {
375 extentX = extentX << 1;
376 }
377 }
378
379 // make sure extentY doesn't wrap during padding
380 double realExtentY = (spacingY == 0) ? VTK_INT_MAX : planeSizeY / spacingY;
381
382 int extentY;
383 if (realExtentY > (VTK_INT_MAX >> 1))
384 {
385 vtkErrorMacro(<< "Invalid Y extent: " << realExtentY);
386 extentY = 0;
387 }
388 else
389 {
390 extentY = 1;
391 while (extentY < realExtentY)
392 {
393 extentY = extentY << 1;
394 }
395 }
396
397 double outputSpacingX = (extentX == 0) ? 1.0 : planeSizeX / extentX;
398 double outputSpacingY = (extentY == 0) ? 1.0 : planeSizeY / extentY;
399 this->Reslice->SetOutputSpacing(outputSpacingX, outputSpacingY, 1);
400 this->Reslice->SetOutputOrigin(0.5 * outputSpacingX, 0.5 * outputSpacingY, 0);
401 this->Reslice->SetOutputExtent(0, extentX - 1, 0, extentY - 1, 0, 0);
402 }
403
404 //------------------------------------------------------------------------------
BuildRepresentation()405 void vtkImplicitImageRepresentation::BuildRepresentation()
406 {
407 // Make sure we're in a valid state
408 if (!this->Renderer || !this->Renderer->GetRenderWindow())
409 {
410 return;
411 }
412
413 // Build the geometry
414 this->Superclass::BuildRepresentation();
415
416 // Now setup the pipeline
417 this->UpdatePlane();
418 }
419
420 //------------------------------------------------------------------------------
SetCropPlaneToBoundingBox(bool val)421 void vtkImplicitImageRepresentation::SetCropPlaneToBoundingBox(bool val)
422 {
423 if (this->CropPlaneToBoundingBox == val)
424 {
425 return;
426 }
427
428 this->CropPlaneToBoundingBox = val;
429 if (val)
430 {
431 this->TextureMapToPlane->SetInputConnection(this->Cutter->GetOutputPort());
432 this->CutMapper->SetInputConnection(this->TextureMapToPlane->GetOutputPort());
433 this->Edges->SetInputConnection(this->Cutter->GetOutputPort());
434 }
435 else
436 {
437 this->CutMapper->SetInputConnection(this->PlaneSource->GetOutputPort());
438 this->Edges->SetInputConnection(this->PlaneSource->GetOutputPort());
439 }
440 this->Modified();
441 }
442
443 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)444 void vtkImplicitImageRepresentation::PrintSelf(ostream& os, vtkIndent indent)
445 {
446 this->Superclass::PrintSelf(os, indent);
447
448 os << indent << "Reslice Interpolate: " << this->ResliceInterpolate << "\n";
449 os << indent
450 << "User Controlled Lookup Table: " << (this->UserControlledLookupTable ? "On\n" : "Off\n");
451
452 os << indent
453 << "User Controlled Lookup Table: " << (this->UserControlledLookupTable ? "On\n" : "Off\n");
454 if (this->LookupTable)
455 {
456 os << indent << "LookupTable:\n";
457 this->LookupTable->PrintSelf(os, indent.GetNextIndent());
458 }
459 else
460 {
461 os << indent << "LookupTable: (none)\n";
462 }
463
464 if (this->ColorMap)
465 {
466 os << indent << "ColorMap:\n";
467 this->ColorMap->PrintSelf(os, indent.GetNextIndent());
468 }
469 else
470 {
471 os << indent << "ColorMap: (none)\n";
472 }
473 }
474