diff --git a/Core/Code/Algorithms/mitkExtractSliceFilter.cpp b/Core/Code/Algorithms/mitkExtractSliceFilter.cpp index 58dd938f99..3f1d857d5d 100644 --- a/Core/Code/Algorithms/mitkExtractSliceFilter.cpp +++ b/Core/Code/Algorithms/mitkExtractSliceFilter.cpp @@ -1,591 +1,591 @@ /*=================================================================== 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 "mitkExtractSliceFilter.h" #include #include #include #include #include #include mitk::ExtractSliceFilter::ExtractSliceFilter(vtkImageReslice* reslicer ){ if(reslicer == NULL){ m_Reslicer = vtkSmartPointer::New(); } else { m_Reslicer = reslicer; } m_TimeStep = 0; m_Reslicer->ReleaseDataFlagOn(); m_InterpolationMode = ExtractSliceFilter::RESLICE_NEAREST; m_ResliceTransform = NULL; m_InPlaneResampleExtentByGeometry = false; m_OutPutSpacing = new mitk::ScalarType[2]; m_OutputDimension = 2; m_ZSpacing = 1.0; m_ZMin = 0; m_ZMax = 0; m_VtkOutputRequested = false; } mitk::ExtractSliceFilter::~ExtractSliceFilter(){ m_ResliceTransform = NULL; m_WorldGeometry = NULL; delete [] m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateOutputInformation(){ //TODO try figure out how to set the specs of the slice before it is actually extracted /*Image::Pointer output = this->GetOutput(); Image::ConstPointer input = this->GetInput(); if (input.IsNull()) return; unsigned int dimensions[2]; dimensions[0] = m_WorldGeometry->GetExtent(0); dimensions[1] = m_WorldGeometry->GetExtent(1); output->Initialize(input->GetPixelType(), 2, dimensions, 1);*/ } void mitk::ExtractSliceFilter::GenerateInputRequestedRegion(){ //As we want all pixel information fo the image in our plane, the requested region //is set to the largest possible region in the image. //This is needed because an oblique plane has a larger extent then the image //and the in pipeline it is checked via PropagateResquestedRegion(). But the //extent of the slice is actually fitting because it is oblique within the image. ImageToImageFilter::InputImagePointer input = const_cast< ImageToImageFilter::InputImageType* > ( this->GetInput() ); input->SetRequestedRegionToLargestPossibleRegion(); } mitk::ScalarType* mitk::ExtractSliceFilter::GetOutputSpacing(){ return m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateData(){ mitk::Image *input = const_cast< mitk::Image * >( this->GetInput() ); if (!input) { MITK_ERROR << "mitk::ExtractSliceFilter: No input image available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No input image available. Please set the input!"); return; } if(!m_WorldGeometry) { MITK_ERROR << "mitk::ExtractSliceFilter: No Geometry for reslicing available." << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No Geometry for reslicing available."); return; } const TimeSlicedGeometry *inputTimeGeometry = this->GetInput()->GetTimeSlicedGeometry(); if ( ( inputTimeGeometry == NULL ) || ( inputTimeGeometry->GetTimeSteps() == 0 ) ) { itkWarningMacro(<<"Error reading input image TimeSlicedGeometry."); return; } // is it a valid timeStep? if ( inputTimeGeometry->IsValidTime( m_TimeStep ) == false ) { itkWarningMacro(<<"This is not a valid timestep: "<< m_TimeStep ); return; } // check if there is something to display. if ( ! input->IsVolumeSet( m_TimeStep ) ) { itkWarningMacro(<<"No volume data existent at given timestep "<< m_TimeStep ); return; } /*================#BEGIN setup vtkImageRslice properties================*/ Point3D origin; Vector3D right, bottom, normal; double widthInMM, heightInMM; Vector2D extent; const PlaneGeometry* planeGeometry = dynamic_cast(m_WorldGeometry); if ( planeGeometry != NULL ) { //if the worldGeomatry is a PlaneGeometry everthing is straight forward origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector( 0 ); bottom = planeGeometry->GetAxisVector( 1 ); normal = planeGeometry->GetNormal(); if ( m_InPlaneResampleExtentByGeometry ) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. extent[0] = m_WorldGeometry->GetExtent( 0 ); extent[1] = m_WorldGeometry->GetExtent( 1 ); } else { // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. Vector3D rightInIndex, bottomInIndex; inputTimeGeometry->GetGeometry3D( m_TimeStep )->WorldToIndex( right, rightInIndex ); inputTimeGeometry->GetGeometry3D( m_TimeStep )->WorldToIndex( bottom, bottomInIndex ); extent[0] = rightInIndex.GetNorm(); extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = m_WorldGeometry->GetExtentInMM( 0 ); heightInMM = m_WorldGeometry->GetExtentInMM( 1 ); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); /* * Transform the origin to center based coordinates. * Note: * This is needed besause vtk's origin is center based too (!!!) ( see 'The VTK book' page 88 ) * and the worldGeometry surrouding the image is no imageGeometry. So the worldGeometry * has its origin at the corner of the voxel and needs to be transformed. */ origin += right * ( m_OutPutSpacing[0] * 0.5 ); origin += bottom * ( m_OutPutSpacing[1] * 0.5 ); //set the tranform for reslicing. // Use inverse transform of the input geometry for reslicing the 3D image. // This is needed if the image volume already transformed if(m_ResliceTransform != NULL) m_Reslicer->SetResliceTransform(m_ResliceTransform->GetVtkTransform()->GetLinearInverse()); // Set background level to TRANSLUCENT (see Geometry2DDataVtkMapper3D), // else the background of the image turns out gray m_Reslicer->SetBackgroundLevel( -32768 ); } else{ //Code for curved planes, mostly taken 1:1 from imageVtkMapper2D and not tested yet. // Do we have an AbstractTransformGeometry? // This is the case for AbstractTransformGeometry's (e.g. a ThinPlateSplineCurvedGeometry ) const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(m_WorldGeometry); if(abstractGeometry != NULL) { m_ResliceTransform = abstractGeometry; extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; origin = abstractGeometry->GetPlane()->GetOrigin(); right = abstractGeometry->GetPlane()->GetAxisVector(0); right.Normalize(); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); bottom.Normalize(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkSmartPointer composedResliceTransform = vtkGeneralTransform::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputTimeGeometry->GetGeometry3D( m_TimeStep )->GetVtkTransform()->GetLinearInverse() ); composedResliceTransform->Concatenate( abstractGeometry->GetVtkAbstractTransform() ); m_Reslicer->SetResliceTransform( composedResliceTransform ); composedResliceTransform->UnRegister( NULL ); // decrease RC // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see Geometry2DDataVtkMapper3D) m_Reslicer->SetBackgroundLevel( -1023 ); } else { itkExceptionMacro("mitk::ExtractSliceFilter: No fitting geometry for reslice axis!"); return; } } if(m_ResliceTransform != NULL){ //if the resliceTransform is set the reslice axis are recalculated. //Thus the geometry information is not fitting. Therefor a unitSpacingFilter //is used to set up a global spacing of 1 and compensate the transform. vtkSmartPointer unitSpacingImageFilter = vtkSmartPointer::New() ; unitSpacingImageFilter->ReleaseDataFlagOn(); unitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); unitSpacingImageFilter->SetInput( input->GetVtkImageData(m_TimeStep) ); m_Reslicer->SetInput(unitSpacingImageFilter->GetOutput() ); } else { //if no tranform is set the image can be used directly m_Reslicer->SetInput(input->GetVtkImageData(m_TimeStep)); } /*setup the plane where vktImageReslice extracts the slice*/ //ResliceAxesOrigin is the ancor point of the plane double originInVtk[3]; itk2vtk(origin, originInVtk); m_Reslicer->SetResliceAxesOrigin(originInVtk); //the cosines define the plane: x and y are the direction vectors, n is the planes normal //this specifies a matrix 3x3 // x1 y1 n1 // x2 y2 n2 // x3 y3 n3 double cosines[9]; vnl2vtk(right.GetVnlVector(), cosines);//x vnl2vtk(bottom.GetVnlVector(), cosines + 3);//y vnl2vtk(normal.GetVnlVector(), cosines + 6);//n m_Reslicer->SetResliceAxesDirectionCosines(cosines); //we only have one slice, not a volume m_Reslicer->SetOutputDimensionality(m_OutputDimension); //set the interpolation mode for slicing switch(this->m_InterpolationMode){ case RESLICE_NEAREST: m_Reslicer->SetInterpolationModeToNearestNeighbor(); break; case RESLICE_LINEAR: m_Reslicer->SetInterpolationModeToLinear(); break; case RESLICE_CUBIC: m_Reslicer->SetInterpolationModeToCubic(); default: //the default interpolation used by mitk m_Reslicer->SetInterpolationModeToNearestNeighbor(); } /*========== BEGIN setup extent of the slice ==========*/ int xMin, xMax, yMin, yMax; vtkFloatingPointType sliceBounds[6]; if(m_WorldGeometry->GetReferenceGeometry()){ for ( int i = 0; i < 6; ++i ) { sliceBounds[i] = 0.0; } this->GetClippedPlaneBounds( m_WorldGeometry->GetReferenceGeometry(), planeGeometry, sliceBounds ); // Calculate output extent (integer values) xMin = static_cast< int >( sliceBounds[0] / m_OutPutSpacing[0] + 0.5 ); xMax = static_cast< int >( sliceBounds[1] / m_OutPutSpacing[0] + 0.5 ); yMin = static_cast< int >( sliceBounds[2] / m_OutPutSpacing[1] + 0.5 ); yMax = static_cast< int >( sliceBounds[3] / m_OutPutSpacing[1] + 0.5 ); }else { // If no reference geometry is available, we also don't know about the // maximum plane size; xMin = yMin = 0; xMax = static_cast< int >( extent[0]); yMax = static_cast< int >( extent[1]); } m_Reslicer->SetOutputExtent(xMin, xMax-1, yMin, yMax-1, m_ZMin, m_ZMax ); /*========== END setup extent of the slice ==========*/ m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); m_Reslicer->SetOutputSpacing( m_OutPutSpacing[0], m_OutPutSpacing[1], m_ZSpacing ); //TODO check the following lines, they are responsible wether vtk error outputs appear or not m_Reslicer->UpdateWholeExtent(); //this produces a bad allocation error for 2D images //m_Reslicer->GetOutput()->UpdateInformation(); //m_Reslicer->GetOutput()->SetUpdateExtentToWholeExtent(); //start the pipeline m_Reslicer->Update(); /*================ #END setup vtkImageRslice properties================*/ if(m_VtkOutputRequested){ return; //no converting to mitk //no mitk geometry will be set, as the output is vtkImageData only!!! } else { /*================ #BEGIN Get the slice from vtkImageReslice and convert it to mit::Image================*/ vtkImageData* reslicedImage; reslicedImage = m_Reslicer->GetOutput(); - if(!reslicedImage) { itkWarningMacro(<<"Reslicer returned empty image"); return; } mitk::Image::Pointer resultImage = this->GetOutput(); //initialize resultimage with the specs of the vtkImageData object returned from vtkImageReslice resultImage->Initialize(reslicedImage); //transfer the voxel data resultImage->SetVolume(reslicedImage->GetScalarPointer()); //set the geometry from current worldgeometry for the reusultimage //this is needed that the image has the correct mitk geometry //the originalGeometry is the Geometry of the result slice AffineGeometryFrame3D::Pointer originalGeometryAGF = m_WorldGeometry->Clone(); Geometry2D::Pointer originalGeometry = dynamic_cast( originalGeometryAGF.GetPointer() ); originalGeometry->GetIndexToWorldTransform()->SetMatrix(m_WorldGeometry->GetIndexToWorldTransform()->GetMatrix()); //the origin of the worldGeometry is transformed to center based coordinates to be an imageGeometry Point3D sliceOrigin = originalGeometry->GetOrigin(); sliceOrigin += right * ( m_OutPutSpacing[0] * 0.5 ); sliceOrigin += bottom * ( m_OutPutSpacing[1] * 0.5 ); //a worldGeometry is no imageGeometry, thus it is manually set to true originalGeometry->ImageGeometryOn(); /*At this point we have to adjust the geometry because the origin isn't correct. The wrong origin is related to the rotation of the current world geometry plane. This causes errors on transfering world to index coordinates. We just shift the origin in each direction about the amount of the expanding (needed while rotating the plane). */ Vector3D axis0 = originalGeometry->GetAxisVector(0); Vector3D axis1 = originalGeometry->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); //adapt the origin. Note that for orthogonal planes the minima are '0' and thus the origin stays the same. sliceOrigin += (axis0 * (xMin * m_OutPutSpacing[0])) + (axis1 * (yMin * m_OutPutSpacing[1])); originalGeometry->SetOrigin(sliceOrigin); originalGeometry->Modified(); resultImage->SetGeometry( originalGeometry ); /*the bounds as well as the extent of the worldGeometry are not adapted correctly during crosshair rotation. This is only a quick fix and has to be evaluated. The new bounds are set via the max values of the calcuted slice extent. It will look like [ 0, x, 0, y, 0, 1]. */ mitk::BoundingBox::BoundsArrayType boundsCopy; boundsCopy[0] = boundsCopy[2] = boundsCopy[4] = 0; boundsCopy[5] = 1; boundsCopy[1] = xMax - xMin; boundsCopy[3] = yMax - yMin; resultImage->GetGeometry()->SetBounds(boundsCopy); /*================ #END Get the slice from vtkImageReslice and convert it to mitk Image================*/ } } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds(vtkFloatingPointType bounds[6]){ if(!m_WorldGeometry || !this->GetInput()) return false; return this->GetClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), dynamic_cast< const PlaneGeometry * >( m_WorldGeometry ), bounds); + } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ) { bool b = this->CalculateClippedPlaneBounds(boundingGeometry, planeGeometry, bounds); return b; } bool mitk::ExtractSliceFilter ::CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ) { // Clip the plane with the bounding geometry. To do so, the corner points // of the bounding box are transformed by the inverse transformation // matrix, and the transformed bounding box edges derived therefrom are // clipped with the plane z=0. The resulting min/max values are taken as // bounds for the image reslicer. const BoundingBox *boundingBox = boundingGeometry->GetBoundingBox(); BoundingBox::PointType bbMin = boundingBox->GetMinimum(); BoundingBox::PointType bbMax = boundingBox->GetMaximum(); vtkPoints *points = vtkPoints::New(); if(boundingGeometry->GetImageGeometry()) { points->InsertPoint( 0, bbMin[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 1, bbMin[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 2, bbMin[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 3, bbMin[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 4, bbMax[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 5, bbMax[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 6, bbMax[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 7, bbMax[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); } else { points->InsertPoint( 0, bbMin[0], bbMin[1], bbMin[2] ); points->InsertPoint( 1, bbMin[0], bbMin[1], bbMax[2] ); points->InsertPoint( 2, bbMin[0], bbMax[1], bbMax[2] ); points->InsertPoint( 3, bbMin[0], bbMax[1], bbMin[2] ); points->InsertPoint( 4, bbMax[0], bbMin[1], bbMin[2] ); points->InsertPoint( 5, bbMax[0], bbMin[1], bbMax[2] ); points->InsertPoint( 6, bbMax[0], bbMax[1], bbMax[2] ); points->InsertPoint( 7, bbMax[0], bbMax[1], bbMin[2] ); } vtkPoints *newPoints = vtkPoints::New(); vtkTransform *transform = vtkTransform::New(); transform->Identity(); transform->Concatenate( planeGeometry->GetVtkTransform()->GetLinearInverse() ); transform->Concatenate( boundingGeometry->GetVtkTransform() ); transform->TransformPoints( points, newPoints ); transform->Delete(); bounds[0] = bounds[2] = 10000000.0; bounds[1] = bounds[3] = -10000000.0; bounds[4] = bounds[5] = 0.0; this->LineIntersectZero( newPoints, 0, 1, bounds ); this->LineIntersectZero( newPoints, 1, 2, bounds ); this->LineIntersectZero( newPoints, 2, 3, bounds ); this->LineIntersectZero( newPoints, 3, 0, bounds ); this->LineIntersectZero( newPoints, 0, 4, bounds ); this->LineIntersectZero( newPoints, 1, 5, bounds ); this->LineIntersectZero( newPoints, 2, 6, bounds ); this->LineIntersectZero( newPoints, 3, 7, bounds ); this->LineIntersectZero( newPoints, 4, 5, bounds ); this->LineIntersectZero( newPoints, 5, 6, bounds ); this->LineIntersectZero( newPoints, 6, 7, bounds ); this->LineIntersectZero( newPoints, 7, 4, bounds ); // clean up vtk data points->Delete(); newPoints->Delete(); if ( (bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0) ) { return false; } else { // The resulting bounds must be adjusted by the plane spacing, since we // we have so far dealt with index coordinates const float *planeSpacing = planeGeometry->GetFloatSpacing(); bounds[0] *= planeSpacing[0]; bounds[1] *= planeSpacing[0]; bounds[2] *= planeSpacing[1]; bounds[3] *= planeSpacing[1]; bounds[4] *= planeSpacing[2]; bounds[5] *= planeSpacing[2]; return true; } } bool mitk::ExtractSliceFilter ::LineIntersectZero( vtkPoints *points, int p1, int p2, vtkFloatingPointType *bounds ) { vtkFloatingPointType point1[3]; vtkFloatingPointType point2[3]; points->GetPoint( p1, point1 ); points->GetPoint( p2, point2 ); if ( (point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]) ) { double x, y; x = ( point1[0] * point2[2] - point1[2] * point2[0] ) / ( point2[2] - point1[2] ); y = ( point1[1] * point2[2] - point1[2] * point2[1] ) / ( point2[2] - point1[2] ); if ( x < bounds[0] ) { bounds[0] = x; } if ( x > bounds[1] ) { bounds[1] = x; } if ( y < bounds[2] ) { bounds[2] = y; } if ( y > bounds[3] ) { bounds[3] = y; } bounds[4] = bounds[5] = 0.0; return true; } return false; } diff --git a/Core/Code/files.cmake b/Core/Code/files.cmake index fd463e7704..2879048138 100644 --- a/Core/Code/files.cmake +++ b/Core/Code/files.cmake @@ -1,302 +1,302 @@ set(H_FILES Algorithms/itkImportMitkImageContainer.h Algorithms/itkImportMitkImageContainer.txx Algorithms/itkLocalVariationImageFilter.h Algorithms/itkLocalVariationImageFilter.txx Algorithms/itkMITKScalarImageToHistogramGenerator.h Algorithms/itkMITKScalarImageToHistogramGenerator.txx Algorithms/itkTotalVariationDenoisingImageFilter.h Algorithms/itkTotalVariationDenoisingImageFilter.txx Algorithms/itkTotalVariationSingleIterationImageFilter.h Algorithms/itkTotalVariationSingleIterationImageFilter.txx Algorithms/mitkBilateralFilter.h Algorithms/mitkBilateralFilter.cpp Algorithms/mitkInstantiateAccessFunctions.h Algorithms/mitkPixelTypeList.h # Preprocessor macros taken from Boost Algorithms/mitkPPArithmeticDec.h Algorithms/mitkPPArgCount.h Algorithms/mitkPPCat.h Algorithms/mitkPPConfig.h Algorithms/mitkPPControlExprIIf.h Algorithms/mitkPPControlIf.h Algorithms/mitkPPControlIIf.h Algorithms/mitkPPDebugError.h Algorithms/mitkPPDetailAutoRec.h Algorithms/mitkPPDetailDMCAutoRec.h Algorithms/mitkPPExpand.h Algorithms/mitkPPFacilitiesEmpty.h Algorithms/mitkPPFacilitiesExpand.h Algorithms/mitkPPLogicalBool.h Algorithms/mitkPPRepetitionDetailDMCFor.h Algorithms/mitkPPRepetitionDetailEDGFor.h Algorithms/mitkPPRepetitionDetailFor.h Algorithms/mitkPPRepetitionDetailMSVCFor.h Algorithms/mitkPPRepetitionFor.h Algorithms/mitkPPSeqElem.h Algorithms/mitkPPSeqForEach.h Algorithms/mitkPPSeqForEachProduct.h Algorithms/mitkPPSeq.h Algorithms/mitkPPSeqEnum.h Algorithms/mitkPPSeqSize.h Algorithms/mitkPPSeqToTuple.h Algorithms/mitkPPStringize.h Algorithms/mitkPPTupleEat.h Algorithms/mitkPPTupleElem.h Algorithms/mitkPPTupleRem.h Algorithms/mitkClippedSurfaceBoundsCalculator.h - Algorithms/mitkExtractSliceFilter.h + Algorithms/mitkExtractSliceFilter.h DataManagement/mitkImageAccessByItk.h DataManagement/mitkImageCast.h DataManagement/mitkITKImageImport.h DataManagement/mitkITKImageImport.txx DataManagement/mitkImageToItk.h DataManagement/mitkImageToItk.txx Interfaces/mitkIDataNodeReader.h - + IO/mitkPixelTypeTraits.h Interactions/mitkEventMapperAddOn.h Common/mitkExceptionMacro.h Common/mitkTestingMacros.h ) set(CPP_FILES Algorithms/mitkBaseDataSource.cpp Algorithms/mitkBaseProcess.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkGeometry2DDataToSurfaceFilter.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkExtractSliceFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkCoreActivator.cpp Controllers/mitkFocusManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkSlicesRotator.cpp Controllers/mitkSlicesSwiveller.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkInteractorCameraController.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataStorage.cpp #DataManagement/mitkDataTree.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataNodeFactory.cpp #DataManagement/mitkDataTreeStorage.cpp DataManagement/mitkDisplayGeometry.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkGeometry2D.cpp DataManagement/mitkGeometry2DData.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkLandmarkBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModeOperation.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStateTransitionOperation.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeSlicedGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkVector.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkShaderProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkMaterial.cpp Interactions/mitkAction.cpp Interactions/mitkAffineInteractor.cpp Interactions/mitkCoordinateSupplier.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkDisplayPositionEvent.cpp Interactions/mitkDisplayVectorInteractor.cpp Interactions/mitkDisplayVectorInteractorLevelWindow.cpp Interactions/mitkDisplayVectorInteractorScroll.cpp Interactions/mitkEvent.cpp Interactions/mitkEventDescription.cpp Interactions/mitkEventMapper.cpp Interactions/mitkGlobalInteraction.cpp Interactions/mitkInteractor.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMovePointSetInteractor.cpp Interactions/mitkMoveSurfaceInteractor.cpp Interactions/mitkNodeDepententPointSetInteractor.cpp Interactions/mitkPointSetInteractor.cpp Interactions/mitkPositionEvent.cpp Interactions/mitkPositionTracker.cpp Interactions/mitkState.cpp Interactions/mitkStateEvent.cpp Interactions/mitkStateMachine.cpp Interactions/mitkStateMachineFactory.cpp Interactions/mitkTransition.cpp Interactions/mitkWheelEvent.cpp Interactions/mitkKeyEvent.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkCrosshairPositionEvent.cpp IO/mitkBaseDataIOFactory.cpp IO/mitkCoreDataNodeReader.cpp IO/mitkDicomSeriesReader.cpp IO/mitkFileReader.cpp IO/mitkFileSeriesReader.cpp IO/mitkFileWriter.cpp #IO/mitkIpPicGet.c IO/mitkImageGenerator.cpp IO/mitkImageWriter.cpp IO/mitkImageWriterFactory.cpp IO/mitkItkImageFileIOFactory.cpp IO/mitkItkImageFileReader.cpp IO/mitkItkPictureWrite.cpp IO/mitkIOUtil.cpp IO/mitkLookupTableProperty.cpp IO/mitkOperation.cpp #IO/mitkPicFileIOFactory.cpp #IO/mitkPicFileReader.cpp #IO/mitkPicFileWriter.cpp #IO/mitkPicHelper.cpp #IO/mitkPicVolumeTimeSeriesIOFactory.cpp #IO/mitkPicVolumeTimeSeriesReader.cpp IO/mitkPixelType.cpp IO/mitkPointSetIOFactory.cpp IO/mitkPointSetReader.cpp IO/mitkPointSetWriter.cpp IO/mitkPointSetWriterFactory.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSTLFileIOFactory.cpp IO/mitkSTLFileReader.cpp IO/mitkSurfaceVtkWriter.cpp IO/mitkSurfaceVtkWriterFactory.cpp IO/mitkVtiFileIOFactory.cpp IO/mitkVtiFileReader.cpp IO/mitkVtkImageIOFactory.cpp IO/mitkVtkImageReader.cpp IO/mitkVtkSurfaceIOFactory.cpp IO/mitkVtkSurfaceReader.cpp IO/vtkPointSetXMLParser.cpp IO/mitkLog.cpp Rendering/mitkBaseRenderer.cpp Rendering/mitkVtkMapper2D.cpp Rendering/mitkVtkMapper3D.cpp Rendering/mitkRenderWindowFrame.cpp Rendering/mitkGeometry2DDataMapper2D.cpp Rendering/mitkGeometry2DDataVtkMapper3D.cpp Rendering/mitkGLMapper2D.cpp Rendering/mitkGradientBackground.cpp Rendering/mitkManufacturerLogo.cpp Rendering/mitkMapper2D.cpp Rendering/mitkMapper3D.cpp Rendering/mitkMapper.cpp Rendering/mitkPointSetGLMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkPolyDataGLMapper2D.cpp Rendering/mitkSurfaceGLMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVolumeDataVtkMapper3D.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkShaderRepository.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkMitkApplyLevelWindowToRGBFilter.cpp Common/mitkException.cpp Common/mitkCommon.h Common/mitkCoreObjectFactoryBase.cpp Common/mitkCoreObjectFactory.cpp ) list(APPEND CPP_FILES ${CppMicroServices_SOURCES}) diff --git a/Modules/ImageExtraction/Testing/files.cmake b/Modules/ImageExtraction/Testing/files.cmake index c983818824..aa6e367684 100644 --- a/Modules/ImageExtraction/Testing/files.cmake +++ b/Modules/ImageExtraction/Testing/files.cmake @@ -1,13 +1,13 @@ set(MODULE_IMAGE_TESTS - + ) set(MODULE_TESTIMAGES US4DCyl.pic.gz Pic3D.pic.gz Pic2DplusT.pic.gz BallBinary30x30x30.pic.gz Png2D-bw.png binary.stl ball.stl ) diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp index 7a910aec58..5829c201cd 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp @@ -1,509 +1,511 @@ /*=================================================================== 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 "mitkExtractDirectedPlaneImageFilter.h" #include "mitkAbstractTransformGeometry.h" //#include "mitkImageMapperGL2D.h" #include #include #include #include #include "vtkMitkThickSlicesFilter.h" #include #include #include #include #include #include #include #include "pic2vtk.h" mitk::ExtractDirectedPlaneImageFilter::ExtractDirectedPlaneImageFilter() : m_WorldGeometry(NULL) { + MITK_WARN << "Class ExtractDirectedPlaneImageFilter is deprecated! Use ExtractSliceFilter instead."; + m_Reslicer = vtkImageReslice::New(); m_TargetTimestep = 0; m_InPlaneResampleExtentByGeometry = true; m_ResliceInterpolationProperty = NULL;//VtkResliceInterpolationProperty::New(); //TODO initial with value m_ThickSlicesMode = 0; m_ThickSlicesNum = 1; } mitk::ExtractDirectedPlaneImageFilter::~ExtractDirectedPlaneImageFilter() { if(m_ResliceInterpolationProperty!=NULL)m_ResliceInterpolationProperty->Delete(); m_Reslicer->Delete(); } void mitk::ExtractDirectedPlaneImageFilter::GenerateData() { // A world geometry must be set... if ( m_WorldGeometry == NULL ) { itkWarningMacro(<<"No world geometry has been set. Returning."); return; } Image *input = const_cast< ImageToImageFilter::InputImageType* >( this->GetInput() ); input->Update(); if ( input == NULL ) { itkWarningMacro(<<"No input set."); return; } const TimeSlicedGeometry *inputTimeGeometry = input->GetTimeSlicedGeometry(); if ( ( inputTimeGeometry == NULL ) || ( inputTimeGeometry->GetTimeSteps() == 0 ) ) { itkWarningMacro(<<"Error reading input image geometry."); return; } // Get the target timestep; if none is set, use the lowest given. unsigned int timestep = 0; if ( ! m_TargetTimestep ) { ScalarType time = m_WorldGeometry->GetTimeBounds()[0]; if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) { timestep = inputTimeGeometry->MSToTimeStep( time ); } } else timestep = m_TargetTimestep; if ( inputTimeGeometry->IsValidTime( timestep ) == false ) { itkWarningMacro(<<"This is not a valid timestep: "<IsVolumeSet( timestep ) ) { itkWarningMacro(<<"No volume data existent at given timestep "<GetLargestPossibleRegion(); requestedRegion.SetIndex( 3, timestep ); requestedRegion.SetSize( 3, 1 ); requestedRegion.SetSize( 4, 1 ); input->SetRequestedRegion( &requestedRegion ); input->Update(); vtkImageData* inputData = input->GetVtkImageData( timestep ); if ( inputData == NULL ) { itkWarningMacro(<<"Could not extract vtk image data for given timestep"<GetSpacing( spacing ); // how big the area is in physical coordinates: widthInMM x heightInMM pixels mitk::ScalarType widthInMM, heightInMM; // where we want to sample Point3D origin; Vector3D right, bottom, normal; Vector3D rightInIndex, bottomInIndex; assert( input->GetTimeSlicedGeometry() == inputTimeGeometry ); // take transform of input image into account Geometry3D* inputGeometry = inputTimeGeometry->GetGeometry3D( timestep ); if ( inputGeometry == NULL ) { itkWarningMacro(<<"There is no Geometry3D at given timestep "<( m_WorldGeometry ) != NULL ) { const PlaneGeometry *planeGeometry = static_cast< const PlaneGeometry * >( m_WorldGeometry ); origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector( 0 ); bottom = planeGeometry->GetAxisVector( 1 ); normal = planeGeometry->GetNormal(); if ( m_InPlaneResampleExtentByGeometry ) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. extent[0] = m_WorldGeometry->GetExtent( 0 ); extent[1] = m_WorldGeometry->GetExtent( 1 ); } else { // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. inputGeometry->WorldToIndex( right, rightInIndex ); inputGeometry->WorldToIndex( bottom, bottomInIndex ); extent[0] = rightInIndex.GetNorm(); extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = m_WorldGeometry->GetExtentInMM( 0 ); heightInMM = m_WorldGeometry->GetExtentInMM( 1 ); mmPerPixel[0] = widthInMM / extent[0]; mmPerPixel[1] = heightInMM / extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); //origin += right * ( mmPerPixel[0] * 0.5 ); //origin += bottom * ( mmPerPixel[1] * 0.5 ); //widthInMM -= mmPerPixel[0]; //heightInMM -= mmPerPixel[1]; // Use inverse transform of the input geometry for reslicing the 3D image m_Reslicer->SetResliceTransform( inputGeometry->GetVtkTransform()->GetLinearInverse() ); // Set background level to TRANSLUCENT (see Geometry2DDataVtkMapper3D) m_Reslicer->SetBackgroundLevel( -32768 ); // Check if a reference geometry does exist (as would usually be the case for // PlaneGeometry). // Note: this is currently not strictly required, but could facilitate // correct plane clipping. if ( m_WorldGeometry->GetReferenceGeometry() ) { // 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. boundsInitialized = this->CalculateClippedPlaneBounds( m_WorldGeometry->GetReferenceGeometry(), planeGeometry, bounds ); } } // Do we have an AbstractTransformGeometry? else if ( dynamic_cast< const AbstractTransformGeometry * >( m_WorldGeometry ) ) { const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(m_WorldGeometry); extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); mmPerPixel[0] = widthInMM / extent[0]; mmPerPixel[1] = heightInMM / extent[1]; origin = abstractGeometry->GetPlane()->GetOrigin(); right = abstractGeometry->GetPlane()->GetAxisVector(0); right.Normalize(); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); bottom.Normalize(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputGeometry->GetVtkTransform()->GetLinearInverse() ); composedResliceTransform->Concatenate( abstractGeometry->GetVtkAbstractTransform() ); m_Reslicer->SetResliceTransform( composedResliceTransform ); // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see Geometry2DDataVtkMapper3D) m_Reslicer->SetBackgroundLevel( -1023 ); composedResliceTransform->Delete(); } else { itkWarningMacro(<<"World Geometry has to be a PlaneGeometry or an AbstractTransformGeometry."); return; } // Make sure that the image to be resliced has a certain minimum size. if ( (extent[0] <= 2) && (extent[1] <= 2) ) { itkWarningMacro(<<"Image is too small to be resliced..."); return; } vtkImageChangeInformation * unitSpacingImageFilter = vtkImageChangeInformation::New() ; unitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); unitSpacingImageFilter->SetInput( inputData ); m_Reslicer->SetInput( unitSpacingImageFilter->GetOutput() ); unitSpacingImageFilter->Delete(); //m_Reslicer->SetInput( inputData ); m_Reslicer->SetOutputDimensionality( 2 ); m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); Vector2D pixelsPerMM; pixelsPerMM[0] = 1.0 / mmPerPixel[0]; pixelsPerMM[1] = 1.0 / mmPerPixel[1]; //calulate the originArray and the orientations for the reslice-filter double originArray[3]; itk2vtk( origin, originArray ); m_Reslicer->SetResliceAxesOrigin( originArray ); double cosines[9]; // direction of the X-axis of the sampled result vnl2vtk( right.Get_vnl_vector(), cosines ); // direction of the Y-axis of the sampled result vnl2vtk( bottom.Get_vnl_vector(), cosines + 3 ); // normal of the plane vnl2vtk( normal.Get_vnl_vector(), cosines + 6 ); m_Reslicer->SetResliceAxesDirectionCosines( cosines ); int xMin, xMax, yMin, yMax; if ( boundsInitialized ) { xMin = static_cast< int >( bounds[0] / mmPerPixel[0] );//+ 0.5 ); xMax = static_cast< int >( bounds[1] / mmPerPixel[0] );//+ 0.5 ); yMin = static_cast< int >( bounds[2] / mmPerPixel[1] );//+ 0.5); yMax = static_cast< int >( bounds[3] / mmPerPixel[1] );//+ 0.5 ); } else { // If no reference geometry is available, we also don't know about the // maximum plane size; so the overlap is just ignored xMin = yMin = 0; xMax = static_cast< int >( extent[0] - pixelsPerMM[0] );//+ 0.5 ); yMax = static_cast< int >( extent[1] - pixelsPerMM[1] );//+ 0.5 ); } m_Reslicer->SetOutputSpacing( mmPerPixel[0], mmPerPixel[1], 1.0 ); // xMax and yMax are meant exclusive until now, whereas // SetOutputExtent wants an inclusive bound. Thus, we need // to subtract 1. m_Reslicer->SetOutputExtent( xMin, xMax-1, yMin, yMax-1, 0, 1 ); // 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. m_Reslicer->Modified(); m_Reslicer->ReleaseDataFlagOn(); m_Reslicer->Update(); // 1. Check the result vtkImageData* reslicedImage = m_Reslicer->GetOutput(); //mitkIpPicDescriptor *pic = Pic2vtk::convert( reslicedImage ); if((reslicedImage == NULL) || (reslicedImage->GetDataDimension() < 1)) { itkWarningMacro(<<"Reslicer returned empty image"); return; } unsigned int dimensions[2]; dimensions[0] = (unsigned int)extent[0]; dimensions[1] = (unsigned int)extent[1]; Vector3D spacingVector; FillVector3D(spacingVector, mmPerPixel[0], mmPerPixel[1], 1.0); mitk::Image::Pointer resultImage = this->GetOutput(); resultImage->Initialize(input->GetPixelType(), 2, dimensions ); //resultImage->Initialize( pic ); resultImage->SetSpacing( spacingVector ); //resultImage->SetPicVolume( pic ); //mitkIpPicFree(pic); /*unsigned int dimensions[2]; dimensions[0] = (unsigned int)extent[0]; dimensions[1] = (unsigned int)extent[1]; Vector3D spacingVector; FillVector3D(spacingVector, mmPerPixel[0], mmPerPixel[1], 1.0); mitk::Image::Pointer resultImage = this->GetOutput(); resultImage->Initialize(m_Reslicer->GetOutput()); resultImage->Initialize(inputImage->GetPixelType(), 2, dimensions); resultImage->SetSpacing(spacingVector); resultImage->SetSlice(m_Reslicer->GetOutput());*/ } void mitk::ExtractDirectedPlaneImageFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } bool mitk::ExtractDirectedPlaneImageFilter ::CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ) { // Clip the plane with the bounding geometry. To do so, the corner points // of the bounding box are transformed by the inverse transformation // matrix, and the transformed bounding box edges derived therefrom are // clipped with the plane z=0. The resulting min/max values are taken as // bounds for the image reslicer. const BoundingBox *boundingBox = boundingGeometry->GetBoundingBox(); BoundingBox::PointType bbMin = boundingBox->GetMinimum(); BoundingBox::PointType bbMax = boundingBox->GetMaximum(); vtkPoints *points = vtkPoints::New(); if(boundingGeometry->GetImageGeometry()) { points->InsertPoint( 0, bbMin[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 1, bbMin[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 2, bbMin[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 3, bbMin[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 4, bbMax[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 5, bbMax[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 6, bbMax[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 7, bbMax[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); } else { points->InsertPoint( 0, bbMin[0], bbMin[1], bbMin[2] ); points->InsertPoint( 1, bbMin[0], bbMin[1], bbMax[2] ); points->InsertPoint( 2, bbMin[0], bbMax[1], bbMax[2] ); points->InsertPoint( 3, bbMin[0], bbMax[1], bbMin[2] ); points->InsertPoint( 4, bbMax[0], bbMin[1], bbMin[2] ); points->InsertPoint( 5, bbMax[0], bbMin[1], bbMax[2] ); points->InsertPoint( 6, bbMax[0], bbMax[1], bbMax[2] ); points->InsertPoint( 7, bbMax[0], bbMax[1], bbMin[2] ); } vtkPoints *newPoints = vtkPoints::New(); vtkTransform *transform = vtkTransform::New(); transform->Identity(); transform->Concatenate( planeGeometry->GetVtkTransform()->GetLinearInverse() ); transform->Concatenate( boundingGeometry->GetVtkTransform() ); transform->TransformPoints( points, newPoints ); transform->Delete(); bounds[0] = bounds[2] = 10000000.0; bounds[1] = bounds[3] = -10000000.0; bounds[4] = bounds[5] = 0.0; this->LineIntersectZero( newPoints, 0, 1, bounds ); this->LineIntersectZero( newPoints, 1, 2, bounds ); this->LineIntersectZero( newPoints, 2, 3, bounds ); this->LineIntersectZero( newPoints, 3, 0, bounds ); this->LineIntersectZero( newPoints, 0, 4, bounds ); this->LineIntersectZero( newPoints, 1, 5, bounds ); this->LineIntersectZero( newPoints, 2, 6, bounds ); this->LineIntersectZero( newPoints, 3, 7, bounds ); this->LineIntersectZero( newPoints, 4, 5, bounds ); this->LineIntersectZero( newPoints, 5, 6, bounds ); this->LineIntersectZero( newPoints, 6, 7, bounds ); this->LineIntersectZero( newPoints, 7, 4, bounds ); // clean up vtk data points->Delete(); newPoints->Delete(); if ( (bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0) ) { return false; } else { // The resulting bounds must be adjusted by the plane spacing, since we // we have so far dealt with index coordinates const float *planeSpacing = planeGeometry->GetFloatSpacing(); bounds[0] *= planeSpacing[0]; bounds[1] *= planeSpacing[0]; bounds[2] *= planeSpacing[1]; bounds[3] *= planeSpacing[1]; bounds[4] *= planeSpacing[2]; bounds[5] *= planeSpacing[2]; return true; } } bool mitk::ExtractDirectedPlaneImageFilter ::LineIntersectZero( vtkPoints *points, int p1, int p2, vtkFloatingPointType *bounds ) { vtkFloatingPointType point1[3]; vtkFloatingPointType point2[3]; points->GetPoint( p1, point1 ); points->GetPoint( p2, point2 ); if ( (point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]) ) { double x, y; x = ( point1[0] * point2[2] - point1[2] * point2[0] ) / ( point2[2] - point1[2] ); y = ( point1[1] * point2[2] - point1[2] * point2[1] ) / ( point2[2] - point1[2] ); if ( x < bounds[0] ) { bounds[0] = x; } if ( x > bounds[1] ) { bounds[1] = x; } if ( y < bounds[2] ) { bounds[2] = y; } if ( y > bounds[3] ) { bounds[3] = y; } bounds[4] = bounds[5] = 0.0; return true; } return false; } diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h index 8161b01819..5afd23ced0 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h @@ -1,121 +1,124 @@ /*=================================================================== 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 mitkExtractDirectedPlaneImageFilter_h_Included #define mitkExtractDirectedPlaneImageFilter_h_Included #include "ImageExtractionExports.h" #include "mitkImageToImageFilter.h" #include "vtkImageReslice.h" #include "mitkVtkResliceInterpolationProperty.h" #define setMacro(name,type) \ virtual void Set##name (type _arg) \ { \ if (this->m_##name != _arg) \ { \ this->m_##name = _arg; \ } \ } #define getMacro(name,type) \ virtual type Get##name () \ { \ return m_##name; \ } class vtkPoints; namespace mitk { - /** +/** + \deprecated This class is deprecated. Use mitk::ExtractSliceFilter instead. + \sa ExtractSliceFilter + \brief Extracts a 2D slice of arbitrary geometry from a 3D or 4D image. \sa mitkImageMapper2D \ingroup ImageToImageFilter This class takes a 3D or 4D mitk::Image as input and tries to extract one slice from it. This slice can be arbitrary oriented in space. The 2D slice is resliced by a vtk::ResliceImage filter if not perpendicular to the input image. The world geometry of the plane to be extracted image must be given as an input to the filter in order to correctly calculate world coordinates of the extracted slice. Setting a timestep from which the plane should be extracted is optional. Output will not be set if there was a problem extracting the desired slice. Last contributor: $Author: T. Schwarz$ - */ +*/ class ImageExtraction_EXPORT ExtractDirectedPlaneImageFilter : public ImageToImageFilter { public: mitkClassMacro(ExtractDirectedPlaneImageFilter, ImageToImageFilter); itkNewMacro(ExtractDirectedPlaneImageFilter); itkSetMacro( WorldGeometry, Geometry2D* ); // The Reslicer is accessible to configure the desired interpolation; // (See vtk::ImageReslice class for documentation). // Misusage is at your own risk... itkGetMacro( Reslicer, vtkImageReslice* ); // The target timestep in a 4D image from which the 2D plane is supposed // to be extracted. itkSetMacro( TargetTimestep, unsigned int ); itkGetMacro( TargetTimestep, unsigned int ); itkSetMacro( InPlaneResampleExtentByGeometry, bool ); itkGetMacro( InPlaneResampleExtentByGeometry, bool ); setMacro( ResliceInterpolationProperty, VtkResliceInterpolationProperty* ); itkGetMacro( ResliceInterpolationProperty, VtkResliceInterpolationProperty* ); setMacro( IsMapperMode, bool ); getMacro( IsMapperMode, bool ); protected: ExtractDirectedPlaneImageFilter(); // purposely hidden virtual ~ExtractDirectedPlaneImageFilter(); virtual void GenerateData(); virtual void GenerateOutputInformation(); bool CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ); bool LineIntersectZero( vtkPoints *points, int p1, int p2, vtkFloatingPointType *bounds ); const Geometry2D* m_WorldGeometry; vtkImageReslice * m_Reslicer; unsigned int m_TargetTimestep; bool m_InPlaneResampleExtentByGeometry; int m_ThickSlicesMode; int m_ThickSlicesNum; bool m_IsMapperMode; VtkResliceInterpolationProperty* m_ResliceInterpolationProperty; }; } // namespace mitk #endif // mitkExtractDirectedPlaneImageFilter_h_Included diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp index ec6ce80698..d58c9cd074 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp @@ -1,296 +1,297 @@ /*=================================================================== 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 "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "itkImageRegionIterator.h" #include mitk::ExtractDirectedPlaneImageFilterNew::ExtractDirectedPlaneImageFilterNew() :m_CurrentWorldGeometry2D(NULL), m_ActualInputTimestep(-1) { + MITK_WARN << "Class ExtractDirectedPlaneImageFilterNew is deprecated! Use ExtractSliceFilter instead."; } mitk::ExtractDirectedPlaneImageFilterNew::~ExtractDirectedPlaneImageFilterNew() { } void mitk::ExtractDirectedPlaneImageFilterNew::GenerateData(){ mitk::Image::ConstPointer inputImage = ImageToImageFilter::GetInput(0); if ( !inputImage ) { MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!"); return; } m_ImageGeometry = inputImage->GetGeometry(); //If no timestep is set, the lowest given will be selected const mitk::TimeSlicedGeometry* inputTimeGeometry = this->GetInput()->GetTimeSlicedGeometry(); if ( m_ActualInputTimestep == -1) { ScalarType time = m_CurrentWorldGeometry2D->GetTimeBounds()[0]; if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) { m_ActualInputTimestep = inputTimeGeometry->MSToTimeStep( time ); } } if ( inputImage->GetDimension() > 4 || inputImage->GetDimension() < 2) { MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew:GenerateData works only with 3D and 3D+t images, sorry." << std::endl; itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew works only with 3D and 3D+t images, sorry."); return; } else if ( inputImage->GetDimension() == 4 ) { mitk::ImageTimeSelector::Pointer timeselector = mitk::ImageTimeSelector::New(); timeselector->SetInput( inputImage ); timeselector->SetTimeNr( m_ActualInputTimestep ); timeselector->UpdateLargestPossibleRegion(); inputImage = timeselector->GetOutput(); } else if ( inputImage->GetDimension() == 2) { mitk::Image::Pointer resultImage = ImageToImageFilter::GetOutput(); resultImage = const_cast( inputImage.GetPointer() ); ImageToImageFilter::SetNthOutput( 0, resultImage); return; } if ( !m_CurrentWorldGeometry2D ) { MITK_ERROR<< "mitk::ExtractDirectedPlaneImageFilterNew::GenerateData has no CurrentWorldGeometry2D set" << std::endl; return; } AccessFixedDimensionByItk( inputImage, ItkSliceExtraction, 3 ); }//Generate Data void mitk::ExtractDirectedPlaneImageFilterNew::GenerateOutputInformation () { Superclass::GenerateOutputInformation(); } /* * The desired slice is extracted by filling the image`s corresponding pixel values in an empty 2 dimensional itk::Image * Therefor the itk image`s extent in pixel (in each direction) is doubled and its spacing (also in each direction) is divided by two * (similar to the shannon theorem). */ template void mitk::ExtractDirectedPlaneImageFilterNew::ItkSliceExtraction (itk::Image* inputImage) { typedef itk::Image InputImageType; typedef itk::Image SliceImageType; typedef itk::ImageRegionConstIterator< SliceImageType > SliceIterator; //Creating an itk::Image that represents the sampled slice typename SliceImageType::Pointer resultSlice = SliceImageType::New(); typename SliceImageType::IndexType start; start[0] = 0; start[1] = 0; Point3D origin = m_CurrentWorldGeometry2D->GetOrigin(); Vector3D right = m_CurrentWorldGeometry2D->GetAxisVector(0); Vector3D bottom = m_CurrentWorldGeometry2D->GetAxisVector(1); //Calculation the sample-spacing, i.e the half of the smallest spacing existing in the original image Vector3D newPixelSpacing = m_ImageGeometry->GetSpacing(); float minSpacing = newPixelSpacing[0]; for (unsigned int i = 1; i < newPixelSpacing.Size(); i++) { if (newPixelSpacing[i] < minSpacing ) { minSpacing = newPixelSpacing[i]; } } newPixelSpacing[0] = 0.5*minSpacing; newPixelSpacing[1] = 0.5*minSpacing; newPixelSpacing[2] = 0.5*minSpacing; float pixelSpacing[2]; pixelSpacing[0] = newPixelSpacing[0]; pixelSpacing[1] = newPixelSpacing[1]; //Calculating the size of the sampled slice typename SliceImageType::SizeType size; Vector2D extentInMM; extentInMM[0] = m_CurrentWorldGeometry2D->GetExtentInMM(0); extentInMM[1] = m_CurrentWorldGeometry2D->GetExtentInMM(1); //The maximum extent is the lenght of the diagonal of the considered plane double maxExtent = sqrt(extentInMM[0]*extentInMM[0]+extentInMM[1]*extentInMM[1]); unsigned int xTranlation = (maxExtent-extentInMM[0]); unsigned int yTranlation = (maxExtent-extentInMM[1]); size[0] = (maxExtent+xTranlation)/newPixelSpacing[0]; size[1] = (maxExtent+yTranlation)/newPixelSpacing[1]; //Creating an ImageRegion Object typename SliceImageType::RegionType region; region.SetSize( size ); region.SetIndex( start ); //Defining the image`s extent and origin by passing the region to it and allocating memory for it resultSlice->SetRegions( region ); resultSlice->SetSpacing( pixelSpacing ); resultSlice->Allocate(); /* * Here we create an new geometry so that the transformations are calculated correctly (our resulting slice has a different bounding box and spacing) * The original current worldgeometry must be cloned because we have to keep the directions of the axis vector which represents the rotation */ right.Normalize(); bottom.Normalize(); //Here we translate the origin to adapt the new geometry to the previous calculated extent origin[0] -= xTranlation*right[0]+yTranlation*bottom[0]; origin[1] -= xTranlation*right[1]+yTranlation*bottom[1]; origin[2] -= xTranlation*right[2]+yTranlation*bottom[2]; //Putting it together for the new geometry mitk::Geometry3D::Pointer newSliceGeometryTest = dynamic_cast(m_CurrentWorldGeometry2D->Clone().GetPointer()); newSliceGeometryTest->ChangeImageGeometryConsideringOriginOffset(true); //Workaround because of BUG (#6505) newSliceGeometryTest->GetIndexToWorldTransform()->SetMatrix(m_CurrentWorldGeometry2D->GetIndexToWorldTransform()->GetMatrix()); //Workaround end newSliceGeometryTest->SetOrigin(origin); ScalarType bounds[6]={0, size[0], 0, size[1], 0, 1}; newSliceGeometryTest->SetBounds(bounds); newSliceGeometryTest->SetSpacing(newPixelSpacing); newSliceGeometryTest->Modified(); //Workaround because of BUG (#6505) itk::MatrixOffsetTransformBase::MatrixType tempTransform = newSliceGeometryTest->GetIndexToWorldTransform()->GetMatrix(); //Workaround end /* * Now we iterate over the recently created slice. * For each slice - pixel we check whether there is an according * pixel in the input - image which can be set in the slice. * In this way a slice is sampled out of the input - image regrading to the given PlaneGeometry */ Point3D currentSliceIndexPointIn2D; Point3D currentImageWorldPointIn3D; typename InputImageType::IndexType inputIndex; SliceIterator sliceIterator ( resultSlice, resultSlice->GetLargestPossibleRegion() ); sliceIterator.GoToBegin(); while ( !sliceIterator.IsAtEnd() ) { /* * Here we add 0.5 to to assure that the indices are correctly transformed. * (Because of the 0.5er Bug) */ currentSliceIndexPointIn2D[0] = sliceIterator.GetIndex()[0]+0.5; currentSliceIndexPointIn2D[1] = sliceIterator.GetIndex()[1]+0.5; currentSliceIndexPointIn2D[2] = 0; newSliceGeometryTest->IndexToWorld( currentSliceIndexPointIn2D, currentImageWorldPointIn3D ); m_ImageGeometry->WorldToIndex( currentImageWorldPointIn3D, inputIndex); if ( m_ImageGeometry->IsIndexInside( inputIndex )) { resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); } else { resultSlice->SetPixel( sliceIterator.GetIndex(), 0); } ++sliceIterator; } Image::Pointer resultImage = ImageToImageFilter::GetOutput(); GrabItkImageMemory(resultSlice, resultImage, NULL, false); resultImage->SetClonedGeometry(newSliceGeometryTest); //Workaround because of BUG (#6505) resultImage->GetGeometry()->GetIndexToWorldTransform()->SetMatrix(tempTransform); //Workaround end } ///**TEST** May ba a little bit more efficient but doesn`t already work/ //right.Normalize(); //bottom.Normalize(); //Point3D currentImagePointIn3D = origin /*+ bottom*newPixelSpacing*/; //unsigned int columns ( 0 ); /**ENDE**/ /****TEST***/ //SliceImageType::IndexType index = sliceIterator.GetIndex(); //if ( columns == (extentInPixel[0]) ) //{ //If we are at the end of a row, then we have to go to the beginning of the next row //currentImagePointIn3D = origin; //currentImagePointIn3D += newPixelSpacing[1]*bottom*index[1]; //columns = 0; //m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //} //else //{ //// //if ( columns != 0 ) //{ //currentImagePointIn3D += newPixelSpacing[0]*right; //} //m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //} //if ( m_ImageGeometry->IsIndexInside( inputIndex )) //{ //resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); //} //else if (currentImagePointIn3D == origin) //{ //Point3D temp; //temp[0] = bottom[0]*newPixelSpacing[0]*0.5; //temp[1] = bottom[1]*newPixelSpacing[1]*0.5; //temp[2] = bottom[2]*newPixelSpacing[2]*0.5; //origin[0] += temp[0]; //origin[1] += temp[1]; //origin[2] += temp[2]; //currentImagePointIn3D = origin; //m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //if ( m_ImageGeometry->IsIndexInside( inputIndex )) //{ //resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); //} //} /****TEST ENDE****/ diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h index 67d0ee37ce..507abc28ea 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h @@ -1,93 +1,96 @@ /*=================================================================== 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 mitkExtractDirectedPlaneImageFilterNew_h_Included #define mitkExtractDirectedPlaneImageFilterNew_h_Included #include "ImageExtractionExports.h" #include "mitkImageToImageFilter.h" #include "itkImage.h" #include "mitkITKImageImport.h" namespace mitk { /** + \deprecated This class is deprecated. Use mitk::ExtractSliceFilter instead. + \sa ExtractSliceFilter + \brief A filter that can extract a 2D slice from a 3D or 4D image especially if the image`s axes are rotated \sa ContourTool \sa SegTool2D \sa ExtractImageFilter \sa OverwriteSliceImageFilter \sa OverwriteDirectedPlaneImageFilter \ingroup Process \ingroup Reliver There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkSegmentationTechnicalPage This class takes an 3D or 4D mitk::Image as input and extracts a slice from it. If you work with a 4D image as input you have to specify the desired timestep at which the slice shall be extracted, otherwise the lowest given timestep is selected by default. The special feature of this filter is, that the planes of the input image can be rotated in any way. To assure a proper extraction you have to set the currentWorldGeometry2D with you can obtain from the BaseRenderer, respectively the positionEvent send by the renderer. The output will not be set if there was a problem with the input image $Author: fetzer $ */ class ImageExtraction_EXPORT ExtractDirectedPlaneImageFilterNew : public ImageToImageFilter { public: mitkClassMacro(ExtractDirectedPlaneImageFilterNew, ImageToImageFilter); itkNewMacro(Self); /** \brief Set macro for the current worldgeometry \a Parameter The current wordgeometry that describes the position (rotation, translation) of the plane (and therefore the slice to be extracted) in our 3D(+t) image */ itkSetMacro(CurrentWorldGeometry2D, Geometry3D* ); itkSetMacro(ImageGeometry, Geometry3D* ); /** \brief Set macro for the current timestep \a Parameter The timestep of the image from which the slice shall be extracted */ itkSetMacro(ActualInputTimestep, int); protected: ExtractDirectedPlaneImageFilterNew(); virtual ~ExtractDirectedPlaneImageFilterNew(); virtual void GenerateData(); virtual void GenerateOutputInformation(); private: const Geometry3D* m_CurrentWorldGeometry2D; const Geometry3D* m_ImageGeometry; int m_ActualInputTimestep; template void ItkSliceExtraction (itk::Image* inputImage); }; }//namespace #endif diff --git a/Modules/ImageExtraction/mitkExtractImageFilter.cpp b/Modules/ImageExtraction/mitkExtractImageFilter.cpp index 474dffb6ac..40cb6bc7ff 100644 --- a/Modules/ImageExtraction/mitkExtractImageFilter.cpp +++ b/Modules/ImageExtraction/mitkExtractImageFilter.cpp @@ -1,244 +1,245 @@ /*=================================================================== 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 "mitkExtractImageFilter.h" #include "mitkImageCast.h" #include "mitkPlaneGeometry.h" #include "mitkITKImageImport.h" #include "mitkImageTimeSelector.h" #include #include mitk::ExtractImageFilter::ExtractImageFilter() :m_SliceIndex(0), m_SliceDimension(0), m_TimeStep(0) { + MITK_WARN << "Class ExtractImageFilter is deprecated! Use ExtractSliceFilter instead."; } mitk::ExtractImageFilter::~ExtractImageFilter() { } void mitk::ExtractImageFilter::GenerateData() { Image::ConstPointer input = ImageToImageFilter::GetInput(0); if ( (input->GetDimension() > 4) || (input->GetDimension() < 2) ) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateData works only with 3D and 3D+t images, sorry." << std::endl; itkExceptionMacro("mitk::ExtractImageFilter works only with 3D and 3D+t images, sorry."); return; } else if (input->GetDimension() == 4) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( input ); timeSelector->SetTimeNr( m_TimeStep ); timeSelector->UpdateLargestPossibleRegion(); input = timeSelector->GetOutput(); } else if (input->GetDimension() == 2) { Image::Pointer resultImage = ImageToImageFilter::GetOutput(); resultImage = const_cast(input.GetPointer()); ImageToImageFilter::SetNthOutput( 0, resultImage ); return; } if ( m_SliceDimension >= input->GetDimension() ) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateData m_SliceDimension == " << m_SliceDimension << " makes no sense with an " << input->GetDimension() << "D image." << std::endl; itkExceptionMacro("This is not a sensible value for m_SliceDimension."); return; } AccessFixedDimensionByItk( input, ItkImageProcessing, 3 ); // set a nice geometry for display and point transformations Geometry3D* inputImageGeometry = ImageToImageFilter::GetInput(0)->GetGeometry(); if (!inputImageGeometry) { MITK_ERROR << "In ExtractImageFilter::ItkImageProcessing: Input image has no geometry!" << std::endl; return; } PlaneGeometry::PlaneOrientation orientation = PlaneGeometry::Transversal; switch ( m_SliceDimension ) { default: case 2: orientation = PlaneGeometry::Transversal; break; case 1: orientation = PlaneGeometry::Frontal; break; case 0: orientation = PlaneGeometry::Sagittal; break; } PlaneGeometry::Pointer planeGeometry = PlaneGeometry::New(); planeGeometry->InitializeStandardPlane( inputImageGeometry, orientation, (ScalarType)m_SliceIndex, true, false ); Image::Pointer resultImage = ImageToImageFilter::GetOutput(); planeGeometry->ChangeImageGeometryConsideringOriginOffset(true); resultImage->SetGeometry( planeGeometry ); } template void mitk::ExtractImageFilter::ItkImageProcessing( itk::Image* itkImage ) { // use the itk::ExtractImageFilter to get a 2D image typedef itk::Image< TPixel, VImageDimension > ImageType3D; typedef itk::Image< TPixel, VImageDimension-1 > ImageType2D; typename ImageType3D::RegionType inSliceRegion = itkImage->GetLargestPossibleRegion(); inSliceRegion.SetSize( m_SliceDimension, 0 ); typedef itk::ExtractImageFilter ExtractImageFilterType; typename ExtractImageFilterType::Pointer sliceExtractor = ExtractImageFilterType::New(); sliceExtractor->SetInput( itkImage ); inSliceRegion.SetIndex( m_SliceDimension, m_SliceIndex ); sliceExtractor->SetExtractionRegion( inSliceRegion ); // calculate the output sliceExtractor->UpdateLargestPossibleRegion(); typename ImageType2D::Pointer slice = sliceExtractor->GetOutput(); // re-import to MITK Image::Pointer resultImage = ImageToImageFilter::GetOutput(); GrabItkImageMemory(slice, resultImage, NULL, false); } /* * What is the input requested region that is required to produce the output * requested region? By default, the largest possible region is always * required but this is overridden in many subclasses. For instance, for an * image processing filter where an output pixel is a simple function of an * input pixel, the input requested region will be set to the output * requested region. For an image processing filter where an output pixel is * a function of the pixels in a neighborhood of an input pixel, then the * input requested region will need to be larger than the output requested * region (to avoid introducing artificial boundary conditions). This * function should never request an input region that is outside the the * input largest possible region (i.e. implementations of this method should * crop the input requested region at the boundaries of the input largest * possible region). */ void mitk::ExtractImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); ImageToImageFilter::InputImagePointer input = const_cast< ImageToImageFilter::InputImageType* > ( this->GetInput() ); Image::Pointer output = this->GetOutput(); if (input->GetDimension() == 2) { input->SetRequestedRegionToLargestPossibleRegion(); return; } Image::RegionType requestedRegion; requestedRegion = output->GetRequestedRegion(); requestedRegion.SetIndex(0, 0); requestedRegion.SetIndex(1, 0); requestedRegion.SetIndex(2, 0); requestedRegion.SetSize(0, input->GetDimension(0)); requestedRegion.SetSize(1, input->GetDimension(1)); requestedRegion.SetSize(2, input->GetDimension(2)); requestedRegion.SetIndex( m_SliceDimension, m_SliceIndex ); // only one slice needed requestedRegion.SetSize( m_SliceDimension, 1 ); input->SetRequestedRegion( &requestedRegion ); } /* * Generate the information decribing the output data. The default * implementation of this method will copy information from the input to the * output. A filter may override this method if its output will have different * information than its input. For instance, a filter that shrinks an image will * need to provide an implementation for this method that changes the spacing of * the pixels. Such filters should call their superclass' implementation of this * method prior to changing the information values they need (i.e. * GenerateOutputInformation() should call * Superclass::GenerateOutputInformation() prior to changing the information. */ void mitk::ExtractImageFilter::GenerateOutputInformation() { Image::Pointer output = this->GetOutput(); Image::ConstPointer input = this->GetInput(); if (input.IsNull()) return; if ( m_SliceDimension >= input->GetDimension() && input->GetDimension() != 2 ) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateOutputInformation m_SliceDimension == " << m_SliceDimension << " makes no sense with an " << input->GetDimension() << "D image." << std::endl; itkExceptionMacro("This is not a sensible value for m_SliceDimension."); return; } unsigned int sliceDimension( m_SliceDimension ); if ( input->GetDimension() == 2) { sliceDimension = 2; } unsigned int tmpDimensions[2]; switch ( sliceDimension ) { default: case 2: // orientation = PlaneGeometry::Transversal; tmpDimensions[0] = input->GetDimension(0); tmpDimensions[1] = input->GetDimension(1); break; case 1: // orientation = PlaneGeometry::Frontal; tmpDimensions[0] = input->GetDimension(0); tmpDimensions[1] = input->GetDimension(2); break; case 0: // orientation = PlaneGeometry::Sagittal; tmpDimensions[0] = input->GetDimension(1); tmpDimensions[1] = input->GetDimension(2); break; } output->Initialize(input->GetPixelType(), 2, tmpDimensions, 1 /*input->GetNumberOfChannels()*/); // initialize the spacing of the output /* Vector3D spacing = input->GetSlicedGeometry()->GetSpacing(); if(input->GetDimension()>=2) spacing[2]=spacing[1]; else spacing[2] = 1.0; output->GetSlicedGeometry()->SetSpacing(spacing); */ output->SetPropertyList(input->GetPropertyList()->Clone()); } diff --git a/Modules/ImageExtraction/mitkExtractImageFilter.h b/Modules/ImageExtraction/mitkExtractImageFilter.h index ef707c168c..8196563b7f 100644 --- a/Modules/ImageExtraction/mitkExtractImageFilter.h +++ b/Modules/ImageExtraction/mitkExtractImageFilter.h @@ -1,98 +1,101 @@ /*=================================================================== 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 mitkExtractImageFilter_h_Included #define mitkExtractImageFilter_h_Included #include "mitkCommon.h" #include "ImageExtractionExports.h" #include "mitkImageToImageFilter.h" #include "itkImage.h" namespace mitk { /** + \deprecated This class is deprecated. Use mitk::ExtractSliceFilter instead. + \sa ExtractSliceFilter + \brief Extracts a 2D slice from a 3D image. \sa SegTool2D \sa OverwriteSliceImageFilter \ingroup Process \ingroup ToolManagerEtAl There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkSegmentationTechnicalPage This class takes a 3D mitk::Image as input and tries to extract one slice from it. Two parameters determine which slice is extracted: the "slice dimension" is that one, which is constant for all points in the plane, e.g. transversal would mean 2. The "slice index" is the slice index in the image direction you specified with "affected dimension". Indices count from zero. Output will not be set if there was a problem extracting the desired slice. Last contributor: $Author$ */ class ImageExtraction_EXPORT ExtractImageFilter : public ImageToImageFilter { public: mitkClassMacro(ExtractImageFilter, ImageToImageFilter); itkNewMacro(ExtractImageFilter); /** \brief Which slice to extract (first one has index 0). */ itkSetMacro(SliceIndex, unsigned int); itkGetConstMacro(SliceIndex, unsigned int); /** \brief The orientation of the slice to be extracted. \a Parameter SliceDimension Number of the dimension which is constant for all pixels of the desired slice (e.g. 2 for transversal) */ itkSetMacro(SliceDimension, unsigned int); itkGetConstMacro(SliceDimension, unsigned int); /** \brief Time step of the image to be extracted. */ itkSetMacro(TimeStep, unsigned int); itkGetConstMacro(TimeStep, unsigned int); protected: ExtractImageFilter(); // purposely hidden virtual ~ExtractImageFilter(); virtual void GenerateOutputInformation(); virtual void GenerateInputRequestedRegion(); virtual void GenerateData(); template void ItkImageProcessing( itk::Image* image ); unsigned int m_SliceIndex; unsigned int m_SliceDimension; unsigned int m_TimeStep; }; } // namespace #endif diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp new file mode 100644 index 0000000000..180e493864 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp @@ -0,0 +1,97 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision: $ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#include "mitkDiffSliceOperation.h" + +#include + +mitk::DiffSliceOperation::DiffSliceOperation():Operation(1) +{ + m_TimeStep = 0; + m_Slice = NULL; + m_Image = NULL; + m_WorldGeometry = NULL; + m_SliceGeometry = NULL; + m_ImageIsValid = false; +} + + +mitk::DiffSliceOperation::DiffSliceOperation(mitk::Image* imageVolume, + vtkImageData* slice, + AffineGeometryFrame3D* sliceGeometry, + unsigned int timestep, + AffineGeometryFrame3D* currentWorldGeometry):Operation(1) + +{ + m_WorldGeometry = currentWorldGeometry->Clone(); + m_SliceGeometry = sliceGeometry->Clone(); + + m_TimeStep = timestep; + + /*m_zlibSliceContainer = CompressedImageContainer::New(); + m_zlibSliceContainer->SetImage( slice );*/ + m_Slice = vtkSmartPointer::New(); + m_Slice->DeepCopy(slice); + + m_Image = imageVolume; + + if ( m_Image) { + /*add an observer to listen to the delete event of the image, this is necessary because the operation is then invalid*/ + itk::SimpleMemberCommand< DiffSliceOperation >::Pointer command = itk::SimpleMemberCommand< DiffSliceOperation >::New(); + command->SetCallbackFunction( this, &DiffSliceOperation::OnImageDeleted ); + //get the id of the observer, used to remove it later on + m_DeleteObserverTag = imageVolume->AddObserver( itk::DeleteEvent(), command ); + + + m_ImageIsValid = true; + } + else + m_ImageIsValid = false; + +} + +mitk::DiffSliceOperation::~DiffSliceOperation() +{ + + m_Slice = NULL; + m_WorldGeometry = NULL; + //m_zlibSliceContainer = NULL; + + if (m_ImageIsValid) + { + //if the image is still there, we have to remove the observer from it + m_Image->RemoveObserver( m_DeleteObserverTag ); + } + m_Image = NULL; +} + +vtkImageData* mitk::DiffSliceOperation::GetSlice() +{ + //Image::ConstPointer image = m_zlibSliceContainer->GetImage().GetPointer(); + return m_Slice; +} + +bool mitk::DiffSliceOperation::IsValid() +{ + return m_ImageIsValid && (m_Slice.GetPointer() != NULL) && (m_WorldGeometry.IsNotNull());//TODO improve +} + +void mitk::DiffSliceOperation::OnImageDeleted() +{ + //if our imageVolume is removed e.g. from the datastorage the operation is no lnger valid + m_ImageIsValid = false; +} \ No newline at end of file diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h new file mode 100644 index 0000000000..f254211c4e --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h @@ -0,0 +1,117 @@ +/*=================================================================== + +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 mitkDiffSliceOperation_h_Included +#define mitkDiffSliceOperation_h_Included + +#include "SegmentationExports.h" +#include "mitkCommon.h" +#include +//#include "mitkCompressedImageContainer.h" + +#include +#include +#include + + +namespace mitk +{ + /** \brief An Operation for applying an edited slice to the volume. + \sa DiffSliceOperationApplier + + The information for the operation is specified by properties: + + imageVolume the volume where the slice was extracted from. + slice the slice to be applied. + timestep the timestep in an 4D image. + currentWorldGeometry specifies the axis where the slice has to be applied in the volume. + + This Operation can be used to realize undo-redo functionality for e.g. segmentation purposes. + */ + class Segmentation_EXPORT DiffSliceOperation : public Operation + { + + public: + + mitkClassMacro(DiffSliceOperation, OperationActor); + + //itkNewMacro(DiffSliceOperation); + + //mitkNewMacro4Param(DiffSliceOperation,mitk::Image,mitk::Image,unsigned int, mitk::Geometry2D); + + /** \brief Creates an empty instance. + Note that it is not valid yet. The properties of the object have to be set. + */ + DiffSliceOperation(); + + /** \brief */ + DiffSliceOperation( mitk::Image* imageVolume, vtkImageData* slice, AffineGeometryFrame3D* sliceGeometry, unsigned int timestep, AffineGeometryFrame3D* currentWorldGeometry); + + /** \brief Check if it is a valid operation.*/ + bool IsValid(); + + /** \brief Set the image volume.*/ + void SetImage(mitk::Image* image){ this->m_Image = image;} + /** \brief Get th image volume.*/ + mitk::Image* GetImage(){return this->m_Image;} + + /** \brief Set thee slice to be applied.*/ + void SetImage(vtkImageData* slice){ this->m_Slice = slice;} + /** \brief Get the slice that is applied in the operation.*/ + vtkImageData* GetSlice(); + + /** \brief Get timeStep.*/ + void SetTimeStep(unsigned int timestep){this->m_TimeStep = timestep;} + /** \brief Set timeStep*/ + unsigned int GetTimeStep(){return this->m_TimeStep;} + + /** \brief Set the axis where the slice has to be applied in the volume.*/ + void SetSliceGeometry(AffineGeometryFrame3D* sliceGeometry){this->m_SliceGeometry = sliceGeometry;} + /** \brief Get the axis where the slice has to be applied in the volume.*/ + AffineGeometryFrame3D* GetSliceGeometry(){return this->m_SliceGeometry;} + + /** \brief Set the axis where the slice has to be applied in the volume.*/ + void SetCurrentWorldGeometry(AffineGeometryFrame3D* worldGeometry){this->m_WorldGeometry = worldGeometry;} + /** \brief Get the axis where the slice has to be applied in the volume.*/ + AffineGeometryFrame3D* GetWorldGeometry(){return this->m_WorldGeometry;} + + + protected: + + virtual ~DiffSliceOperation(); + + /** \brief Callback for image observer.*/ + void OnImageDeleted(); + + //CompressedImageContainer::Pointer m_zlibSliceContainer; + + mitk::Image* m_Image; + + vtkSmartPointer m_Slice; + + AffineGeometryFrame3D::Pointer m_SliceGeometry; + + unsigned int m_TimeStep; + + AffineGeometryFrame3D::Pointer m_WorldGeometry; + + bool m_ImageIsValid; + + unsigned long m_DeleteObserverTag; + + }; +} +#endif \ No newline at end of file diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.cpp b/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.cpp new file mode 100644 index 0000000000..09e0d21381 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.cpp @@ -0,0 +1,77 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision: $ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#include "mitkDiffSliceOperationApplier.h" +#include "mitkRenderingManager.h" +#include + +mitk::DiffSliceOperationApplier::DiffSliceOperationApplier() +{ +} + +mitk::DiffSliceOperationApplier::~DiffSliceOperationApplier() +{ +} + +void mitk::DiffSliceOperationApplier::ExecuteOperation( Operation* operation ) +{ + DiffSliceOperation* imageOperation = dynamic_cast( operation ); + + //as we only support DiffSliceOperation return if operation is not type of DiffSliceOperation + if(!imageOperation) + return; + + + //chak if the operation is valid + if(imageOperation->IsValid()) + { + //the actual overwrite filter (vtk) + vtkSmartPointer reslice = vtkSmartPointer::New(); + + //Set the slice as 'input' + reslice->SetInputSlice(imageOperation->GetSlice()); + + //set overwrite mode to true to write back to the image volume + reslice->SetOverwriteMode(true); + reslice->Modified(); + + //a wrapper for vtkImageOverwrite + mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); + extractor->SetInput( imageOperation->GetImage() ); + extractor->SetTimeStep( imageOperation->GetTimeStep() ); + extractor->SetWorldGeometry( dynamic_cast(imageOperation->GetWorldGeometry()) ); + extractor->SetVtkOutputRequest(true); + extractor->SetResliceTransformByGeometry( imageOperation->GetImage()->GetTimeSlicedGeometry()->GetGeometry3D( imageOperation->GetTimeStep() ) ); + + extractor->Modified(); + extractor->Update(); + + //make sure the modification is rendered + RenderingManager::GetInstance()->RequestUpdateAll(); + imageOperation->GetImage()->Modified(); + } +} + +//mitk::DiffSliceOperationApplier* mitk::DiffSliceOperationApplier::s_Instance = NULL; + +mitk::DiffSliceOperationApplier* mitk::DiffSliceOperationApplier::GetInstance() +{ + //if(!s_Instance) + static DiffSliceOperationApplier* s_Instance = new DiffSliceOperationApplier(); + + return s_Instance; +} \ No newline at end of file diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.h b/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.h new file mode 100644 index 0000000000..675aad602b --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.h @@ -0,0 +1,63 @@ +/*=================================================================== + +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 mitkDiffSliceOpertationApplier_h_Included +#define mitkDiffSliceOpertationApplier_h_Included + +#include "SegmentationExports.h" +#include "mitkCommon.h" +#include + +#include "mitkDiffSliceOperation.h" +#include +#include + +namespace mitk +{ + /** \brief Executes a DiffSliceOperation. + \sa DiffSliceOperation + */ + class Segmentation_EXPORT DiffSliceOperationApplier : public OperationActor + { + + public: + + mitkClassMacro(DiffSliceOperationApplier, OperationActor); + + //itkNewMacro(DiffSliceOperationApplier); + + /** \brief Returns an instance of the class */ + static DiffSliceOperationApplier* GetInstance(); + + /** \brief Executes a DiffSliceOperation. + \sa DiffSliceOperation + Note: + Only DiffSliceOperation is supported. + */ + virtual void ExecuteOperation(Operation* op); + + + protected: + + DiffSliceOperationApplier(); + + virtual ~DiffSliceOperationApplier(); + + //static DiffSliceOperationApplier* s_Instance; + + }; +} +#endif \ No newline at end of file diff --git a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp index 397fdd10a4..0ef5827bff 100644 --- a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp @@ -1,490 +1,491 @@ /*=================================================================== 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 "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkSegmentationInterpolationController.h" #include "mitkApplyDiffImageOperation.h" #include "mitkOperationEvent.h" #include "mitkInteractionConst.h" #include "mitkUndoController.h" #include "mitkDiffImageApplier.h" #include "mitkImageTimeSelector.h" #include #include mitk::OverwriteDirectedPlaneImageFilter::OverwriteDirectedPlaneImageFilter() :m_PlaneGeometry(0), m_ImageGeometry3D(0), m_TimeStep(0), m_Dimension0(0), m_Dimension1(1), m_CreateUndoInformation(false) { + MITK_WARN << "Class is deprecated! Use mitkVtkImageOverwrite instead."; } mitk::OverwriteDirectedPlaneImageFilter::~OverwriteDirectedPlaneImageFilter() { } void mitk::OverwriteDirectedPlaneImageFilter::GenerateData() { // // this is the place to implement the major part of undo functionality (bug #491) // here we have to create undo/do operations // // WHO is the operation actor? This object may not be destroyed ever (design of undo stack)! // -> some singleton method of this filter? // // neccessary additional objects: // - something that executes the operations // - the operation class (must hold a binary diff or something) // - observer commands to know when the image is deleted (no further action then, perhaps even remove the operations from the undo stack) // //Image::ConstPointer input = ImageToImageFilter::GetInput(0); Image::ConstPointer input3D = ImageToImageFilter::GetInput(0); //Image::ConstPointer slice = m_SliceImage; if ( input3D.IsNull() || m_SliceImage.IsNull() ) return; if ( input3D->GetDimension() == 4 ) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( input3D ); timeSelector->SetTimeNr( m_TimeStep ); timeSelector->UpdateLargestPossibleRegion(); input3D = timeSelector->GetOutput(); } m_ImageGeometry3D = input3D->GetGeometry(); /* if ( m_SliceDifferenceImage.IsNull() || m_SliceDifferenceImage->GetDimension(0) != m_SliceImage->GetDimension(0) || m_SliceDifferenceImage->GetDimension(1) != m_SliceImage->GetDimension(1) ) { m_SliceDifferenceImage = mitk::Image::New(); mitk::PixelType pixelType( typeid(short signed int) ); m_SliceDifferenceImage->Initialize( pixelType, 2, m_SliceImage->GetDimensions() ); } */ //MITK_INFO << "Overwriting slice " << m_SliceIndex << " in dimension " << m_SliceDimension << " at time step " << m_TimeStep << std::endl; // this will do a long long if/else to find out both pixel types /*AccessFixedDimensionByItk( input3D, ItkImageSwitch, 3 );*/ AccessFixedDimensionByItk( input3D, ItkSliceOverwriting, 3 ); //SegmentationInterpolationController* interpolator = SegmentationInterpolationController::InterpolatorForImage( input3D ); //if (interpolator) //{ // interpolator->BlockModified(true); // //interpolator->SetChangedSlice( m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep ); //} /* if ( m_CreateUndoInformation ) { // create do/undo operations (we don't execute the doOp here, because it has already been executed during calculation of the diff image ApplyDiffImageOperation* doOp = new ApplyDiffImageOperation( OpTEST, const_cast(input.GetPointer()), m_SliceDifferenceImage, m_TimeStep, m_SliceDimension, m_SliceIndex ); ApplyDiffImageOperation* undoOp = new ApplyDiffImageOperation( OpTEST, const_cast(input.GetPointer()), m_SliceDifferenceImage, m_TimeStep, m_SliceDimension, m_SliceIndex ); undoOp->SetFactor( -1.0 ); OperationEvent* undoStackItem = new OperationEvent( DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, this->EventDescription(m_SliceDimension, m_SliceIndex, m_TimeStep) ); UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); } */ // this image is modified (good to know for the renderer) input3D->Modified(); /*if (interpolator) { interpolator->BlockModified(false); }*/ } template void mitk::OverwriteDirectedPlaneImageFilter::ItkSliceOverwriting( itk::Image* input3D ) { typedef itk::Image SliceImageType; typedef itk::Image VolumeImageType; typedef itk::ImageSliceIteratorWithIndex< VolumeImageType > OutputSliceIteratorType; typedef itk::ImageRegionConstIterator< SliceImageType > SliceIteratorType; typename SliceImageType::Pointer sliceImage = SliceImageType::New(); CastToItkImage(m_SliceImage,sliceImage); SliceIteratorType sliceIterator( sliceImage, sliceImage->GetLargestPossibleRegion() ); sliceIterator.GoToBegin(); Point3D currentPointIn2D; Point3D worldPointIn3D; //Here we just iterate over the slice which must be written into the 3D volumen and set the corresponding pixel in our 3D volume while ( !sliceIterator.IsAtEnd() ) { currentPointIn2D[0] = sliceIterator.GetIndex()[0]+0.5; currentPointIn2D[1] = sliceIterator.GetIndex()[1]+0.5; currentPointIn2D[2] = 0; m_PlaneGeometry->IndexToWorld( currentPointIn2D, worldPointIn3D ); typename itk::Image::IndexType outputIndex; m_ImageGeometry3D->WorldToIndex( worldPointIn3D, outputIndex ); // Only access ITK image if it's inside if ( m_ImageGeometry3D->IsIndexInside( outputIndex ) ) { input3D->SetPixel( outputIndex, (TPixel)sliceIterator.Get() ); } ++sliceIterator; } } /****TEST***/ //Maybe a bit more efficient but doesn`t already work. See also ExtractCirectedPlaneImageFilter //typename itk::Image::IndexType outputIndex; //if ( columns == extent[0] ) //{ ////If we are at the end of a row, then we have to go to the beginning of the next row //currentImagePointIn3D = origin; //currentImagePointIn3D += spacing[1]*bottom*currentPointIn2D[1]; //columns = 0; //m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); //} //else //{ //if ( columns != 0 ) //{ //currentImagePointIn3D += spacing[0]*right; //} //m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); //} //if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) //{ //outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); //} //else if (currentImagePointIn3D == origin) //{ //Point3D temp; //temp[0] = bottom[0]*spacing[0]*0.5; //temp[1] = bottom[1]*spacing[1]*0.5; //temp[2] = bottom[2]*spacing[2]*0.5; //origin[0] += temp[0]; //origin[1] += temp[1]; //origin[2] += temp[2]; //currentImagePointIn3D = origin; //m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); //if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) //{ //outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); //} //} //columns++; /****TEST ENDE****/ //* // // Offset the world coordinate by one pixel to compensate for // // index/world origin differences. // Point3D offsetIndex; // offsetIndex.Fill( 1 ); // Point3D offsetWorld; // offsetWorld.Fill( 0 ); // m_PlaneGeometry->IndexToWorld( offsetIndex, offsetWorld ); // // remove origin shift // const Point3D origin = m_PlaneGeometry->GetOrigin(); // offsetWorld[0] -= origin[0]; // offsetWorld[1] -= origin[1]; // offsetWorld[2] -= origin[2]; // // offset world coordinate // worldPointIn3D[ 0 ] += offsetWorld[0]; // worldPointIn3D[ 1 ] += offsetWorld[1]; // worldPointIn3D[ 2 ] += offsetWorld[2]; //*/ // basically copied from mitk/Core/Algorithms/mitkImageAccessByItk.h /*#define myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, pixeltype, dimension, itkimage2) \ // if ( typeId == typeid(pixeltype) ) \ //{ \ // typedef itk::Image ImageType; \ // typedef mitk::ImageToItk ImageToItkType; \ // itk::SmartPointer imagetoitk = ImageToItkType::New(); \ // imagetoitk->SetInput(mitkImage); \ // imagetoitk->Update(); \ // itkImageTypeFunction(imagetoitk->GetOutput(), itkimage2); \ //} // //#define myMITKOverwriteDirectedPlaneImageFilterAccessAllTypesByItk(mitkImage, itkImageTypeFunction, dimension, itkimage2) \ //{ \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, double, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, float, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, int, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned int, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, short, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned short, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, char, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned char, dimension, itkimage2) \ //}*/ // // //template //void mitk::OverwriteDirectedPlaneImageFilter::ItkImageSwitch( itk::Image* itkImage ) //{ // const std::type_info& typeId=*(m_SliceImage->GetPixelType().GetTypeId()); // // myMITKOverwriteDirectedPlaneImageFilterAccessAllTypesByItk( m_SliceImage, ItkImageProcessing, 2, itkImage ); //} //template //void mitk::OverwriteDirectedPlaneImageFilter::ItkImageProcessing( itk::Image* inputImage, itk::Image* outputImage ) //{ // typedef itk::Image SliceImageType; // typedef itk::Image DiffImageType; // typedef itk::Image VolumeImageType; // // typedef itk::ImageSliceIteratorWithIndex< VolumeImageType > OutputSliceIteratorType; // typedef itk::ImageRegionConstIterator< SliceImageType > InputSliceIteratorType; // //typedef itk::ImageRegionIterator< DiffImageType > DiffSliceIteratorType; // // InputSliceIteratorType inputIterator( inputImage, inputImage->GetLargestPossibleRegion() ); // // //typename DiffImageType::Pointer diffImage; // //CastToItkImage( m_SliceDifferenceImage, diffImage ); // //DiffSliceIteratorType diffIterator( diffImage, diffImage->GetLargestPossibleRegion() ); // // inputIterator.GoToBegin(); // //diffIterator.GoToBegin(); // // //TEST // Point3D origin = m_PlaneGeometry->GetOrigin(); // Vector3D right = m_PlaneGeometry->GetAxisVector(0); // Vector3D bottom = m_PlaneGeometry->GetAxisVector(1); // right.Normalize(); // bottom.Normalize(); // // Vector2D spacing = inputImage->GetSpacing(); // // Vector2D extentInMM; // extentInMM[0] = m_PlaneGeometry->GetExtentInMM(0); // extentInMM[1] = m_PlaneGeometry->GetExtentInMM(1); // // Vector2D extent; // extent[0] = m_PlaneGeometry->GetExtent(0); // extent[1] = m_PlaneGeometry->GetExtent(1); // //TEST ENDE // // Point3D currentPointIn2D, worldPointIn3D; // TPixel2 outputPixel = 0; // // int debugCounter( 0 ); // // std::ofstream geometryFile; // geometryFile.precision(30); // geometryFile.open("C:/Users/fetzer/Desktop/TEST/geometryFileOv.txt"); // // geometryFile<<"Offset: [ "<GetIndexToWorldTransform()->GetOffset()[0]<<", "<GetIndexToWorldTransform()->GetOffset()[1]<<", "<GetIndexToWorldTransform()->GetOffset()[2]<<" ]"<GetIndexToWorldTransform()->GetMatrix()<IndexToWorld( currentPointIn2D, worldPointIn3D ); // //typename itk::Image::IndexType outputIndex; // m_ImageGeometry3D->WorldToIndex( worldPointIn3D, outputIndex ); // // // Only access ITK image if it's inside // if ( m_ImageGeometry3D->IsIndexInside( outputIndex ) ) // { // //outputPixel = outputImage->GetPixel( outputIndex ); // outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); // /*if( inputIterator.Get() == mitk::paint::addPixelValue ) // { // outputImage->SetPixel( outputIndex, (TPixel2)( 1 ) ); // } // else if( inputIterator.Get() == mitk::paint::subPixelValue ) // { // outputImage->SetPixel( outputIndex, (TPixel2)( 0 ) ); // }*/ // } // ///****TEST***/ // ////typename itk::Image::IndexType outputIndex; // ////if ( columns == extent[0] ) ////{ //////If we are at the end of a row, then we have to go to the beginning of the next row ////currentImagePointIn3D = origin; ////currentImagePointIn3D += spacing[1]*bottom*currentPointIn2D[1]; //columns = 0; ////m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); ////} ////else ////{ ////if ( columns != 0 ) ////{ ////currentImagePointIn3D += spacing[0]*right; ////} ////m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); ////} // ////if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) ////{ ////outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); ////} ////else if (currentImagePointIn3D == origin) ////{ ////Point3D temp; ////temp[0] = bottom[0]*spacing[0]*0.5; ////temp[1] = bottom[1]*spacing[1]*0.5; ////temp[2] = bottom[2]*spacing[2]*0.5; ////origin[0] += temp[0]; ////origin[1] += temp[1]; ////origin[2] += temp[2]; ////currentImagePointIn3D = origin; ////m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); ////if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) ////{ ////outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); ////} ////} ////columns++; // ///****TEST ENDE****/ // ////* //// // Offset the world coordinate by one pixel to compensate for //// // index/world origin differences. //// Point3D offsetIndex; //// offsetIndex.Fill( 1 ); //// Point3D offsetWorld; //// offsetWorld.Fill( 0 ); //// m_PlaneGeometry->IndexToWorld( offsetIndex, offsetWorld ); //// // remove origin shift //// const Point3D origin = m_PlaneGeometry->GetOrigin(); //// offsetWorld[0] -= origin[0]; //// offsetWorld[1] -= origin[1]; //// offsetWorld[2] -= origin[2]; //// // offset world coordinate //// worldPointIn3D[ 0 ] += offsetWorld[0]; //// worldPointIn3D[ 1 ] += offsetWorld[1]; //// worldPointIn3D[ 2 ] += offsetWorld[2]; ////*/ // // Output index // // ////For the purpose of debug ////if( debugCounter%100 == 0) //////{ ////Point3D contIndex; ////m_ImageGeometry3D->WorldToIndex(worldPointIn3D,contIndex); ////overriderFile.precision(10); ////overriderFile<<"2D-Index: [ "<(inputIterator.Get() - outputPixel ) ); // oh oh, not good for bigger values // ++inputIterator; ////++debugCounter; // //++diffIterator; // } // /*overriderFile.close(); // overriderFileIndex.close();*/ // geometryFile.close(); // ///* // typename VolumeImageType::RegionType sliceInVolumeRegion; // // sliceInVolumeRegion = outputImage->GetLargestPossibleRegion(); // sliceInVolumeRegion.SetSize( m_SliceDimension, 1 ); // just one slice // sliceInVolumeRegion.SetIndex( m_SliceDimension, m_SliceIndex ); // exactly this slice, please // // OutputSliceIteratorType outputIterator( outputImage, sliceInVolumeRegion ); // outputIterator.SetFirstDirection(m_Dimension0); // outputIterator.SetSecondDirection(m_Dimension1); // // // iterate over output slice (and over input slice simultaneously) // outputIterator.GoToBegin(); // while ( !outputIterator.IsAtEnd() ) // { // while ( !outputIterator.IsAtEndOfSlice() ) // { // while ( !outputIterator.IsAtEndOfLine() ) // { // diffIterator.Set( static_cast(inputIterator.Get() - outputIterator.Get()) ); // oh oh, not good for bigger values // outputIterator.Set( (TPixel2) inputIterator.Get() ); // ++outputIterator; // ++inputIterator; // ++diffIterator; // } // outputIterator.NextLine(); // } // outputIterator.NextSlice(); // } // */ //} /* std::string mitk::OverwriteDirectedPlaneImageFilter::EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ) { std::stringstream s; s << "Changed slice ("; switch (sliceDimension) { default: case 2: s << "T"; break; case 1: s << "C"; break; case 0: s << "S"; break; } s << " " << sliceIndex << " " << timeStep << ")"; return s.str(); } */ diff --git a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h index 32db491432..5e8aaeefaf 100644 --- a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h +++ b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h @@ -1,118 +1,121 @@ /*=================================================================== 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 mitkOverwriteDirectedPlaneImageFilter_h_Included #define mitkOverwriteDirectedPlaneImageFilter_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkImageToImageFilter.h" #include namespace mitk { /** + \deprecated This class is deprecated. Use mitkVtkImageOverwrite instead. + \sa mitkVtkImageOverwrite + \brief Writes a 2D slice into a 3D image. \sa SegTool2D \sa ContourTool \sa ExtractImageFilter \ingroup Process \ingroup Reliver There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage This class takes a 3D mitk::Image as input and tries to replace one slice in it with the second input image, which is specified by calling SetSliceImage with a 2D mitk::Image. Two parameters determine which slice is replaced: the "slice dimension" is that one, which is constant for all points in the plane, e.g. transversal would mean 2. The "slice index" is the slice index in the image direction you specified with "affected dimension". Indices count from zero. This class works with all kind of image types, the only restrictions being that the input is 3D, and the slice image is 2D. If requested by SetCreateUndoInformation(true), this class will create instances of ApplyDiffImageOperation for the undo stack. These operations will (on user request) be executed by DiffImageApplier to perform undo. Last contributor: $Author: maleike $ */ class Segmentation_EXPORT OverwriteDirectedPlaneImageFilter : public ImageToImageFilter { public: mitkClassMacro(OverwriteDirectedPlaneImageFilter, ImageToImageFilter); itkNewMacro(OverwriteDirectedPlaneImageFilter); /** \brief Which plane to overwrite */ const Geometry3D* GetPlaneGeometry3D() const { return m_PlaneGeometry; } void SetPlaneGeometry3D( const Geometry3D *geometry ) { m_PlaneGeometry = geometry; } /** \brief Time step of the slice to overwrite */ itkSetMacro(TimeStep, unsigned int); itkGetConstMacro(TimeStep, unsigned int); /** \brief Whether to create undo operation in the MITK undo stack */ itkSetMacro(CreateUndoInformation, bool); itkGetConstMacro(CreateUndoInformation, bool); itkSetObjectMacro(SliceImage, Image); const Image* GetSliceImage() { return m_SliceImage.GetPointer(); } const Image* GetLastDifferenceImage() { return m_SliceDifferenceImage.GetPointer(); } protected: OverwriteDirectedPlaneImageFilter(); // purposely hidden virtual ~OverwriteDirectedPlaneImageFilter(); virtual void GenerateData(); template void ItkSliceOverwriting (itk::Image* input3D); template void ItkImageSwitch( itk::Image* image ); template void ItkImageProcessing( itk::Image* itkImage1, itk::Image* itkImage2 ); //std::string EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ); Image::ConstPointer m_SliceImage; Image::Pointer m_SliceDifferenceImage; const Geometry3D *m_PlaneGeometry; const Geometry3D *m_ImageGeometry3D; unsigned int m_TimeStep; unsigned int m_Dimension0; unsigned int m_Dimension1; bool m_CreateUndoInformation; }; } // namespace #endif diff --git a/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.cpp index 8024b01963..da25152a48 100644 --- a/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.cpp @@ -1,246 +1,247 @@ /*=================================================================== 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 "mitkOverwriteSliceImageFilter.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkSegmentationInterpolationController.h" #include "mitkApplyDiffImageOperation.h" #include "mitkOperationEvent.h" #include "mitkInteractionConst.h" #include "mitkUndoController.h" #include "mitkDiffImageApplier.h" #include "mitkImageTimeSelector.h" #include #include mitk::OverwriteSliceImageFilter::OverwriteSliceImageFilter() :m_SliceIndex(0), m_SliceDimension(0), m_TimeStep(0), m_Dimension0(0), m_Dimension1(1), m_CreateUndoInformation(false) { + MITK_WARN << "Class is deprecated! Use mitkVtkImageOverwrite instead."; } mitk::OverwriteSliceImageFilter::~OverwriteSliceImageFilter() { } void mitk::OverwriteSliceImageFilter::GenerateData() { // // this is the place to implement the major part of undo functionality (bug #491) // here we have to create undo/do operations // // WHO is the operation actor? This object may not be destroyed ever (design of undo stack)! // -> some singleton method of this filter? // // neccessary additional objects: // - something that executes the operations // - the operation class (must hold a binary diff or something) // - observer commands to know when the image is deleted (no further action then, perhaps even remove the operations from the undo stack) // Image::ConstPointer input = ImageToImageFilter::GetInput(0); Image::ConstPointer input3D = input; Image::ConstPointer slice = m_SliceImage; if ( input.IsNull() || slice.IsNull() ) return; switch (m_SliceDimension) { default: case 2: m_Dimension0 = 0; m_Dimension1 = 1; break; case 1: m_Dimension0 = 0; m_Dimension1 = 2; break; case 0: m_Dimension0 = 1; m_Dimension1 = 2; break; } if ( slice->GetDimension() < 2 || input->GetDimension() > 4 || slice->GetDimension(0) != input->GetDimension(m_Dimension0) || slice->GetDimension(1) != input->GetDimension(m_Dimension1) || m_SliceIndex >= input->GetDimension(m_SliceDimension) ) { itkExceptionMacro("Slice and image dimensions differ or slice index is too large. Sorry, cannot work like this."); return; } if ( input->GetDimension() == 4 ) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( input ); timeSelector->SetTimeNr( m_TimeStep ); timeSelector->UpdateLargestPossibleRegion(); input3D = timeSelector->GetOutput(); } if ( m_SliceDifferenceImage.IsNull() || m_SliceDifferenceImage->GetDimension(0) != m_SliceImage->GetDimension(0) || m_SliceDifferenceImage->GetDimension(1) != m_SliceImage->GetDimension(1) ) { m_SliceDifferenceImage = mitk::Image::New(); mitk::PixelType pixelType( mitk::MakeScalarPixelType() ); m_SliceDifferenceImage->Initialize( pixelType, 2, m_SliceImage->GetDimensions() ); } //MITK_INFO << "Overwriting slice " << m_SliceIndex << " in dimension " << m_SliceDimension << " at time step " << m_TimeStep << std::endl; // this will do a long long if/else to find out both pixel types AccessFixedDimensionByItk( input3D, ItkImageSwitch, 3 ); SegmentationInterpolationController* interpolator = SegmentationInterpolationController::InterpolatorForImage( input ); if (interpolator) { interpolator->BlockModified(true); interpolator->SetChangedSlice( m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep ); } if ( m_CreateUndoInformation ) { // create do/undo operations (we don't execute the doOp here, because it has already been executed during calculation of the diff image ApplyDiffImageOperation* doOp = new ApplyDiffImageOperation( OpTEST, const_cast(input.GetPointer()), m_SliceDifferenceImage, m_TimeStep, m_SliceDimension, m_SliceIndex ); ApplyDiffImageOperation* undoOp = new ApplyDiffImageOperation( OpTEST, const_cast(input.GetPointer()), m_SliceDifferenceImage, m_TimeStep, m_SliceDimension, m_SliceIndex ); undoOp->SetFactor( -1.0 ); OperationEvent* undoStackItem = new OperationEvent( DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, this->EventDescription(m_SliceDimension, m_SliceIndex, m_TimeStep) ); UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); } // this image is modified (good to know for the renderer) input->Modified(); if (interpolator) { interpolator->BlockModified(false); } } // basically copied from mitk/Core/Algorithms/mitkImageAccessByItk.h #define myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, pixeltype, dimension, itkimage2) \ if ( typeId == typeid(pixeltype) ) \ { \ typedef itk::Image ImageType; \ typedef mitk::ImageToItk ImageToItkType; \ itk::SmartPointer imagetoitk = ImageToItkType::New(); \ imagetoitk->SetInput(mitkImage); \ imagetoitk->Update(); \ itkImageTypeFunction(imagetoitk->GetOutput(), itkimage2); \ } #define myMITKOverwriteSliceImageFilterAccessAllTypesByItk(mitkImage, itkImageTypeFunction, dimension, itkimage2) \ { \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, double, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, float, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, int, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned int, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, short, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned short, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, char, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned char, dimension, itkimage2) \ } template void mitk::OverwriteSliceImageFilter::ItkImageSwitch( itk::Image* itkImage ) { const std::type_info& typeId=m_SliceImage->GetPixelType().GetTypeId(); myMITKOverwriteSliceImageFilterAccessAllTypesByItk( m_SliceImage, ItkImageProcessing, 2, itkImage ); } template void mitk::OverwriteSliceImageFilter::ItkImageProcessing( itk::Image* inputImage, itk::Image* outputImage ) { typedef itk::Image SliceImageType; typedef itk::Image DiffImageType; typedef itk::Image VolumeImageType; typedef itk::ImageSliceIteratorWithIndex< VolumeImageType > OutputSliceIteratorType; typedef itk::ImageRegionConstIterator< SliceImageType > InputSliceIteratorType; typedef itk::ImageRegionIterator< DiffImageType > DiffSliceIteratorType; typename VolumeImageType::RegionType sliceInVolumeRegion; sliceInVolumeRegion = outputImage->GetLargestPossibleRegion(); sliceInVolumeRegion.SetSize( m_SliceDimension, 1 ); // just one slice sliceInVolumeRegion.SetIndex( m_SliceDimension, m_SliceIndex ); // exactly this slice, please OutputSliceIteratorType outputIterator( outputImage, sliceInVolumeRegion ); outputIterator.SetFirstDirection(m_Dimension0); outputIterator.SetSecondDirection(m_Dimension1); InputSliceIteratorType inputIterator( inputImage, inputImage->GetLargestPossibleRegion() ); typename DiffImageType::Pointer diffImage; CastToItkImage( m_SliceDifferenceImage, diffImage ); DiffSliceIteratorType diffIterator( diffImage, diffImage->GetLargestPossibleRegion() ); // iterate over output slice (and over input slice simultaneously) outputIterator.GoToBegin(); inputIterator.GoToBegin(); diffIterator.GoToBegin(); while ( !outputIterator.IsAtEnd() ) { while ( !outputIterator.IsAtEndOfSlice() ) { while ( !outputIterator.IsAtEndOfLine() ) { diffIterator.Set( static_cast(inputIterator.Get() - outputIterator.Get()) ); // oh oh, not good for bigger values outputIterator.Set( (TPixel2) inputIterator.Get() ); ++outputIterator; ++inputIterator; ++diffIterator; } outputIterator.NextLine(); } outputIterator.NextSlice(); } } std::string mitk::OverwriteSliceImageFilter::EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ) { std::stringstream s; s << "Changed slice ("; switch (sliceDimension) { default: case 2: s << "T"; break; case 1: s << "C"; break; case 0: s << "S"; break; } s << " " << sliceIndex << " " << timeStep << ")"; return s.str(); } diff --git a/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h index bb165108ae..1c0e2d9624 100644 --- a/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h +++ b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h @@ -1,123 +1,126 @@ /*=================================================================== 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 mitkOverwriteSliceImageFilter_h_Included #define mitkOverwriteSliceImageFilter_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkImageToImageFilter.h" #include namespace mitk { /** + \deprecated This class is deprecated. Use mitkVtkImageOverwrite instead. + \sa mitkVtkImageOverwrite + \brief Writes a 2D slice into a 3D image. \sa SegTool2D \sa ContourTool \sa ExtractImageFilter \ingroup Process \ingroup ToolManagerEtAl There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage This class takes a 3D mitk::Image as input and tries to replace one slice in it with the second input image, which is specified by calling SetSliceImage with a 2D mitk::Image. Two parameters determine which slice is replaced: the "slice dimension" is that one, which is constant for all points in the plane, e.g. transversal would mean 2. The "slice index" is the slice index in the image direction you specified with "affected dimension". Indices count from zero. This class works with all kind of image types, the only restrictions being that the input is 3D, and the slice image is 2D. If requested by SetCreateUndoInformation(true), this class will create instances of ApplyDiffImageOperation for the undo stack. These operations will (on user request) be executed by DiffImageApplier to perform undo. Last contributor: $Author$ */ class Segmentation_EXPORT OverwriteSliceImageFilter : public ImageToImageFilter { public: mitkClassMacro(OverwriteSliceImageFilter, ImageToImageFilter); itkNewMacro(OverwriteSliceImageFilter); /** \brief Which slice to overwrite (first one has index 0). */ itkSetMacro(SliceIndex, unsigned int); itkGetConstMacro(SliceIndex, unsigned int); /** \brief The orientation of the slice to overwrite. \a Parameter \a SliceDimension Number of the dimension which is constant for all pixels of the desired slices (e.g. 0 for transversal) */ itkSetMacro(SliceDimension, unsigned int); itkGetConstMacro(SliceDimension, unsigned int); /** \brief Time step of the slice to overwrite */ itkSetMacro(TimeStep, unsigned int); itkGetConstMacro(TimeStep, unsigned int); /** \brief Whether to create undo operation in the MITK undo stack */ itkSetMacro(CreateUndoInformation, bool); itkGetConstMacro(CreateUndoInformation, bool); itkSetObjectMacro(SliceImage, Image); const Image* GetSliceImage() { return m_SliceImage.GetPointer(); } const Image* GetLastDifferenceImage() { return m_SliceDifferenceImage.GetPointer(); } protected: OverwriteSliceImageFilter(); // purposely hidden virtual ~OverwriteSliceImageFilter(); virtual void GenerateData(); template void ItkImageSwitch( itk::Image* image ); template void ItkImageProcessing( itk::Image* itkImage1, itk::Image* itkImage2 ); std::string EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ); Image::ConstPointer m_SliceImage; Image::Pointer m_SliceDifferenceImage; unsigned int m_SliceIndex; unsigned int m_SliceDimension; unsigned int m_TimeStep; unsigned int m_Dimension0; unsigned int m_Dimension1; bool m_CreateUndoInformation; }; } // namespace #endif diff --git a/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.cpp b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.cpp new file mode 100644 index 0000000000..95a14ff564 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.cpp @@ -0,0 +1,901 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: mitkVtkImageOverwrite.cpp + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "mitkVtkImageOverwrite.h" + +#include "vtkImageData.h" +#include "vtkImageStencilData.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkMath.h" +#include "vtkObjectFactory.h" +#include "vtkStreamingDemandDrivenPipeline.h" +#include "vtkTransform.h" +#include "vtkDataSetAttributes.h" +#include "vtkGarbageCollector.h" + +#include "vtkTemplateAliasMacro.h" +// turn off 64-bit ints when templating over all types +# undef VTK_USE_INT64 +# define VTK_USE_INT64 0 +# undef VTK_USE_UINT64 +# define VTK_USE_UINT64 0 + +#include +#include +#include + + +vtkStandardNewMacro(mitkVtkImageOverwrite); + + + + +//-------------------------------------------------------------------------- +// The 'floor' function on x86 and mips is many times slower than these +// and is used a lot in this code, optimize for different CPU architectures +template +inline int vtkResliceFloor(double x, F &f) +{ +#if defined mips || defined sparc || defined __ppc__ + x += 2147483648.0; + unsigned int i = static_cast(x); + f = x - i; + return static_cast(i - 2147483648U); +#elif defined i386 || defined _M_IX86 + union { double d; unsigned short s[4]; unsigned int i[2]; } dual; + dual.d = x + 103079215104.0; // (2**(52-16))*1.5 + f = dual.s[0]*0.0000152587890625; // 2**(-16) + return static_cast((dual.i[1]<<16)|((dual.i[0])>>16)); +#elif defined ia64 || defined __ia64__ || defined IA64 + x += 103079215104.0; + long long i = static_cast(x); + f = x - i; + return static_cast(i - 103079215104LL); +#else + double y = floor(x); + f = x - y; + return static_cast(y); +#endif +} + +inline int vtkResliceRound(double x) +{ +#if defined mips || defined sparc || defined __ppc__ + return static_cast(static_cast(x + 2147483648.5) - 2147483648U); +#elif defined i386 || defined _M_IX86 + union { double d; unsigned int i[2]; } dual; + dual.d = x + 103079215104.5; // (2**(52-16))*1.5 + return static_cast((dual.i[1]<<16)|((dual.i[0])>>16)); +#elif defined ia64 || defined __ia64__ || defined IA64 + x += 103079215104.5; + long long i = static_cast(x); + return static_cast(i - 103079215104LL); +#else + return static_cast(floor(x+0.5)); +#endif +} + +//---------------------------------------------------------------------------- +mitkVtkImageOverwrite::mitkVtkImageOverwrite() +{ + m_Overwrite_Mode = false; + this->GetOutput()->SetScalarTypeToUnsignedInt(); +} + +//---------------------------------------------------------------------------- +mitkVtkImageOverwrite::~mitkVtkImageOverwrite() +{ +} + + + + +//---------------------------------------------------------------------------- +// constants for different boundary-handling modes + +#define VTK_RESLICE_BACKGROUND 0 // use background if out-of-bounds +#define VTK_RESLICE_WRAP 1 // wrap to opposite side of image +#define VTK_RESLICE_MIRROR 2 // mirror off of the boundary +#define VTK_RESLICE_BORDER 3 // use a half-voxel border +#define VTK_RESLICE_NULL 4 // do nothing to *outPtr if out-of-bounds + +//---------------------------------------------------------------------------- +// rounding functions for each type, where 'F' is a floating-point type + +#if (VTK_USE_INT8 != 0) +template +inline void vtkResliceRound(F val, vtkTypeInt8& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_UINT8 != 0) +template +inline void vtkResliceRound(F val, vtkTypeUInt8& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_INT16 != 0) +template +inline void vtkResliceRound(F val, vtkTypeInt16& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_UINT16 != 0) +template +inline void vtkResliceRound(F val, vtkTypeUInt16& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_INT32 != 0) +template +inline void vtkResliceRound(F val, vtkTypeInt32& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_UINT32 != 0) +template +inline void vtkResliceRound(F val, vtkTypeUInt32& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_FLOAT32 != 0) +template +inline void vtkResliceRound(F val, vtkTypeFloat32& rnd) +{ + rnd = val; +} +#endif + +#if (VTK_USE_FLOAT64 != 0) +template +inline void vtkResliceRound(F val, vtkTypeFloat64& rnd) +{ + rnd = val; +} +#endif + +//---------------------------------------------------------------------------- +// clamping functions for each type + +#if (VTK_USE_INT8 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeInt8& clamp) +{ + if (val < -128.0) + { + val = -128.0; + } + if (val > 127.0) + { + val = 127.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_UINT8 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeUInt8& clamp) +{ + if (val < 0) + { + val = 0; + } + if (val > 255.0) + { + val = 255.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_INT16 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeInt16& clamp) +{ + if (val < -32768.0) + { + val = -32768.0; + } + if (val > 32767.0) + { + val = 32767.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_UINT16 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeUInt16& clamp) +{ + if (val < 0) + { + val = 0; + } + if (val > 65535.0) + { + val = 65535.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_INT32 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeInt32& clamp) +{ + if (val < -2147483648.0) + { + val = -2147483648.0; + } + if (val > 2147483647.0) + { + val = 2147483647.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_UINT32 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeUInt32& clamp) +{ + if (val < 0) + { + val = 0; + } + if (val > 4294967295.0) + { + val = 4294967295.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_FLOAT32 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeFloat32& clamp) +{ + clamp = val; +} +#endif + +#if (VTK_USE_FLOAT64 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeFloat64& clamp) +{ + clamp = val; +} +#endif + +//---------------------------------------------------------------------------- +// Perform a wrap to limit an index to [0,range). +// Ensures correct behaviour when the index is negative. + +inline int vtkInterpolateWrap(int num, int range) +{ + if ((num %= range) < 0) + { + num += range; // required for some % implementations + } + return num; +} + +//---------------------------------------------------------------------------- +// Perform a mirror to limit an index to [0,range). + +inline int vtkInterpolateMirror(int num, int range) +{ + if (num < 0) + { + num = -num - 1; + } + int count = num/range; + num %= range; + if (count & 0x1) + { + num = range - num - 1; + } + return num; +} + +//---------------------------------------------------------------------------- +// If the value is within one half voxel of the range [0,inExtX), then +// set it to "0" or "inExtX-1" as appropriate. + +inline int vtkInterpolateBorder(int &inIdX0, int &inIdX1, int inExtX, + double fx) +{ + if (inIdX0 >= 0 && inIdX1 < inExtX) + { + return 0; + } + if (inIdX0 == -1 && fx >= 0.5) + { + inIdX1 = inIdX0 = 0; + return 0; + } + if (inIdX0 == inExtX - 1 && fx < 0.5) + { + inIdX1 = inIdX0; + return 0; + } + + return 1; +} + +inline int vtkInterpolateBorderCheck(int inIdX0, int inIdX1, int inExtX, + double fx) +{ + if ((inIdX0 >= 0 && inIdX1 < inExtX) || + (inIdX0 == -1 && fx >= 0.5) || + (inIdX0 == inExtX - 1 && fx < 0.5)) + { + return 0; + } + + return 1; +} + +//---------------------------------------------------------------------------- +// Do nearest-neighbor interpolation of the input data 'inPtr' of extent +// 'inExt' at the 'point'. The result is placed at 'outPtr'. +// If the lookup data is beyond the extent 'inExt', set 'outPtr' to +// the background color 'background'. +// The number of scalar components in the data is 'numscalars' +template +static int vtkNearestNeighborInterpolation(T *&outPtr, const T *inPtr, + const int inExt[6], + const vtkIdType inInc[3], + int numscalars, const F point[3], + int mode, const T *background, + mitkVtkImageOverwrite *self) +{ + int inIdX0 = vtkResliceRound(point[0]) - inExt[0]; + int inIdY0 = vtkResliceRound(point[1]) - inExt[2]; + int inIdZ0 = vtkResliceRound(point[2]) - inExt[4]; + + int inExtX = inExt[1] - inExt[0] + 1; + int inExtY = inExt[3] - inExt[2] + 1; + int inExtZ = inExt[5] - inExt[4] + 1; + + if (inIdX0 < 0 || inIdX0 >= inExtX || + inIdY0 < 0 || inIdY0 >= inExtY || + inIdZ0 < 0 || inIdZ0 >= inExtZ) + { + if (mode == VTK_RESLICE_WRAP) + { + inIdX0 = vtkInterpolateWrap(inIdX0, inExtX); + inIdY0 = vtkInterpolateWrap(inIdY0, inExtY); + inIdZ0 = vtkInterpolateWrap(inIdZ0, inExtZ); + } + else if (mode == VTK_RESLICE_MIRROR) + { + inIdX0 = vtkInterpolateMirror(inIdX0, inExtX); + inIdY0 = vtkInterpolateMirror(inIdY0, inExtY); + inIdZ0 = vtkInterpolateMirror(inIdZ0, inExtZ); + } + else if (mode == VTK_RESLICE_BACKGROUND || + mode == VTK_RESLICE_BORDER) + { + do + { + *outPtr++ = *background++; + } + while (--numscalars); + return 0; + } + else + { + return 0; + } + } + + inPtr += inIdX0*inInc[0]+inIdY0*inInc[1]+inIdZ0*inInc[2]; + + do + { + + if(!self->IsOverwriteMode()) + { + //just copy from input to output + *outPtr++ = *inPtr++; + } + else + { + //copy from output to input in overwrite mode + *(const_cast(inPtr)) = *outPtr++; + inPtr++; + } + } + while (--numscalars); + + return 1; +} + + +//-------------------------------------------------------------------------- +// get appropriate interpolation function according to scalar type +template +static void vtkGetResliceInterpFunc(mitkVtkImageOverwrite *self, + int (**interpolate)(void *&outPtr, + const void *inPtr, + const int inExt[6], + const vtkIdType inInc[3], + int numscalars, + const F point[3], + int mode, + const void *background, + mitkVtkImageOverwrite *self)) +{ + int dataType = self->GetOutput()->GetScalarType(); + + switch (dataType) + { + vtkTemplateAliasMacro(*((int (**)(VTK_TT *&outPtr, const VTK_TT *inPtr, + const int inExt[6], + const vtkIdType inInc[3], + int numscalars, const F point[3], + int mode, + const VTK_TT *background, + mitkVtkImageOverwrite *self))interpolate) = \ + &vtkNearestNeighborInterpolation); + default: + interpolate = 0; + } +} + + +//---------------------------------------------------------------------------- +// Some helper functions for 'RequestData' +//---------------------------------------------------------------------------- + +//-------------------------------------------------------------------------- +// pixel copy function, templated for different scalar types +template +struct vtkImageResliceSetPixels +{ +static void Set(void *&outPtrV, const void *inPtrV, int numscalars, int n) +{ + const T* inPtr = static_cast(inPtrV); + T* outPtr = static_cast(outPtrV); + for (int i = 0; i < n; i++) + { + const T *tmpPtr = inPtr; + int m = numscalars; + do + { + *outPtr++ = *tmpPtr++; + } + while (--m); + } + outPtrV = outPtr; +} + +// optimized for 1 scalar components +static void Set1(void *&outPtrV, const void *inPtrV, + int vtkNotUsed(numscalars), int n) +{ + const T* inPtr = static_cast(inPtrV); + T* outPtr = static_cast(outPtrV); + T val = *inPtr; + for (int i = 0; i < n; i++) + { + *outPtr++ = val; + } + outPtrV = outPtr; +} +}; + +// get a pixel copy function that is appropriate for the data type +static void vtkGetSetPixelsFunc(mitkVtkImageOverwrite *self, + void (**setpixels)(void *&out, const void *in, + int numscalars, int n)) +{ + int dataType = self->GetOutput()->GetScalarType(); + int numscalars = self->GetOutput()->GetNumberOfScalarComponents(); + + switch (numscalars) + { + case 1: + switch (dataType) + { + vtkTemplateAliasMacro( + *setpixels = &vtkImageResliceSetPixels::Set1 + ); + default: + setpixels = 0; + } + default: + switch (dataType) + { + vtkTemplateAliasMacro( + *setpixels = &vtkImageResliceSetPixels::Set + ); + default: + setpixels = 0; + } + } +} + +//---------------------------------------------------------------------------- +// Convert background color from float to appropriate type +template +static void vtkAllocBackgroundPixelT(mitkVtkImageOverwrite *self, + T **background_ptr, int numComponents) +{ + *background_ptr = new T[numComponents]; + T *background = *background_ptr; + + for (int i = 0; i < numComponents; i++) + { + if (i < 4) + { + vtkResliceClamp(self->GetBackgroundColor()[i], background[i]); + } + else + { + background[i] = 0; + } + } +} + +static void vtkAllocBackgroundPixel(mitkVtkImageOverwrite *self, void **rval, + int numComponents) +{ + switch (self->GetOutput()->GetScalarType()) + { + vtkTemplateAliasMacro(vtkAllocBackgroundPixelT(self, (VTK_TT **)rval, + numComponents)); + } +} + +static void vtkFreeBackgroundPixel(mitkVtkImageOverwrite *self, void **rval) +{ + switch (self->GetOutput()->GetScalarType()) + { + vtkTemplateAliasMacro(delete [] *((VTK_TT **)rval)); + } + + *rval = 0; +} + +//---------------------------------------------------------------------------- +// helper function for clipping of the output with a stencil +static int vtkResliceGetNextExtent(vtkImageStencilData *stencil, + int &r1, int &r2, int rmin, int rmax, + int yIdx, int zIdx, + void *&outPtr, void *background, + int numscalars, + void (*setpixels)(void *&out, + const void *in, + int numscalars, + int n), + int &iter) +{ + // trivial case if stencil is not set + if (!stencil) + { + if (iter++ == 0) + { + r1 = rmin; + r2 = rmax; + return 1; + } + return 0; + } + + // for clearing, start at last r2 plus 1 + int clear1 = r2 + 1; + if (iter == 0) + { // if no 'last time', start at rmin + clear1 = rmin; + } + + int rval = stencil->GetNextExtent(r1, r2, rmin, rmax, yIdx, zIdx, iter); + int clear2 = r1 - 1; + if (rval == 0) + { + clear2 = rmax; + } + + setpixels(outPtr, background, numscalars, clear2 - clear1 + 1); + + return rval; +} + +//---------------------------------------------------------------------------- +// This function simply clears the entire output to the background color, +// for cases where the transformation places the output extent completely +// outside of the input extent. +static void vtkImageResliceClearExecute(mitkVtkImageOverwrite *self, + vtkImageData *, void *, + vtkImageData *outData, void *outPtr, + int outExt[6], int id) +{ + int numscalars; + int idY, idZ; + vtkIdType outIncX, outIncY, outIncZ; + int scalarSize; + unsigned long count = 0; + unsigned long target; + void *background; + void (*setpixels)(void *&out, const void *in, int numscalars, int n); + + // for the progress meter + target = static_cast + ((outExt[5]-outExt[4]+1)*(outExt[3]-outExt[2]+1)/50.0); + target++; + + // Get Increments to march through data + outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); + scalarSize = outData->GetScalarSize(); + numscalars = outData->GetNumberOfScalarComponents(); + + // allocate a voxel to copy into the background (out-of-bounds) regions + vtkAllocBackgroundPixel(self, &background, numscalars); + // get the appropriate function for pixel copying + vtkGetSetPixelsFunc(self, &setpixels); + + // Loop through output voxels + for (idZ = outExt[4]; idZ <= outExt[5]; idZ++) + { + for (idY = outExt[2]; idY <= outExt[3]; idY++) + { + if (id == 0) + { // update the progress if this is the main thread + if (!(count%target)) + { + self->UpdateProgress(count/(50.0*target)); + } + count++; + } + // clear the pixels to background color and go to next row + setpixels(outPtr, background, numscalars, outExt[1]-outExt[0]+1); + outPtr = static_cast( + static_cast(outPtr) + outIncY*scalarSize); + } + outPtr = static_cast( + static_cast(outPtr) + outIncZ*scalarSize); + } + + vtkFreeBackgroundPixel(self, &background); +} + +//---------------------------------------------------------------------------- +// This function executes the filter for any type of data. It is much simpler +// in structure than vtkImageResliceOptimizedExecute. +static void vtkImageResliceExecute(mitkVtkImageOverwrite *self, + vtkImageData *inData, void *inPtr, + vtkImageData *outData, void *outPtr, + int outExt[6], int id) +{ + int numscalars; + int idX, idY, idZ; + int idXmin, idXmax, iter; + vtkIdType outIncX, outIncY, outIncZ; + int scalarSize; + int inExt[6]; + vtkIdType inInc[3]; + unsigned long count = 0; + unsigned long target; + double point[4]; + double f; + double *inSpacing, *inOrigin, *outSpacing, *outOrigin, inInvSpacing[3]; + void *background; + int (*interpolate)(void *&outPtr, const void *inPtr, + const int inExt[6], const vtkIdType inInc[3], + int numscalars, const double point[3], + int mode, const void *background, mitkVtkImageOverwrite *self); + void (*setpixels)(void *&out, const void *in, int numscalars, int n); + + // the 'mode' species what to do with the 'pad' (out-of-bounds) area + int mode = VTK_RESLICE_BACKGROUND; + if (self->GetMirror()) + { + mode = VTK_RESLICE_MIRROR; + } + else if (self->GetWrap()) + { + mode = VTK_RESLICE_WRAP; + } + else if (self->GetBorder()) + { + mode = VTK_RESLICE_BORDER; + } + + // the transformation to apply to the data + vtkAbstractTransform *transform = self->GetResliceTransform(); + vtkMatrix4x4 *matrix = self->GetResliceAxes(); + + // for conversion to data coordinates + inOrigin = inData->GetOrigin(); + inSpacing = inData->GetSpacing(); + outOrigin = outData->GetOrigin(); + outSpacing = outData->GetSpacing(); + + // save effor later: invert inSpacing + inInvSpacing[0] = 1.0/inSpacing[0]; + inInvSpacing[1] = 1.0/inSpacing[1]; + inInvSpacing[2] = 1.0/inSpacing[2]; + + // find maximum input range + inData->GetExtent(inExt); + + // for the progress meter + target = static_cast + ((outExt[5]-outExt[4]+1)*(outExt[3]-outExt[2]+1)/50.0); + target++; + + // Get Increments to march through data + inData->GetIncrements(inInc); + outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); + scalarSize = outData->GetScalarSize(); + numscalars = inData->GetNumberOfScalarComponents(); + + // allocate a voxel to copy into the background (out-of-bounds) regions + vtkAllocBackgroundPixel(self, &background, numscalars); + + // get the appropriate functions for interpolation and pixel copying + vtkGetResliceInterpFunc(self, &interpolate); + vtkGetSetPixelsFunc(self, &setpixels); + + // get the stencil + vtkImageStencilData *stencil = self->GetStencil(); + + // Loop through output voxels + for (idZ = outExt[4]; idZ <= outExt[5]; idZ++) + { + for (idY = outExt[2]; idY <= outExt[3]; idY++) + { + if (id == 0) + { // update the progress if this is the main thread + if (!(count%target)) + { + self->UpdateProgress(count/(50.0*target)); + } + count++; + } + + iter = 0; // if there is a stencil, it is applied here + while (vtkResliceGetNextExtent(stencil, idXmin, idXmax, + outExt[0], outExt[1], idY, idZ, + outPtr, background, numscalars, + setpixels, iter)) + { + for (idX = idXmin; idX <= idXmax; idX++) + { + // convert to data coordinates + point[0] = idX*outSpacing[0] + outOrigin[0]; + point[1] = idY*outSpacing[1] + outOrigin[1]; + point[2] = idZ*outSpacing[2] + outOrigin[2]; + + // apply ResliceAxes matrix + if (matrix) + { + point[3] = 1.0; + matrix->MultiplyPoint(point, point); + f = 1.0/point[3]; + point[0] *= f; + point[1] *= f; + point[2] *= f; + } + + // apply ResliceTransform + if (transform) + { + transform->InternalTransformPoint(point, point); + } + + // convert back to voxel indices + point[0] = (point[0] - inOrigin[0])*inInvSpacing[0]; + point[1] = (point[1] - inOrigin[1])*inInvSpacing[1]; + point[2] = (point[2] - inOrigin[2])*inInvSpacing[2]; + + // interpolate output voxel from input data set + interpolate(outPtr, inPtr, inExt, inInc, numscalars, + point, mode, background, self); + } + } + outPtr = static_cast( + static_cast(outPtr) + outIncY*scalarSize); + } + outPtr = static_cast( + static_cast(outPtr) + outIncZ*scalarSize); + } + + vtkFreeBackgroundPixel(self, &background); +} + + +void mitkVtkImageOverwrite::SetOverwriteMode(bool b){ + m_Overwrite_Mode = b; +} + + +void mitkVtkImageOverwrite::SetInputSlice(vtkImageData* slice){ + //set the output as input + this->SetOutput(slice); +} + + + + +//---------------------------------------------------------------------------- +// This method is passed a input and output region, and executes the filter +// algorithm to fill the output from the input or vice versa. +void mitkVtkImageOverwrite::ThreadedRequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **vtkNotUsed(inputVector), + vtkInformationVector *vtkNotUsed(outputVector), + vtkImageData ***inData, + vtkImageData **outData, + int outExt[6], int id) +{ + + vtkDebugMacro(<< "Execute: inData = " << inData[0][0] + << ", outData = " << outData[0]); + // this filter expects that input is the same type as output. + if (inData[0][0]->GetScalarType() != outData[0]->GetScalarType()) + { + vtkErrorMacro(<< "Execute: input ScalarType, " + << inData[0][0]->GetScalarType() + << ", must match out ScalarType " + << outData[0]->GetScalarType()); + return; + } + + int inExt[6]; + inData[0][0]->GetExtent(inExt); + // check for empty input extent + if (inExt[1] < inExt[0] || + inExt[3] < inExt[2] || + inExt[5] < inExt[4]) + { + return; + } + + // Get the output pointer + void *outPtr = outData[0]->GetScalarPointerForExtent(outExt); + + if (this->HitInputExtent == 0) + { + vtkImageResliceClearExecute(this, inData[0][0], 0, outData[0], outPtr, + outExt, id); + return; + } + + // Now that we know that we need the input, get the input pointer + void *inPtr = inData[0][0]->GetScalarPointerForExtent(inExt); + + + vtkImageResliceExecute(this, inData[0][0], inPtr, outData[0], outPtr, + outExt, id); +} + + diff --git a/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h new file mode 100644 index 0000000000..c6a5e40913 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h @@ -0,0 +1,88 @@ +/*=================================================================== + +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 mitkVtkImageOverwrite_h_Included +#define mitkVtkImageOverwrite_h_Included + +#include +#include "SegmentationExports.h" + + /** \brief A vtk Filter based on vtkImageReslice with the aditional feature to write a slice into the given input volume. + All optimizations for e.g. the plane directions or interpolation are stripped away, the algorithm only interpolates nearest + neighbor and uses the non optimized execute function of vtkImageReslice. Note that any interpolation doesn't make sense + for round trip use extract->edit->overwrite, because it is nearly impossible to invert the interolation. + There are two use cases for the Filter which are specified by the overwritemode property: + + 1)Extract slices from a 3D volume. + Overwritemode = false + + In this mode the class can be used like vtkImageReslice. The usual way to do this is: + - Set an image volume as input + - Set the ResliceAxes via SetResliceAxesDirectionCosines and SetResliceAxesOrigin + - Set the the OutputSpacing, OutputOrigin and OutputExtent + - Call Update + + + 2)Overwrite a 3D volume at a given slice. + Overwritemode = true + + The handling in this mode is quite similar to the description above with the addition that the + InputSlice needs to be specified via SetInputSlice(vtkImageData*). + - Set the properties mentioned above (Note that SetInput specifies the volume to write to) + - Set the slice to that has to be overwritten in the volume ( SetInputSlice(vtkImageData*) + + After calling Update() there is no need to retrieve the output as the input volume is modified. + + \sa vtkImageReslice + (Note that the execute and interpolation functions are no members and thus can not be overriden) + */ + class Segmentation_EXPORT mitkVtkImageOverwrite : public vtkImageReslice + { + public: + static mitkVtkImageOverwrite *New(); + vtkTypeMacro(mitkVtkImageOverwrite, vtkImageReslice); + + /** \brief Set the mode either to reslice (false) or to overwrite (true). + Default: false + */ + void SetOverwriteMode(bool b); + bool IsOverwriteMode(){return m_Overwrite_Mode;} + + /** \brief Set the slice for overwrite mode. + Note: + It is recommend not to use this in reslice mode because otherwise the slice will be modified! + */ + void SetInputSlice(vtkImageData* slice); + + + protected: + + mitkVtkImageOverwrite(); + virtual ~mitkVtkImageOverwrite(); + + bool m_Overwrite_Mode; + + /** Overridden from vtkImageReslice. \sa vtkImageReslice::ThreadedRequestData */ + virtual void ThreadedRequestData(vtkInformation *vtkNotUsed(request), + vtkInformationVector **vtkNotUsed(inputVector), + vtkInformationVector *vtkNotUsed(outputVector), + vtkImageData ***inData, + vtkImageData **outData, + int outExt[6], int id); + }; + + +#endif //mitkVtkImageOverwrite_h_Included diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index 53084af156..8e5433a2c5 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,403 +1,393 @@ /*=================================================================== 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 "mitkSegTool2D.h" #include "mitkToolManager.h" #include "mitkDataStorage.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkExtractImageFilter.h" #include "mitkExtractDirectedPlaneImageFilter.h" //Include of the new ImageExtractor #include "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkPlanarCircle.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkGetModuleContext.h" //Includes for 3DSurfaceInterpolation #include "mitkImageToContourFilter.h" #include "mitkSurfaceInterpolationController.h" +//includes for resling and overwriting +#include +#include +#include +#include + +#include +#include "mitkOperationEvent.h" +#include "mitkUndoController.h" #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) mitk::SegTool2D::SegTool2D(const char* type) :Tool(type), m_LastEventSender(NULL), m_LastEventSlice(0), m_Contourmarkername ("Position"), m_ShowMarkerNodes (true), m_3DInterpolationEnabled (true) { // great magic numbers CONNECT_ACTION( 80, OnMousePressed ); CONNECT_ACTION( 90, OnMouseMoved ); CONNECT_ACTION( 42, OnMouseReleased ); CONNECT_ACTION( 49014, OnInvertLogic ); + + } mitk::SegTool2D::~SegTool2D() { } bool mitk::SegTool2D::OnMousePressed (Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( positionEvent->GetSender()->GetMapperID() != BaseRenderer::Standard2D ) return false; // we don't want anything but 2D m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); return true; } bool mitk::SegTool2D::OnMouseMoved (Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } bool mitk::SegTool2D::OnMouseReleased(Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } bool mitk::SegTool2D::OnInvertLogic(Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } bool mitk::SegTool2D::DetermineAffectedImageSlice( const Image* image, const PlaneGeometry* plane, int& affectedDimension, int& affectedSlice ) { assert(image); assert(plane); // compare normal of plane to the three axis vectors of the image Vector3D normal = plane->GetNormal(); Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0); Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1); Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2); normal.Normalize(); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); imageNormal0.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal0.Get_vnl_vector()) ); imageNormal1.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal1.Get_vnl_vector()) ); imageNormal2.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal2.Get_vnl_vector()) ); double eps( 0.00001 ); // transversal if ( imageNormal2.GetNorm() <= eps ) { affectedDimension = 2; } // sagittal else if ( imageNormal1.GetNorm() <= eps ) { affectedDimension = 1; } // frontal else if ( imageNormal0.GetNorm() <= eps ) { affectedDimension = 0; } else { affectedDimension = -1; // no idea return false; } // determine slice number in image Geometry3D* imageGeometry = image->GetGeometry(0); Point3D testPoint = imageGeometry->GetCenter(); Point3D projectedPoint; plane->Project( testPoint, projectedPoint ); Point3D indexPoint; imageGeometry->WorldToIndex( projectedPoint, indexPoint ); affectedSlice = ROUND( indexPoint[affectedDimension] ); MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice " << affectedSlice; // check if this index is still within the image if ( affectedSlice < 0 || affectedSlice >= static_cast(image->GetDimension(affectedDimension)) ) return false; return true; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PositionEvent* positionEvent, const Image* image) { if (!positionEvent) return NULL; assert( positionEvent->GetSender() ); // sure, right? unsigned int timeStep = positionEvent->GetSender()->GetTimeStep( image ); // get the timestep of the visible part (time-wise) of the image // first, we determine, which slice is affected const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); if ( !image || !planeGeometry ) return NULL; - int affectedDimension( -1 ); - int affectedSlice( -1 ); - //DetermineAffectedImageSlice( image, planeGeometry, affectedDimension, affectedSlice ); - if ( DetermineAffectedImageSlice( image, planeGeometry, affectedDimension, affectedSlice ) ) - { - try - { - // now we extract the correct slice from the volume, resulting in a 2D image - ExtractImageFilter::Pointer extractor= ExtractImageFilter::New(); - extractor->SetInput( image ); - extractor->SetSliceDimension( affectedDimension ); - extractor->SetSliceIndex( affectedSlice ); - extractor->SetTimeStep( timeStep ); - extractor->Update(); - - // here we have a single slice that can be modified - Image::Pointer slice = extractor->GetOutput(); - - //Workaround because of bug #7079 - Point3D origin = slice->GetGeometry()->GetOrigin(); - - int affectedDimension(-1); - - if(positionEvent->GetSender()->GetRenderWindow() == mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")) - { - affectedDimension = 2; - } - if(positionEvent->GetSender()->GetRenderWindow() == mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2")) - { - affectedDimension = 0; - } - if(positionEvent->GetSender()->GetRenderWindow() == mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")) - { - affectedDimension = 1; - } - - if (affectedDimension != -1) - { - origin[affectedDimension] = planeGeometry->GetOrigin()[affectedDimension]; - slice->GetGeometry()->SetOrigin(origin); - } - //Workaround end - - return slice; - } - catch(...) - { - // not working - return NULL; - } - } - else - { - ExtractDirectedPlaneImageFilterNew::Pointer newExtractor = ExtractDirectedPlaneImageFilterNew::New(); - newExtractor->SetInput( image ); - newExtractor->SetActualInputTimestep( timeStep ); - newExtractor->SetCurrentWorldGeometry2D( planeGeometry ); - newExtractor->Update(); - Image::Pointer slice = newExtractor->GetOutput(); - return slice; - } + //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer + vtkSmartPointer reslice = vtkSmartPointer::New(); + //set to false to extract a slice + reslice->SetOverwriteMode(false); + reslice->Modified(); + + //use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting + mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); + extractor->SetInput( image ); + extractor->SetTimeStep( timeStep ); + extractor->SetWorldGeometry( planeGeometry ); + extractor->SetVtkOutputRequest(false); + extractor->SetResliceTransformByGeometry( image->GetTimeSlicedGeometry()->GetGeometry3D( timeStep ) ); + + extractor->Modified(); + extractor->Update(); + + Image::Pointer slice = extractor->GetOutput(); + + /*============= BEGIN undo feature block ========================*/ + //specify the undo operation with the non edited slice + m_undoOperation = new DiffSliceOperation(const_cast(image), extractor->GetVtkOutput(), slice->GetGeometry(), timeStep, const_cast(planeGeometry)); + /*============= END undo feature block ========================*/ + + return slice; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const PositionEvent* positionEvent) { DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if ( !workingNode ) return NULL; Image* workingImage = dynamic_cast(workingNode->GetData()); if ( !workingImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, workingImage ); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const PositionEvent* positionEvent) { DataNode* referenceNode( m_ToolManager->GetReferenceData(0) ); if ( !referenceNode ) return NULL; Image* referenceImage = dynamic_cast(referenceNode->GetData()); if ( !referenceImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, referenceImage ); } void mitk::SegTool2D::WriteBackSegmentationResult (const PositionEvent* positionEvent, Image* slice) { const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); Image* image = dynamic_cast(workingNode->GetData()); - int affectedDimension( -1 ); - int affectedSlice( -1 ); - DetermineAffectedImageSlice( image, planeGeometry, affectedDimension, affectedSlice ); - - if (affectedDimension != -1) { - OverwriteSliceImageFilter::Pointer slicewriter = OverwriteSliceImageFilter::New(); - slicewriter->SetInput( image ); - slicewriter->SetCreateUndoInformation( true ); - slicewriter->SetSliceImage( slice ); - slicewriter->SetSliceDimension( affectedDimension ); - slicewriter->SetSliceIndex( affectedSlice ); - slicewriter->SetTimeStep( positionEvent->GetSender()->GetTimeStep( image ) ); - slicewriter->Update(); - } - else { - OverwriteDirectedPlaneImageFilter::Pointer slicewriter = OverwriteDirectedPlaneImageFilter::New(); - slicewriter->SetInput( image ); - slicewriter->SetCreateUndoInformation( false ); - slicewriter->SetSliceImage( slice ); - slicewriter->SetPlaneGeometry3D( slice->GetGeometry() ); - slicewriter->SetTimeStep( positionEvent->GetSender()->GetTimeStep( image ) ); - slicewriter->Update(); - } + //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer + vtkSmartPointer reslice = vtkSmartPointer::New(); + + //Set the slice as 'input' + reslice->SetInputSlice(slice->GetVtkImageData(this->m_TimeStep)); + + //set overwrite mode to true to write back to the image volume + reslice->SetOverwriteMode(true); + reslice->Modified(); + + mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); + extractor->SetInput( image ); + extractor->SetTimeStep( this->m_TimeStep ); + extractor->SetWorldGeometry( planeGeometry ); + extractor->SetVtkOutputRequest(true); + extractor->SetResliceTransformByGeometry( image->GetTimeSlicedGeometry()->GetGeometry3D( this->m_TimeStep ) ); + + extractor->Modified(); + extractor->Update(); + + //the image was modified within the pipeline, but not marked so + image->Modified(); + + /*============= BEGIN undo feature block ========================*/ + //specify the undo operation with the edited slice + m_doOperation = new DiffSliceOperation(image, extractor->GetVtkOutput(),slice->GetGeometry(), this->m_TimeStep, const_cast(planeGeometry)); + + //create an operation event for the undo stack + OperationEvent* undoStackItem = new OperationEvent( DiffSliceOperationApplier::GetInstance(), m_doOperation, m_undoOperation, "Segmentation" ); + + //add it to the undo controller + UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); + + //clear the pointers as the operation are stored in the undocontroller and also deleted from there + m_undoOperation = NULL; + m_doOperation = NULL; + /*============= END undo feature block ========================*/ + if ( m_3DInterpolationEnabled ) { slice->DisconnectPipeline(); ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); contourExtractor->SetInput(slice); contourExtractor->Update(); mitk::Surface::Pointer contour = contourExtractor->GetOutput(); if (contour->GetVtkPolyData()->GetNumberOfPoints() > 0 ) { unsigned int pos = this->AddContourmarker(positionEvent); mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); mitk::SurfaceInterpolationController::GetInstance()->AddNewContour( contour, service->GetPlanePosition(pos)); contour->DisconnectPipeline(); } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::SetShowMarkerNodes(bool status) { m_ShowMarkerNodes = status; } void mitk::SegTool2D::Enable3DInterpolation(bool status) { m_3DInterpolationEnabled = status; } unsigned int mitk::SegTool2D::AddContourmarker ( const PositionEvent* positionEvent ) { const mitk::Geometry2D* plane = dynamic_cast (dynamic_cast< const mitk::SlicedGeometry3D*>( positionEvent->GetSender()->GetSliceNavigationController()->GetCurrentGeometry3D())->GetGeometry2D(0)); mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); unsigned int size = service->GetNumberOfPlanePositions(); unsigned int id = service->AddNewPlanePosition(plane, positionEvent->GetSender()->GetSliceNavigationController()->GetSlice()->GetPos()); mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); contourMarker->SetGeometry2D( const_cast(plane)); std::stringstream markerStream; mitk::DataNode* workingNode (m_ToolManager->GetWorkingData(0)); markerStream << m_Contourmarkername ; markerStream << " "; markerStream << id+1; DataNode::Pointer rotatedContourNode = DataNode::New(); rotatedContourNode->SetData(contourMarker); rotatedContourNode->SetProperty( "name", StringProperty::New(markerStream.str()) ); rotatedContourNode->SetProperty( "isContourMarker", BoolProperty::New(true)); rotatedContourNode->SetBoolProperty( "PlanarFigureInitializedWindow", true, positionEvent->GetSender() ); rotatedContourNode->SetProperty( "includeInBoundingBox", BoolProperty::New(false)); rotatedContourNode->SetProperty( "helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes)); if (plane) { if ( id == size ) { m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } else { mitk::NodePredicateProperty::Pointer isMarker = mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer markers = m_ToolManager->GetDataStorage()->GetDerivations(workingNode,isMarker); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = markers->begin(); iter != markers->end(); ++iter) { std::string nodeName = (*iter)->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int markerId = atof(nodeName.substr(t+1).c_str())-1; if(id == markerId) { return id; } } m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } } return id; } void mitk::SegTool2D::InteractiveSegmentationBugMessage( const std::string& message ) { MITK_ERROR << "********************************************************************************" << std::endl << " " << message << std::endl << "********************************************************************************" << std::endl << " " << std::endl << " If your image is rotated or the 2D views don't really contain the patient image, try to press the button next to the image selection. " << std::endl << " " << std::endl << " Please file a BUG REPORT: " << std::endl << " http://bugs.mitk.org" << std::endl << " Contain the following information:" << std::endl << " - What image were you working on?" << std::endl << " - Which region of the image?" << std::endl << " - Which tool did you use?" << std::endl << " - What did you do?" << std::endl << " - What happened (not)? What did you expect?" << std::endl; } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.h b/Modules/Segmentation/Interactions/mitkSegTool2D.h index 236e7f58fc..1a21d36089 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.h +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.h @@ -1,135 +1,140 @@ /*=================================================================== 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 mitkSegTool2D_h_Included #define mitkSegTool2D_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkTool.h" #include "mitkImage.h" #include "mitkStateEvent.h" #include "mitkPositionEvent.h" #include "mitkPlanePositionManager.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkInteractionConst.h" +#include + + namespace mitk { class BaseRenderer; /** \brief Abstract base class for segmentation tools. \sa Tool \ingroup Interaction \ingroup ToolManagerEtAl Implements 2D segmentation specific helper methods, that might be of use to all kind of 2D segmentation tools. At the moment these are: - Determination of the slice where the user paints upon (DetermineAffectedImageSlice) - Projection of a 3D contour onto a 2D plane/slice SegTool2D tries to structure the interaction a bit. If you pass "PressMoveRelease" as the interaction type of your derived tool, you might implement the methods OnMousePressed, OnMouseMoved, and OnMouseReleased. Yes, your guess about when they are called is correct. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ class Segmentation_EXPORT SegTool2D : public Tool { public: mitkClassMacro(SegTool2D, Tool); /** \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index corrdinates) is meant by the plane. \return false, if no slice direction seems right (e.g. rotated planes) \param affectedDimension The image dimension, which is constant for all points in the plane, e.g. Transversal --> 2 \param affectedSlice The index of the image slice */ static bool DetermineAffectedImageSlice( const Image* image, const PlaneGeometry* plane, int& affectedDimension, int& affectedSlice ); void SetShowMarkerNodes(bool); void Enable3DInterpolation(bool); protected: SegTool2D(); // purposely hidden SegTool2D(const char*); // purposely hidden virtual ~SegTool2D(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMouseMoved (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); virtual bool OnInvertLogic (Action*, const StateEvent*); /** \brief Extract the slice of an image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position. */ Image::Pointer GetAffectedImageSliceAs2DImage(const PositionEvent*, const Image* image); /** \brief Extract the slice of the currently selected working image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no working image is selected. */ Image::Pointer GetAffectedWorkingSlice(const PositionEvent*); /** \brief Extract the slice of the currently selected reference image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no reference image is selected. */ Image::Pointer GetAffectedReferenceSlice(const PositionEvent*); void WriteBackSegmentationResult (const PositionEvent*, Image*); /** \brief Adds a new node called Contourmarker to the datastorage which holds a mitk::PlanarFigure. By selecting this node the slicestack will be reoriented according to the PlanarFigure's Geometry */ unsigned int AddContourmarker ( const PositionEvent* ); void InteractiveSegmentationBugMessage( const std::string& message ); - private: BaseRenderer* m_LastEventSender; unsigned int m_LastEventSlice; //The prefix of the contourmarkername. Suffix is a consecutive number const std::string m_Contourmarkername; bool m_ShowMarkerNodes; bool m_3DInterpolationEnabled; + + DiffSliceOperation* m_doOperation; + DiffSliceOperation* m_undoOperation; }; } // namespace #endif diff --git a/Modules/Segmentation/Testing/CMakeLists.txt b/Modules/Segmentation/Testing/CMakeLists.txt index e69de29bb2..d45daf111d 100644 --- a/Modules/Segmentation/Testing/CMakeLists.txt +++ b/Modules/Segmentation/Testing/CMakeLists.txt @@ -0,0 +1 @@ +MITK_CREATE_MODULE_TESTS() \ No newline at end of file diff --git a/Modules/Segmentation/Testing/files.cmake b/Modules/Segmentation/Testing/files.cmake index 2f9a9aa8a0..ce4ad96b02 100644 --- a/Modules/Segmentation/Testing/files.cmake +++ b/Modules/Segmentation/Testing/files.cmake @@ -1,22 +1,23 @@ set(MODULE_TESTS mitkContourMapper2DTest.cpp mitkContourTest.cpp mitkDataNodeSegmentationTest.cpp # mitkSegmentationInterpolationTest.cpp ) set(MODULE_IMAGE_TESTS mitkManualSegmentationToSurfaceFilterTest.cpp mitkOverwriteSliceImageFilterTest.cpp + mitkOverwriteSliceFilterTest.cpp ) set(MODULE_CUSTOM_TESTS ) set(MODULE_TESTIMAGES US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png binary.stl ball.stl ) diff --git a/Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp b/Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp index 3c7cb5ec21..5a4570cc9c 100644 --- a/Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp +++ b/Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp @@ -1,164 +1,164 @@ /*=================================================================== 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 "mitkDataNode.h" #include #include "mitkVtkPropRenderer.h" #include "mitkTestingMacros.h" #include "mitkGlobalInteraction.h" #include //Basedata Test #include #include #include //Mapper Test #include #include #include #include #include #include #include //Interactors #include #include #include //Propertylist Test #include #include #include #include #include #include #include #include #include #include #include /** * Extended test for mitk::DataNode. A number of tests from the core test * mitkDataNodeTest are assumed to pass! */ class mitkDataNodeSegmentationTestClass { public: static void TestDataSetting(mitk::DataNode::Pointer dataNode) { mitk::BaseData::Pointer baseData; //NULL pointer Test dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a NULL pointer was set correctly" ) baseData = mitk::Contour::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a Contour object was set correctly" ) baseData = mitk::ContourSet::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a ContourSet object was set correctly" ) } static void TestMapperSetting(mitk::DataNode::Pointer dataNode) { //tests the SetMapper() method //in dataNode is a mapper vector which can be accessed by index //in this test method we use only slot 0 (filled with null) and slot 1 //so we also test the destructor of the mapper classes mitk::Mapper::Pointer mapper; dataNode->SetMapper(0,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(0), "Testing if a NULL pointer was set correctly" ) mapper = mitk::ContourMapper2D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourMapper2D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::ContourSetMapper2D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourSetMapper2D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) //3D Mappers mapper = mitk::ContourSetVtkMapper3D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourSetVtkMapper3D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::ContourVtkMapper3D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourVtkMapper3D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) } static void TestInteractorSetting(mitk::DataNode::Pointer dataNode) { //this method tests the SetInteractor() and GetInteractor methods //the Interactor base class calls the DataNode->SetInteractor method mitk::Interactor::Pointer interactor; MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a NULL pointer was set correctly (Interactor)" ) interactor = mitk::ContourInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a ContourInteractor was set correctly" ) interactor = mitk::ExtrudedContourInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a ExtrudedContourInteractor was set correctly" ) } }; -int mitkDataNodeExtTest(int /* argc */, char* /*argv*/[]) +int mitkDataNodeSegmentationTest(int /* argc */, char* /*argv*/[]) { // always start with this! MITK_TEST_BEGIN("DataNode") // Global interaction must(!) be initialized mitk::GlobalInteraction::GetInstance()->Initialize("global"); // let's create an object of our class mitk::DataNode::Pointer myDataNode = mitk::DataNode::New(); // first test: did this work? // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since // it makes no sense to continue without an object. MITK_TEST_CONDITION_REQUIRED(myDataNode.IsNotNull(),"Testing instantiation") //test setData() Method - mitkDataNodeExtTestClass::TestDataSetting(myDataNode); - mitkDataNodeExtTestClass::TestMapperSetting(myDataNode); + mitkDataNodeSegmentationTestClass::TestDataSetting(myDataNode); + mitkDataNodeSegmentationTestClass::TestMapperSetting(myDataNode); //note, that no data is set to the dataNode - mitkDataNodeExtTestClass::TestInteractorSetting(myDataNode); + mitkDataNodeSegmentationTestClass::TestInteractorSetting(myDataNode); // write your own tests here and use the macros from mitkTestingMacros.h !!! // do not write to std::cout and do not return from this function yourself! // always end with this! MITK_TEST_END() } diff --git a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp new file mode 100644 index 0000000000..2d60f29ed7 --- /dev/null +++ b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp @@ -0,0 +1,391 @@ +/*=================================================================== + +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 + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + + + + +int VolumeSize = 128; + + +static void OverwriteByPointerTest(mitk::Image* workingImage, mitk::Image* refImg) +{ + + /* ============= setup plane ============*/ + int sliceindex = 55;//rand() % 32; + bool isFrontside = true; + bool isRotated = false; + + mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); + plane->InitializeStandardPlane(workingImage->GetGeometry(), mitk::PlaneGeometry::Transversal, sliceindex, isFrontside, isRotated); + mitk::Point3D origin = plane->GetOrigin(); + mitk::Vector3D normal; + normal = plane->GetNormal(); + normal.Normalize(); + origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 + plane->SetOrigin(origin); + + + + /* ============= extract slice ============*/ + vtkSmartPointer resliceIdx = vtkSmartPointer::New(); + mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(resliceIdx); + slicer->SetInput(workingImage); + slicer->SetWorldGeometry(plane); + slicer->SetVtkOutputRequest(true); + slicer->Modified(); + slicer->Update(); + + vtkSmartPointer slice = vtkSmartPointer::New(); + slice = slicer->GetVtkOutput(); + + + + /* ============= overwrite slice ============*/ + resliceIdx->SetOverwriteMode(true); + resliceIdx->Modified(); + slicer->Modified(); + slicer->Update();//implicit overwrite + + + + /* ============= check ref == working ============*/ + bool areSame = true; + mitk::Index3D id; + id[0] = id[1] = id[2] = 0; + for (int x = 0; x < VolumeSize; ++x){ + id[0] = x; + for (int y = 0; y < VolumeSize; ++y){ + id[1] = y; + for (int z = 0; z < VolumeSize; ++z){ + id[2] = z; + areSame = refImg->GetPixelValueByIndex(id) == workingImage->GetPixelValueByIndex(id); + if(!areSame) + goto stop; + } + } + } +stop: + MITK_TEST_CONDITION(areSame,"comparing images [orthogonal]"); + + + + /* ============= edit slice ============*/ + int idX = std::abs(VolumeSize-59); + int idY = std::abs(VolumeSize-23); + int idZ = 0; + int component = 0; + double val = 33.0; + + slice->SetScalarComponentFromDouble(idX,idY,idZ,component,val); + + + + /* ============= overwrite slice ============*/ + + vtkSmartPointer resliceIdx2 = vtkSmartPointer::New(); + resliceIdx2->SetOverwriteMode(true); + resliceIdx2->SetInputSlice(slice); + mitk::ExtractSliceFilter::Pointer slicer2 = mitk::ExtractSliceFilter::New(resliceIdx2); + slicer2->SetInput(workingImage); + slicer2->SetWorldGeometry(plane); + slicer2->SetVtkOutputRequest(true); + slicer2->Modified(); + slicer2->Update(); + + + + + /* ============= check ============*/ + areSame = true; + + int x,y,z; + + for ( x = 0; x < VolumeSize; ++x){ + id[0] = x; + for ( y = 0; y < VolumeSize; ++y){ + id[1] = y; + for ( z = 0; z < VolumeSize; ++z){ + id[2] = z; + areSame = refImg->GetPixelValueByIndex(id) == workingImage->GetPixelValueByIndex(id); + if(!areSame) + goto stop2; + } + } + } +stop2: + //MITK_INFO << "index: [" << x << ", " << y << ", " << z << "]"; + MITK_TEST_CONDITION(x==idX && y==idY && z==sliceindex,"overwrited the right index [orthogonal]"); +} + + + +static void OverwriteByPointerForObliquePlaneTest(mitk::Image* workingImage, mitk::Image* refImg) +{ + +/*==============TEST WITHOUT MITK CONVERTION=============================*/ + + /* ============= setup plane ============*/ + int sliceindex = (int)(VolumeSize/2);//rand() % 32; + bool isFrontside = true; + bool isRotated = false; + + mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New(); + obliquePlane->InitializeStandardPlane(workingImage->GetGeometry(), mitk::PlaneGeometry::Transversal, sliceindex, isFrontside, isRotated); + mitk::Point3D origin = obliquePlane->GetOrigin(); + mitk::Vector3D normal; + normal = obliquePlane->GetNormal(); + normal.Normalize(); + origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 + obliquePlane->SetOrigin(origin); + + mitk::Vector3D rotationVector = obliquePlane->GetAxisVector(0); + rotationVector.Normalize(); + + float degree = 45.0; + + mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree); + obliquePlane->ExecuteOperation(op); + delete op; + + + /* ============= extract slice ============*/ + mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); + slicer->SetInput(workingImage); + slicer->SetWorldGeometry(obliquePlane); + slicer->SetVtkOutputRequest(true); + slicer->Modified(); + slicer->Update(); + + vtkSmartPointer slice = vtkSmartPointer::New(); + slice = slicer->GetVtkOutput(); + + + + /* ============= overwrite slice ============*/ + vtkSmartPointer resliceIdx = vtkSmartPointer::New(); + mitk::ExtractSliceFilter::Pointer overwriter = mitk::ExtractSliceFilter::New(resliceIdx); + resliceIdx->SetOverwriteMode(true); + resliceIdx->SetInputSlice(slice); + resliceIdx->Modified(); + overwriter->SetInput(workingImage); + overwriter->SetWorldGeometry(obliquePlane); + overwriter->SetVtkOutputRequest(true); + overwriter->Modified(); + overwriter->Update(); + + + + /* ============= check ref == working ============*/ + bool areSame = true; + mitk::Index3D id; + id[0] = id[1] = id[2] = 0; + for (int x = 0; x < VolumeSize; ++x){ + id[0] = x; + for (int y = 0; y < VolumeSize; ++y){ + id[1] = y; + for (int z = 0; z < VolumeSize; ++z){ + id[2] = z; + areSame = refImg->GetPixelValueByIndex(id) == workingImage->GetPixelValueByIndex(id); + if(!areSame) + goto stop; + } + } + } +stop: + MITK_TEST_CONDITION(areSame,"comparing images (no mitk convertion) [oblique]"); + + + + + +/*==============TEST WITH MITK CONVERTION=============================*/ + + /* ============= extract slice ============*/ + mitk::ExtractSliceFilter::Pointer slicer2 = mitk::ExtractSliceFilter::New(); + slicer2->SetInput(workingImage); + slicer2->SetWorldGeometry(obliquePlane); + slicer2->Modified(); + slicer2->Update(); + + + mitk::Image::Pointer sliceInMitk = slicer2->GetOutput(); + vtkSmartPointer slice2 = vtkSmartPointer::New(); + slice2 = sliceInMitk->GetVtkImageData(); + + + /* ============= overwrite slice ============*/ + vtkSmartPointer resliceIdx2 = vtkSmartPointer::New(); + mitk::ExtractSliceFilter::Pointer overwriter2 = mitk::ExtractSliceFilter::New(resliceIdx2); + resliceIdx2->SetOverwriteMode(true); + resliceIdx2->SetInputSlice(slice2); + resliceIdx2->Modified(); + overwriter2->SetInput(workingImage); + overwriter2->SetWorldGeometry(obliquePlane); + overwriter2->SetVtkOutputRequest(true); + overwriter2->Modified(); + overwriter2->Update(); + + + + /* ============= check ref == working ============*/ + areSame = true; + id[0] = id[1] = id[2] = 0; + for (int x = 0; x < VolumeSize; ++x){ + id[0] = x; + for (int y = 0; y < VolumeSize; ++y){ + id[1] = y; + for (int z = 0; z < VolumeSize; ++z){ + id[2] = z; + areSame = refImg->GetPixelValueByIndex(id) == workingImage->GetPixelValueByIndex(id); + if(!areSame) + goto stop2; + } + } + } +stop2: + MITK_TEST_CONDITION(areSame,"comparing images (with mitk convertion) [oblique]"); + + +/*==============TEST EDIT WITHOUT MITK CONVERTION=============================*/ + + /* ============= edit slice ============*/ + int idX = std::abs(VolumeSize-59); + int idY = std::abs(VolumeSize-23); + int idZ = 0; + int component = 0; + double val = 33.0; + + slice->SetScalarComponentFromDouble(idX,idY,idZ,component,val); + + mitk::Vector3D indx; + indx[0] = idX; indx[1] = idY; indx[2] = idZ; + sliceInMitk->GetGeometry()->IndexToWorld(indx, indx); + + /* ============= overwrite slice ============*/ + + vtkSmartPointer resliceIdx3 = vtkSmartPointer::New(); + resliceIdx3->SetOverwriteMode(true); + resliceIdx3->SetInputSlice(slice); + mitk::ExtractSliceFilter::Pointer overwriter3 = mitk::ExtractSliceFilter::New(resliceIdx3); + overwriter3->SetInput(workingImage); + overwriter3->SetWorldGeometry(obliquePlane); + overwriter3->SetVtkOutputRequest(true); + overwriter3->Modified(); + overwriter3->Update(); + + + + + /* ============= check ============*/ + areSame = true; + + int x,y,z; + + for ( x = 0; x < VolumeSize; ++x){ + id[0] = x; + for ( y = 0; y < VolumeSize; ++y){ + id[1] = y; + for ( z = 0; z < VolumeSize; ++z){ + id[2] = z; + areSame = refImg->GetPixelValueByIndex(id) == workingImage->GetPixelValueByIndex(id); + if(!areSame) + goto stop3; + } + } + } +stop3: + //MITK_INFO << "index: [" << x << ", " << y << ", " << z << "]"; + //MITK_INFO << indx; + MITK_TEST_CONDITION(x==idX && y==z,"overwrited the right index [oblique]"); +} + + + + +/*================ #BEGIN test main ================*/ +int mitkOverwriteSliceFilterTest(int argc, char* argv[]) +{ + + MITK_TEST_BEGIN("mitkOverwriteSliceFilterTest") + + + + + typedef itk::Image ImageType; + + typedef itk::ImageRegionConstIterator< ImageType > ImageIterator; + + ImageType::Pointer image = ImageType::New(); + + ImageType::IndexType start; + start[0] = start[1] = start[2] = 0; + + ImageType::SizeType size; + size[0] = size[1] = size[2] = VolumeSize; + + ImageType::RegionType imgRegion; + imgRegion.SetSize(size); + imgRegion.SetIndex(start); + + image->SetRegions(imgRegion); + image->SetSpacing(1.0); + image->Allocate(); + + + ImageIterator imageIterator( image, image->GetLargestPossibleRegion() ); + imageIterator.GoToBegin(); + + + unsigned short pixelValue = 0; + + //fill the image with distinct values + while ( !imageIterator.IsAtEnd() ) + { + image->SetPixel(imageIterator.GetIndex(), pixelValue); + ++imageIterator; + ++pixelValue; + } + /* end setup itk image */ + + + + mitk::Image::Pointer refImage; + CastToMitkImage(image, refImage); + mitk::Image::Pointer workingImg; + CastToMitkImage(image, workingImg); + OverwriteByPointerTest(workingImg, refImage); + + + mitk::Image::Pointer workingImg3; + CastToMitkImage(image, workingImg3); + OverwriteByPointerForObliquePlaneTest(workingImg3, refImage); + + MITK_TEST_END() +} diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index 9911599184..fe671afe8a 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,53 +1,56 @@ set(CPP_FILES Algorithms/mitkCalculateSegmentationVolume.cpp Algorithms/mitkComputeContourSetNormalsFilter.cpp Algorithms/mitkContourSetToPointSetFilter.cpp Algorithms/mitkContourUtils.cpp Algorithms/mitkCorrectorAlgorithm.cpp Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp Algorithms/mitkDiffImageApplier.cpp Algorithms/mitkImageToContourFilter.cpp Algorithms/mitkManualSegmentationToSurfaceFilter.cpp Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp Algorithms/mitkOverwriteSliceImageFilter.cpp Algorithms/mitkReduceContourSetFilter.cpp Algorithms/mitkSegmentationObjectFactory.cpp Algorithms/mitkSegmentationSink.cpp Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp Algorithms/mitkShowSegmentationAsSurface.cpp +Algorithms/mitkVtkImageOverwrite.cpp +Algorithms/mitkDiffSliceOperation.cpp +Algorithms/mitkDiffSliceOperationApplier.cpp Controllers/mitkSegmentationInterpolationController.cpp Controllers/mitkSurfaceInterpolationController.cpp # DataManagement/mitkApplyDiffImageOperation.cpp DataManagement/mitkContour.cpp DataManagement/mitkContourSet.cpp DataManagement/mitkExtrudedContour.cpp Interactions/mitkAddContourTool.cpp Interactions/mitkAutoCropTool.cpp Interactions/mitkAutoSegmentationTool.cpp Interactions/mitkBinaryThresholdTool.cpp Interactions/mitkBinaryThresholdULTool.cpp Interactions/mitkCalculateGrayValueStatisticsTool.cpp Interactions/mitkCalculateVolumetryTool.cpp Interactions/mitkContourInteractor.cpp Interactions/mitkContourTool.cpp Interactions/mitkCorrectorTool2D.cpp Interactions/mitkCreateSurfaceTool.cpp Interactions/mitkDrawPaintbrushTool.cpp Interactions/mitkErasePaintbrushTool.cpp Interactions/mitkEraseRegionTool.cpp Interactions/mitkExtrudedContourInteractor.cpp Interactions/mitkFeedbackContourTool.cpp Interactions/mitkFillRegionTool.cpp Interactions/mitkPaintbrushTool.cpp Interactions/mitkRegionGrow3DTool.cpp Interactions/mitkRegionGrowingTool.cpp Interactions/mitkSegmentationsProcessingTool.cpp Interactions/mitkSetRegionTool.cpp Interactions/mitkSegTool2D.cpp Interactions/mitkSubtractContourTool.cpp Rendering/mitkContourMapper2D.cpp Rendering/mitkContourSetMapper2D.cpp Rendering/mitkContourSetVtkMapper3D.cpp Rendering/mitkContourVtkMapper3D.cpp ) diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index ef850ce1f0..af969d549f 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,1239 +1,1239 @@ /*=================================================================== 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 "mitkDataNodeObject.h" #include "mitkProperties.h" #include "mitkSegTool2D.h" #include "mitkGlobalInteraction.h" #include "QmitkStdMultiWidget.h" #include "QmitkNewSegmentationDialog.h" #include #include #include "QmitkSegmentationView.h" #include "QmitkSegmentationPostProcessing.h" #include "QmitkSegmentationOrganNamesHandling.cpp" #include #include //For Segmentation in rotated slices //TODO clean up includes #include "mitkVtkResliceInterpolationProperty.h" #include "mitkPlanarCircle.h" #include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleRegistry.h" #include "mitkSegmentationObjectFactory.h" const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; // public methods QmitkSegmentationView::QmitkSegmentationView() :m_Parent(NULL) ,m_Controls(NULL) ,m_MultiWidget(NULL) ,m_RenderingManagerObserverTag(0) { RegisterSegmentationObjectFactory(); } QmitkSegmentationView::~QmitkSegmentationView() { // delete m_PostProcessing; delete m_Controls; } void QmitkSegmentationView::NewNodesGenerated() { // ForceDisplayPreferencesUponAllImages(); } void QmitkSegmentationView::NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType* nodes) { if (!nodes) return; mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); if (!toolManager) return; for (mitk::ToolManager::DataVectorType::iterator iter = nodes->begin(); iter != nodes->end(); ++iter) { this->FireNodeSelected( *iter ); // only last iteration meaningful, multiple generated objects are not taken into account here } } void QmitkSegmentationView::Activated() { // should be moved to ::BecomesVisible() or similar if( m_Controls ) { m_Controls->m_ManualToolSelectionBox->setEnabled( true ); m_Controls->m_OrganToolSelectionBox->setEnabled( true ); m_Controls->m_LesionToolSelectionBox->setEnabled( true ); m_Controls->m_SlicesInterpolator->Enable3DInterpolation( m_Controls->widgetStack->currentWidget() == m_Controls->pageManual ); //TODO Remove Observer itk::ReceptorMemberCommand::Pointer command1 = itk::ReceptorMemberCommand::New(); command1->SetCallbackFunction( this, &QmitkSegmentationView::RenderingManagerReinitialized ); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver( mitk::RenderingManagerViewsInitializedEvent(), command1 ); //Adding observers for node visibility to existing segmentations mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isSegmentation = mitk::NodePredicateAnd::New( isImage, isBinary ); mitk::DataStorage::SetOfObjects::ConstPointer segmentations = this->GetDefaultDataStorage()->GetSubset( isSegmentation ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = segmentations->begin(); iter != segmentations->end(); ++iter) { mitk::DataNode* node = *iter; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( node, node->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); } if(segmentations->Size() > 0) { FireNodeSelected(segmentations->ElementAt(0)); segmentations->ElementAt(0)->GetProperty("visible")->Modified(); } } } void QmitkSegmentationView::Deactivated() { if( m_Controls ) { mitk::RenderingManager::GetInstance()->RemoveObserver( m_RenderingManagerObserverTag ); m_Controls->m_ManualToolSelectionBox->setEnabled( false ); //deactivate all tools m_Controls->m_ManualToolSelectionBox->GetToolManager()->ActivateTool(-1); m_Controls->m_OrganToolSelectionBox->setEnabled( false ); m_Controls->m_LesionToolSelectionBox->setEnabled( false ); m_Controls->m_SlicesInterpolator->EnableInterpolation( false ); //Removing all observers for ( NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter ) { (*dataIter).first->GetProperty("visible")->RemoveObserver( (*dataIter).second ); } m_WorkingDataObserverTags.clear(); if (m_MultiWidget) { mitk::SlicesCoordinator *coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) coordinator->RemoveObserver(m_SlicesRotationObserverTag1); coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) coordinator->RemoveObserver(m_SlicesRotationObserverTag2); } // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); service->RemoveAllPlanePositions(); } } void QmitkSegmentationView::StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget ) { SetMultiWidget(&stdMultiWidget); } void QmitkSegmentationView::StdMultiWidgetNotAvailable() { SetMultiWidget(NULL); } void QmitkSegmentationView::StdMultiWidgetClosed( QmitkStdMultiWidget& /*stdMultiWidget*/ ) { SetMultiWidget(NULL); } void QmitkSegmentationView::SetMultiWidget(QmitkStdMultiWidget* multiWidget) { if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { coordinator->RemoveObserver( m_SlicesRotationObserverTag1 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { coordinator->RemoveObserver( m_SlicesRotationObserverTag2 ); } } // save the current multiwidget as the working widget m_MultiWidget = multiWidget; //TODO Remove Observers if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSegmentationView::SliceRotation ); m_SlicesRotationObserverTag1 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSegmentationView::SliceRotation ); m_SlicesRotationObserverTag2 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } } //TODO End Remove Observers if (m_Parent) { m_Parent->setEnabled(m_MultiWidget); } // tell the interpolation about toolmanager and multiwidget (and data storage) if (m_Controls && m_MultiWidget) { mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); m_Controls->m_SlicesInterpolator->SetDataStorage( *(this->GetDefaultDataStorage())); m_Controls->m_SlicesInterpolator->Initialize( toolManager, m_MultiWidget ); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences*) { ForceDisplayPreferencesUponAllImages(); } //TODO remove function void QmitkSegmentationView::RenderingManagerReinitialized(const itk::EventObject&) { CheckImageAlignment(); } //TODO remove function void QmitkSegmentationView::SliceRotation(const itk::EventObject&) { CheckImageAlignment(); } // protected slots void QmitkSegmentationView::CreateNewSegmentation() { mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>1) { // ask about the name and organ type of the new segmentation QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog( m_Parent ); // needs a QWidget as parent, "this" is not QWidget QString storedList = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); QStringList organColors; if (storedList.isEmpty()) { organColors = GetDefaultOrganColorString(); } else { /* a couple of examples of how organ names are stored: a simple item is built up like 'name#AABBCC' where #AABBCC is the hexadecimal notation of a color as known from HTML items are stored separated by ';' this makes it necessary to escape occurrences of ';' in name. otherwise the string "hugo;ypsilon#AABBCC;eugen#AABBCC" could not be parsed as two organs but we would get "hugo" and "ypsilon#AABBCC" and "eugen#AABBCC" so the organ name "hugo;ypsilon" is stored as "hugo\;ypsilon" and must be unescaped after loading the following lines could be one split with Perl's negative lookbehind */ // recover string list from BlueBerry view's preferences QString storedString = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); MITK_DEBUG << "storedString: " << storedString.toStdString(); // match a string consisting of any number of repetitions of either "anything but ;" or "\;". This matches everything until the next unescaped ';' QRegExp onePart("(?:[^;]|\\\\;)*"); MITK_DEBUG << "matching " << onePart.pattern().toStdString(); int count = 0; int pos = 0; while( (pos = onePart.indexIn( storedString, pos )) != -1 ) { ++count; int length = onePart.matchedLength(); if (length == 0) break; QString matchedString = storedString.mid(pos, length); MITK_DEBUG << " Captured length " << length << ": " << matchedString.toStdString(); pos += length + 1; // skip separating ';' // unescape possible occurrences of '\;' in the string matchedString.replace("\\;", ";"); // add matched string part to output list organColors << matchedString; } MITK_DEBUG << "Captured " << count << " organ name/colors"; } dialog->SetSuggestionList( organColors ); int dialogReturnValue = dialog->exec(); if ( dialogReturnValue == QDialog::Rejected ) return; // user clicked cancel or pressed Esc or something similar // ask the user about an organ type and name, add this information to the image's (!) propertylist // create a new image of the same dimensions and smallest possible pixel type mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); mitk::Tool* firstTool = toolManager->GetToolById(0); if (firstTool) { try { mitk::DataNode::Pointer emptySegmentation = firstTool->CreateEmptySegmentationNode( image, dialog->GetSegmentationName().toStdString(), dialog->GetColor() ); //Here we change the reslice interpolation mode for a segmentation, so that contours in rotated slice can be shown correctly - emptySegmentation->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_LINEAR) ); + emptySegmentation->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_NEAREST) ); // initialize showVolume to false to prevent recalculating the volume while working on the segmentation emptySegmentation->SetProperty( "showVolume", mitk::BoolProperty::New( false ) ); if (!emptySegmentation) return; // could be aborted by user UpdateOrganList( organColors, dialog->GetSegmentationName(), dialog->GetColor() ); /* escape ';' here (replace by '\;'), see longer comment above */ std::string stringForStorage = organColors.replaceInStrings(";","\\;").join(";").toStdString(); MITK_DEBUG << "Will store: " << stringForStorage; this->GetPreferences()->PutByteArray("Organ-Color-List", stringForStorage ); this->GetPreferences()->Flush(); if(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)) { m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)->SetSelected(false); } emptySegmentation->SetSelected(true); this->GetDefaultDataStorage()->Add( emptySegmentation, node ); // add as a child, because the segmentation "derives" from the original this->FireNodeSelected( emptySegmentation ); this->OnSelectionChanged( emptySegmentation ); this->SetToolManagerSelection(node, emptySegmentation); } catch (std::bad_alloc) { QMessageBox::warning(NULL,"Create new segmentation","Could not allocate memory for new segmentation"); } } } else { QMessageBox::information(NULL,"Segmentation","Segmentation is currently not supported for 2D images"); } } } else { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a patient image is selected..."; } } void QmitkSegmentationView::OnWorkingNodeVisibilityChanged(/*const itk::Object* caller, const itk::EventObject& e*/) { if (!m_Parent || !m_Parent->isVisible()) return; // The new selection behaviour is: // // When clicking on the checkbox of a segmentation the node will e selected and its reference node either // The previous selected segmentation (if there is one) will be deselected. Additionally a reinit on the // selected segmenation will be performed. // If more than one segmentation is selected the tools will be disabled. if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceDataNew = mitk::DataNode::New(); mitk::DataNode::Pointer workingData; bool workingNodeIsVisible (true); unsigned int numberOfSelectedSegmentations (0); // iterate all images mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( isImage ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); bool isSegmentation(false); node->GetBoolProperty("binary", isSegmentation); if (node->IsSelected() && isSegmentation) { workingNodeIsVisible = node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); if (!workingNodeIsVisible) return; numberOfSelectedSegmentations++; workingData = node; if (this->GetDefaultDataStorage()->GetSources(node)->Size() != 0) { referenceDataNew = this->GetDefaultDataStorage()->GetSources(node)->ElementAt(0); } bool isBinary(false); //Find topmost source or first source which is no binary image while (referenceDataNew && this->GetDefaultDataStorage()->GetSources(referenceDataNew)->Size() != 0) { referenceDataNew = this->GetDefaultDataStorage()->GetSources(referenceDataNew)->ElementAt(0); referenceDataNew->GetBoolProperty("binary",isBinary); if (!isBinary) break; } if (workingNodeIsVisible && referenceDataNew) { //Since the binary property of a segmentation can be set to false and afterwards you can create a new segmentation out of it //->could lead to a deadloop NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( referenceDataNew ); if ( searchIter != m_WorkingDataObserverTags.end()) { referenceDataNew->GetProperty("visible")->RemoveObserver( (*searchIter).second ); } referenceDataNew->SetVisibility(true); } //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(referenceDataNew) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); continue; } if (workingData.IsNull() || (workingNodeIsVisible && node != referenceDataNew)) { node->SetVisibility((false)); } } if(numberOfSelectedSegmentations == 1) SetToolManagerSelection(referenceDataNew, workingData); mitk::DataStorage::SetOfObjects::Pointer temp = mitk::DataStorage::SetOfObjects::New(); temp->InsertElement(0,workingData); mitk::TimeSlicedGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(temp); // initialize the views to the bounding geometry /*mitk::RenderingManager::GetInstance()->InitializeViews(bounds); mitk::RenderingManager::GetInstance()->RequestUpdateAll();*/ } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { bool isSeg(false); bool isHelperObject(false); node->GetBoolProperty("helper object", isHelperObject); node->GetBoolProperty("binary", isSeg); if(isSeg && !isHelperObject) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(node, mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } mitk::DataNode* tempNode = const_cast(node); node->GetProperty("visible")->RemoveObserver( m_WorkingDataObserverTags[tempNode] ); m_WorkingDataObserverTags.erase(tempNode); this->SetToolManagerSelection(NULL, NULL); } } void QmitkSegmentationView::CreateSegmentationFromSurface() { mitk::DataNode::Pointer surfaceNode = m_Controls->MaskSurfaces->GetSelectedNode(); mitk::Surface::Pointer surface(0); if(surfaceNode.IsNotNull()) surface = dynamic_cast ( surfaceNode->GetData() ); if(surface.IsNull()) { this->HandleException( "No surface selected.", m_Parent, true); return; } mitk::DataNode::Pointer imageNode = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::Image::Pointer image(0); if (imageNode.IsNotNull()) image = dynamic_cast( imageNode->GetData() ); if(image.IsNull()) { this->HandleException( "No image selected.", m_Parent, true); return; } mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); s2iFilter->SetInput(surface); s2iFilter->SetImage(image); s2iFilter->Update(); mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); std::string nameOfResultImage = imageNode->GetName(); nameOfResultImage.append(surfaceNode->GetName()); resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); resultNode->SetProperty("binary", mitk::BoolProperty::New(true) ); resultNode->SetData( s2iFilter->GetOutput() ); this->GetDataStorage()->Add(resultNode, imageNode); } void QmitkSegmentationView::ManualToolSelected(int id) { // disable crosshair movement when a manual drawing tool is active (otherwise too much visual noise) if (m_MultiWidget) { if (id >= 0) { m_MultiWidget->DisableNavigationControllerEventListening(); } else { m_MultiWidget->EnableNavigationControllerEventListening(); } } } void QmitkSegmentationView::ToolboxStackPageChanged(int id) { // interpolation only with manual tools visible m_Controls->m_SlicesInterpolator->EnableInterpolation( id == 0 ); if( id == 0 ) { mitk::DataNode::Pointer workingData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0); if( workingData.IsNotNull() ) { m_Controls->lblSegmentation->setText( workingData->GetName().c_str() ); m_Controls->lblSegImage->show(); m_Controls->lblSegmentation->show(); } } else { m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } // this is just a workaround, should be removed when all tools support 3D+t if (id==2) // lesions { mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>3) { m_Controls->widgetStack->setCurrentIndex(0); QMessageBox::information(NULL,"Segmentation","Lesion segmentation is currently not supported for 4D images"); } } } } } // protected void QmitkSegmentationView::OnComboBoxSelectionChanged( const mitk::DataNode* node ) { mitk::DataNode* selectedNode = const_cast(node); if( selectedNode != NULL ) { m_Controls->refImageSelector->show(); m_Controls->lblReferenceImageSelectionWarning->hide(); bool isBinary(false); selectedNode->GetBoolProperty("binary", isBinary); if ( isBinary ) { FireNodeSelected(selectedNode); selectedNode->SetVisibility(true); } else if (node != m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)) { if (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)) m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)->SetVisibility(false); if (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)) { m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)->SetVisibility(false); } FireNodeSelected(selectedNode); selectedNode->SetVisibility(true); SetToolManagerSelection(selectedNode, NULL); } } else { m_Controls->refImageSelector->hide(); m_Controls->lblReferenceImageSelectionWarning->show(); } } void QmitkSegmentationView::OnShowMarkerNodes (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { if(state == true) { manualSegmentationTool->SetShowMarkerNodes( true ); } else { manualSegmentationTool->SetShowMarkerNodes( false ); } } } } void QmitkSegmentationView::On3DInterpolationEnabled (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { manualSegmentationTool->Enable3DInterpolation( state ); } } } void QmitkSegmentationView::OnSelectionChanged(mitk::DataNode* node) { std::vector nodes; nodes.push_back( node ); this->OnSelectionChanged( nodes ); } void QmitkSegmentationView::OnSurfaceSelectionChanged() { // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull())) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); } void QmitkSegmentationView::OnSelectionChanged(std::vector nodes) { // if the selected node is a contourmarker if ( !nodes.empty() ) { std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at( 0 )->GetName(); if ( ( numberOfNodes == 1 ) && ( nodeName.find( markerName ) == 0) ) { this->OnContourMarkerSelected( nodes.at( 0 ) ); } } // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull())) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); if (!m_Parent || !m_Parent->isVisible()) return; // reaction to BlueBerry selection events // this method will try to figure out if a relevant segmentation and its corresponding original image were selected // a warning is issued if the selection is invalid // appropriate reactions are triggered otherwise mitk::DataNode::Pointer referenceData = FindFirstRegularImage( nodes ); //m_Controls->refImageSelector->GetSelectedNode(); //FindFirstRegularImage( nodes ); mitk::DataNode::Pointer workingData = FindFirstSegmentation( nodes ); if(referenceData.IsNull() && workingData.IsNull()) return; bool invalidSelection( !nodes.empty() && ( nodes.size() > 2 || // maximum 2 selected nodes (nodes.size() == 2 && (workingData.IsNull() || referenceData.IsNull()) ) || // with two nodes, one must be the original image, one the segmentation ( workingData.GetPointer() == referenceData.GetPointer() ) //one node is selected as reference and working image // one item is always ok (might be working or reference or nothing ) ); if (invalidSelection) { // TODO visible warning when two images are selected MITK_ERROR << "WARNING: No image, too many (>2) or two equal images were selected."; workingData = NULL; if( m_Controls->refImageSelector->GetSelectedNode().IsNull() ) referenceData = NULL; } if ( workingData.IsNotNull() && referenceData.IsNull() ) { // find the DataStorage parent of workingData // try to find a "normal image" parent, select this as reference image mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinary = mitk::NodePredicateNot::New( isBinary ); mitk::NodePredicateAnd::Pointer isNormalImage = mitk::NodePredicateAnd::New( isImage, isNotBinary ); mitk::DataStorage::SetOfObjects::ConstPointer possibleParents = this->GetDefaultDataStorage()->GetSources( workingData, isNormalImage ); if (possibleParents->size() > 0) { if (possibleParents->size() > 1) { // TODO visible warning for this rare case MITK_ERROR << "Selected binary image has multiple parents. Using arbitrary first one for segmentation."; } referenceData = (*possibleParents)[0]; } NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( workingData ); if ( searchIter == m_WorkingDataObserverTags.end() ) { //MITK_INFO<<"Creating new observer"; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( workingData, workingData->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); workingData->GetProperty("visible")->Modified(); return; } if(workingData->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")))) { //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(workingData) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull()) || (!referenceData)) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); SetToolManagerSelection(referenceData, workingData); FireNodeSelected(workingData); } else { SetToolManagerSelection(NULL, NULL); FireNodeSelected(workingData); } } else { //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(referenceData) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull()) || (!referenceData)) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); SetToolManagerSelection(referenceData, workingData); FireNodeSelected(referenceData); } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode *node) { //TODO renderWindow anders bestimmen, siehe CheckAlignment QmitkRenderWindow* selectedRenderWindow = 0; QmitkRenderWindow* RenderWindow1 = this->GetActiveStdMultiWidget()->GetRenderWindow1(); QmitkRenderWindow* RenderWindow2 = this->GetActiveStdMultiWidget()->GetRenderWindow2(); QmitkRenderWindow* RenderWindow3 = this->GetActiveStdMultiWidget()->GetRenderWindow3(); QmitkRenderWindow* RenderWindow4 = this->GetActiveStdMultiWidget()->GetRenderWindow4(); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow1->GetRenderer())) { selectedRenderWindow = RenderWindow1; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow2->GetRenderer())) { selectedRenderWindow = RenderWindow2; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow3->GetRenderer())) { selectedRenderWindow = RenderWindow3; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow4->GetRenderer())) { selectedRenderWindow = RenderWindow4; } // make node visible if (selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); selectedRenderWindow->GetRenderer()->GetDisplayGeometry()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } mitk::DataNode::Pointer QmitkSegmentationView::FindFirstRegularImage( std::vector nodes ) { if (nodes.empty()) return NULL; for(unsigned int i = 0; i < nodes.size(); ++i) { //mitk::DataNode::Pointer node = i.value() bool isImage(false); if (nodes.at(i)->GetData()) { isImage = dynamic_cast(nodes.at(i)->GetData()) != NULL; } // make sure this is not a binary image bool isSegmentation(false); nodes.at(i)->GetBoolProperty("binary", isSegmentation); // return first proper mitk::Image if (isImage && !isSegmentation) return nodes.at(i); } return NULL; } mitk::DataNode::Pointer QmitkSegmentationView::FindFirstSegmentation( std::vector nodes ) { if (nodes.empty()) return NULL; for(unsigned int i = 0; i < nodes.size(); ++i) { bool isImage(false); if (nodes.at(i)->GetData()) { isImage = dynamic_cast(nodes.at(i)->GetData()) != NULL; } bool isSegmentation(false); nodes.at(i)->GetBoolProperty("binary", isSegmentation); // return first proper binary mitk::Image if (isImage && isSegmentation) { return nodes.at(i); } } return NULL; } void QmitkSegmentationView::SetToolManagerSelection(const mitk::DataNode* referenceData, const mitk::DataNode* workingData) { // called as a result of new BlueBerry selections // tells the ToolManager for manual segmentation about new selections // updates GUI information about what the user should select mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); toolManager->SetReferenceData(const_cast(referenceData)); toolManager->SetWorkingData( const_cast(workingData)); // check original image m_Controls->btnNewSegmentation->setEnabled(referenceData != NULL); if (referenceData) { m_Controls->lblReferenceImageSelectionWarning->hide(); } else { m_Controls->lblReferenceImageSelectionWarning->show(); m_Controls->lblWorkingImageSelectionWarning->hide(); m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } //TODO remove statement // check, wheter reference image is aligned like render windows. Otherwise display a visible warning (because 2D tools will probably not work) CheckImageAlignment(); // check segmentation if (referenceData) { if (!workingData) { m_Controls->lblWorkingImageSelectionWarning->show(); if( m_Controls->widgetStack->currentIndex() == 0 ) { m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } } else { m_Controls->lblWorkingImageSelectionWarning->hide(); this->FireNodeSelected(const_cast(workingData)); if( m_Controls->widgetStack->currentIndex() == 0 ) { m_Controls->lblSegmentation->setText( workingData->GetName().c_str() ); m_Controls->lblSegmentation->show(); m_Controls->lblSegImage->show(); } } } } //TODO remove function void QmitkSegmentationView::CheckImageAlignment() { bool wrongAlignment(true); mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull() && m_MultiWidget) { wrongAlignment = !( IsRenderWindowAligned(m_MultiWidget->GetRenderWindow1(), image ) && IsRenderWindowAligned(m_MultiWidget->GetRenderWindow2(), image ) && IsRenderWindowAligned(m_MultiWidget->GetRenderWindow3(), image ) ); } } m_Controls->lblAlignmentWarning->setVisible(wrongAlignment); } //TODO remove function bool QmitkSegmentationView::IsRenderWindowAligned(QmitkRenderWindow* renderWindow, mitk::Image* image) { if (!renderWindow) return false; // for all 2D renderwindows of m_MultiWidget check alignment mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast( renderWindow->GetRenderer()->GetCurrentWorldGeometry2D() ); if (displayPlane.IsNull()) return false; int affectedDimension(-1); int affectedSlice(-1); return mitk::SegTool2D::DetermineAffectedImageSlice( image, displayPlane, affectedDimension, affectedSlice ); } //TODO remove function void QmitkSegmentationView::ForceDisplayPreferencesUponAllImages() { if (!m_Parent || !m_Parent->isVisible()) return; // check all images and segmentations in DataStorage: // (items in brackets are implicitly done by previous steps) // 1. // if a reference image is selected, // show the reference image // and hide all other images (orignal and segmentation), // (and hide all segmentations of the other original images) // and show all the reference's segmentations // if no reference image is selected, do do nothing // // 2. // if a segmentation is selected, // show it // (and hide all all its siblings (childs of the same parent, incl, NULL parent)) // if no segmentation is selected, do nothing if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::DataNode::Pointer workingData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0); // 1. if (referenceData.IsNotNull()) { // iterate all images mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( isImage ); //mitk::DataStorage::SetOfObjects::ConstPointer allSegmentationChilds = this->GetDefaultDataStorage()->GetDerivations(referenceData, isImage ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); // set visibility if(!node->IsSelected() || (node->IsSelected() && !node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))))) node->SetVisibility((node == referenceData) || node->IsSelected() ); } } // 2. //if (workingData.IsNotNull() && !workingData->IsSelected()) //{ // workingData->SetVisibility(true); //} mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (!node) return; bool isBinary(false); node->GetPropertyValue("binary", isBinary); if (isBinary) { node->SetProperty( "outline binary", mitk::BoolProperty::New( this->GetPreferences()->GetBool("draw outline", true)) ); node->SetProperty( "outline width", mitk::FloatProperty::New( 2.0 ) ); node->SetProperty( "opacity", mitk::FloatProperty::New( this->GetPreferences()->GetBool("draw outline", true) ? 1.0 : 0.3 ) ); node->SetProperty( "volumerendering", mitk::BoolProperty::New( this->GetPreferences()->GetBool("volume rendering", false) ) ); } } void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls = new Ui::QmitkSegmentationControls; m_Controls->setupUi(parent); m_Controls->lblWorkingImageSelectionWarning->hide(); m_Controls->lblAlignmentWarning->hide(); m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); m_Controls->refImageSelector->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->refImageSelector->SetPredicate(mitk::NodePredicateDataType::New("Image")); if( m_Controls->refImageSelector->GetSelectedNode().IsNotNull() ) m_Controls->lblReferenceImageSelectionWarning->hide(); else m_Controls->refImageSelector->hide(); mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); toolManager->SetDataStorage( *(this->GetDefaultDataStorage()) ); assert ( toolManager ); // all part of open source MITK m_Controls->m_ManualToolSelectionBox->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox->SetToolGUIArea( m_Controls->m_ManualToolGUIContainer ); m_Controls->m_ManualToolSelectionBox->SetDisplayedToolGroups("Add Subtract Paint Wipe 'Region Growing' Correction Fill Erase"); m_Controls->m_ManualToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingData ); // available only in the 3M application if ( !m_Controls->m_OrganToolSelectionBox->children().count() ) { m_Controls->widgetStack->setItemEnabled( 1, false ); } m_Controls->m_OrganToolSelectionBox->SetToolManager( *toolManager ); m_Controls->m_OrganToolSelectionBox->SetToolGUIArea( m_Controls->m_OrganToolGUIContainer ); m_Controls->m_OrganToolSelectionBox->SetDisplayedToolGroups("'Hippocampus left' 'Hippocampus right' 'Lung left' 'Lung right' 'Liver' 'Heart LV' 'Endocard LV' 'Epicard LV' 'Prostate'"); m_Controls->m_OrganToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceData ); // available only in the 3M application if ( !m_Controls->m_LesionToolSelectionBox->children().count() ) { m_Controls->widgetStack->setItemEnabled( 2, false ); } m_Controls->m_LesionToolSelectionBox->SetToolManager( *toolManager ); m_Controls->m_LesionToolSelectionBox->SetToolGUIArea( m_Controls->m_LesionToolGUIContainer ); m_Controls->m_LesionToolSelectionBox->SetDisplayedToolGroups("'Lymph Node'"); m_Controls->m_LesionToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceData ); toolManager->NewNodesGenerated += mitk::MessageDelegate( this, &QmitkSegmentationView::NewNodesGenerated ); // update the list of segmentations toolManager->NewNodeObjectsGenerated += mitk::MessageDelegate1( this, &QmitkSegmentationView::NewNodeObjectsGenerated ); // update the list of segmentations // create signal/slot connections connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); connect( m_Controls->btnNewSegmentation, SIGNAL(clicked()), this, SLOT(CreateNewSegmentation()) ); connect( m_Controls->CreateSegmentationFromSurface, SIGNAL(clicked()), this, SLOT(CreateSegmentationFromSurface()) ); connect( m_Controls->m_ManualToolSelectionBox, SIGNAL(ToolSelected(int)), this, SLOT(ManualToolSelected(int)) ); connect( m_Controls->widgetStack, SIGNAL(currentChanged(int)), this, SLOT(ToolboxStackPageChanged(int)) ); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->m_SlicesInterpolator, SIGNAL(SignalShowMarkerNodes(bool)), this, SLOT(OnShowMarkerNodes(bool))); connect(m_Controls->m_SlicesInterpolator, SIGNAL(Signal3DInterpolationEnabled(bool)), this, SLOT(On3DInterpolationEnabled(bool))); m_Controls->MaskSurfaces->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->MaskSurfaces->SetPredicate(mitk::NodePredicateDataType::New("Surface")); //// create helper class to provide context menus for segmentations in data manager // m_PostProcessing = new QmitkSegmentationPostProcessing(this->GetDefaultDataStorage(), this, m_Parent); } //void QmitkSegmentationView::OnPlaneModeChanged(int i) //{ // //if plane mode changes, disable all tools // if (m_MultiWidget) // { // mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); // // if (toolManager) // { // if (toolManager->GetActiveToolID() >= 0) // { // toolManager->ActivateTool(-1); // } // else // { // m_MultiWidget->EnableNavigationControllerEventListening(); // } // } // } //} // ATTENTION some methods for handling the known list of (organ names, colors) are defined in QmitkSegmentationOrganNamesHandling.cpp