Index: Algorithms/mitkOverwriteSliceImageFilter.cpp =================================================================== --- Algorithms/mitkOverwriteSliceImageFilter.cpp (revision 21081) +++ Algorithms/mitkOverwriteSliceImageFilter.cpp (working copy) @@ -25,8 +25,11 @@ #include "mitkDiffImageApplier.h" #include "mitkImageTimeSelector.h" +#include "mitkContourTool.h" + #include #include +#include mitk::OverwriteSliceImageFilter::OverwriteSliceImageFilter() :m_SliceIndex(0), @@ -34,8 +37,11 @@ m_TimeStep(0), m_Dimension0(0), m_Dimension1(1), + m_pixelValue(1), + m_bEraseOnlySelectedColor(false), m_CreateUndoInformation(false) { + m_bSliceAndImageOriented = true; } mitk::OverwriteSliceImageFilter::~OverwriteSliceImageFilter() @@ -80,15 +86,33 @@ 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) - ) + if ( slice->GetDimension() != 2 || + ( input->GetDimension() != 2 && input->GetDimension() != 3 && input->GetDimension() != 4 ) ) { itkExceptionMacro("Slice and image dimensions differ or slice index is too large. Sorry, cannot work like this."); return; } + + if ( slice->GetDimension(0) != input->GetDimension(m_Dimension0) || + slice->GetDimension(1) != input->GetDimension(m_Dimension1) || + m_SliceIndex >= input->GetDimension(m_SliceDimension) + ) + { + m_bSliceAndImageOriented = false; + } + else + { + m_bSliceAndImageOriented = true; + } + + if ( input->GetDimension() == 4 ) + { + ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); + timeSelector->SetInput( input ); + timeSelector->SetTimeNr( m_TimeStep ); + timeSelector->UpdateLargestPossibleRegion(); + input3D = timeSelector->GetOutput(); + } if ( input->GetDimension() == 4 ) { @@ -108,18 +132,28 @@ 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; + //LOG_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 ); + if ( input->GetDimension() >= 3 ) + { + AccessFixedDimensionByItk( input3D, ItkImageSwitch, 3 ); + } + else + { + AccessFixedDimensionByItk( input3D, ItkImageSwitch, 2 ); + } SegmentationInterpolationController* interpolator = SegmentationInterpolationController::InterpolatorForImage( input ); if (interpolator) { interpolator->BlockModified(true); - interpolator->SetChangedSlice( m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep ); + if ( m_bSliceAndImageOriented ) + { + interpolator->SetChangedSlice( m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep ); + } } - if ( m_CreateUndoInformation ) + if ( m_bSliceAndImageOriented && 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 ); @@ -182,16 +216,6 @@ 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; @@ -199,25 +223,123 @@ DiffSliceIteratorType diffIterator( diffImage, diffImage->GetLargestPossibleRegion() ); // iterate over output slice (and over input slice simultaneously) - outputIterator.GoToBegin(); inputIterator.GoToBegin(); diffIterator.GoToBegin(); - while ( !outputIterator.IsAtEnd() ) + if ( m_bSliceAndImageOriented ) { - 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(); + typename VolumeImageType::RegionType sliceInVolumeRegion; + + sliceInVolumeRegion = outputImage->GetLargestPossibleRegion(); + if ( outputImage->GetImageDimension( ) == 3 ) + { + sliceInVolumeRegion.SetSize( m_SliceDimension, 1 ); // just one slice + sliceInVolumeRegion.SetIndex( m_SliceDimension, m_SliceIndex ); // exactly this slice, please + } + + // Get an iterator to the output image slice + OutputSliceIteratorType outputIterator( outputImage, sliceInVolumeRegion ); + outputIterator.SetFirstDirection(m_Dimension0); + outputIterator.SetSecondDirection(m_Dimension1); + + 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 + if( inputIterator.Get() == mitk::paint::addPixelValue ) + { + outputIterator.Set( (TPixel2)( m_pixelValue ) ); + } + else if( inputIterator.Get() == mitk::paint::subPixelValue ) + { + if (m_bEraseOnlySelectedColor) + { + if ( outputIterator.Get() == (TPixel2)( m_pixelValue ) ) + { + outputIterator.Set( (TPixel2)( 0 ) ); + } + } + else + { + outputIterator.Set( (TPixel2)( 0 ) ); + } + } + ++outputIterator; + ++inputIterator; + ++diffIterator; + } + outputIterator.NextLine(); + } + outputIterator.NextSlice(); + } } + else + { + while ( !inputIterator.IsAtEnd() ) + { + // Input world point + Point3D currentPointIn2D; + currentPointIn2D.Fill(0); + for (int i = 0; i < 2; ++i) currentPointIn2D[i] = inputIterator.GetIndex()[i]; + Point3D worldPointIn3D; + worldPointIn3D.Fill(0); + m_SliceGeometry3D->IndexToWorld( currentPointIn2D, worldPointIn3D ); + + // 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_SliceGeometry3D->IndexToWorld( offsetIndex, offsetWorld ); + // remove origin shift + const Point3D origin = m_SliceGeometry3D->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 + typename itk::Image::IndexType outputIndex; + m_ImageGeometry3D->WorldToIndex( worldPointIn3D, outputIndex ); + + // Only access ITK image if it's inside + TPixel2 outputPixel = 0; + if ( m_ImageGeometry3D->IsIndexInside( outputIndex ) ) + { + outputPixel = outputImage->GetPixel( outputIndex ); + if( inputIterator.Get() == mitk::paint::addPixelValue ) + { + outputImage->SetPixel( outputIndex, (TPixel2)( m_pixelValue ) ); + } + else if( inputIterator.Get() == mitk::paint::subPixelValue ) + { + if (m_bEraseOnlySelectedColor) + { + if ( outputImage->GetPixel( outputIndex ) == (TPixel2)( m_pixelValue ) ) + { + outputImage->SetPixel( outputIndex, (TPixel2)( 0 ) ); + } + } + else + { + outputImage->SetPixel( outputIndex, (TPixel2)( 0 ) ); + } + } + } + + // Set difference image + diffIterator.Set( static_cast(inputIterator.Get() - outputPixel ) ); // oh oh, not good for bigger values + ++inputIterator; + ++diffIterator; + } + } } std::string mitk::OverwriteSliceImageFilter::EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ) @@ -244,3 +366,42 @@ return s.str(); } + +const mitk::Geometry3D* mitk::OverwriteSliceImageFilter::GetSliceGeometry3D() const +{ + return m_SliceGeometry3D; +} + +void mitk::OverwriteSliceImageFilter::SetSliceGeometry3D( const mitk::Geometry3D* val ) +{ + m_SliceGeometry3D = val; +} + +const mitk::Geometry3D* mitk::OverwriteSliceImageFilter::GetImageGeometry3D() const +{ + return m_ImageGeometry3D; +} + +void mitk::OverwriteSliceImageFilter::SetImageGeometry3D( const mitk::Geometry3D* val ) +{ + m_ImageGeometry3D = val; +} + + +void mitk::OverwriteSliceImageFilter::SetPixelValue(int n) { + m_pixelValue = n; +} + +int mitk::OverwriteSliceImageFilter::GetPixelValue() { + return m_pixelValue; +} + +void mitk::OverwriteSliceImageFilter::SetEraseOnlySelectedColor(bool b) +{ + m_bEraseOnlySelectedColor = b; +} + +bool mitk::OverwriteSliceImageFilter::GetEraseOnlySelectedColor() +{ + return m_bEraseOnlySelectedColor; +} \ No newline at end of file Index: Algorithms/mitkOverwriteSliceImageFilter.h =================================================================== --- Algorithms/mitkOverwriteSliceImageFilter.h (revision 21081) +++ Algorithms/mitkOverwriteSliceImageFilter.h (working copy) @@ -88,6 +88,19 @@ const Image* GetSliceImage() { return m_SliceImage.GetPointer(); } const Image* GetLastDifferenceImage() { return m_SliceDifferenceImage.GetPointer(); } + + const Geometry3D* GetSliceGeometry3D() const; + void SetSliceGeometry3D(const Geometry3D* val); + + const Geometry3D* GetImageGeometry3D() const; + void SetImageGeometry3D(const Geometry3D* val); + + void SetPixelValue(int n); + int GetPixelValue(); + + void SetEraseOnlySelectedColor(bool b); + bool GetEraseOnlySelectedColor(); + protected: @@ -114,6 +127,17 @@ unsigned int m_Dimension1; bool m_CreateUndoInformation; + + //! Slice and image are oriented on the same direction + bool m_bSliceAndImageOriented; + + const Geometry3D* m_ImageGeometry3D; + const Geometry3D* m_SliceGeometry3D; + + //! Pixel value used to paint the region + int m_pixelValue; + //! If true then is only possible to remove pixels with value m_pixelValue + bool m_bEraseOnlySelectedColor; }; } // namespace Index: Interactions/mitkContourTool.cpp =================================================================== --- Interactions/mitkContourTool.cpp (revision 21081) +++ Interactions/mitkContourTool.cpp (working copy) @@ -23,10 +23,13 @@ #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" //#include "mitkProperties.h" +#include "mitkImageWriter.h" mitk::ContourTool::ContourTool(int paintingPixelValue) :FeedbackContourTool("PressMoveReleaseWithCTRLInversion"), - m_PaintingPixelValue(paintingPixelValue) + m_PaintingPixelValue(paintingPixelValue), + m_pixelValue( 1 ), + m_bEraseOnlySelectedColor( false ) { } @@ -102,7 +105,7 @@ DataTreeNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return false; - + Image* image = dynamic_cast(workingNode->GetData()); const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); if ( !image || !planeGeometry ) return false; @@ -113,51 +116,43 @@ { // 2. Slice is known, now we try to get it as a 2D image and project the contour into index coordinates of this slice Image::Pointer slice = SegTool2D::GetAffectedImageSliceAs2DImage( positionEvent, image ); - if ( slice.IsNull() ) { MITK_ERROR << "Unable to extract slice." << std::endl; return false; } + + // 3. Get the contour and project it on the 2D slice + Contour* feedbackContour( FeedbackContourTool::GetFeedbackContour() ); + // arg3: true: actually no idea why this is neccessary, but it works :-( + // arg4: false: don't constrain the contour to the image's inside + Contour::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( slice, feedbackContour, true, false ); + + if (projectedContour.IsNull()) + { + MITK_ERROR << "Unable to project contour." << std::endl; + return false; + } - /* - DataTreeNode::Pointer debugNode = DataTreeNode::New(); - debugNode->SetData( slice ); - debugNode->SetProperty( "name", StringProperty::New("extracted slice") ); - debugNode->SetProperty( "color", ColorProperty::New(1.0, 0.0, 0.0) ); - debugNode->SetProperty( "layer", FloatProperty::New(100) ); - m_ToolManager->GetDataStorage()->Add( debugNode ); - */ - - Contour* feedbackContour( FeedbackContourTool::GetFeedbackContour() ); - Contour::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( slice, feedbackContour, true, false ); // true: actually no idea why this is neccessary, but it works :-( - // false: don't constrain the contour to the image's inside - /* - Contour::Pointer back = FeedbackContourTool::BackProjectContourFrom2DSlice( slice, projectedContour, true ); // true: actually no idea why this is neccessary, but it works :-( - for (unsigned int idx = 0; idx < back->GetNumberOfPoints(); ++idx) - { - Point3D before = feedbackContour->GetPoints()->ElementAt(idx); - Point3D inbtw = projectedContour->GetPoints()->ElementAt(idx); - Point3D after = back->GetPoints()->ElementAt(idx); - - MITK_DEBUG << "before " << before << " zwischen " << inbtw << " after " << after; - } - */ - - if (projectedContour.IsNull()) return false; - - FeedbackContourTool::FillContourInSlice( projectedContour, slice, m_PaintingPixelValue ); - + // 4. Fill in the contour + FeedbackContourTool::FillContourInSlice( projectedContour, slice, m_PaintingPixelValue ); + // 5. Write the modified 2D working data slice back into the image + const Geometry3D* geometry3D = positionEvent->GetSender()->GetCurrentWorldGeometry(); + Geometry3D* imageGeometry = image->GetGeometry(0); OverwriteSliceImageFilter::Pointer slicewriter = OverwriteSliceImageFilter::New(); slicewriter->SetInput( image ); slicewriter->SetCreateUndoInformation( true ); slicewriter->SetSliceImage( slice ); slicewriter->SetSliceDimension( affectedDimension ); slicewriter->SetSliceIndex( affectedSlice ); + slicewriter->SetSliceGeometry3D( slice->GetGeometry() ); + slicewriter->SetImageGeometry3D( imageGeometry ); slicewriter->SetTimeStep( positionEvent->GetSender()->GetTimeStep( image ) ); + slicewriter->SetPixelValue(GetPixelValue()); + slicewriter->SetEraseOnlySelectedColor(GetEraseOnlySelectedColor()); slicewriter->Update(); - + // 6. Make sure the result is drawn again --> is visible then. assert( positionEvent->GetSender()->GetRenderWindow() ); @@ -179,17 +174,32 @@ if (!FeedbackContourTool::OnInvertLogic(action, stateEvent)) return false; // Inversion only for 0 and 1 as painting values - if (m_PaintingPixelValue == 1) + if (m_PaintingPixelValue == mitk::paint::addPixelValue) { - m_PaintingPixelValue = 0; + m_PaintingPixelValue = mitk::paint::subPixelValue; FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); } - else if (m_PaintingPixelValue == 0) + else if (m_PaintingPixelValue == mitk::paint::subPixelValue) { - m_PaintingPixelValue = 1; + m_PaintingPixelValue = mitk::paint::addPixelValue; FeedbackContourTool::SetFeedbackContourColorDefault(); } return true; } +void mitk::ContourTool::SetPixelValue(int n) { + m_pixelValue = n; +} + +int mitk::ContourTool::GetPixelValue() { + return m_pixelValue; +} + +void mitk::ContourTool::SetEraseOnlySelectedColor(bool b) { + m_bEraseOnlySelectedColor = b; +} + +bool mitk::ContourTool::GetEraseOnlySelectedColor() { + return m_bEraseOnlySelectedColor; +} \ No newline at end of file Index: Interactions/mitkContourTool.h =================================================================== --- Interactions/mitkContourTool.h (revision 21081) +++ Interactions/mitkContourTool.h (working copy) @@ -26,6 +26,14 @@ class Image; +// The intermediate data and the final one do not use the same pixel values +// in order not to confuse and re-process them. +namespace paint +{ + const int addPixelValue = 253; // unsigned + const int subPixelValue = 254; // unsigned +} + /** \brief Simple contour filling tool. @@ -52,6 +60,10 @@ public: mitkClassMacro(ContourTool, FeedbackContourTool); + void SetPixelValue(int n); + int GetPixelValue(); + void SetEraseOnlySelectedColor(bool b); + bool GetEraseOnlySelectedColor(); protected: @@ -68,6 +80,10 @@ int m_PaintingPixelValue; + //! Pixel value used to paint the region. Passed to OverwriteSliceImageFilter + int m_pixelValue; + //! If true then is only possible to remove pixels with value m_pixelValue + bool m_bEraseOnlySelectedColor; }; } // namespace