diff --git a/Core/Code/Rendering/mitkImageVtkMapper2D.cpp b/Core/Code/Rendering/mitkImageVtkMapper2D.cpp index e8fe387139..c3368f6d9d 100644 --- a/Core/Code/Rendering/mitkImageVtkMapper2D.cpp +++ b/Core/Code/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1173 +1,1173 @@ /*=================================================================== 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. ===================================================================*/ //MITK #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" //MITK Rendering #include "mitkImageVtkMapper2D.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkNeverTranslucentTexture.h" #include "vtkMitkShaderTexture.h" #include "vtkImageToImageFilter.h" #include //VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //ITK #include #include mitk::ImageVtkMapper2D::ImageVtkMapper2D() { } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { //The 3D RW Mapper (Geometry2DDataVtkMapper3D) is listening to this event, //in order to delete the images from the 3D RW. this->InvokeEvent( itk::DeleteEvent() ); } //set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer* renderer, vtkFloatingPointType planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); //Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct //plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); //These two points define the axes of the plane in combination with the origin. //Point 1 is the x-axis and point 2 the y-axis. //Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1] , planeBounds[2], depth); //P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); //P2: (xMin, yMax, depth) } float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer* renderer) { //get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; //Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange*0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty( "layer", layer, renderer); //add the layer property for each image to render images with a higher layer on top of the others depth += layer*10; //*10: keep some room for each image (e.g. for QBalls in between) if(depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image* mitk::ImageVtkMapper2D::GetInput( void ) { return static_cast< const mitk::Image * >( GetDataNode()->GetData() ); } vtkProp* mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer* renderer) { //return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::ImageVtkMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::Image *input = const_cast< mitk::Image * >( this->GetInput() ); mitk::DataNode* datanode = this->GetDataNode(); if ( input == NULL || input->IsInitialized() == false ) { return; } //check if there is a valid worldGeometry const Geometry2D *worldGeometry = renderer->GetCurrentWorldGeometry2D(); if( ( worldGeometry == NULL ) || ( !worldGeometry->IsValid() ) || ( !worldGeometry->HasReferenceGeometry() )) { return; } input->Update(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if ( !RenderingGeometryIntersectsImage( worldGeometry, input->GetSlicedGeometry() ) ) { // set image to NULL, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = NULL; localStorage->m_Mapper->SetInput( localStorage->m_EmptyPolyData ); return; } //set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(input); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep( this->GetTimestep() ); //set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( input->GetTimeSlicedGeometry()->GetGeometry3D( this->GetTimestep() ) ); //is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ( (input->GetDimension() >= 3) && (input->GetDimension(2) > 1) ) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty( resliceInterpolationProperty, "reslice interpolation" ); int interpolationMode = VTK_RESLICE_NEAREST; if ( resliceInterpolationProperty != NULL ) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch ( interpolationMode ) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } //set the vtk output property to true, makes sure that no unneeded mitk image convertion //is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); //Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if( input->GetPixelType().GetNumberOfComponents() == 1 ) // for now only single component are allowed { DataNode *dn=renderer->GetCurrentWorldGeometry2DNode(); if(dn) { ResliceMethodProperty *resliceMethodEnumProperty=0; if( dn->GetProperty( resliceMethodEnumProperty, "reslice.thickslices" ) && resliceMethodEnumProperty ) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty=0; if( dn->GetProperty( intProperty, "reslice.thickslices.num" ) && intProperty ) { thickSlicesNum = intProperty->GetValue(); if(thickSlicesNum < 1) thickSlicesNum=1; if(thickSlicesNum > 10) thickSlicesNum=10; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const PlaneGeometry *planeGeometry = dynamic_cast< const PlaneGeometry * >( worldGeometry ); if(thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; if ( planeGeometry != NULL ){ normal = planeGeometry->GetNormal(); }else{ const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(worldGeometry); if(abstractGeometry != NULL) normal = abstractGeometry->GetPlane()->GetNormal(); else return; //no fitting geometry set } normal.Normalize(); input->GetTimeSlicedGeometry()->GetGeometry3D( this->GetTimestep() )->WorldToIndex( normal, normInIndex ); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality( 3 ); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection( -thickSlicesNum, 0+thickSlicesNum ); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode( thickSlicesMode-1 ); localStorage->m_TSFilter->SetInput( localStorage->m_Reslicer->GetVtkOutput() ); //vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { //this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality( 2 ); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection( 0, 0 ); localStorage->m_Reslicer->Modified(); //start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) //this used for generating a vtkPLaneSource with the right size vtkFloatingPointType sliceBounds[6]; for ( int i = 0; i < 6; ++i ) { sliceBounds[i] = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); //get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { vtkFloatingPointType textureClippingBounds[6]; for ( int i = 0; i < 6; ++i ) { textureClippingBounds[i] = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds( input->GetGeometry(), planeGeometry, textureClippingBounds ); textureClippingBounds[0] = static_cast< int >( textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5 ); textureClippingBounds[1] = static_cast< int >( textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5 ); textureClippingBounds[2] = static_cast< int >( textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5 ); textureClippingBounds[3] = static_cast< int >( textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5 ); //clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } //get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); //get the binary property bool binary = false; bool binaryOutline = false; datanode->GetBoolProperty( "binary", binary, renderer ); if(binary) //binary image { datanode->GetBoolProperty( "outline binary", binaryOutline, renderer ); if(binaryOutline) //contour rendering { if ( input->GetPixelType().GetBpe() <= 8 ) { //generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); float binaryOutlineWidth(1.0); if ( datanode->GetFloatProperty( "outline width", binaryOutlineWidth, renderer ) ) { if ( localStorage->m_Actors->GetNumberOfPaths() > 1 ) { float binaryOutlineShadowWidth(1.5); datanode->GetFloatProperty( "outline shadow width", binaryOutlineShadowWidth, renderer ); dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty()->SetLineWidth( binaryOutlineWidth * binaryOutlineShadowWidth ); } localStorage->m_Actor->GetProperty()->SetLineWidth( binaryOutlineWidth ); } } else { binaryOutline = false; this->ApplyLookuptable(renderer); MITK_WARN << "Type of all binary images should be (un)signed char. Outline does not work on other pixel types!"; } } else //standard binary image { if(numberOfComponents != 1) { MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!"; } } } this->ApplyOpacity( renderer ); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->MapColorScalarsThroughLookupTableOff(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInput(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { //connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInput(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty( "texture interpolation", textureInterpolation, renderer ); //set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); - bool doseProperty; - if(datanode->GetBoolProperty("dose",doseProperty) && doseProperty) - { - // if the input is a dose file dont use the levelwindow filter because we need a float texture - // and the levelwindow filter is casting to unsigned char - localStorage->m_Texture->SetInput(localStorage->m_ReslicedImage); - } - else - { - // connect the texture with the output of the levelwindow filter - localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); - } +// bool doseProperty; +// if(datanode->GetBoolProperty("dose",doseProperty) && doseProperty) +// { +// // if the input is a dose file dont use the levelwindow filter because we need a float texture +// // and the levelwindow filter is casting to unsigned char +// localStorage->m_Texture->SetInput(localStorage->m_ReslicedImage); +// } +// else +// { + // connect the texture with the output of the levelwindow filter + localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); +// } this->TransformActor( renderer ); vtkActor* contourShadowActor = dynamic_cast (localStorage->m_Actors->GetParts()->GetItemAsObject(0)); if(binary && binaryOutline) //connect the mapper with the polyData which contains the lines { //We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInput(localStorage->m_OutlinePolyData); localStorage->m_Actor->SetTexture(NULL); //no texture for contours bool binaryOutlineShadow( false ); datanode->GetBoolProperty( "outline binary shadow", binaryOutlineShadow, renderer ); if ( binaryOutlineShadow ) contourShadowActor->SetVisibility( true ); else contourShadowActor->SetVisibility( false ); } else { //Connect the mapper with the input texture. This is the standard case. //setup the textured plane this->GeneratePlane( renderer, sliceBounds ); //set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); //set the texture for the actor localStorage->m_Actor->SetTexture(localStorage->m_Texture); contourShadowActor->SetVisibility( false ); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage( renderer ); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow( levelWindow, renderer, "levelwindow" ); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange( levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound() ); mitk::LevelWindow opacLevelWindow; if( this->GetDataNode()->GetLevelWindow( opacLevelWindow, renderer, "opaclevelwindow" ) ) { //pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { //no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::ImageVtkMapper2D::ApplyColor( mitk::BaseRenderer* renderer ) { LocalStorage *localStorage = this->GetLocalStorage( renderer ); float rgb[3]= { 1.0f, 1.0f, 1.0f }; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); if(hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.hoveringcolor", renderer)); if(colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); } else { GetDataNode()->GetColor( rgb, renderer, "color" ); } } if(selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("binaryimage.selectedcolor", renderer)); if(colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if(!hover && !selected) { GetDataNode()->GetColor( rgb, renderer, "color" ); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; //conversion to double for VTK dynamic_cast (localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); localStorage->m_Actor->GetProperty()->SetColor(rgbConv); if ( localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1 ) { float rgb[3]= { 1.0f, 1.0f, 1.0f }; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty ("outline binary shadow color", renderer)); if(colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; //conversion to double for VTK dynamic_cast( localStorage->m_Actors->GetParts()->GetItemAsObject(0) )->GetProperty()->SetColor(rgbConv); } } void mitk::ImageVtkMapper2D::ApplyShader( mitk::BaseRenderer* renderer) { LocalStorage *localStorage = this->GetLocalStorage( renderer ); localStorage->m_Texture = vtkSmartPointer::New().GetPointer(); CoreServicePointer shaderRepo(CoreServices::GetShaderRepository()); itk::TimeStamp timestamp; shaderRepo->ApplyProperties(this->GetDataNode(),localStorage->m_Actor,renderer,timestamp); } void mitk::ImageVtkMapper2D::ApplyOpacity( mitk::BaseRenderer* renderer ) { LocalStorage* localStorage = this->GetLocalStorage( renderer ); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity( opacity, renderer, "opacity" ); //set the opacity according to the properties localStorage->m_Actor->GetProperty()->SetOpacity(opacity); if ( localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1 ) { dynamic_cast( localStorage->m_Actors->GetParts()->GetItemAsObject(0) )->GetProperty()->SetOpacity(opacity); } } void mitk::ImageVtkMapper2D::ApplyRenderingMode( mitk::BaseRenderer* renderer ) { LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty( "binary", binary, renderer ); if(binary) // is it a binary image? { //for binary images, we always use our default LuT and map every value to (0,1) //the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { //all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty( "Image Rendering.Mode", renderer )); if(mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch(renderingMode) { case mitk::RenderingModeProperty::LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_Color"; localStorage->m_LevelWindowFilter->SetLookupTable( localStorage->m_DefaultLookupTable ); this->ApplyLevelWindow( renderer ); break; case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable( renderer ); this->ApplyLevelWindow( renderer ); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction( renderer ); this->ApplyLevelWindow( renderer ); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable( renderer ); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction( renderer ); break; case mitk::RenderingModeProperty::ISODOSESHADER_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = IsoDoseShader_Color"; localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_DefaultLookupTable); this->ApplyLevelWindow( renderer ); - this->ApplyShader( renderer ); +// this->ApplyShader( renderer ); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set"; break; } } //we apply color for all images (including binaries). this->ApplyColor( renderer ); } void mitk::ImageVtkMapper2D::ApplyLookuptable( mitk::BaseRenderer* renderer ) { LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable* usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable")); if( lookupTableProp.IsNotNull() ) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { MITK_WARN << "Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. A default (rainbow) lookup table will be used."; } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Transfer Function",renderer )); if( transferFunctionProp.IsNull() ) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); //pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer* renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) { return; } mitk::Image* data = const_cast( this->GetInput() ); if ( data == NULL ) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep( renderer ); // Check if time step is valid const TimeSlicedGeometry *dataTimeGeometry = data->GetTimeSlicedGeometry(); if ( ( dataTimeGeometry == NULL ) || ( dataTimeGeometry->GetTimeSteps() == 0 ) || ( !dataTimeGeometry->IsValidTime( this->GetTimestep() ) ) ) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //check if something important has changed and we need to rerender if ( (localStorage->m_LastUpdateTime < node->GetMTime()) //was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) //Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldGeometry2DUpdateTime()) //was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldGeometry2D()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ) { this->GenerateDataForRenderer( renderer ); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty( "depthOffset", mitk::FloatProperty::New( 0.0 ), renderer, overwrite ); node->AddProperty( "outline binary", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "outline width", mitk::FloatProperty::New( 1.0 ), renderer, overwrite ); node->AddProperty( "outline binary shadow", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "outline binary shadow color", ColorProperty::New(0.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "outline shadow width", mitk::FloatProperty::New( 1.5 ), renderer, overwrite ); if(image->IsRotated()) node->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC) ); else node->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); node->AddProperty( "texture interpolation", mitk::BoolProperty::New( mitk::DataNodeFactory::m_TextureInterpolationActive ) ); // set to user configurable default value (see global options) node->AddProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); node->AddProperty( "bounding box", mitk::BoolProperty::New( false ) ); CoreServicePointer shaderRepo(CoreServices::GetShaderRepository()); shaderRepo->AddDefaultProperties(node,renderer,overwrite); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty( "Image Rendering.Mode", renderingModeProperty); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if ( node->GetStringProperty( "dicom.pixel.PhotometricInterpretation", photometricInterpretation ) ) { // modality provided by DICOM or other reader if ( photometricInterpretation.find("MONOCHROME1") != std::string::npos ) // meaning: display MINIMUM pixels as WHITE { // generate LUT (white to black) mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); vtkLookupTable* bwLut = mitkLut->GetVtkLookupTable(); bwLut->SetTableRange (0, 1); bwLut->SetSaturationRange (0, 0); bwLut->SetHueRange (0, 0); bwLut->SetValueRange (1, 0); bwLut->SetAlphaRange (1, 1); bwLut->SetRampToLinear(); bwLut->Build(); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty( "LookupTable", mitkLutProp ); } else if ( photometricInterpretation.find("MONOCHROME2") != std::string::npos ) // meaning: display MINIMUM pixels as BLACK { // apply default LUT (black to white) node->SetProperty( "color", mitk::ColorProperty::New( 1,1,1 ), renderer ); } // PALETTE interpretation should be handled ok by RGB loading } bool isBinaryImage(false); if ( ! node->GetBoolProperty("binary", isBinaryImage) ) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2)/2); sliceSelector->SetTimeNr(image->GetDimension(3)/2); sliceSelector->SetChannelNr(image->GetDimension(4)/2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if ( centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized() ) { minValue = centralSliceImage->GetStatistics()->GetScalarValueMin(); maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax(); min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax(); } if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue) { // centralSlice is strange, lets look at all data minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = ( maxValue == min2ndValue && minValue == max2ndValue ); } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty( "opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite ); node->AddProperty( "color", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.selectedcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.selectedannotationcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.hoveringcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binaryimage.hoveringannotationcolor", ColorProperty::New(1.0,0.0,0.0), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( true ), renderer, overwrite ); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty( "opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite ); node->AddProperty( "color", ColorProperty::New(1.0,1.0,1.0), renderer, overwrite ); node->AddProperty( "binary", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "QBallImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelTypeAsString() == "vector" && numComponents > 1) || numComponents == 2 || numComponents > 4) node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } if(image.IsNotNull() && image->IsInitialized()) { if((overwrite) || (node->GetProperty("levelwindow", renderer)==NULL)) { /* initialize level/window from DICOM tags */ std::string sLevel; std::string sWindow; if ( image->GetPropertyList()->GetStringProperty( "dicom.voilut.WindowCenter", sLevel ) && image->GetPropertyList()->GetStringProperty( "dicom.voilut.WindowWidth", sWindow ) ) { float level = atof( sLevel.c_str() ); float window = atof( sWindow.c_str() ); mitk::LevelWindow contrast; std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if ( image->GetPropertyList()->GetStringProperty( "dicom.series.SmallestPixelValueInSeries", sSmallestPixelValueInSeries ) && image->GetPropertyList()->GetStringProperty( "dicom.series.LargestPixelValueInSeries", sLargestPixelValueInSeries ) ) { float smallestPixelValueInSeries = atof( sSmallestPixelValueInSeries.c_str() ); float largestPixelValueInSeries = atof( sLargestPixelValueInSeries.c_str() ); contrast.SetRangeMinMax( smallestPixelValueInSeries-1, largestPixelValueInSeries+1 ); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto( static_cast(node->GetData()), false, true ); // we need this as a fallback } contrast.SetLevelWindow( level, window, true ); node->SetProperty( "levelwindow", LevelWindowProperty::New( contrast ), renderer ); } } if(((overwrite) || (node->GetProperty("opaclevelwindow", renderer)==NULL)) && (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR) ) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0,255); opaclevwin.SetWindowBounds(0,255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty( "opaclevelwindow", prop, renderer ); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::ImageVtkMapper2D::LocalStorage* mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer* renderer) { return m_LSH.GetLocalStorage(renderer); } vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer* renderer ){ // LocalStorage* localStorage = this->GetLocalStorage(renderer); // vtkMarchingSquares* squares = vtkMarchingSquares::New(); // vtkSmartPointer polyData = vtkSmartPointer::New(); // float depth = CalculateLayerDepth(renderer); // squares->SetInput(localStorage->m_ReslicedImage); // squares->SetNumberOfContours(1); // squares->SetValue(0,1); // squares->Update(); // polyData = squares->GetOutput(); // vtkIdType numberOfPoints = polyData->GetNumberOfPoints(); // fstream f; // f.open("mapper_neu.txt", ios::out); // for(int i=0;iGetPoint(i); // x[2] = depth; // f << "Point" << i << ": " << x[0] << ", " << x[1] << ", " << x[2] << endl; // } // polyData->BuildCells(); // polyData->BuildLinks(); // f.close(); LocalStorage* localStorage = this->GetLocalStorage(renderer); //get the min and max index values of each direction int* extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int* dims = localStorage->m_ReslicedImage->GetDimensions(); //dimensions of the image int line = dims[0]; //how many pixels per line? int x = xMin; //pixel index x int y = yMin; //pixel index y char* currentPixel; //get the depth for each contour float depth = CalculateLayerDepth(renderer); vtkSmartPointer points = vtkSmartPointer::New(); //the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); //the lines to connect the points // We take the pointer to the first pixel of the image currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer() ); while (y <= yMax) { //if the current pixel value is set to something if ((currentPixel) && (*currentPixel != 0)) { //check in which direction a line is necessary //a line is added if the neighbor of the current pixel has the value 0 //and if the pixel is located at the edge of the image //if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel-line) == 0) { //x direction - bottom edge of the pixel //add the 2 points vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); //add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel+line) == 0) { //x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv not the first pixel vvvvv if ( (x > xMin || y > yMin) && *(currentPixel-1) == 0) { //y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv not the last pixel vvvvv if ( (y < yMax || (x < xMax) ) && *(currentPixel+1) == 0) { //y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } /* now consider pixels at the edge of the image */ //if vvvvv left edge of image vvvvv if (x == xMin) { //draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv right edge of image vvvvv if (x == xMax) { //draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv bottom edge of image vvvvv if (y == yMin) { //draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], y*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } //if vvvvv top edge of image vvvvv if (y == yMax) { //draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x+1)*localStorage->m_mmPerPixel[0], (y+1)*localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } }//end if currentpixel is set x++; if (x > xMax) { //reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; }//end of while // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); fstream f; f.open("mapper_alt.txt", ios::out); for(int i=0;iGetNumberOfPoints();i++) { double x[3]; polyData->GetPoint(i,x); f << "Point" << i << ": " << x[0] << ", " << x[1] << ", " << x[2] << endl; } f.close(); return polyData; } void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer* renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); //get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); //transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_Actor->SetUserTransform(trans); //transform the origin to center based coordinates, because MITK is center based. localStorage->m_Actor->SetPosition( -0.5*localStorage->m_mmPerPixel[0], -0.5*localStorage->m_mmPerPixel[1], 0.0); if ( localStorage->m_Actors->GetNumberOfPaths() > 1 ) { vtkActor* secondaryActor = dynamic_cast( localStorage->m_Actors->GetParts()->GetItemAsObject(0) ); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition( -0.5*localStorage->m_mmPerPixel[0], -0.5*localStorage->m_mmPerPixel[1], 0.0); } } bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage( const Geometry2D* renderingGeometry, SlicedGeometry3D* imageGeometry ) { // if either one of the two geometries is NULL we return true // for safety reasons if ( renderingGeometry == NULL || imageGeometry == NULL ) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance( imageGeometry->GetCornerPoint( 0 ) ); for( int i=1; i<8; i++ ) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint( i ); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance( cornerPoint ); // if it has not the same signing as the distance of the first point if ( initialDistance * distance < 0 ) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); //Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); //the following actions are always the same and thus can be performed //in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); //built a default lookuptable m_DefaultLookupTable->SetRampToLinear(); m_DefaultLookupTable->SetSaturationRange( 0.0, 0.0 ); m_DefaultLookupTable->SetHueRange( 0.0, 0.0 ); m_DefaultLookupTable->SetValueRange( 0.0, 1.0 ); m_DefaultLookupTable->Build(); m_BinaryLookupTable->SetRampToLinear(); m_BinaryLookupTable->SetSaturationRange( 0.0, 0.0 ); m_BinaryLookupTable->SetHueRange( 0.0, 0.0 ); m_BinaryLookupTable->SetValueRange( 0.0, 1.0 ); m_BinaryLookupTable->SetRange(0.0, 1.0); m_BinaryLookupTable->Build(); // add a default rainbow lookup table for color mapping m_ColorLookupTable->SetRampToLinear(); m_ColorLookupTable->SetHueRange(0.6667, 0.0); m_ColorLookupTable->SetTableRange(0.0, 20.0); m_ColorLookupTable->Build(); // make first value transparent { double rgba[4]; m_BinaryLookupTable->GetTableValue(0, rgba); m_BinaryLookupTable->SetTableValue(0, rgba[0], rgba[1], rgba[2], 0.0); // background to 0 } //do not repeat the texture (the image) m_Texture->RepeatOff(); //set the mapper for the actor m_Actor->SetMapper( m_Mapper ); vtkSmartPointer outlineShadowActor = vtkSmartPointer::New(); outlineShadowActor->SetMapper( m_Mapper ); m_Actors->AddPart( outlineShadowActor ); m_Actors->AddPart( m_Actor ); } diff --git a/Modules/DicomRT/mitkDicomRTReader.cpp b/Modules/DicomRT/mitkDicomRTReader.cpp index 3016a74458..db6bd32095 100644 --- a/Modules/DicomRT/mitkDicomRTReader.cpp +++ b/Modules/DicomRT/mitkDicomRTReader.cpp @@ -1,645 +1,541 @@ /*=================================================================== 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 "mitkDicomRTReader.h" +#include +#include +#include + namespace mitk { - DicomRTReader::DicomRTReader() - { - } + DicomRTReader::DicomRTReader(){} DicomRTReader::~DicomRTReader(){} DicomRTReader::RoiEntry::RoiEntry() { Number=0; DisplayColor[0]=1.0; DisplayColor[1]=0.0; DisplayColor[2]=0.0; - PolyData=NULL; + ContourModelSet=mitk::ContourModelSet::New(); } - DicomRTReader::RoiEntry::~RoiEntry() - { - SetPolyData(NULL); - } + DicomRTReader::RoiEntry::~RoiEntry(){} DicomRTReader::RoiEntry::RoiEntry(const RoiEntry& src) { Number=src.Number; Name=src.Name; Description=src.Description; DisplayColor[0]=src.DisplayColor[0]; DisplayColor[1]=src.DisplayColor[1]; DisplayColor[2]=src.DisplayColor[2]; - PolyData=NULL; - SetPolyData(src.PolyData); + ContourModelSet=mitk::ContourModelSet::New(); + SetPolyData(src.ContourModelSet); } DicomRTReader::RoiEntry& DicomRTReader::RoiEntry::operator=(const RoiEntry &src) { Number=src.Number; Name=src.Name; Description=src.Description; DisplayColor[0]=src.DisplayColor[0]; DisplayColor[1]=src.DisplayColor[1]; DisplayColor[2]=src.DisplayColor[2]; - SetPolyData(src.PolyData); + SetPolyData(src.ContourModelSet); return (*this); } - void DicomRTReader::RoiEntry::SetPolyData(vtkPolyData* roiPolyData) + void DicomRTReader::RoiEntry::SetPolyData(mitk::ContourModelSet::Pointer roiPolyData) { - if (roiPolyData == this->PolyData) - { - // not changed + if (roiPolyData == this->ContourModelSet) return; - } - if (this->PolyData != NULL) - { - this->PolyData->UnRegister(NULL); - } - - this->PolyData = roiPolyData; - - if (this->PolyData != NULL) - { - this->PolyData->Register(NULL); - } + this->ContourModelSet = roiPolyData; } std::deque DicomRTReader::ReadDicomFile(char* filename) { - std::cout << "\n\n" << "Filename:" << filename << "\n\n"; DcmFileFormat file; OFCondition outp; outp = file.loadFile(filename, EXS_Unknown); if(outp.good()) { DcmDataset *dataset = file.getDataset(); OFString sopClass; if(dataset->findAndGetOFString(DCM_SOPClassUID, sopClass).good() && !sopClass.empty()) { if(sopClass == UID_RTDoseStorage) { mitk::DataNode::Pointer x = mitk::DataNode::New(); x = this->DicomRTReader::LoadRTDose(dataset, filename); ContourModelSetVector y; return y; } else if(sopClass == UID_RTStructureSetStorage) { ContourModelSetVector x = this->DicomRTReader::ReadStructureSet(dataset); return x; } else if(sopClass == UID_RTPlanStorage) { - int x = this->DicomRTReader::LoadRTPlan(dataset); +// Function isnt implemented yet +// int x = this->DicomRTReader::LoadRTPlan(dataset); ContourModelSetVector y; return y; } else { - std::cout << "Error reading the RTStructureSetStorage\n\n"; + MITK_ERROR << "Error matching the SOP Class, maybe an unsupported type" << endl; ContourModelSetVector y; return y; } } else { - std::cout << "Error reading the SOPClassID\n\n"; + MITK_ERROR << "Error reading the SOPClassID" << endl; ContourModelSetVector y; return y; } } else { - std::cout << "Cant read the input file\n\n"; + MITK_ERROR << "Error rading the input file" << endl; ContourModelSetVector y; return y; } } size_t DicomRTReader::GetNumberOfRois() { return this->RoiSequenceVector.size(); } - DicomRTReader::RoiEntry* DicomRTReader::FindRoiByNumber(int roiNumber) + DicomRTReader::RoiEntry* DicomRTReader::FindRoiByNumber(unsigned int roiNumber) { - for(int i=0; iRoiSequenceVector.size(); i++) + for(unsigned int i=0; iRoiSequenceVector.size(); ++i) { if(this->RoiSequenceVector[i].Number == roiNumber) - { return &this->RoiSequenceVector[i]; - } } return NULL; } std::deque DicomRTReader::ReadStructureSet(DcmDataset* dataset) { /** * @brief For storing contourmodelsets that belongs to the same object * * e.g. An eye consists of several contourmodels (contourmodel consists of several 3D-Points) * and together they are a contourmodelset */ ContourModelSetVector contourModelSetVector; DRTStructureSetIOD structureSetObject; OFCondition outp = structureSetObject.read(*dataset); if(!outp.good()) { - std::cout << "Error reading the file\n\n"; + MITK_ERROR << "Error reading the file" << endl; std::deque x; return x; } DRTStructureSetROISequence &roiSequence = structureSetObject.getStructureSetROISequence(); if(!roiSequence.gotoFirstItem().good()) { - std::cout << "Error reading the structure sequence\n\n"; + MITK_ERROR << "Error reading the structure sequence" << endl; std::deque x; return x; } do{ DRTStructureSetROISequence::Item ¤tSequence = roiSequence.getCurrentItem(); if(!currentSequence.isValid()) { continue; } OFString roiName; OFString roiDescription; Sint32 roiNumber; RoiEntry roi; currentSequence.getROIName(roiName); currentSequence.getROIDescription(roiDescription); currentSequence.getROINumber(roiNumber); roi.Name = roiName.c_str(); roi.Description = roiDescription.c_str(); roi.Number = roiNumber; this->RoiSequenceVector.push_back(roi); } while(roiSequence.gotoNextItem().good()); - OFString refSOPInstUID = GetReferencedFrameOfReferenceSOPInstanceUID(structureSetObject); - - double sliceThickness = 2.0; +// Dont know anymore >.< +// OFString refSOPInstUID = GetReferencedFrameOfReferenceSOPInstanceUID(structureSetObject); +// double sliceThickness = 2.0; Sint32 refRoiNumber; DRTROIContourSequence &roiContourSeqObject = structureSetObject.getROIContourSequence(); if(!roiContourSeqObject.gotoFirstItem().good()) { - std::cout << "Error reading the contour sequence\n\n"; + MITK_ERROR << "Error reading the contour sequence" << endl; std::deque x; return x; } do { mitk::ContourModelSet::Pointer contourSet = mitk::ContourModelSet::New(); DRTROIContourSequence::Item ¤tRoiObject = roiContourSeqObject.getCurrentItem(); if(!currentRoiObject.isValid()) { continue; } currentRoiObject.getReferencedROINumber(refRoiNumber); DRTContourSequence &contourSeqObject = currentRoiObject.getContourSequence(); if(contourSeqObject.gotoFirstItem().good()) { do { DRTContourSequence::Item &contourItem = contourSeqObject.getCurrentItem(); if(!contourItem.isValid()) { continue; } int number; OFString contourNumber; OFString numberOfPoints; OFVector contourData_LPS; mitk::ContourModel::Pointer contourSequence = mitk::ContourModel::New(); contourItem.getContourNumber(contourNumber); contourItem.getNumberOfContourPoints(numberOfPoints); contourItem.getContourData(contourData_LPS); std::stringstream stream; stream << numberOfPoints; stream >> number; - for(int i=0; iAddVertex(point); } contourSequence->Close(); contourSet->AddContourModel(contourSequence); } while(contourSeqObject.gotoNextItem().good()); } else { - std::cout << "Error reading contourSeqObject\n\n"; + MITK_ERROR << "Error reading contourSeqObject" << endl; } RoiEntry* refROI = this->FindRoiByNumber(refRoiNumber); if(refROI==NULL) { - std::cout << "Cant find referenced ROI\n\n"; + MITK_ERROR << "Can not find references ROI" << endl; continue; } Sint32 roiColor; for(int j=0;j<3;j++) { currentRoiObject.getROIDisplayColor(roiColor, j); refROI->DisplayColor[j] = roiColor/255.0; } + //TODO check for ordering maybe outsource contourmodelsetvector to + //Roientry and maybe it can replace the ContoruModelSet + refROI->ContourModelSet = contourSet; contourSet->SetProperty("name", mitk::StringProperty::New(refROI->Name)); contourModelSetVector.push_back(contourSet); } while(roiContourSeqObject.gotoNextItem().good()); - std::cout << "Anzahl von ROI: " << contourModelSetVector.size() << "\n\n"; + MITK_INFO << "Number of ROIs found: " << contourModelSetVector.size() << endl; return contourModelSetVector; } OFString DicomRTReader::GetReferencedFrameOfReferenceSOPInstanceUID(DRTStructureSetIOD &structSetObject) { OFString invalid; DRTReferencedFrameOfReferenceSequence &refFrameOfRefSeqObject = structSetObject.getReferencedFrameOfReferenceSequence(); if(!refFrameOfRefSeqObject.gotoFirstItem().good()) - { return invalid; - } DRTReferencedFrameOfReferenceSequence::Item ¤tRefFrameOfRefSeqItem = refFrameOfRefSeqObject.getCurrentItem(); if(!currentRefFrameOfRefSeqItem.isValid()) - { return invalid; - } DRTRTReferencedStudySequence &refStudySeqObject = currentRefFrameOfRefSeqItem.getRTReferencedStudySequence(); if(!refStudySeqObject.gotoFirstItem().good()) - { return invalid; - } DRTRTReferencedStudySequence::Item &refStudySeqItem = refStudySeqObject.getCurrentItem(); if(!refStudySeqItem.isValid()) - { return invalid; - } DRTRTReferencedSeriesSequence &refSeriesSeqObject = refStudySeqItem.getRTReferencedSeriesSequence(); if(!refSeriesSeqObject.gotoFirstItem().good()) - { return invalid; - } DRTRTReferencedSeriesSequence::Item &refSeriesSeqItem = refSeriesSeqObject.getCurrentItem(); if(!refSeriesSeqItem.isValid()) - { return invalid; - } DRTContourImageSequence &contourImageSeqObject = refSeriesSeqItem.getContourImageSequence(); if(!contourImageSeqObject.gotoFirstItem().good()) - { return invalid; - } DRTContourImageSequence::Item &contourImageSeqItem = contourImageSeqObject.getCurrentItem(); if(!contourImageSeqItem.isValid()) - { return invalid; - } OFString resultUid; contourImageSeqItem.getReferencedSOPInstanceUID(resultUid); return resultUid; } int DicomRTReader::LoadRTPlan(DcmDataset *dataset) { DRTPlanIOD planObject; OFCondition result = planObject.read(*dataset); if(result.good()) { OFString tmpString, dummyString; DRTBeamSequence &planeBeamSeqObject = planObject.getBeamSequence(); if(planeBeamSeqObject.gotoFirstItem().good()) { do { DRTBeamSequence::Item ¤tBeamSeqItem = planeBeamSeqObject.getCurrentItem(); if(!currentBeamSeqItem.isValid()) { std::cout << "Invalid Beam Sequence \n\n"; continue; } BeamEntry beamEntry; OFString beamName, beamDescription, beamType; Sint32 beamNumber; Float64 srcAxisDistance; currentBeamSeqItem.getBeamName(beamName); currentBeamSeqItem.getBeamDescription(beamDescription); currentBeamSeqItem.getBeamType(beamType); currentBeamSeqItem.getBeamNumber(beamNumber); currentBeamSeqItem.getSourceAxisDistance(srcAxisDistance); beamEntry.Name = beamName.c_str(); beamEntry.Description = beamDescription.c_str(); beamEntry.Type = beamType.c_str(); beamEntry.Number = beamNumber; beamEntry.SrcAxisDistance = srcAxisDistance; DRTControlPointSequence &controlPointSeqObject = currentBeamSeqItem.getControlPointSequence(); if(controlPointSeqObject.gotoFirstItem().good()) { DRTControlPointSequence::Item &controlPointItem = controlPointSeqObject.getCurrentItem(); if(controlPointItem.isValid()) { OFVector isocenterPosData_LPS; Float64 gantryAngle, patientSupportAngle, beamLimitingDeviceAngle; unsigned int numOfCollimatorPosItems = 0; controlPointItem.getIsocenterPosition(isocenterPosData_LPS); controlPointItem.getGantryAngle(gantryAngle); controlPointItem.getPatientSupportAngle(patientSupportAngle); controlPointItem.getBeamLimitingDeviceAngle(beamLimitingDeviceAngle); beamEntry.GantryAngle = gantryAngle; beamEntry.PatientSupportAngle = patientSupportAngle; beamEntry.BeamLimitingDeviceAngle = beamLimitingDeviceAngle; DRTBeamLimitingDevicePositionSequence ¤tCollimatorPosSeqObject = controlPointItem.getBeamLimitingDevicePositionSequence(); if(currentCollimatorPosSeqObject.gotoFirstItem().good()) { do { if(++numOfCollimatorPosItems > 2) { std::cout << "Number of collimator position items is higher than 2 but should be exactly 2 ..."; return 0; } DRTBeamLimitingDevicePositionSequence::Item &collimatorPositionItem = currentCollimatorPosSeqObject.getCurrentItem(); if(collimatorPositionItem.isValid()) { OFString beamLimitingDeviceType; OFVector leafJawPositions; collimatorPositionItem.getRTBeamLimitingDeviceType(beamLimitingDeviceType); collimatorPositionItem.getLeafJawPositions(leafJawPositions); if(!beamLimitingDeviceType.compare("ASYMX") || !beamLimitingDeviceType.compare("X")) { beamEntry.LeafJawPositions[0][0] = leafJawPositions[0]; beamEntry.LeafJawPositions[0][1] = leafJawPositions[1]; } else if(!beamLimitingDeviceType.compare("ASYMY") || !beamLimitingDeviceType.compare("Y")) { beamEntry.LeafJawPositions[1][0] = leafJawPositions[0]; beamEntry.LeafJawPositions[1][1] = leafJawPositions[0]; } else { std::cout << "Unknown collimator type: " << beamLimitingDeviceType << "\n\n"; } } } while(currentCollimatorPosSeqObject.gotoNextItem().good()); } }//endif controlPointItem.isValid() } this->BeamSequenceVector.push_back(beamEntry); } while(planeBeamSeqObject.gotoNextItem().good()); } } return 1; } mitk::DataNode::Pointer DicomRTReader::LoadRTDose(DcmDataset* dataset, char* filename) { -// mitk::CoreServicePointer shadoRepo(mitk::CoreServices::GetShaderRepository()); - -// std::string path = "/home/riecker/mitkShaderLighting.xml"; //mitk::StandardFileLocations::GetInstance()->FindFile("mitkShaderTOF.xml"); -// MITK_INFO << "shader found under: " << path; -// std::ifstream str(path.c_str()); -// shadoRepo->LoadShader(str,"mitkIsoLineShader"); - std::string name = filename; itk::FilenamesContainer file; file.push_back(name); mitk::DicomSeriesReader* reader = new mitk::DicomSeriesReader; mitk::DataNode::Pointer originalNode = reader->LoadDicomSeries(file,false); mitk::Image::Pointer originalImage = dynamic_cast(originalNode->GetData()); mitk::Geometry3D::Pointer geo = originalImage->GetGeometry()->Clone(); DRTDoseIOD doseObject; OFCondition result = doseObject.read(*dataset); if(result.bad()) { std::cout << "Error reading the RT Dose dataset\n\n"; return 0; } Uint16 rows, columns, frames, planarConfig, samplesPP; OFString nrframes, doseUnits, doseType, summationType, gridScaling, photoInterpret, lutShape; Uint16 &rows_ref = rows; Uint16 &columns_ref = columns; Float32 gridscale; const Uint16 *pixelData = NULL; unsigned long count = 0; doseObject.getRows(rows_ref); doseObject.getColumns(columns_ref); doseObject.getNumberOfFrames(nrframes); doseObject.getDoseUnits(doseUnits); doseObject.getDoseType(doseType); doseObject.getDoseSummationType(summationType); doseObject.getDoseGridScaling(gridScaling); doseObject.getPhotometricInterpretation(photoInterpret); doseObject.getPlanarConfiguration(planarConfig); doseObject.getSamplesPerPixel(samplesPP); doseObject.getPresentationLUTShape(lutShape); //standard testing picture: 0.001 gridscale = OFStandard::atof(gridScaling.c_str()); - std::cout << std::setprecision(50) << "Gridscale " << gridscale << endl; + std::cout << std::setprecision(10) << "GRIDSCALE >> " << gridscale << endl; frames = atoi(nrframes.c_str()); dataset->findAndGetUint16Array(DCM_PixelData, pixelData, &count); mitk::Image::Pointer image = mitk::Image::New(); mitk::PixelType pt = mitk::MakeScalarPixelType(); unsigned int dim[] = {columns,rows,frames}; image->Initialize( pt, 3, dim); image->SetSpacing(1.0); mitk::Point3D m_origin; m_origin[0] = 0.0; m_origin[1] = 0.0; m_origin[2] = 0.0; image->SetOrigin(m_origin); float* pixel = (float*)image->GetData(); int size = dim[0]*dim[1]*dim[2]; for(int i=0; iSetGeometry(geo); - double prescripeDose = this->GetDefaultPrescriptionDose(dataset); - double hsvValue = 0.002778; - - vtkSmartPointer transferFunction = vtkSmartPointer::New(); - transferFunction->AddHSVPoint(prescripeDose*0.0,(240*hsvValue),1.0,1.0,1.0,1.0); - transferFunction->AddHSVPoint(prescripeDose*0.2,(180*hsvValue),1.0,1.0,1.0,1.0); - transferFunction->AddHSVPoint(prescripeDose*0.4,(120*hsvValue),1.0,0.5,1.0,1.0); - transferFunction->AddHSVPoint(prescripeDose*0.5,(120*hsvValue),1.0,1.0,1.0,1.0); - transferFunction->AddHSVPoint(prescripeDose*0.6,(060*hsvValue),1.0,1.0,1.0,1.0); - transferFunction->AddHSVPoint(prescripeDose*0.7,(026*hsvValue),1.0,1.0,1.0,1.0); - transferFunction->AddHSVPoint(prescripeDose*1.0,(000*hsvValue),1.0,1.0,1.0,1.0); - transferFunction->Build(); - - mitk::TransferFunction::Pointer mitkTransFunc = mitk::TransferFunction::New(); - mitk::TransferFunctionProperty::Pointer mitkTransFuncProp = mitk::TransferFunctionProperty::New(); - mitkTransFunc->SetColorTransferFunction(transferFunction); - mitkTransFuncProp->SetValue(mitkTransFunc); - - mitk::RenderingModeProperty::Pointer renderingMode = mitk::RenderingModeProperty::New(); - renderingMode->SetValue(mitk::RenderingModeProperty::ISODOSESHADER_COLOR); + double prescripeDose = this->GetMaxDoseValue(dataset); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetName("DicomRT Dosis"); - node->SetProperty("shader.mitkIsoLineShader.Gridscale", mitk::FloatProperty::New(10.0)); +// node->SetProperty("shader.mitkIsoLineShader.Gridscale", mitk::FloatProperty::New(10.0)); + node->SetFloatProperty(mitk::rt::Constants::PRESCRIBED_DOSE_PROPERTY_NAME.c_str(),prescripeDose); node->SetBoolProperty(mitk::rt::Constants::DOSE_PROPERTY_NAME.c_str(),true); - node->SetProperty("Image Rendering.Mode", renderingMode); // node->SetProperty("Image Rendering.Transfer Function", mitkTransFuncProp); - node->SetProperty("opacity", mitk::FloatProperty::New(0.3)); +// node->SetProperty("Image Rendering.Mode", renderingMode); +// node->SetProperty("opacity", mitk::FloatProperty::New(0.3)); node->SetData(image); + MITK_INFO << "PRESCRIPEDOSE >> " << prescripeDose << endl; + return node; } - double DicomRTReader::GetDefaultPrescriptionDose(DcmDataset* dataSet) + double DicomRTReader::GetMaxDoseValue(DcmDataset* dataSet) { DRTDoseIOD doseObject; OFCondition result = doseObject.read(*dataSet); if(result.bad()) { std::cout << "Error reading the RT Dose dataset\n\n"; return 0; } Uint16 rows, columns, frames; OFString nrframes, gridScaling; const Uint16 *pixelData = NULL; Float32 gridscale; Uint16 &rows_ref = rows; Uint16 &columns_ref = columns; doseObject.getRows(rows_ref); doseObject.getColumns(columns_ref); doseObject.getNumberOfFrames(nrframes); doseObject.getDoseGridScaling(gridScaling); frames = atoi(nrframes.c_str()); gridscale = OFStandard::atof(gridScaling.c_str()); dataSet->findAndGetUint16Array(DCM_PixelData, pixelData, 0); int size = columns*rows*frames; double highest = 0; for(int i=0; ihighest) { highest = pixelData[i] * gridscale; } } - return highest * 0.8; - } - - vtkSmartPointer DicomRTReader::GetIsoLine(double value, vtkDataObject* dataObject) - { - vtkSmartPointer contourFilter = vtkSmartPointer::New(); - vtkSmartPointer polyData = vtkSmartPointer::New(); - - contourFilter->SetInput(dataObject); - contourFilter->SetNumberOfContours(1); - contourFilter->SetValue(0,value); - contourFilter->Update(); - - polyData = contourFilter->GetOutput(); - return polyData; - } - - vtkSmartPointer DicomRTReader::GetStandardIsoLines(vtkDataObject* dataObject) - { - vtkSmartPointer contourFilter = vtkSmartPointer::New(); - vtkSmartPointer polyData = vtkSmartPointer::New(); - - contourFilter->SetInput(dataObject); - contourFilter->SetNumberOfContours(1); - contourFilter->SetValue(0,10); - - polyData = contourFilter->GetOutput(); - return polyData; - } - - bool DicomRTReader::Equals(mitk::ContourModel::Pointer first, mitk::ContourModel::Pointer second) - { - if(first->GetNumberOfVertices() != second->GetNumberOfVertices()) - { - return false; - } - for( int i = 0; i < first->GetNumberOfVertices(); i++) - { - const mitk::ContourElement::VertexType* x = first->GetVertexAt(i); - const mitk::ContourElement::VertexType* y = second->GetVertexAt(i); - - mitk::ContourElement::VertexType xx = *x; - mitk::ContourElement::VertexType yy = *y; - - for(int j=0; j<3; j++) - { - if(xx.Coordinates[j] != yy.Coordinates[j]) - { - return false; - } - } - } - return true; + return highest; } } diff --git a/Modules/DicomRT/mitkDicomRTReader.h b/Modules/DicomRT/mitkDicomRTReader.h index 47b1b8d2f3..790843e327 100644 --- a/Modules/DicomRT/mitkDicomRTReader.h +++ b/Modules/DicomRT/mitkDicomRTReader.h @@ -1,272 +1,232 @@ /*=================================================================== 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 mitkDicomRTReader_h #define mitkDicomRTReader_h #include #include #include #include #include "mitkContourModel.h" #include "mitkContourElement.h" #include #include "dcmtk/config/osconfig.h" #include "dcmtk/ofstd/ofconapp.h" #include "dcmtk/ofstd/ofcond.h" #include "dcmtk/dcmrt/drtdose.h" #include "dcmtk/dcmrt/drtimage.h" #include "dcmtk/dcmrt/drtplan.h" #include "dcmtk/dcmrt/drttreat.h" #include "dcmtk/dcmrt/drtionpl.h" #include "dcmtk/dcmrt/drtiontr.h" -//###################################################### -#include -#include -#include -#include #include -#include #include #include #include -#include -#include #include #include #include #include #include #include #include #include -//###################################################### #include "dcmtk/dcmrt/drtstrct.h" #include #include #include #include #include #include #include #include class vtkPolyData; class DcmDataset; class OFString; class DRTContourSequence; class DRTStructureSetIOD; namespace mitk { class mitkDicomRT_EXPORT DicomRTReader: public itk::Object { typedef std::deque ContourModelSetVector; /** * @brief Describes and holds some information about the beams. */ class BeamEntry { public: BeamEntry() { Number=-1; SrcAxisDistance=0.0; GantryAngle=0.0; PatientSupportAngle=0.0; BeamLimitingDeviceAngle=0.0; LeafJawPositions[0][0]=0.0; LeafJawPositions[0][1]=0.0; LeafJawPositions[1][0]=0.0; LeafJawPositions[1][1]=0.0; } unsigned int Number; std::string Name; std::string Type; std::string Description; double SrcAxisDistance; double GantryAngle; double PatientSupportAngle; double BeamLimitingDeviceAngle; double LeafJawPositions[2][2]; }; /** * @brief Describes and holds some information about the Rois. */ class RoiEntry { public: RoiEntry(); virtual ~RoiEntry(); RoiEntry(const RoiEntry& src); RoiEntry &operator=(const RoiEntry &src); - void SetPolyData(vtkPolyData* roiPolyData); + void SetPolyData(ContourModelSet::Pointer roiPolyData); unsigned int Number; std::string Name; std::string Description; double DisplayColor[3]; - //vllt direkt durch die contourmodels ersetzen ?! - vtkPolyData* PolyData; + mitk::ContourModelSet::Pointer ContourModelSet; }; public: mitkClassMacro( DicomRTReader, itk::Object ); itkNewMacro( Self ); /** - * @brief Used for getting a custom Isoline - * @param value The gray-value which the isoline represents - * @param dataObject The vtkDataObject from the DicomRT dose image - * @return Returns a vtkPolyData including the contour/isoline - * - * This method is used for a custom Isoline e.g. from the DicomRT IsoSlider. - * You define the value where the Isoline is by setting the value as a parameter. - */ - vtkSmartPointer GetIsoLine(double value, vtkDataObject* dataObject); - - /** - * @brief Used for getting the StandardIsoLines - * @param dataObject The vtkDataObject from the DicomRT dose image - * @return Returns a vtkPolyData including the contours/isolines - * - * The IsoLines should be equal to the contours of the colorwash. - */ - vtkSmartPointer GetStandardIsoLines(vtkDataObject* dataObject); - - /** - * @brief Get a default prescription dose + * @brief Get the maximum dose value from the dose file * @param dataSet The DcmDataset of the DicomRTDose file * @return The dose value * - * If you dont define a prescription dose use this method, it calculates a - * default value by reading the dose values from the dose file and multiplys - * the highest value with 0.8. + * Checks all pixel values for the maximum value */ - double GetDefaultPrescriptionDose(DcmDataset* dataSet); + double GetMaxDoseValue(DcmDataset* dataSet); /** * @brief Used for reading a DicomRT file * @param filename The path with your file which you want to read * * Calls the right method for reading a dose, structure or plan file. */ ContourModelSetVector ReadDicomFile(char* filename); /** * @brief Reads a DcmDataset from a DicomRT structureset file * @param dataset DcmDataset-object from DCMTK * @return Returns a Deque with mitk::ContourModelSet * * The returned mitk::ContourModelSet represent exactly one Roi/Structureset. * So the size of the returned deque is the number of Rois. The names of the * rois is stored in their mitk::Property. */ ContourModelSetVector ReadStructureSet(DcmDataset* dataset); /** * @brief Reads a DcmDataset from a DicomRT plan file * @param dataset DcmDataset-object from DCMTK * @return The return doesnt make senese at the moment * * This method isnt ready for use at the moment. Dont use it! */ int LoadRTPlan(DcmDataset* dataset); /** * @brief Reads a DcmDataset from a DicomRT dose file * @param dataset DcmDataset-object from DCMTK * @param filename The path with the dose file used for getting the geometry * @return Returns a mitk::DataNode::Pointer in which a mitk::Image is stored * * The method reads the PixelData from the DicomRT dose file and scales them * with a factor for getting Gray-values instead of pixel-values. * The Gray-values are stored in a mitk::Image with an vtkColorTransferFunction. * Relative values are used for coloring the image. The relative values are * relative to a PrescriptionDose definied in the RT-Plan. If there is no * RT-Plan file PrescriptionDose is set to 80% of the maximum dose. */ mitk::DataNode::Pointer LoadRTDose(DcmDataset* dataset, char* filename); /** * @brief Returns the number of Rois stored in the RoiSequenceVector * @return unsigned long size_t Number of Rois */ size_t GetNumberOfRois(); /** * @brief Find a Roi stored in the RoiSequenceVector by his number * @param roiNumber The number of the searched roi * @return Returns a mitk::DicomRTReader::RoiEntry object */ - RoiEntry* FindRoiByNumber(int roiNumber); + RoiEntry* FindRoiByNumber(unsigned int roiNumber); /** * @brief GetReferencedFrameOfReferenceSOPInstanceUID * @param structSetObject * @return */ OFString GetReferencedFrameOfReferenceSOPInstanceUID(DRTStructureSetIOD &structSetObject); - /** - * @brief Compares two mitk::ContourModel::Pointer for testing the Structuresets. - * @param first The first mitk::ContourModel::Pointer - * @param second The second mitk::ContourModel::Pointer - * @return Returns true if they are equal and false if they are different - */ - bool Equals(mitk::ContourModel::Pointer first, mitk::ContourModel::Pointer second); - /** * Virtual destructor. */ virtual ~DicomRTReader(); protected: /** * @brief Storing the Rois found in the Structureset file */ std::vector RoiSequenceVector; /** * @brief Storing the Beams foud in the RT Plan file */ std::vector BeamSequenceVector; /** * Constructor. */ DicomRTReader(); }; } #endif diff --git a/Plugins/org.mitk.gui.qt.rt.dosevisualization/files.cmake b/Plugins/org.mitk.gui.qt.rt.dosevisualization/files.cmake index 0c9095f1ed..e2146cc1e1 100644 --- a/Plugins/org.mitk.gui.qt.rt.dosevisualization/files.cmake +++ b/Plugins/org.mitk.gui.qt.rt.dosevisualization/files.cmake @@ -1,48 +1,52 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES org_mitk_gui_qt_rt_dosevisualization_Activator.cpp RTDoseVisualizer.cpp RTUIPreferencePage.cpp DoseVisualizationPreferencePage.cpp mitkDoseVisPreferenceHelper.cpp + LoadingRTView.cpp ) set(UI_FILES src/internal/RTDoseVisualizerControls.ui src/internal/DoseVisualizationPreferencePageControls.ui src/internal/RTUIPreferencePageControls.ui + src/internal/LoadingRTViewControls.ui ) set(MOC_H_FILES src/internal/org_mitk_gui_qt_rt_dosevisualization_Activator.h src/internal/RTDoseVisualizer.h src/internal/RTUIPreferencePage.h src/internal/DoseVisualizationPreferencePage.h + src/internal/LoadingRTView.h ) # list of resource files which can be used by the plug-in # system without loading the plug-ins shared library, # for example the icon used in the menu and tabs for the # plug-in views in the workbench set(CACHED_RESOURCE_FILES resources/iso.png + resources/iso2.png plugin.xml ) # list of Qt .qrc files which contain additional resources # specific to this plugin set(QRC_FILES ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.rt.dosevisualization/plugin.xml b/Plugins/org.mitk.gui.qt.rt.dosevisualization/plugin.xml index bf6f3accea..393d651209 100644 --- a/Plugins/org.mitk.gui.qt.rt.dosevisualization/plugin.xml +++ b/Plugins/org.mitk.gui.qt.rt.dosevisualization/plugin.xml @@ -1,15 +1,19 @@ + diff --git a/Plugins/org.mitk.gui.qt.rt.dosevisualization/resources/iso2.png b/Plugins/org.mitk.gui.qt.rt.dosevisualization/resources/iso2.png new file mode 100644 index 0000000000..2a73d321db Binary files /dev/null and b/Plugins/org.mitk.gui.qt.rt.dosevisualization/resources/iso2.png differ diff --git a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/LoadingRTView.cpp b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/LoadingRTView.cpp new file mode 100644 index 0000000000..d57b173c73 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/LoadingRTView.cpp @@ -0,0 +1,131 @@ +/*=================================================================== + +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. + +===================================================================*/ + + +// Blueberry +#include +#include + +// Qmitk +#include "LoadingRTView.h" + +// Qt +#include +#include + +#include + +const std::string LoadingRTView::VIEW_ID = "org.mitk.views.loadingrtview"; + +void LoadingRTView::SetFocus(){} + +void LoadingRTView::CreateQtPartControl( QWidget *parent ) +{ + // create GUI widgets from the Qt Designer's .ui file + m_Controls.setupUi( parent ); + connect( m_Controls.btnLoadStruct, SIGNAL(clicked()), this, SLOT(LoadRTStructureSet()) ); + connect( m_Controls.btnLoadDose, SIGNAL(clicked()), this, SLOT(LoadRTDoseFile()) ); +} + +void LoadingRTView::LoadRTStructureSet() +{ + QFileDialog dialog; + dialog.setNameFilter(tr("Images (*.dcm")); + + mitk::DicomSeriesReader::StringContainer files; + QStringList fileNames = dialog.getOpenFileNames(); + if(fileNames.empty()) + { + return; + } + QStringListIterator fileNamesIterator(fileNames); + while(fileNamesIterator.hasNext()) + { + files.push_back(fileNamesIterator.next().toStdString()); + } + + std::string tmp = files.front(); + const char* filename = tmp.c_str(); + char* filenameC = const_cast(filename); + + DcmFileFormat file; + OFCondition outp = file.loadFile(filename, EXS_Unknown); + if(outp.bad()) + { + QMessageBox::information(NULL,"Error","Cant read the file"); + } + DcmDataset *dataset = file.getDataset(); + + mitk::DicomRTReader::Pointer readerRT = mitk::DicomRTReader::New(); + std::deque modelVector; + modelVector = readerRT->ReadDicomFile(filenameC); + + if(modelVector.empty()) + { + QMessageBox::information(NULL, "Error", "Vector is empty ..."); + } + + for(int i=0; iSetData(modelVector.at(i)); + x->SetProperty("name", modelVector.at(i)->GetProperty("name")); + x->SetVisibility(true); + GetDataStorage()->Add(x); + } + mitk::TimeSlicedGeometry::Pointer geo = this->GetDataStorage()->ComputeBoundingGeometry3D(this->GetDataStorage()->GetAll()); + mitk::RenderingManager::GetInstance()->InitializeViews( geo ); +} + +void LoadingRTView::LoadRTDoseFile() +{ + QFileDialog dialog; + dialog.setNameFilter(tr("Images (*.dcm")); + + mitk::DicomSeriesReader::StringContainer files; + QStringList fileNames = dialog.getOpenFileNames(); + if(fileNames.empty()) + { + return; + } + QStringListIterator fileNamesIterator(fileNames); + while(fileNamesIterator.hasNext()) + { + files.push_back(fileNamesIterator.next().toStdString()); + } + + std::string tmp = files.front(); + const char* filename = tmp.c_str(); + char* ncFilename = const_cast(filename); + + mitk::DicomRTReader::Pointer _DicomRTReader = mitk::DicomRTReader::New(); + + DcmFileFormat file; + OFCondition outp = file.loadFile(filename, EXS_Unknown); + if(outp.bad()) + { + QMessageBox::information(NULL,"Error","Cant read the file"); + } + DcmDataset *dataset = file.getDataset(); + + mitk::DataNode::Pointer mitkImage = mitk::DataNode::New(); + mitkImage = _DicomRTReader->LoadRTDose(dataset,ncFilename); + + GetDataStorage()->Add(mitkImage); + + mitk::TimeSlicedGeometry::Pointer geo3 = this->GetDataStorage()->ComputeBoundingGeometry3D(this->GetDataStorage()->GetAll()); + mitk::RenderingManager::GetInstance()->InitializeViews( geo3 ); +} diff --git a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/LoadingRTView.h b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/LoadingRTView.h new file mode 100644 index 0000000000..893221b596 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/LoadingRTView.h @@ -0,0 +1,61 @@ +/*=================================================================== + +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 LoadingRTView_h +#define LoadingRTView_h + +#include + +#include + +#include "ui_LoadingRTViewControls.h" + + +/** + \brief LoadingRTView + + \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. + + \sa QmitkAbstractView + \ingroup ${plugin_target}_internal +*/ +class LoadingRTView : public QmitkAbstractView +{ + // this is needed for all Qt objects that should have a Qt meta-object + // (everything that derives from QObject and wants to have signal/slots) + Q_OBJECT + + public: + + static const std::string VIEW_ID; + + protected slots: + + void LoadRTDoseFile(); + void LoadRTStructureSet(); + + protected: + + virtual void CreateQtPartControl(QWidget *parent); + + virtual void SetFocus(); + + Ui::LoadingRTViewControls m_Controls; + +}; + +#endif // LoadingRTView_h diff --git a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/LoadingRTViewControls.ui b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/LoadingRTViewControls.ui new file mode 100644 index 0000000000..0c2785ca7a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/LoadingRTViewControls.ui @@ -0,0 +1,78 @@ + + + LoadingRTViewControls + + + + 0 + 0 + 222 + 161 + + + + + 0 + 0 + + + + QmitkTemplate + + + + + + Loading RT Data + + + + + + + + + + 0 + 50 + + + + RT Struct + + + + + + + + 0 + 50 + + + + RT Dose + + + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizer.cpp b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizer.cpp index 8b65127594..4c5fe1bbc2 100644 --- a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizer.cpp +++ b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizer.cpp @@ -1,488 +1,596 @@ /*=================================================================== 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. ===================================================================*/ // Blueberry #include #include // MITK #include // Qmitk #include "RTDoseVisualizer.h" #include #include #include #include #include #include #include #include #include // Qt #include +#include #include +#include +#include + const std::string RTDoseVisualizer::VIEW_ID = "org.mitk.views.rt.dosevisualization"; RTDoseVisualizer::RTDoseVisualizer() { m_freeIsoValues = mitk::IsoDoseLevelVector::New(); m_selectedNodeIsoSet = mitk::IsoDoseLevelSet::New(); m_selectedNode = NULL; m_selectedPresetName = ""; m_internalUpdate = false; m_PrescribedDose_Data = 0.0; - mitk::CoreServicePointer shaderRepo(mitk::CoreServices::GetShaderRepository()); - std::string path = "/home/riecker/mitkShaderLightning.xml"; + mitk::CoreServicePointer shadoRepo(mitk::CoreServices::GetShaderRepository()); + std::string path = "/home/riecker/mitkShaderLighting.xml"; std::string isoShaderName = "mitkIsoLineShader"; MITK_INFO << "shader found under: " << path; std::ifstream str(path.c_str()); - shaderRepo->LoadShader(str,isoShaderName); + shadoRepo->LoadShader(str,isoShaderName); } RTDoseVisualizer::~RTDoseVisualizer() { delete m_LevelSetModel; delete m_DoseColorDelegate; delete m_DoseValueDelegate; delete m_DoseVisualDelegate; } void RTDoseVisualizer::SetFocus() { } void RTDoseVisualizer::CreateQtPartControl( QWidget *parent ) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi( parent ); m_LevelSetModel = new QmitkIsoDoseLevelSetModel(this); m_LevelSetModel->setVisibilityEditOnly(true); m_DoseColorDelegate = new QmitkDoseColorDelegate(this); m_DoseValueDelegate = new QmitkDoseValueDelegate(this); m_DoseVisualDelegate = new QmitkDoseVisualStyleDelegate(this); this->UpdateByPreferences(); this->ActualizeIsoLevelsForAllDoseDataNodes(); this->ActualizeReferenceDoseForAllDoseDataNodes(); this->ActualizeDisplayStyleForAllDoseDataNodes(); this->m_Controls.isoLevelSetView->setModel(m_LevelSetModel); this->m_Controls.isoLevelSetView->setItemDelegateForColumn(0,m_DoseColorDelegate); this->m_Controls.isoLevelSetView->setItemDelegateForColumn(1,m_DoseValueDelegate); this->m_Controls.isoLevelSetView->setItemDelegateForColumn(2,m_DoseVisualDelegate); this->m_Controls.isoLevelSetView->setItemDelegateForColumn(3,m_DoseVisualDelegate); this->m_Controls.isoLevelSetView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_Controls.btnloadDose, SIGNAL(clicked()), this, SLOT(LoadRTDoseFile())); + connect(m_Controls.btnIsoLines, SIGNAL(clicked()), this, SLOT(LoadISOLines())); connect(m_Controls.btnConvert, SIGNAL(clicked()), this, SLOT(OnConvertButtonClicked())); connect(m_Controls.spinReferenceDose, SIGNAL(valueChanged(double)), this, SLOT(OnReferenceDoseChanged(double))); connect(m_Controls.spinReferenceDose, SIGNAL(valueChanged(double)), m_LevelSetModel, SLOT(setReferenceDose(double))); connect(m_Controls.radioAbsDose, SIGNAL(toggled(bool)), m_LevelSetModel, SLOT(setShowAbsoluteDose(bool))); connect(m_Controls.radioAbsDose, SIGNAL(toggled(bool)), this, SLOT(OnAbsDoseToggled(bool))); connect(m_Controls.btnAddFreeValue, SIGNAL(clicked()), this, SLOT(OnAddFreeValueClicked())); connect(m_Controls.btnRemoveFreeValue, SIGNAL(clicked()), this, SLOT(OnRemoveFreeValueClicked())); connect(m_Controls.checkGlobalVisColorWash, SIGNAL(toggled(bool)), this, SLOT(OnGlobalVisColorWashToggled(bool))); connect(m_Controls.checkGlobalVisIsoLine, SIGNAL(toggled(bool)), this, SLOT(OnGlobalVisIsoLineToggled(bool))); connect(m_Controls.isoLevelSetView, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(OnShowContextMenuIsoSet(const QPoint&))); connect(m_Controls.comboPresets, SIGNAL(currentIndexChanged ( const QString&)), this, SLOT(OnCurrentPresetChanged(const QString&))); connect(m_Controls.btnUsePrescribedDose, SIGNAL(clicked()), this, SLOT(OnUsePrescribedDoseClicked())); this->UpdateBySelectedNode(); } +void RTDoseVisualizer::LoadISOLines() +{ + mitk::rt::PresetMapType preset = mitk::rt::LoadPresetsMap(); + mitk::IsoDoseLevelSet::Pointer levelSets = preset.at("Virtuos"); + for(mitk::IsoDoseLevelSet::ConstIterator levelSet = levelSets->Begin(); levelSet != levelSets->End(); ++levelSet) + { + MITK_INFO << "X###############################################################" << levelSet->GetColor() << endl; + } + + bool result; + if(m_selectedNode->GetBoolProperty(mitk::rt::Constants::DOSE_PROPERTY_NAME.c_str(),result) && result) + { + m_selectedNode->SetProperty("shader",mitk::ShaderProperty::New("mitkIsoLineShader")); + m_selectedNode->SetProperty("shader.mitkIsoLineShader.CustomISO", mitk::FloatProperty::New(20)); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + else + { + MITK_WARN << "Selected file has to be a Dose file!"; + } +} + +void RTDoseVisualizer::LoadRTDoseFile() +{ + QFileDialog dialog; + dialog.setNameFilter(tr("Images (*.dcm")); + + mitk::DicomSeriesReader::StringContainer files; + QStringList fileNames = dialog.getOpenFileNames(); + if(fileNames.empty()) + { + return; + } + QStringListIterator fileNamesIterator(fileNames); + while(fileNamesIterator.hasNext()) + { + files.push_back(fileNamesIterator.next().toStdString()); + } + + std::string tmp = files.front(); + const char* filename = tmp.c_str(); + char* ncFilename = const_cast(filename); + + mitk::DicomRTReader::Pointer _DicomRTReader = mitk::DicomRTReader::New(); + + DcmFileFormat file; + OFCondition outp = file.loadFile(filename, EXS_Unknown); + if(outp.bad()) + { + QMessageBox::information(NULL,"Error","Cant read the file"); + } + DcmDataset *dataset = file.getDataset(); + + mitk::DataNode::Pointer mitkImage = mitk::DataNode::New(); + mitkImage = _DicomRTReader->LoadRTDose(dataset,ncFilename); + + GetDataStorage()->Add(mitkImage); + + mitk::TimeSlicedGeometry::Pointer geo3 = this->GetDataStorage()->ComputeBoundingGeometry3D(this->GetDataStorage()->GetAll()); + mitk::RenderingManager::GetInstance()->InitializeViews( geo3 ); +} + void RTDoseVisualizer::OnReferenceDoseChanged(double value) { if (! m_internalUpdate) { mitk::DoseValueAbs referenceDose = 0.0; bool globalSync = mitk::rt::GetReferenceDoseValue(referenceDose); if (globalSync) { mitk::rt::SetReferenceDoseValue(globalSync, value); this->ActualizeReferenceDoseForAllDoseDataNodes(); } else { if (this->m_selectedNode.IsNotNull()) { this->m_selectedNode->SetFloatProperty(mitk::rt::Constants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), value); } } } } void RTDoseVisualizer::OnAddFreeValueClicked() { QColor newColor; //Use HSV schema of QColor to calculate a different color depending on the //number of already existing free iso lines. newColor.setHsv((m_freeIsoValues->Size()*85)%360,255,255); mitk::IsoDoseLevel::ColorType color; color[0] = newColor.redF(); color[1] = newColor.greenF(); color[2] = newColor.blueF(); m_freeIsoValues->push_back(mitk::IsoDoseLevel::New(0.5,color,true,false)); UpdateFreeIsoValues(); } void RTDoseVisualizer::OnRemoveFreeValueClicked() { m_freeIsoValues->pop_back(); UpdateFreeIsoValues(); } void RTDoseVisualizer::OnUsePrescribedDoseClicked() { m_Controls.spinReferenceDose->setValue(this->m_PrescribedDose_Data); }; void RTDoseVisualizer::OnShowContextMenuIsoSet(const QPoint& pos) { QPoint globalPos = m_Controls.isoLevelSetView->viewport()->mapToGlobal(pos); QMenu viewMenu; QAction* invertIsoLineAct = viewMenu.addAction("Invert iso line visibility"); QAction* activateIsoLineAct = viewMenu.addAction("Activate all iso lines"); QAction* deactivateIsoLineAct = viewMenu.addAction("Deactivate all iso lines"); viewMenu.addSeparator(); QAction* invertColorWashAct = viewMenu.addAction("Invert color wash visibility"); QAction* activateColorWashAct = viewMenu.addAction("Activate all color wash levels"); QAction* deactivateColorWashAct = viewMenu.addAction("Deactivate all color wash levels"); viewMenu.addSeparator(); QAction* swapAct = viewMenu.addAction("Swap iso line/color wash visibility"); // ... QAction* selectedItem = viewMenu.exec(globalPos); if (selectedItem == invertIsoLineAct) { this->m_LevelSetModel->invertVisibilityIsoLines(); } else if (selectedItem == activateIsoLineAct) { this->m_LevelSetModel->switchVisibilityIsoLines(true); } else if (selectedItem == deactivateIsoLineAct) { this->m_LevelSetModel->switchVisibilityIsoLines(false); } else if (selectedItem == invertColorWashAct) { this->m_LevelSetModel->invertVisibilityColorWash(); } else if (selectedItem == activateColorWashAct) { this->m_LevelSetModel->switchVisibilityColorWash(true); } else if (selectedItem == deactivateColorWashAct) { this->m_LevelSetModel->switchVisibilityColorWash(false); } else if (selectedItem == swapAct) { this->m_LevelSetModel->swapVisibility(); } } void RTDoseVisualizer::UpdateFreeIsoValues() { this->m_Controls.listFreeValues->clear(); for (mitk::IsoDoseLevelVector::Iterator pos = this->m_freeIsoValues->Begin(); pos != this->m_freeIsoValues->End(); ++pos) { QListWidgetItem* item = new QListWidgetItem; item->setSizeHint(QSize(0,25)); QmitkFreeIsoDoseLevelWidget* widget = new QmitkFreeIsoDoseLevelWidget; widget->setIsoDoseLevel(pos->Value().GetPointer()); connect(m_Controls.spinReferenceDose, SIGNAL(valueChanged(double)), widget, SLOT(setReferenceDose(double))); this->m_Controls.listFreeValues->addItem(item); this->m_Controls.listFreeValues->setItemWidget(item,widget); } } void RTDoseVisualizer::OnAbsDoseToggled(bool showAbs) { if (! m_internalUpdate) { mitk::rt::SetDoseDisplayAbsolute(showAbs); this->ActualizeDisplayStyleForAllDoseDataNodes(); } } void RTDoseVisualizer::OnGlobalVisColorWashToggled(bool showColorWash) { if (m_selectedNode.IsNotNull()) { m_selectedNode->SetBoolProperty(mitk::rt::Constants::DOSE_SHOW_COLORWASH_PROPERTY_NAME.c_str(), showColorWash); } } void RTDoseVisualizer::OnGlobalVisIsoLineToggled(bool showIsoLines) { if (m_selectedNode.IsNotNull()) { m_selectedNode->SetBoolProperty(mitk::rt::Constants::DOSE_SHOW_ISOLINES_PROPERTY_NAME.c_str(), showIsoLines); } } void RTDoseVisualizer::OnConvertButtonClicked() { QList dataNodes = this->GetDataManagerSelection(); mitk::DataNode* selectedNode = NULL; if (!dataNodes.empty()) { selectedNode = dataNodes[0]; } if(selectedNode) { selectedNode->SetBoolProperty(mitk::rt::Constants::DOSE_PROPERTY_NAME.c_str(), true); selectedNode->SetBoolProperty(mitk::rt::Constants::DOSE_SHOW_COLORWASH_PROPERTY_NAME.c_str(), true); selectedNode->SetBoolProperty(mitk::rt::Constants::DOSE_SHOW_ISOLINES_PROPERTY_NAME.c_str(), true); selectedNode->SetFloatProperty(mitk::rt::Constants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), m_Controls.spinReferenceDose->value()); - selectedNode->GetData()->SetProperty(mitk::rt::Constants::PRESCRIBED_DOSE_PROPERTY_NAME.c_str(), mitk::DoubleProperty::New(1.0)); + //selectedNode->GetData()->SetProperty(mitk::rt::Constants::PRESCRIBED_DOSE_PROPERTY_NAME.c_str(), mitk::DoubleProperty::New(1.0)); mitk::IsoDoseLevelSet::Pointer clonedPreset = this->m_Presets[this->m_selectedPresetName]->Clone(); mitk::IsoDoseLevelSetProperty::Pointer levelSetProp = mitk::IsoDoseLevelSetProperty::New(clonedPreset); selectedNode->SetProperty(mitk::rt::Constants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str(),levelSetProp); + + double hsvValue = 0.002778; + float prescribed; + m_selectedNode->GetFloatProperty(mitk::rt::Constants::PRESCRIBED_DOSE_PROPERTY_NAME.c_str(),prescribed); + + vtkSmartPointer transferFunction = vtkSmartPointer::New(); + transferFunction->SetColorSpaceToRGB(); + + + mitk::IsoDoseLevelSet::Pointer isoDoseLevelSet = this->m_Presets[this->m_selectedPresetName]; + + for(mitk::IsoDoseLevelSet::ConstIterator setIT = isoDoseLevelSet->Begin(); setIT != isoDoseLevelSet->End(); ++setIT) + { +// transferFunction->AddHSVPoint(setIT->GetDoseValue()*100,(10*hsvValue),1.0,1.0,1.0,1.0); + transferFunction->AddRGBPoint(setIT->GetDoseValue()*prescribed,setIT->GetColor()[0],setIT->GetColor()[1],setIT->GetColor()[2]); + MITK_INFO << "ISOVALUE: " << setIT->GetDoseValue()*prescribed << endl; + MITK_INFO << "R: " << setIT->GetColor()[0] << " G: " << setIT->GetColor()[1] << " B: " << setIT->GetColor()[2] << endl; + } + + mitk::TransferFunction::Pointer mitkTransFunc = mitk::TransferFunction::New(); + mitk::TransferFunctionProperty::Pointer mitkTransFuncProp = mitk::TransferFunctionProperty::New(); + mitkTransFunc->SetColorTransferFunction(transferFunction); + mitkTransFuncProp->SetValue(mitkTransFunc); + + mitk::RenderingModeProperty::Pointer renderingMode = mitk::RenderingModeProperty::New(); + renderingMode->SetValue(mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR); + + selectedNode->SetProperty("Image Rendering.Transfer Function", mitkTransFuncProp); + selectedNode->SetProperty("Image Rendering.Mode", renderingMode); + mitk::IsoDoseLevelVector::Pointer levelVector = mitk::IsoDoseLevelVector::New(); mitk::IsoDoseLevelVectorProperty::Pointer levelVecProp = mitk::IsoDoseLevelVectorProperty::New(levelVector); selectedNode->SetProperty(mitk::rt::Constants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str(),levelVecProp); UpdateBySelectedNode(); +// mitk::RenderingModeProperty::Pointer renderingMode = mitk::RenderingModeProperty::New(); +// renderingMode->SetValue(mitk::RenderingModeProperty::ISODOSESHADER_COLOR); +// selectedNode->SetProperty("shader.mitkIsoLineShader.Gridscale", mitk::FloatProperty::New(10.0)); +// selectedNode->SetProperty("Image Rendering.Mode", renderingMode); + mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } } void RTDoseVisualizer::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*source*/, const QList& nodes ) { QList dataNodes = this->GetDataManagerSelection(); mitk::DataNode* selectedNode = NULL; if (!dataNodes.empty()) { bool isDoseNode = false; dataNodes[0]->GetBoolProperty(mitk::rt::Constants::DOSE_PROPERTY_NAME.c_str(),isDoseNode); if (isDoseNode) { selectedNode = dataNodes[0]; } } if (selectedNode != m_selectedNode.GetPointer()) { m_selectedNode = selectedNode; } UpdateBySelectedNode(); } void RTDoseVisualizer::UpdateBySelectedNode() { m_Controls.groupNodeSpecific->setEnabled(m_selectedNode.IsNotNull()); m_Controls.groupFreeValues->setEnabled(m_selectedNode.IsNotNull()); if(m_selectedNode.IsNull()) { m_Controls.NrOfFractions->setText(QString("N/A. No dose selected")); m_Controls.prescribedDoseSpecific->setText(QString("N/A. No dose selected")); m_freeIsoValues = mitk::IsoDoseLevelVector::New(); UpdateFreeIsoValues(); } else { /////////////////////////////////////////// //dose specific information int fracCount = 1; m_selectedNode->GetIntProperty(mitk::rt::Constants::DOSE_FRACTION_COUNT_PROPERTY_NAME.c_str(),fracCount); m_Controls.NrOfFractions->setText(QString::number(fracCount)); m_PrescribedDose_Data = 0.0; - mitk::DoubleProperty* propDouble = dynamic_cast(m_selectedNode->GetData()->GetProperty(mitk::rt::Constants::PRESCRIBED_DOSE_PROPERTY_NAME.c_str()).GetPointer()); - if (propDouble) - { - m_PrescribedDose_Data = propDouble->GetValue(); - } + + float tmp; + m_selectedNode->GetFloatProperty(mitk::rt::Constants::PRESCRIBED_DOSE_PROPERTY_NAME.c_str(),tmp); + m_PrescribedDose_Data = (double)tmp; +// dynamic cast von float zu doubleproperty funktioniert nicht & getProperty von node->getData() funktioniert das ? +// mitk::DoubleProperty* propDouble = dynamic_cast(m_selectedNode->GetData()->GetProperty(mitk::rt::Constants::PRESCRIBED_DOSE_PROPERTY_NAME.c_str()).GetPointer()); +// if (propDouble) +// { +// m_PrescribedDose_Data = propDouble->GetValue(); +// } m_Controls.prescribedDoseSpecific->setText(QString::number(m_PrescribedDose_Data)); /////////////////////////////////////////// //free iso lines mitk::IsoDoseLevelVectorProperty::Pointer propIsoVector; m_selectedNode->GetProperty(propIsoVector, mitk::rt::Constants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str()); if (propIsoVector.IsNull()) { m_freeIsoValues = mitk::IsoDoseLevelVector::New(); propIsoVector = mitk::IsoDoseLevelVectorProperty::New(m_freeIsoValues); m_selectedNode->SetProperty(mitk::rt::Constants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str(),propIsoVector); } else { m_freeIsoValues = propIsoVector->GetValue(); } UpdateFreeIsoValues(); /////////////////////////////////////////// //global dose issues bool showIsoLine = false; m_selectedNode->GetBoolProperty(mitk::rt::Constants::DOSE_SHOW_COLORWASH_PROPERTY_NAME.c_str(),showIsoLine); m_Controls.checkGlobalVisIsoLine->setChecked(showIsoLine); bool showColorWash = false; m_selectedNode->GetBoolProperty(mitk::rt::Constants::DOSE_SHOW_COLORWASH_PROPERTY_NAME.c_str(),showColorWash); m_Controls.checkGlobalVisColorWash->setChecked(showColorWash); float referenceDose = 0.0; m_selectedNode->GetFloatProperty(mitk::rt::Constants::REFERENCE_DOSE_PROPERTY_NAME.c_str(),referenceDose); m_Controls.spinReferenceDose->setValue(referenceDose); mitk::IsoDoseLevelSetProperty::Pointer propIsoSet = dynamic_cast(m_selectedNode->GetProperty(mitk::rt::Constants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str())); if (propIsoSet) { this->m_selectedNodeIsoSet = propIsoSet->GetValue(); this->m_LevelSetModel->setIsoDoseLevelSet(m_selectedNodeIsoSet); } } } void RTDoseVisualizer::UpdateByPreferences() { m_Presets = mitk::rt::LoadPresetsMap(); m_internalUpdate = true; m_Controls.comboPresets->clear(); this->m_selectedPresetName = mitk::rt::GetSelectedPresetName(); int index = 0; int selectedIndex = -1; for (mitk::rt::PresetMapType::const_iterator pos = m_Presets.begin(); pos != m_Presets.end(); ++pos, ++index) { m_Controls.comboPresets->addItem(QString(pos->first.c_str())); if (this->m_selectedPresetName == pos->first) { selectedIndex = index; } } if (selectedIndex == -1) { selectedIndex = 0; MITK_WARN << "Error. Cannot iso dose level preset specified in preferences does not exist. Preset name: "<m_selectedPresetName; this->m_selectedPresetName = m_Presets.begin()->first; mitk::rt::SetSelectedPresetName(this->m_selectedPresetName); MITK_INFO << "Changed selected iso dose level preset to first existing preset. New preset name: "<m_selectedPresetName; } m_Controls.comboPresets->setCurrentIndex(selectedIndex); this->m_selectedNodeIsoSet = this->m_Presets[this->m_selectedPresetName]; this->m_LevelSetModel->setIsoDoseLevelSet(m_selectedNodeIsoSet); mitk::DoseValueAbs referenceDose = 0.0; bool globalSync = mitk::rt::GetReferenceDoseValue(referenceDose); if (globalSync || this->m_selectedNode.IsNull()) { m_Controls.spinReferenceDose->setValue(referenceDose); } bool displayAbsoluteDose = mitk::rt::GetDoseDisplayAbsolute(); m_Controls.radioAbsDose->setChecked(displayAbsoluteDose); m_Controls.radioRelDose->setChecked(!displayAbsoluteDose); this->m_LevelSetModel->setShowAbsoluteDose(displayAbsoluteDose); m_internalUpdate = false; } void RTDoseVisualizer::OnCurrentPresetChanged(const QString& presetName) { if (! m_internalUpdate) { mitk::rt::SetSelectedPresetName(presetName.toStdString()); this->UpdateByPreferences(); this->ActualizeIsoLevelsForAllDoseDataNodes(); this->UpdateBySelectedNode(); } } void RTDoseVisualizer::ActualizeIsoLevelsForAllDoseDataNodes() { std::string presetName = mitk::rt::GetSelectedPresetName(); mitk::rt::PresetMapType presetMap = mitk::rt::LoadPresetsMap(); mitk::NodePredicateProperty::Pointer isDoseNode = mitk::NodePredicateProperty::New(mitk::rt::Constants::DOSE_PROPERTY_NAME.c_str(), mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetSubset(isDoseNode); mitk::IsoDoseLevelSet* selectedPreset = presetMap[presetName]; if (!selectedPreset) { mitkThrow() << "Error. Cannot actualize iso dose level preset. Selected preset idoes not exist. Preset name: "<begin(); pos != nodes->end(); ++pos) { mitk::IsoDoseLevelSet::Pointer clonedPreset = selectedPreset->Clone(); mitk::IsoDoseLevelSetProperty::Pointer propIsoSet = mitk::IsoDoseLevelSetProperty::New(clonedPreset); (*pos)->SetProperty(mitk::rt::Constants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str(),propIsoSet); } } void RTDoseVisualizer::ActualizeReferenceDoseForAllDoseDataNodes() { /** @TODO Klären ob diese präsentations info genauso wie*/ mitk::DoseValueAbs value = 0; bool sync = mitk::rt::GetReferenceDoseValue(value); if (sync) { mitk::NodePredicateProperty::Pointer isDoseNode = mitk::NodePredicateProperty::New(mitk::rt::Constants::DOSE_PROPERTY_NAME.c_str(), mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetSubset(isDoseNode); for(mitk::DataStorage::SetOfObjects::const_iterator pos = nodes->begin(); pos != nodes->end(); ++pos) { (*pos)->SetFloatProperty(mitk::rt::Constants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), value); } } } void RTDoseVisualizer::ActualizeDisplayStyleForAllDoseDataNodes() { /** @TODO Klären ob diese präsentations info global oder auch per node gespeichert wird*/ } diff --git a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizer.h b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizer.h index 17c25e8474..149682e876 100644 --- a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizer.h +++ b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizer.h @@ -1,145 +1,149 @@ /*=================================================================== 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 RTDoseVisualizer_h #define RTDoseVisualizer_h #include #include #include #include #include "ui_RTDoseVisualizerControls.h" #include #include #include "mitkDoseVisPreferenceHelper.h" // Shader #include #include #include /*forward declarations*/ class QmitkIsoDoseLevelSetModel; class QmitkDoseColorDelegate; class QmitkDoseValueDelegate; class QmitkDoseVisualStyleDelegate; /** \brief RTDoseVisualizer \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. \sa QmitkAbstractView \ingroup ${plugin_target}_internal */ class RTDoseVisualizer : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: RTDoseVisualizer(); virtual ~RTDoseVisualizer(); static const std::string VIEW_ID; protected slots: + void LoadISOLines(); + + void LoadRTDoseFile(); + void OnConvertButtonClicked(); void OnAddFreeValueClicked(); void OnRemoveFreeValueClicked(); void OnUsePrescribedDoseClicked(); void OnAbsDoseToggled(bool); void OnGlobalVisColorWashToggled(bool); void OnGlobalVisIsoLineToggled(bool); void OnShowContextMenuIsoSet(const QPoint&); void OnCurrentPresetChanged(const QString&); void OnReferenceDoseChanged(double); protected: virtual void CreateQtPartControl(QWidget *parent); virtual void SetFocus(); /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( berry::IWorkbenchPart::Pointer source, const QList& nodes ); /** Method updates the list widget according to the current m_freeIsoValues.*/ void UpdateFreeIsoValues(); /** Update the members according to the currently selected node */ void UpdateBySelectedNode(); /** Update the member widgets according to the information stored in the application preferences*/ void UpdateByPreferences(); /**helper function that iterates throug all data nodes and sets there iso level set property according to the selected preset. @TODO: should be moved outside the class, to be available for other classes at well.*/ void ActualizeIsoLevelsForAllDoseDataNodes(); /**helper function that iterates throug all data nodes and sets there reference dose value according to the preference. @TODO: should be moved outside the class, to be available for other classes at well.*/ void ActualizeReferenceDoseForAllDoseDataNodes(); /**helper function that iterates through all data nodes and sets there dose display style (relative/absolute) according to the preference. @TODO: should be moved outside the class, to be available for other classes at well.*/ void ActualizeDisplayStyleForAllDoseDataNodes(); Ui::RTDoseVisualizerControls m_Controls; mitk::DataNode::Pointer m_selectedNode; mitk::IsoDoseLevelVector::Pointer m_freeIsoValues; /** Iso level set of the current node. Should normaly be a clone of the * current iso preset. It held as own member because visibility * settings may differ.*/ mitk::IsoDoseLevelSet::Pointer m_selectedNodeIsoSet; mitk::rt::PresetMapType m_Presets; std::string m_selectedPresetName; /** Prescribed Dose of the selected data.*/ mitk::DoseValueAbs m_PrescribedDose_Data; QmitkIsoDoseLevelSetModel* m_LevelSetModel; QmitkDoseColorDelegate* m_DoseColorDelegate; QmitkDoseValueDelegate* m_DoseValueDelegate; QmitkDoseVisualStyleDelegate* m_DoseVisualDelegate; bool m_internalUpdate; }; #endif // RTDoseVisualizer_h diff --git a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizerControls.ui b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizerControls.ui index 8aed8dd35b..e2b09959ae 100644 --- a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizerControls.ui +++ b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/RTDoseVisualizerControls.ui @@ -1,465 +1,480 @@ RTDoseVisualizerControls 0 0 - 389 + 421 651 0 0 QmitkTemplate 5 5 Dose specific information: 5 5 Qt::Horizontal 40 20 true 30 Gy Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter true Number of fractions: Prescribed dose [Gy]: Use as reference dose true 0 0 Free iso lines: 5 5 0 0 0 40 16777215 110 0 25 Qt::Horizontal 40 20 Add Remove Global iso dose visualization: 5 5 0 0 65 0 Dose display: 0 0 65 0 Preset style: 0 0 105 0 Qt::NoFocus Reference dose [Gy]: 1 9999.000000000000000 + + 0.000000000000000 + relative [%] true absolute [Gy] true QAbstractItemView::SingleSelection QAbstractItemView::SelectRows 30 20 80 Global visibility: Qt::Horizontal 40 20 Isolines :/RTUI/eye_open.png 24 16 Colorwash :/RTUI/eye_open.png 24 16 255 0 0 255 0 0 255 0 0 255 0 0 120 120 120 120 120 120 Convert nod to dose node (for testing only!) + + + + LoadDoseFile + + + + + + + IsoLines + + + - - - + diff --git a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/org_mitk_gui_qt_rt_dosevisualization_Activator.cpp b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/org_mitk_gui_qt_rt_dosevisualization_Activator.cpp index 46f6463837..9c88998735 100644 --- a/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/org_mitk_gui_qt_rt_dosevisualization_Activator.cpp +++ b/Plugins/org.mitk.gui.qt.rt.dosevisualization/src/internal/org_mitk_gui_qt_rt_dosevisualization_Activator.cpp @@ -1,42 +1,44 @@ /*=================================================================== 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 "org_mitk_gui_qt_rt_dosevisualization_Activator.h" #include #include "RTDoseVisualizer.h" #include "RTUIPreferencePage.h" #include "DoseVisualizationPreferencePage.h" +#include "LoadingRTView.h" namespace mitk { void org_mitk_gui_qt_rt_dosevisualization_Activator::start(ctkPluginContext* context) { BERRY_REGISTER_EXTENSION_CLASS(RTDoseVisualizer, context); BERRY_REGISTER_EXTENSION_CLASS(RTUIPreferencePage, context) BERRY_REGISTER_EXTENSION_CLASS(DoseVisualizationPreferencePage, context) + BERRY_REGISTER_EXTENSION_CLASS(LoadingRTView, context) } void org_mitk_gui_qt_rt_dosevisualization_Activator::stop(ctkPluginContext* context) { Q_UNUSED(context) } } Q_EXPORT_PLUGIN2(org_mitk_gui_qt_rt_dosevisualization, mitk::org_mitk_gui_qt_rt_dosevisualization_Activator)