diff --git a/Core/Code/Rendering/vtkMitkLevelWindowFilter.cpp b/Core/Code/Rendering/vtkMitkLevelWindowFilter.cpp index 53533ccc8b..57ef77f6fb 100644 --- a/Core/Code/Rendering/vtkMitkLevelWindowFilter.cpp +++ b/Core/Code/Rendering/vtkMitkLevelWindowFilter.cpp @@ -1,600 +1,595 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "vtkMitkLevelWindowFilter.h" #include #include #include #include #include "vtkObjectFactory.h" +#include +#include + +#include //used for acos etc. #include //used for PI #include #include static const double PI = itk::Math::pi; vtkStandardNewMacro(vtkMitkLevelWindowFilter); vtkMitkLevelWindowFilter::vtkMitkLevelWindowFilter(): m_LookupTable(NULL), m_MinOpacity(0.0),m_MaxOpacity(255.0) { //MITK_INFO << "mitk level/window filter uses " << GetNumberOfThreads() << " thread(s)"; } vtkMitkLevelWindowFilter::~vtkMitkLevelWindowFilter() { } unsigned long int vtkMitkLevelWindowFilter::GetMTime() { unsigned long mTime=this->vtkObject::GetMTime(); unsigned long time; if ( this->m_LookupTable != NULL ) { time = this->m_LookupTable->GetMTime(); mTime = ( time > mTime ? time : mTime ); } return mTime; } void vtkMitkLevelWindowFilter::SetLookupTable(vtkScalarsToColors *lookupTable) { if (m_LookupTable != lookupTable) { m_LookupTable = lookupTable; this->Modified(); } } vtkScalarsToColors* vtkMitkLevelWindowFilter::GetLookupTable() { return m_LookupTable; } //This code was copied from the iil. The template works only for float and double. //Internal method which should never be used anywhere else and should not be in th header. // Convert color pixels from (R,G,B) to (H,S,I). // Reference: "Digital Image Processing, 2nd. edition", R. Gonzalez and R. Woods. Prentice Hall, 2002. template void RGBtoHSI(T* RGB, T* HSI) { T R = RGB[0], G = RGB[1], B = RGB[2], nR = (R<0?0:(R>255?255:R))/255, nG = (G<0?0:(G>255?255:G))/255, nB = (B<0?0:(B>255?255:B))/255, m = nR0) H = (nB<=nG)?theta:360-theta; if (sum>0) S = 1 - 3/sum*m; I = sum/3; HSI[0] = (T)H; HSI[1] = (T)S; HSI[2] = (T)I; } //This code was copied from the iil. The template works only for float and double. //Internal method which should never be used anywhere else and should not be in th header. // Convert color pixels from (H,S,I) to (R,G,B). template void HSItoRGB(T* HSI, T* RGB) { T H = (T)HSI[0], S = (T)HSI[1], I = (T)HSI[2], a = I*(1-S), R = 0, G = 0, B = 0; if (H<120) { B = a; R = (T)(I*(1+S*std::cos(H*PI/180)/std::cos((60-H)*PI/180))); G = 3*I-(R+B); } else if (H<240) { H-=120; R = a; G = (T)(I*(1+S*std::cos(H*PI/180)/std::cos((60-H)*PI/180))); B = 3*I-(R+G); } else { H-=240; G = a; B = (T)(I*(1+S*std::cos(H*PI/180)/std::cos((60-H)*PI/180))); R = 3*I-(G+B); } R*=255; G*=255; B*=255; RGB[0] = (T)(R<0?0:(R>255?255:R)); RGB[1] = (T)(G<0?0:(G>255?255:G)); RGB[2] = (T)(B<0?0:(B>255?255:B)); } //Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnRGBA(vtkMitkLevelWindowFilter* self, vtkImageData* inData, vtkImageData* outData, int outExt[6], double* clippingBounds, T*) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); vtkLookupTable* lookupTable; const int maxC = inData->GetNumberOfScalarComponents(); double tableRange[2]; lookupTable = dynamic_cast(self->GetLookupTable()); lookupTable->GetTableRange(tableRange); //parameters for RGB level window double scale = (tableRange[1] -tableRange[0] > 0 ? 255.0 / (tableRange[1] - tableRange[0]) : 0.0); double bias = tableRange[0] * scale; //parameters for opaque level window double scaleOpac = (self->GetMaxOpacity() -self->GetMinOpacity() > 0 ? 255.0 / (self->GetMaxOpacity() - self->GetMinOpacity()) : 0.0); double biasOpac = self->GetMinOpacity() * scaleOpac; int y = outExt[2]; // Loop through ouput pixels while (!outputIt.IsAtEnd()) { T* inputSI = inputIt.BeginSpan(); T* outputSI = outputIt.BeginSpan(); T* outputSIEnd = outputIt.EndSpan(); if( y >= clippingBounds[2] && y < clippingBounds[3] ) { int x = outExt[0]; while (outputSI != outputSIEnd) { if ( x >= clippingBounds[0] && x < clippingBounds[1]) { double rgb[3], alpha, hsi[3]; // level/window mechanism for intensity in HSI space rgb[0] = static_cast(*inputSI); inputSI++; rgb[1] = static_cast(*inputSI); inputSI++; rgb[2] = static_cast(*inputSI); inputSI++; RGBtoHSI(rgb,hsi); hsi[2] = hsi[2] * 255.0 * scale - bias; hsi[2] = (hsi[2] > 255.0 ? 255 : (hsi[2] < 0.0 ? 0 : hsi[2])); hsi[2] /= 255.0; HSItoRGB(hsi,rgb); *outputSI = static_cast(rgb[0]); outputSI++; *outputSI = static_cast(rgb[1]); outputSI++; *outputSI = static_cast(rgb[2]); outputSI++; unsigned char finalAlpha = 255; //RGBA case if(maxC >= 4) { // level/window mechanism for opacity alpha = static_cast(*inputSI); inputSI++; alpha = alpha * scaleOpac - biasOpac; if(alpha > 255.0) { alpha = 255.0; } else if(alpha < 0.0) { alpha = 0.0; } finalAlpha = static_cast(alpha); for( int c = 4; c < maxC; c++ ) inputSI++; } *outputSI = static_cast(finalAlpha); outputSI++; } else { inputSI+=maxC; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; } x++; } } else { while (outputSI != outputSIEnd) { *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; } } inputIt.NextSpan(); outputIt.NextSpan(); y++; } } //Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnScalarsFast(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); double tableRange[2]; // access vtkLookupTable vtkLookupTable* lookupTable = dynamic_cast(self->GetLookupTable()); lookupTable->GetTableRange(tableRange); // access elements of the vtkLookupTable int * realLookupTable = reinterpret_cast(lookupTable->GetTable()->GetPointer(0)); int maxIndex = lookupTable->GetNumberOfColors() - 1; float scale = (tableRange[1] -tableRange[0] > 0 ? (maxIndex + 1) / (tableRange[1] - tableRange[0]) : 0.0); // ensuring that starting point is zero float bias = - tableRange[0] * scale; // due to later conversion to int for rounding bias += 0.5f; // Loop through ouput pixels while (!outputIt.IsAtEnd()) { unsigned char* outputSI = outputIt.BeginSpan(); unsigned char* outputSIEnd = outputIt.EndSpan(); T* inputSI = inputIt.BeginSpan(); while (outputSI != outputSIEnd) { // map to an index int idx = static_cast( *inputSI * scale + bias ); if (idx < 0) idx = 0; else if (idx > maxIndex) idx = maxIndex; * reinterpret_cast(outputSI) = realLookupTable[idx]; inputSI++; outputSI+=4; } inputIt.NextSpan(); outputIt.NextSpan(); } } //Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnScalars(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double* clippingBounds, T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); vtkScalarsToColors* lookupTable = self->GetLookupTable(); int y = outExt[2]; // Loop through ouput pixels while (!outputIt.IsAtEnd()) { unsigned char* outputSI = outputIt.BeginSpan(); unsigned char* outputSIEnd = outputIt.EndSpan(); // do we iterate over the inner vertical clipping bounds if( y >= clippingBounds[2] && y < clippingBounds[3] ) { T* inputSI = inputIt.BeginSpan(); int x= outExt[0]; while (outputSI != outputSIEnd) { // is this pixel within horizontal clipping bounds if ( x >= clippingBounds[0] && x < clippingBounds[1]) { // fetching original value double grayValue = static_cast(*inputSI); // applying lookuptable - copy the 4 (RGBA) chars as a single int *reinterpret_cast(outputSI) = *reinterpret_cast(lookupTable->MapValue( grayValue )); } else { // outer horizontal clipping bounds - write a transparent RGBA pixel as a single int *reinterpret_cast(outputSI) = 0; } inputSI++; outputSI+=4; x++; } } else { // outer vertical clipping bounds - write a transparent RGBA line as ints while (outputSI != outputSIEnd) { *reinterpret_cast(outputSI) = 0; outputSI+=4; } } inputIt.NextSpan(); outputIt.NextSpan(); y++; } } //Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnScalarsCTF(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double* clippingBounds, T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); vtkColorTransferFunction* lookupTable = dynamic_cast(self->GetLookupTable()); int y = outExt[2]; // Loop through ouput pixels while (!outputIt.IsAtEnd()) { unsigned char* outputSI = outputIt.BeginSpan(); unsigned char* outputSIEnd = outputIt.EndSpan(); // do we iterate over the inner vertical clipping bounds if( y >= clippingBounds[2] && y < clippingBounds[3] ) { T* inputSI = inputIt.BeginSpan(); int x= outExt[0]; while (outputSI != outputSIEnd) { // is this pixel within horizontal clipping bounds if ( x >= clippingBounds[0] && x < clippingBounds[1]) { // fetching original value double grayValue = static_cast(*inputSI); // applying directly colortransferfunction // because vtkColorTransferFunction::MapValue is not threadsafe double rgb[3]; lookupTable->GetColor( grayValue, rgb ); outputSI[0] = static_cast(255.0*rgb[0] + 0.5); outputSI[1] = static_cast(255.0*rgb[1] + 0.5); outputSI[2] = static_cast(255.0*rgb[2] + 0.5); outputSI[3] = 255; } else { // outer horizontal clipping bounds - write a transparent RGBA pixel as a single int *reinterpret_cast(outputSI) = 0; } inputSI++; outputSI+=4; x++; } } else { // outer vertical clipping bounds - write a transparent RGBA line as ints while (outputSI != outputSIEnd) { *reinterpret_cast(outputSI) = 0; outputSI+=4; } } inputIt.NextSpan(); outputIt.NextSpan(); y++; } } -//void vtkMitkLevelWindowFilter::ExecuteInformation() -//{ -// vtkImageData *input = this->GetInput(); -// vtkImageData *output = this->GetOutput(); - -// if (!input) -// { -// vtkErrorMacro(<< "Input not set."); -// return; -// } -// output->CopyTypeSpecificInformation( input ); - -// // TODO make output RGBA -// output->SetScalarTypeToUnsignedChar(); -// output->SetNumberOfScalarComponents(4); - -// int extent[6]; -// input->GetWholeExtent(extent); -// output->SetExtent(extent); -// output->SetWholeExtent(extent); -// output->SetUpdateExtent(extent); -// output->AllocateScalars(); -//} +int vtkMitkLevelWindowFilter::RequestInformation(vtkInformation* request, + vtkInformationVector** inputVector, + vtkInformationVector* outputVector) +{ + vtkInformation* outInfo = outputVector->GetInformationObject(0); + + // do nothing except copy scalar type info + this->CopyInputArrayAttributesToOutput(request,inputVector,outputVector); + + vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_UNSIGNED_CHAR, 4); + + return 1; + +} //Method to run the filter in different threads. void vtkMitkLevelWindowFilter::ThreadedExecute(vtkImageData *inData, vtkImageData *outData, int extent[6], int /*id*/) { if(inData->GetNumberOfScalarComponents() > 2) { switch (inData->GetScalarType()) { vtkTemplateMacro( vtkApplyLookupTableOnRGBA( this, inData, outData, extent, m_ClippingBounds, static_cast(0))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } else { bool dontClip = extent[2] >= m_ClippingBounds[2] && extent[3] <= m_ClippingBounds[3] && extent[0] >= m_ClippingBounds[0] && extent[1] <= m_ClippingBounds[1]; if(this->GetLookupTable()) this->GetLookupTable()->Build(); vtkLookupTable *vlt = dynamic_cast(this->GetLookupTable()); vtkColorTransferFunction *ctf = dynamic_cast(this->GetLookupTable()); bool linearLookupTable = vlt && vlt->GetScale() == VTK_SCALE_LINEAR; bool useFast = dontClip && linearLookupTable; if(ctf) { switch (inData->GetScalarType()) { vtkTemplateMacro( vtkApplyLookupTableOnScalarsCTF( this, inData, outData, extent, m_ClippingBounds, static_cast(0))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } else if(useFast) { switch (inData->GetScalarType()) { vtkTemplateMacro( vtkApplyLookupTableOnScalarsFast( this, inData, outData, extent, static_cast(0))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } else { switch (inData->GetScalarType()) { vtkTemplateMacro( vtkApplyLookupTableOnScalars( this, inData, outData, extent, m_ClippingBounds, static_cast(0))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } } } //void vtkMitkLevelWindowFilter::ExecuteInformation( // vtkImageData *vtkNotUsed(inData), vtkImageData *vtkNotUsed(outData)) //{ //} void vtkMitkLevelWindowFilter::SetMinOpacity(double minOpacity) { m_MinOpacity = minOpacity; } inline double vtkMitkLevelWindowFilter::GetMinOpacity() const { return m_MinOpacity; } void vtkMitkLevelWindowFilter::SetMaxOpacity(double maxOpacity) { m_MaxOpacity = maxOpacity; } inline double vtkMitkLevelWindowFilter::GetMaxOpacity() const { return m_MaxOpacity; } void vtkMitkLevelWindowFilter::SetClippingBounds(double* bounds) // TODO does double[4] work?? { for (unsigned int i = 0 ; i < 4; ++i) m_ClippingBounds[i] = bounds[i]; } diff --git a/Core/Code/Rendering/vtkMitkLevelWindowFilter.h b/Core/Code/Rendering/vtkMitkLevelWindowFilter.h index 01dd11643c..9b610d7069 100644 --- a/Core/Code/Rendering/vtkMitkLevelWindowFilter.h +++ b/Core/Code/Rendering/vtkMitkLevelWindowFilter.h @@ -1,94 +1,94 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __vtkMitkLevelWindowFilter_h #define __vtkMitkLevelWindowFilter_h class vtkScalarsToColors; #include #include #include /** Documentation * \brief Applies the grayvalue or color/opacity level window to scalar or RGB(A) images. * * This filter is used to apply the color level window to RGB images (e.g. * diffusion tensor images). Therefore, the RGB channels are converted to * the HSI color space, where the level window can be applied. Afterwards, * the HSI values transformed back to the RGB space. * * The filter is also able to apply an opacity level window to RGBA images. * * \ingroup Renderer */ class MITK_CORE_EXPORT vtkMitkLevelWindowFilter : public vtkThreadedImageAlgorithm { public: vtkTypeMacro(vtkMitkLevelWindowFilter,vtkThreadedImageAlgorithm); static vtkMitkLevelWindowFilter *New(); virtual unsigned long int GetMTime(); /** \brief Get the lookup table for the RGB level window */ vtkScalarsToColors* GetLookupTable(); /** \brief Set the lookup table for the RGB level window */ void SetLookupTable(vtkScalarsToColors *lookupTable); /** \brief Get/Set the lower window opacity for the alpha level window */ void SetMinOpacity(double minOpacity); inline double GetMinOpacity() const; /** \brief Get/Set the upper window opacity for the alpha level window */ void SetMaxOpacity(double maxOpacity); inline double GetMaxOpacity() const; /** \brief Set clipping bounds for the opaque part of the resliced 2d image */ void SetClippingBounds(double*); protected: /** Default constructor. */ vtkMitkLevelWindowFilter(); /** Default deconstructor. */ ~vtkMitkLevelWindowFilter(); /** \brief Method for threaded execution of the filter. * \param *inData: The input. * \param *outData: The output of the filter. * \param extent: Specifies the region of the image to be updated inside this thread. * It is a six-component array of the form (xmin, xmax, ymin, ymax, zmin, zmax). * \param id: The thread id. */ void ThreadedExecute(vtkImageData *inData, vtkImageData *outData,int extent[6], int id); // /** Standard VTK filter method to apply the filter. See VTK documentation.*/ -// void ExecuteInformation(); + int RequestInformation(vtkInformation* request,vtkInformationVector** inputVector, vtkInformationVector* outputVector); // /** Standard VTK filter method to apply the filter. See VTK documentation. Not used at the moment.*/ // void ExecuteInformation(vtkImageData *vtkNotUsed(inData), vtkImageData *vtkNotUsed(outData)); private: /** m_LookupTable contains the lookup table for the RGB level window.*/ vtkScalarsToColors* m_LookupTable; /** m_MinOpacity contains the lower bound for the alpha level window.*/ double m_MinOpacity; /** m_MaxOpacity contains the upper bound for the alpha level window.*/ double m_MaxOpacity; double m_ClippingBounds[4]; }; #endif