From f750b420182687170c60f12494c4651cf430b183 Mon Sep 17 00:00:00 2001 From: malaterre Date: Wed, 12 Sep 2007 13:37:47 +0000 Subject: [PATCH] ENH: add ouw own version of color window level --- vtk/CMakeLists.txt | 3 + vtk/vtkImageColorViewer.cxx | 780 ++++++++++++++++++++++++ vtk/vtkImageColorViewer.h | 231 +++++++ vtk/vtkImageMapToWindowLevelColors2.cxx | 465 ++++++++++++++ vtk/vtkImageMapToWindowLevelColors2.h | 84 +++ 5 files changed, 1563 insertions(+) create mode 100644 vtk/vtkImageColorViewer.cxx create mode 100644 vtk/vtkImageColorViewer.h create mode 100644 vtk/vtkImageMapToWindowLevelColors2.cxx create mode 100644 vtk/vtkImageMapToWindowLevelColors2.h diff --git a/vtk/CMakeLists.txt b/vtk/CMakeLists.txt index c62e39b5..b213ecb0 100644 --- a/vtk/CMakeLists.txt +++ b/vtk/CMakeLists.txt @@ -16,6 +16,8 @@ INCLUDE_DIRECTORIES( SET(VTKGDCM_LIB_SRCS vtkGdcmReader.cxx vtkGdcmWriter.cxx + vtkImageColorViewer.cxx + vtkImageMapToWindowLevelColors2.cxx ) #----------------------------------------------------------------------------- @@ -26,6 +28,7 @@ TARGET_LINK_LIBRARIES(vtkgdcm vtkCommon vtkIO vtkFiltering + vtkRendering ) #----------------------------------------------------------------------------- diff --git a/vtk/vtkImageColorViewer.cxx b/vtk/vtkImageColorViewer.cxx new file mode 100644 index 00000000..bb053798 --- /dev/null +++ b/vtk/vtkImageColorViewer.cxx @@ -0,0 +1,780 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: $RCSfile: vtkImageColorViewer.cxx,v $ + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkImageColorViewer.h" + +#include "vtkCamera.h" +#include "vtkCommand.h" +#include "vtkImageActor.h" +#include "vtkImageData.h" +#include "vtkImageData.h" +#include "vtkImageMapToWindowLevelColors2.h" +#include "vtkInteractorStyleImage.h" +#include "vtkObjectFactory.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkRenderer.h" + +vtkCxxRevisionMacro(vtkImageColorViewer, "$Revision: 1.1 $"); +vtkStandardNewMacro(vtkImageColorViewer); + +//---------------------------------------------------------------------------- +vtkImageColorViewer::vtkImageColorViewer() +{ + this->RenderWindow = NULL; + this->Renderer = NULL; + this->ImageActor = vtkImageActor::New(); + this->WindowLevel = vtkImageMapToWindowLevelColors2::New(); + this->Interactor = NULL; + this->InteractorStyle = NULL; + + this->Slice = 0; + this->FirstRender = 1; + this->SliceOrientation = vtkImageColorViewer::SLICE_ORIENTATION_XY; + + // Setup the pipeline + + vtkRenderWindow *renwin = vtkRenderWindow::New(); + this->SetRenderWindow(renwin); + renwin->Delete(); + + vtkRenderer *ren = vtkRenderer::New(); + this->SetRenderer(ren); + ren->Delete(); + + this->InstallPipeline(); +} + +//---------------------------------------------------------------------------- +vtkImageColorViewer::~vtkImageColorViewer() +{ + if (this->WindowLevel) + { + this->WindowLevel->Delete(); + this->WindowLevel = NULL; + } + + if (this->ImageActor) + { + this->ImageActor->Delete(); + this->ImageActor = NULL; + } + + if (this->Renderer) + { + this->Renderer->Delete(); + this->Renderer = NULL; + } + + if (this->RenderWindow) + { + this->RenderWindow->Delete(); + this->RenderWindow = NULL; + } + + if (this->Interactor) + { + this->Interactor->Delete(); + this->Interactor = NULL; + } + + if (this->InteractorStyle) + { + this->InteractorStyle->Delete(); + this->InteractorStyle = NULL; + } +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetupInteractor(vtkRenderWindowInteractor *arg) +{ + if (this->Interactor == arg) + { + return; + } + + this->UnInstallPipeline(); + + if (this->Interactor) + { + this->Interactor->UnRegister(this); + } + + this->Interactor = arg; + + if (this->Interactor) + { + this->Interactor->Register(this); + } + + this->InstallPipeline(); + + if (this->Renderer) + { + this->Renderer->GetActiveCamera()->ParallelProjectionOn(); + } +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetRenderWindow(vtkRenderWindow *arg) +{ + if (this->RenderWindow == arg) + { + return; + } + + this->UnInstallPipeline(); + + if (this->RenderWindow) + { + this->RenderWindow->UnRegister(this); + } + + this->RenderWindow = arg; + + if (this->RenderWindow) + { + this->RenderWindow->Register(this); + } + + this->InstallPipeline(); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetRenderer(vtkRenderer *arg) +{ + if (this->Renderer == arg) + { + return; + } + + this->UnInstallPipeline(); + + if (this->Renderer) + { + this->Renderer->UnRegister(this); + } + + this->Renderer = arg; + + if (this->Renderer) + { + this->Renderer->Register(this); + } + + this->InstallPipeline(); + this->UpdateOrientation(); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetSize(int a,int b) +{ + this->RenderWindow->SetSize(a, b); +} + +//---------------------------------------------------------------------------- +int* vtkImageColorViewer::GetSize() +{ + return this->RenderWindow->GetSize(); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::GetSliceRange(int &min, int &max) +{ + vtkImageData *input = this->GetInput(); + if (input) + { + input->UpdateInformation(); + int *w_ext = input->GetWholeExtent(); + min = w_ext[this->SliceOrientation * 2]; + max = w_ext[this->SliceOrientation * 2 + 1]; + } +} + +//---------------------------------------------------------------------------- +int* vtkImageColorViewer::GetSliceRange() +{ + vtkImageData *input = this->GetInput(); + if (input) + { + input->UpdateInformation(); + return input->GetWholeExtent() + this->SliceOrientation * 2; + } + return NULL; +} + +//---------------------------------------------------------------------------- +int vtkImageColorViewer::GetSliceMin() +{ + int *range = this->GetSliceRange(); + if (range) + { + return range[0]; + } + return 0; +} + +//---------------------------------------------------------------------------- +int vtkImageColorViewer::GetSliceMax() +{ + int *range = this->GetSliceRange(); + if (range) + { + return range[1]; + } + return 0; +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetSlice(int slice) +{ + int *range = this->GetSliceRange(); + if (range) + { + if (slice < range[0]) + { + slice = range[0]; + } + else if (slice > range[1]) + { + slice = range[1]; + } + } + + if (this->Slice == slice) + { + return; + } + + this->Slice = slice; + this->Modified(); + + this->UpdateDisplayExtent(); + this->Render(); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetSliceOrientation(int orientation) +{ + if (orientation < vtkImageColorViewer::SLICE_ORIENTATION_YZ || + orientation > vtkImageColorViewer::SLICE_ORIENTATION_XY) + { + vtkErrorMacro("Error - invalid slice orientation " << orientation); + return; + } + + if (this->SliceOrientation == orientation) + { + return; + } + + this->SliceOrientation = orientation; + + // Update the viewer + + int *range = this->GetSliceRange(); + if (range) + { + this->Slice = static_cast((range[0] + range[1]) * 0.5); + } + + this->UpdateOrientation(); + this->UpdateDisplayExtent(); + + if (this->Renderer && this->GetInput()) + { + double scale = this->Renderer->GetActiveCamera()->GetParallelScale(); + this->Renderer->ResetCamera(); + this->Renderer->GetActiveCamera()->SetParallelScale(scale); + } + + this->Render(); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::UpdateOrientation() +{ + // Set the camera position + + vtkCamera *cam = this->Renderer ? this->Renderer->GetActiveCamera() : NULL; + if (cam) + { + switch (this->SliceOrientation) + { + case vtkImageColorViewer::SLICE_ORIENTATION_XY: + cam->SetFocalPoint(0,0,0); + cam->SetPosition(0,0,1); // -1 if medical ? + cam->SetViewUp(0,1,0); + break; + + case vtkImageColorViewer::SLICE_ORIENTATION_XZ: + cam->SetFocalPoint(0,0,0); + cam->SetPosition(0,-1,0); // 1 if medical ? + cam->SetViewUp(0,0,1); + break; + + case vtkImageColorViewer::SLICE_ORIENTATION_YZ: + cam->SetFocalPoint(0,0,0); + cam->SetPosition(1,0,0); // -1 if medical ? + cam->SetViewUp(0,0,1); + break; + } + } +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::UpdateDisplayExtent() +{ + vtkImageData *input = this->GetInput(); + if (!input || !this->ImageActor) + { + return; + } + + input->UpdateInformation(); + int *w_ext = input->GetWholeExtent(); + + // Is the slice in range ? If not, fix it + + int slice_min = w_ext[this->SliceOrientation * 2]; + int slice_max = w_ext[this->SliceOrientation * 2 + 1]; + if (this->Slice < slice_min || this->Slice > slice_max) + { + this->Slice = static_cast((slice_min + slice_max) * 0.5); + } + + // Set the image actor + + switch (this->SliceOrientation) + { + case vtkImageColorViewer::SLICE_ORIENTATION_XY: + this->ImageActor->SetDisplayExtent( + w_ext[0], w_ext[1], w_ext[2], w_ext[3], this->Slice, this->Slice); + break; + + case vtkImageColorViewer::SLICE_ORIENTATION_XZ: + this->ImageActor->SetDisplayExtent( + w_ext[0], w_ext[1], this->Slice, this->Slice, w_ext[4], w_ext[5]); + break; + + case vtkImageColorViewer::SLICE_ORIENTATION_YZ: + this->ImageActor->SetDisplayExtent( + this->Slice, this->Slice, w_ext[2], w_ext[3], w_ext[4], w_ext[5]); + break; + } + + // Figure out the correct clipping range + + if (this->Renderer) + { + if (this->InteractorStyle && + this->InteractorStyle->GetAutoAdjustCameraClippingRange()) + { + this->Renderer->ResetCameraClippingRange(); + } + else + { + vtkCamera *cam = this->Renderer->GetActiveCamera(); + if (cam) + { + double bounds[6]; + this->ImageActor->GetBounds(bounds); + double spos = (double)bounds[this->SliceOrientation * 2]; + double cpos = (double)cam->GetPosition()[this->SliceOrientation]; + double range = fabs(spos - cpos); + double *spacing = input->GetSpacing(); + double avg_spacing = + ((double)spacing[0] + (double)spacing[1] + (double)spacing[2]) / 3.0; + cam->SetClippingRange( + range - avg_spacing * 3.0, range + avg_spacing * 3.0); + } + } + } +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetPosition(int a,int b) +{ + this->RenderWindow->SetPosition(a, b); +} + +//---------------------------------------------------------------------------- +int* vtkImageColorViewer::GetPosition() +{ + return this->RenderWindow->GetPosition(); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetDisplayId(void *a) +{ + this->RenderWindow->SetDisplayId(a); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetWindowId(void *a) +{ + this->RenderWindow->SetWindowId(a); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetParentId(void *a) +{ + this->RenderWindow->SetParentId(a); +} + +//---------------------------------------------------------------------------- +double vtkImageColorViewer::GetColorWindow() +{ + return this->WindowLevel->GetWindow(); +} + +//---------------------------------------------------------------------------- +double vtkImageColorViewer::GetColorLevel() +{ + return this->WindowLevel->GetLevel(); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetColorWindow(double s) +{ + this->WindowLevel->SetWindow(s); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetColorLevel(double s) +{ + this->WindowLevel->SetLevel(s); +} + +//---------------------------------------------------------------------------- +class vtkImageColorViewerCallback : public vtkCommand +{ +public: + static vtkImageColorViewerCallback *New() { return new vtkImageColorViewerCallback; } + + void Execute(vtkObject *caller, + unsigned long event, + void *vtkNotUsed(callData)) + { + if (this->IV->GetInput() == NULL) + { + return; + } + + // Reset + + if (event == vtkCommand::ResetWindowLevelEvent) + { + this->IV->GetInput()->UpdateInformation(); + this->IV->GetInput()->SetUpdateExtent + (this->IV->GetInput()->GetWholeExtent()); + this->IV->GetInput()->Update(); + double *range = this->IV->GetInput()->GetScalarRange(); + this->IV->SetColorWindow(range[1] - range[0]); + this->IV->SetColorLevel(0.5 * (range[1] + range[0])); + this->IV->Render(); + return; + } + + // Start + + if (event == vtkCommand::StartWindowLevelEvent) + { + this->InitialWindow = this->IV->GetColorWindow(); + this->InitialLevel = this->IV->GetColorLevel(); + return; + } + + // Adjust the window level here + + vtkInteractorStyleImage *isi = + static_cast(caller); + + int *size = this->IV->GetRenderWindow()->GetSize(); + double window = this->InitialWindow; + double level = this->InitialLevel; + + // Compute normalized delta + + double dx = 4.0 * + (isi->GetWindowLevelCurrentPosition()[0] - + isi->GetWindowLevelStartPosition()[0]) / size[0]; + double dy = 4.0 * + (isi->GetWindowLevelStartPosition()[1] - + isi->GetWindowLevelCurrentPosition()[1]) / size[1]; + + // Scale by current values + + if (fabs(window) > 0.01) + { + dx = dx * window; + } + else + { + dx = dx * (window < 0 ? -0.01 : 0.01); + } + if (fabs(level) > 0.01) + { + dy = dy * level; + } + else + { + dy = dy * (level < 0 ? -0.01 : 0.01); + } + + // Abs so that direction does not flip + + if (window < 0.0) + { + dx = -1*dx; + } + if (level < 0.0) + { + dy = -1*dy; + } + + // Compute new window level + + double newWindow = dx + window; + double newLevel; + newLevel = level - dy; + + // Stay away from zero and really + + if (fabs(newWindow) < 0.01) + { + newWindow = 0.01*(newWindow < 0 ? -1 : 1); + } + if (fabs(newLevel) < 0.01) + { + newLevel = 0.01*(newLevel < 0 ? -1 : 1); + } + + this->IV->SetColorWindow(newWindow); + this->IV->SetColorLevel(newLevel); + this->IV->Render(); + } + + vtkImageColorViewer *IV; + double InitialWindow; + double InitialLevel; +}; + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::InstallPipeline() +{ + if (this->RenderWindow && this->Renderer) + { + this->RenderWindow->AddRenderer(this->Renderer); + } + + if (this->Interactor) + { + if (!this->InteractorStyle) + { + this->InteractorStyle = vtkInteractorStyleImage::New(); + vtkImageColorViewerCallback *cbk = vtkImageColorViewerCallback::New(); + cbk->IV = this; + this->InteractorStyle->AddObserver( + vtkCommand::WindowLevelEvent, cbk); + this->InteractorStyle->AddObserver( + vtkCommand::StartWindowLevelEvent, cbk); + this->InteractorStyle->AddObserver( + vtkCommand::ResetWindowLevelEvent, cbk); + cbk->Delete(); + } + + this->Interactor->SetInteractorStyle(this->InteractorStyle); + this->Interactor->SetRenderWindow(this->RenderWindow); + } + + if (this->Renderer && this->ImageActor) + { + this->Renderer->AddViewProp(this->ImageActor); + } + + if (this->ImageActor && this->WindowLevel) + { + this->ImageActor->SetInput(this->WindowLevel->GetOutput()); + } +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::UnInstallPipeline() +{ + if (this->ImageActor) + { + this->ImageActor->SetInput(NULL); + } + + if (this->Renderer && this->ImageActor) + { + this->Renderer->RemoveViewProp(this->ImageActor); + } + + if (this->RenderWindow && this->Renderer) + { + this->RenderWindow->RemoveRenderer(this->Renderer); + } + + if (this->Interactor) + { + this->Interactor->SetInteractorStyle(NULL); + this->Interactor->SetRenderWindow(NULL); + } +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::Render() +{ + if (this->FirstRender) + { + // Initialize the size if not set yet + + vtkImageData *input = this->GetInput(); + if (this->RenderWindow->GetSize()[0] == 0 && input) + { + input->UpdateInformation(); + int *w_ext = input->GetWholeExtent(); + int xs = 0, ys = 0; + + switch (this->SliceOrientation) + { + case vtkImageColorViewer::SLICE_ORIENTATION_XY: + default: + xs = w_ext[1] - w_ext[0] + 1; + ys = w_ext[3] - w_ext[2] + 1; + break; + + case vtkImageColorViewer::SLICE_ORIENTATION_XZ: + xs = w_ext[1] - w_ext[0] + 1; + ys = w_ext[5] - w_ext[4] + 1; + break; + + case vtkImageColorViewer::SLICE_ORIENTATION_YZ: + xs = w_ext[3] - w_ext[2] + 1; + ys = w_ext[5] - w_ext[4] + 1; + break; + } + + // if it would be smaller than 150 by 100 then limit to 150 by 100 + this->RenderWindow->SetSize( + xs < 150 ? 150 : xs, ys < 100 ? 100 : ys); + + if (this->Renderer) + { + this->Renderer->ResetCamera(); + this->Renderer->GetActiveCamera()->SetParallelScale( + xs < 150 ? 75 : (xs - 1 ) / 2.0); + } + this->FirstRender = 0; + } + } + if (this->GetInput()) + { + this->RenderWindow->Render(); + } +} + +//---------------------------------------------------------------------------- +const char* vtkImageColorViewer::GetWindowName() +{ + return this->RenderWindow->GetWindowName(); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetOffScreenRendering(int i) +{ + this->RenderWindow->SetOffScreenRendering(i); +} + +//---------------------------------------------------------------------------- +int vtkImageColorViewer::GetOffScreenRendering() +{ + return this->RenderWindow->GetOffScreenRendering(); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetInput(vtkImageData *in) +{ + this->WindowLevel->SetInput(in); + this->UpdateDisplayExtent(); +} +//---------------------------------------------------------------------------- +vtkImageData* vtkImageColorViewer::GetInput() +{ + return vtkImageData::SafeDownCast(this->WindowLevel->GetInput()); +} + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::SetInputConnection(vtkAlgorithmOutput* input) +{ + this->WindowLevel->SetInputConnection(input); + this->UpdateDisplayExtent(); +}; + +//---------------------------------------------------------------------------- +#ifndef VTK_LEGACY_REMOVE +int vtkImageColorViewer::GetWholeZMin() +{ + VTK_LEGACY_REPLACED_BODY(vtkImageColorViewer::GetWholeZMin, "VTK 5.0", + vtkImageColorViewer::GetSliceMin); + return this->GetSliceMin(); +} +int vtkImageColorViewer::GetWholeZMax() +{ + VTK_LEGACY_REPLACED_BODY(vtkImageColorViewer::GetWholeZMax, "VTK 5.0", + vtkImageColorViewer::GetSliceMax); + return this->GetSliceMax(); +} +int vtkImageColorViewer::GetZSlice() +{ + VTK_LEGACY_REPLACED_BODY(vtkImageColorViewer::GetZSlice, "VTK 5.0", + vtkImageColorViewer::GetSlice); + return this->GetSlice(); +} +void vtkImageColorViewer::SetZSlice(int s) +{ + VTK_LEGACY_REPLACED_BODY(vtkImageColorViewer::SetZSlice, "VTK 5.0", + vtkImageColorViewer::SetSlice); + this->SetSlice(s); +} +#endif + +//---------------------------------------------------------------------------- +void vtkImageColorViewer::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + + os << indent << "RenderWindow:\n"; + this->RenderWindow->PrintSelf(os,indent.GetNextIndent()); + os << indent << "Renderer:\n"; + this->Renderer->PrintSelf(os,indent.GetNextIndent()); + os << indent << "ImageActor:\n"; + this->ImageActor->PrintSelf(os,indent.GetNextIndent()); + os << indent << "WindowLevel:\n" << endl; + this->WindowLevel->PrintSelf(os,indent.GetNextIndent()); + os << indent << "Slice: " << this->Slice << endl; + os << indent << "SliceOrientation: " << this->SliceOrientation << endl; + os << indent << "InteractorStyle: " << endl; + if (this->InteractorStyle) + { + os << "\n"; + this->InteractorStyle->PrintSelf(os,indent.GetNextIndent()); + } + else + { + os << "None"; + } +} diff --git a/vtk/vtkImageColorViewer.h b/vtk/vtkImageColorViewer.h new file mode 100644 index 00000000..2da036dc --- /dev/null +++ b/vtk/vtkImageColorViewer.h @@ -0,0 +1,231 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: $RCSfile: vtkImageColorViewer.h,v $ + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkImageColorViewer - Display a 2D image. +// .SECTION Description +// vtkImageColorViewer is a convenience class for displaying a 2D image. It +// packages up the functionality found in vtkRenderWindow, vtkRenderer, +// vtkImageActor and vtkImageMapToWindowLevelColors into a single easy to use +// class. This class also creates an image interactor style +// (vtkInteractorStyleImage) that allows zooming and panning of images, and +// supports interactive window/level operations on the image. Note that +// vtkImageColorViewer is simply a wrapper around these classes. +// +// vtkImageColorViewer uses the 3D rendering and texture mapping engine +// to draw an image on a plane. This allows for rapid rendering, +// zooming, and panning. The image is placed in the 3D scene at a +// depth based on the z-coordinate of the particular image slice. Each +// call to SetSlice() changes the image data (slice) displayed AND +// changes the depth of the displayed slice in the 3D scene. This can +// be controlled by the AutoAdjustCameraClippingRange ivar of the +// InteractorStyle member. +// +// It is possible to mix images and geometry, using the methods: +// +// viewer->SetInput( myImage ); +// viewer->GetRenderer()->AddActor( myActor ); +// +// This can be used to annotate an image with a PolyData of "edges" or +// or highlight sections of an image or display a 3D isosurface +// with a slice from the volume, etc. Any portions of your geometry +// that are in front of the displayed slice will be visible; any +// portions of your geometry that are behind the displayed slice will +// be obscured. A more general framework (with respect to viewing +// direction) for achieving this effect is provided by the +// vtkImagePlaneWidget . +// +// Note that pressing 'r' will reset the window/level and pressing +// shift+'r' or control+'r' will reset the camera. +// +// .SECTION See Also +// vtkRenderWindow vtkRenderer vtkImageActor vtkImageMapToWindowLevelColors + +#ifndef __vtkImageColorViewer_h +#define __vtkImageColorViewer_h + +#include "vtkObject.h" + +class vtkAlgorithmOutput; +class vtkImageActor; +class vtkImageData; +class vtkImageMapToWindowLevelColors2; +class vtkInteractorStyleImage; +class vtkRenderWindow; +class vtkRenderer; +class vtkRenderWindowInteractor; + +class VTK_RENDERING_EXPORT vtkImageColorViewer : public vtkObject +{ +public: + static vtkImageColorViewer *New(); + vtkTypeRevisionMacro(vtkImageColorViewer,vtkObject); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Get the name of rendering window. + virtual const char *GetWindowName(); + + // Description: + // Render the resulting image. + virtual void Render(void); + + // Description: + // Set/Get the input image to the viewer. + virtual void SetInput(vtkImageData *in); + virtual vtkImageData *GetInput(); + virtual void SetInputConnection(vtkAlgorithmOutput* input); + + // Description: + // Set/get the slice orientation + //BTX + enum + { + SLICE_ORIENTATION_YZ = 0, + SLICE_ORIENTATION_XZ = 1, + SLICE_ORIENTATION_XY = 2 + }; + //ETX + vtkGetMacro(SliceOrientation, int); + virtual void SetSliceOrientation(int orientation); + virtual void SetSliceOrientationToXY() + { this->SetSliceOrientation(vtkImageColorViewer::SLICE_ORIENTATION_XY); }; + virtual void SetSliceOrientationToYZ() + { this->SetSliceOrientation(vtkImageColorViewer::SLICE_ORIENTATION_YZ); }; + virtual void SetSliceOrientationToXZ() + { this->SetSliceOrientation(vtkImageColorViewer::SLICE_ORIENTATION_XZ); }; + + // Description: + // Set/Get the current slice to display (depending on the orientation + // this can be in X, Y or Z). + vtkGetMacro(Slice, int); + virtual void SetSlice(int s); + + // Description: + // Update the display extent manually so that the proper slice for the + // given orientation is displayed. It will also try to set a + // reasonable camera clipping range. + // This method is called automatically when the Input is changed, but + // most of the time the input of this class is likely to remain the same, + // i.e. connected to the output of a filter, or an image reader. When the + // input of this filter or reader itself is changed, an error message might + // be displayed since the current display extent is probably outside + // the new whole extent. Calling this method will ensure that the display + // extent is reset properly. + virtual void UpdateDisplayExtent(); + + // Description: + // Return the minimum and maximum slice values (depending on the orientation + // this can be in X, Y or Z). + virtual int GetSliceMin(); + virtual int GetSliceMax(); + virtual void GetSliceRange(int range[2]) + { this->GetSliceRange(range[0], range[1]); } + virtual void GetSliceRange(int &min, int &max); + virtual int* GetSliceRange(); + + // Description: + // Set window and level for mapping pixels to colors. + virtual double GetColorWindow(); + virtual double GetColorLevel(); + virtual void SetColorWindow(double s); + virtual void SetColorLevel(double s); + + // Description: + // These are here when using a Tk window. + virtual void SetDisplayId(void *a); + virtual void SetWindowId(void *a); + virtual void SetParentId(void *a); + + // Description: + // Set/Get the position in screen coordinates of the rendering window. + virtual int* GetPosition(); + virtual void SetPosition(int a,int b); + virtual void SetPosition(int a[2]) { this->SetPosition(a[0],a[1]); } + + // Description: + // Set/Get the size of the window in screen coordinates in pixels. + virtual int* GetSize(); + virtual void SetSize(int a, int b); + virtual void SetSize(int a[2]) { this->SetSize(a[0],a[1]); } + + // Description: + // Get the internal render window, renderer, image actor, and + // image map instances. + vtkGetObjectMacro(RenderWindow,vtkRenderWindow); + vtkGetObjectMacro(Renderer, vtkRenderer); + vtkGetObjectMacro(ImageActor,vtkImageActor); + vtkGetObjectMacro(WindowLevel,vtkImageMapToWindowLevelColors2); + vtkGetObjectMacro(InteractorStyle,vtkInteractorStyleImage); + + // Description: + // Set your own renderwindow and renderer + virtual void SetRenderWindow(vtkRenderWindow *arg); + virtual void SetRenderer(vtkRenderer *arg); + + // Description: + // Attach an interactor for the internal render window. + virtual void SetupInteractor(vtkRenderWindowInteractor*); + + // Description: + // Create a window in memory instead of on the screen. This may not + // be supported for every type of window and on some windows you may + // need to invoke this prior to the first render. + virtual void SetOffScreenRendering(int); + virtual int GetOffScreenRendering(); + vtkBooleanMacro(OffScreenRendering,int); + + // Description: + // @deprecated Replaced by vtkImageColorViewer::GetSliceMin() as of VTK 5.0. + VTK_LEGACY(int GetWholeZMin()); + + // Description: + // @deprecated Replaced by vtkImageColorViewer::GetSliceMax() as of VTK 5.0. + VTK_LEGACY(int GetWholeZMax()); + + // Description: + // @deprecated Replaced by vtkImageColorViewer::GetSlice() as of VTK 5.0. + VTK_LEGACY(int GetZSlice()); + + // Description: + // @deprecated Replaced by vtkImageColorViewer::SetSlice() as of VTK 5.0. + VTK_LEGACY(void SetZSlice(int)); + +protected: + vtkImageColorViewer(); + ~vtkImageColorViewer(); + + virtual void InstallPipeline(); + virtual void UnInstallPipeline(); + + vtkImageMapToWindowLevelColors2 *WindowLevel; + vtkRenderWindow *RenderWindow; + vtkRenderer *Renderer; + vtkImageActor *ImageActor; + vtkRenderWindowInteractor *Interactor; + vtkInteractorStyleImage *InteractorStyle; + + int SliceOrientation; + int FirstRender; + int Slice; + + virtual void UpdateOrientation(); + +private: + vtkImageColorViewer(const vtkImageColorViewer&); // Not implemented. + void operator=(const vtkImageColorViewer&); // Not implemented. +}; + +#endif + + diff --git a/vtk/vtkImageMapToWindowLevelColors2.cxx b/vtk/vtkImageMapToWindowLevelColors2.cxx new file mode 100644 index 00000000..77607b0e --- /dev/null +++ b/vtk/vtkImageMapToWindowLevelColors2.cxx @@ -0,0 +1,465 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: $RCSfile: vtkImageMapToWindowLevelColors2.cxx,v $ + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkImageMapToWindowLevelColors2.h" + +#include "vtkDataArray.h" +#include "vtkImageData.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkObjectFactory.h" +#include "vtkScalarsToColors.h" +#include "vtkPointData.h" + +vtkCxxRevisionMacro(vtkImageMapToWindowLevelColors2, "$Revision: 1.1 $"); +vtkStandardNewMacro(vtkImageMapToWindowLevelColors2); + +// Constructor sets default values +vtkImageMapToWindowLevelColors2::vtkImageMapToWindowLevelColors2() +{ + this->Window = 255; + this->Level = 127.5; +} + +vtkImageMapToWindowLevelColors2::~vtkImageMapToWindowLevelColors2() +{ +} + +//---------------------------------------------------------------------------- +// This method checks to see if we can simply reference the input data +int vtkImageMapToWindowLevelColors2::RequestData( + vtkInformation *request, + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + vtkInformation *outInfo = outputVector->GetInformationObject(0); + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + + vtkImageData *outData = vtkImageData::SafeDownCast( + outInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkImageData *inData = vtkImageData::SafeDownCast( + inInfo->Get(vtkDataObject::DATA_OBJECT())); + + // If LookupTable is null and window / level produces no change, + // then just pass the data + if (this->LookupTable == NULL && + (inData->GetScalarType() == VTK_UNSIGNED_CHAR && + this->Window == 255 && this->Level == 127.5)) + { + vtkDebugMacro("ExecuteData: LookupTable not set, "\ + "Window / Level at default, "\ + "passing input to output."); + + outData->SetExtent(inData->GetExtent()); + outData->GetPointData()->PassData(inData->GetPointData()); + this->DataWasPassed = 1; + } + else + // normal behaviour - skip up a level since we don't want to + // call the superclasses ExecuteData - it would pass the data if there + // is no lookup table even if there is a window / level - wrong + // behavior. + { + if (this->DataWasPassed) + { + outData->GetPointData()->SetScalars(NULL); + this->DataWasPassed = 0; + } + + return this->vtkThreadedImageAlgorithm::RequestData(request, inputVector, + outputVector); + } + + return 1; +} + +//---------------------------------------------------------------------------- +int vtkImageMapToWindowLevelColors2::RequestInformation ( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **inputVector, + vtkInformationVector *outputVector) +{ + vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation *outInfo = outputVector->GetInformationObject(0); + + vtkInformation *inScalarInfo = + vtkDataObject::GetActiveFieldInformation(inInfo, + vtkDataObject::FIELD_ASSOCIATION_POINTS, vtkDataSetAttributes::SCALARS); + if (!inScalarInfo) + { + vtkErrorMacro("Missing scalar field on input information!"); + return 0; + } + + // If LookupTable is null and window / level produces no change, + // then the data will be passed + if ( this->LookupTable == NULL && + (inScalarInfo->Get(vtkDataObject::FIELD_ARRAY_TYPE()) == + VTK_UNSIGNED_CHAR && + this->Window == 255 && this->Level == 127.5) ) + { + if (inScalarInfo->Get(vtkDataObject::FIELD_ARRAY_TYPE()) != + VTK_UNSIGNED_CHAR) + { + vtkErrorMacro("ExecuteInformation: No LookupTable was set and input data is not VTK_UNSIGNED_CHAR!"); + } + else + { + // no lookup table, pass the input if it was UNSIGNED_CHAR + vtkDataObject::SetPointDataActiveScalarInfo + (outInfo, VTK_UNSIGNED_CHAR, + inScalarInfo->Get(vtkDataObject::FIELD_NUMBER_OF_COMPONENTS())); + } + } + else // the lookup table was set or window / level produces a change + { + int numComponents = 4; + switch (this->OutputFormat) + { + case VTK_RGBA: + numComponents = 4; + break; + case VTK_RGB: + numComponents = 3; + break; + case VTK_LUMINANCE_ALPHA: + numComponents = 2; + break; + case VTK_LUMINANCE: + numComponents = 1; + break; + default: + vtkErrorMacro("ExecuteInformation: Unrecognized color format."); + break; + } + vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_UNSIGNED_CHAR, numComponents); + } + + return 1; +} + +/* + * This templated routine calculates effective lower and upper limits + * for a window of values of type T, lower and upper. + */ +template +void vtkImageMapToWindowLevelClamps ( vtkImageData *data, double w, + double l, T& lower, T& upper, + unsigned char &lower_val, + unsigned char &upper_val) +{ + double f_lower, f_upper, f_lower_val, f_upper_val; + double adjustedLower, adjustedUpper; + double range[2]; + + data->GetPointData()->GetScalars()->GetDataTypeRange( range ); + + f_lower = l - fabs(w) / 2.0; + f_upper = f_lower + fabs(w); + + // Set the correct lower value + if ( f_lower <= range[1]) + { + if (f_lower >= range[0]) + { + lower = (T) f_lower; + adjustedLower = f_lower; + } + else + { + lower = (T) range[0]; + adjustedLower = range[0]; + } + } + else + { + lower = (T) range[1]; + adjustedLower = range[1]; + } + + // Set the correct upper value + if ( f_upper >= range[0]) + { + if (f_upper <= range[1]) + { + upper = (T) f_upper; + adjustedUpper = f_upper; + } + else + { + upper = (T) range[1]; + adjustedUpper = range[1]; + } + } + else + { + upper = (T) range [0]; + adjustedUpper = range [0]; + } + + // now compute the lower and upper values + if (w >= 0) + { + f_lower_val = 255.0*(adjustedLower - f_lower)/w; + f_upper_val = 255.0*(adjustedUpper - f_lower)/w; + } + else + { + f_lower_val = 255.0 + 255.0*(adjustedLower - f_lower)/w; + f_upper_val = 255.0 + 255.0*(adjustedUpper - f_lower)/w; + } + + if (f_upper_val > 255) + { + upper_val = 255; + } + else if (f_upper_val < 0) + { + upper_val = 0; + } + else + { + upper_val = (unsigned char)(f_upper_val); + } + + if (f_lower_val > 255) + { + lower_val = 255; + } + else if (f_lower_val < 0) + { + lower_val = 0; + } + else + { + lower_val = (unsigned char)(f_lower_val); + } +} + +//---------------------------------------------------------------------------- +// Small helper to do the clamp: +template +void vtkClampHelper1(T* iptr, unsigned char *optr, + T lower, T upper, + unsigned char lower_val, unsigned char upper_val, + double shift, double scale) +{ + unsigned short ushort_val; + if (*iptr <= lower) + { + ushort_val = lower_val; + } + else if (*iptr >= upper) + { + ushort_val = upper_val; + } + else + { + ushort_val = (unsigned char) ((*iptr + shift)*scale); + } + *optr = (unsigned char)((*optr * ushort_val) >> 8); +} + +//---------------------------------------------------------------------------- +template +void vtkClampHelper2(T* iptr, unsigned char *optr, + T lower, T upper, + unsigned char lower_val, unsigned char upper_val, + double shift, double scale) +{ + unsigned char result_val; + if (*iptr <= lower) + { + result_val = lower_val; + } + else if (*iptr >= upper) + { + result_val = upper_val; + } + else + { + result_val = (unsigned char) ((*iptr + shift)*scale); + } + *optr = result_val; +} + +//---------------------------------------------------------------------------- +// This non-templated function executes the filter for any type of data. +template +void vtkImageMapToWindowLevelColors2Execute( + vtkImageMapToWindowLevelColors2 *self, + vtkImageData *inData, T *inPtr, + vtkImageData *outData, + unsigned char *outPtr, + int outExt[6], int id) +{ + int idxX, idxY, idxZ; + int extX, extY, extZ; + vtkIdType inIncX, inIncY, inIncZ; + vtkIdType outIncX, outIncY, outIncZ; + unsigned long count = 0; + unsigned long target; + int dataType = inData->GetScalarType(); + int numberOfComponents,numberOfOutputComponents,outputFormat; + int rowLength; + vtkScalarsToColors *lookupTable = self->GetLookupTable(); + unsigned char *outPtr1; + T *inPtr1; + unsigned char *optr; + T *iptr; + double shift = self->GetWindow() / 2.0 - self->GetLevel(); + double scale = 255.0 / self->GetWindow(); + + T lower, upper; + unsigned char lower_val, upper_val; + vtkImageMapToWindowLevelClamps( inData, self->GetWindow(), + self->GetLevel(), + lower, upper, lower_val, upper_val ); + + // find the region to loop over + extX = outExt[1] - outExt[0] + 1; + extY = outExt[3] - outExt[2] + 1; + extZ = outExt[5] - outExt[4] + 1; + + target = (unsigned long)(extZ*extY/50.0); + target++; + + // Get increments to march through data + inData->GetContinuousIncrements(outExt, inIncX, inIncY, inIncZ); + + outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); + numberOfComponents = inData->GetNumberOfScalarComponents(); + numberOfOutputComponents = outData->GetNumberOfScalarComponents(); + outputFormat = self->GetOutputFormat(); + + rowLength = extX*numberOfComponents; + + // Loop through output pixels + outPtr1 = outPtr; + inPtr1 = inPtr; + for (idxZ = 0; idxZ < extZ; idxZ++) + { + for (idxY = 0; !self->AbortExecute && idxY < extY; idxY++) + { + if (!id) + { + if (!(count%target)) + { + self->UpdateProgress(count/(50.0*target)); + } + count++; + } + + iptr = inPtr1; + optr = outPtr1; + + if ( lookupTable ) + { + lookupTable->MapScalarsThroughTable2(inPtr1,(unsigned char *)outPtr1, + dataType,extX,numberOfComponents, + outputFormat); + + for (idxX = 0; idxX < extX; idxX++) + { + vtkClampHelper1(iptr,optr,lower,upper,lower_val,upper_val,shift,scale); + switch (outputFormat) + { + case VTK_RGBA: + vtkClampHelper1(iptr+1,optr+1,lower,upper,lower_val,upper_val,shift,scale); + vtkClampHelper1(iptr+2,optr+2,lower,upper,lower_val,upper_val,shift,scale); + *(optr+3) = 255; + break; + case VTK_RGB: + vtkClampHelper1(iptr+1,optr+1,lower,upper,lower_val,upper_val,shift,scale); + vtkClampHelper1(iptr+2,optr+2,lower,upper,lower_val,upper_val,shift,scale); + break; + case VTK_LUMINANCE_ALPHA: + *(optr+1) = 255; + break; + } + iptr += numberOfComponents; + optr += numberOfOutputComponents; + } + } + else + { + for (idxX = 0; idxX < extX; idxX++) + { + vtkClampHelper2(iptr,optr,lower,upper,lower_val,upper_val,shift,scale); + switch (outputFormat) + { + case VTK_RGBA: + vtkClampHelper2(iptr+1,optr+1,lower,upper,lower_val,upper_val,shift,scale); + vtkClampHelper2(iptr+2,optr+2,lower,upper,lower_val,upper_val,shift,scale); + *(optr+3) = 255; + break; + case VTK_RGB: + vtkClampHelper2(iptr+1,optr+1,lower,upper,lower_val,upper_val,shift,scale); + vtkClampHelper2(iptr+2,optr+2,lower,upper,lower_val,upper_val,shift,scale); + break; + case VTK_LUMINANCE_ALPHA: + *(optr+1) = 255; + break; + } + iptr += numberOfComponents; + optr += numberOfOutputComponents; + } + } + outPtr1 += outIncY + extX*numberOfOutputComponents; + inPtr1 += inIncY + rowLength; + } + outPtr1 += outIncZ; + inPtr1 += inIncZ; + } +} + +//---------------------------------------------------------------------------- +// This method is passed a input and output data, and executes the filter +// algorithm to fill the output from the input. + +void vtkImageMapToWindowLevelColors2::ThreadedRequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **vtkNotUsed(inputVector), + vtkInformationVector *vtkNotUsed(outputVector), + vtkImageData ***inData, + vtkImageData **outData, + int outExt[6], int id) +{ + void *inPtr = inData[0][0]->GetScalarPointerForExtent(outExt); + void *outPtr = outData[0]->GetScalarPointerForExtent(outExt); + + switch (inData[0][0]->GetScalarType()) + { + vtkTemplateMacro( + vtkImageMapToWindowLevelColors2Execute( this, + inData[0][0], + (VTK_TT *)(inPtr), + outData[0], + (unsigned char *)(outPtr), + outExt, + id)); + default: + vtkErrorMacro(<< "Execute: Unknown ScalarType"); + return; + } +} + +//---------------------------------------------------------------------------- +void vtkImageMapToWindowLevelColors2::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + os << indent << "Window: " << this->Window << endl; + os << indent << "Level: " << this->Level << endl; +} diff --git a/vtk/vtkImageMapToWindowLevelColors2.h b/vtk/vtkImageMapToWindowLevelColors2.h new file mode 100644 index 00000000..5500d7af --- /dev/null +++ b/vtk/vtkImageMapToWindowLevelColors2.h @@ -0,0 +1,84 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: $RCSfile: vtkImageMapToWindowLevelColors2.h,v $ + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +// .NAME vtkImageMapToWindowLevelColors2 - map the input image through a lookup table and window / level it +// .SECTION Description +// The vtkImageMapToWindowLevelColors2 filter will take an input image of any +// valid scalar type, and map the first component of the image through a +// lookup table. This resulting color will be modulated with value obtained +// by a window / level operation. The result is an image of type +// VTK_UNSIGNED_CHAR. If the lookup table is not set, or is set to NULL, then +// the input data will be passed through if it is already of type +// UNSIGNED_CHAR. +// +// .SECTION See Also +// vtkLookupTable vtkScalarsToColors + +#ifndef __vtkImageMapToWindowLevelColors2_h +#define __vtkImageMapToWindowLevelColors2_h + + +#include "vtkImageMapToColors.h" + +class VTK_IMAGING_EXPORT vtkImageMapToWindowLevelColors2 : public vtkImageMapToColors +{ +public: + static vtkImageMapToWindowLevelColors2 *New(); + vtkTypeRevisionMacro(vtkImageMapToWindowLevelColors2,vtkImageMapToColors); + void PrintSelf(ostream& os, vtkIndent indent); + + // Description: + // Set / Get the Window to use -> modulation will be performed on the + // color based on (S - (L - W/2))/W where S is the scalar value, L is + // the level and W is the window. + vtkSetMacro( Window, double ); + vtkGetMacro( Window, double ); + + // Description: + // Set / Get the Level to use -> modulation will be performed on the + // color based on (S - (L - W/2))/W where S is the scalar value, L is + // the level and W is the window. + vtkSetMacro( Level, double ); + vtkGetMacro( Level, double ); + +protected: + vtkImageMapToWindowLevelColors2(); + ~vtkImageMapToWindowLevelColors2(); + + virtual int RequestInformation (vtkInformation *, vtkInformationVector **, vtkInformationVector *); + void ThreadedRequestData(vtkInformation *request, + vtkInformationVector **inputVector, + vtkInformationVector *outputVector, + vtkImageData ***inData, vtkImageData **outData, + int extent[6], int id); + virtual int RequestData(vtkInformation *request, + vtkInformationVector **inputVector, + vtkInformationVector *outputVector); + + double Window; + double Level; + +private: + vtkImageMapToWindowLevelColors2(const vtkImageMapToWindowLevelColors2&); // Not implemented. + void operator=(const vtkImageMapToWindowLevelColors2&); // Not implemented. +}; + +#endif + + + + + + + -- 2.45.1