void mitk::RegionGrowingTool::OnMouseReleased(StateMachineAction*, InteractionEvent* interactionEvent)
{
    // Until OnMousePressedInside() implements a behaviour, we're just returning here whenever m_PaintingPixelValue is 0, i.e. when the user clicked inside the segmentation
    if (m_PaintingPixelValue == 0)
    {
        return;
    }

    mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>(interactionEvent);

    if (m_WorkingSlice.IsNotNull() && m_FillFeedbackContour && positionEvent)
    {
        // Project contour into working slice
        ContourModel* feedbackContour(FeedbackContourTool::GetFeedbackContour());
        ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, feedbackContour, false, false);

        // If there is a projected contour, fill it
        if (projectedContour.IsNotNull())
        {
            MITK_DEBUG << "Filling Segmentation";
            FeedbackContourTool::FillContourInSlice(projectedContour, positionEvent->GetSender()->GetTimeStep(), m_WorkingSlice, m_PaintingPixelValue);
            this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice);
            FeedbackContourTool::SetFeedbackContourVisible(false);
        }
    }
}
Beispiel #2
0
mitk::ContourModel::Pointer mitk::ContourUtils::BackProjectContourFrom2DSlice(const Geometry3D* sliceGeometry, ContourModel* contourIn2D, bool itkNotUsed( correctionForIpSegmentation ) )
{
  if ( !sliceGeometry || !contourIn2D ) return NULL;

  ContourModel::Pointer worldContour = ContourModel::New();
  worldContour->Initialize(*contourIn2D);

  int numberOfTimesteps = contourIn2D->GetTimeGeometry()->CountTimeSteps();

  for(int currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++)
  {
  ContourModel::VertexIterator iter = contourIn2D->Begin(currentTimestep);
  ContourModel::VertexIterator end = contourIn2D->End(currentTimestep);

  while( iter != end)
  {
    Point3D currentPointIn2D = (*iter)->Coordinates;

    Point3D worldPointIn3D;
    worldPointIn3D.Fill(0.0);
    sliceGeometry->IndexToWorld( currentPointIn2D, worldPointIn3D );
    //MITK_INFO << "index " << currentPointIn2D << " world " << worldPointIn3D << std::endl;

    worldContour->AddVertex( worldPointIn3D, currentTimestep );
    iter++;
  }
  }

  return worldContour;
}
Beispiel #3
0
/**
 3.1 Create a skeletonization of the segmentation and try to find a nice cut
   3.1.1 Call a ipSegmentation algorithm to create a nice cut
   3.1.2 Set the result of this algorithm as the feedback contour
*/
bool mitk::RegionGrowingTool::OnMousePressedInside( StateMachineAction*, InteractionEvent* interactionEvent, mitkIpPicDescriptor* workingPicSlice, int initialWorkingOffset)
{
  if ( SegTool2D::CanHandleEvent(interactionEvent) < 1.0 )
      return false;

  mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent );
  //const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent()); // checked in OnMousePressed
  // 3.1.1. Create a skeletonization of the segmentation and try to find a nice cut
  // apply the skeletonization-and-cut algorithm
  // generate contour to remove
  // set m_ReferenceSlice = NULL so nothing will happen during mouse move
  // remember to fill the contour with 0 in mouserelease
  mitkIpPicDescriptor* segmentationHistory = ipMITKSegmentationCreateGrowerHistory( workingPicSlice, m_LastWorkingSeed, NULL ); // free again
  if (segmentationHistory)
  {
    tCutResult cutContour = ipMITKSegmentationGetCutPoints( workingPicSlice, segmentationHistory, initialWorkingOffset ); // tCutResult is a ipSegmentation type
    mitkIpPicFree( segmentationHistory );
    if (cutContour.cutIt)
    {
      int timestep = positionEvent->GetSender()->GetTimeStep();
      // 3.1.2 copy point from float* to mitk::Contour
      ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New();
      contourInImageIndexCoordinates->Expand(timestep + 1);
      contourInImageIndexCoordinates->SetClosed(true, timestep);
      Point3D newPoint;
      for (int index = 0; index < cutContour.deleteSize; ++index)
      {
        newPoint[0] = cutContour.deleteCurve[ 2 * index + 0 ] - 0.5;//correction is needed because the output of the algorithm is center based
        newPoint[1] = cutContour.deleteCurve[ 2 * index + 1 ] - 0.5;//and we want our contour displayed corner based.
        newPoint[2] = 0.0;

        contourInImageIndexCoordinates->AddVertex( newPoint, timestep );
      }

      free(cutContour.traceline);
      free(cutContour.deleteCurve); // perhaps visualize this for fun?
      free(cutContour.onGradient);

      ContourModel::Pointer contourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true: sub 0.5 for ipSegmentation correction

      FeedbackContourTool::SetFeedbackContour( *contourInWorldCoordinates );
      FeedbackContourTool::SetFeedbackContourVisible(true);
      mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() );
      m_FillFeedbackContour = true;
    }
    else
    {
      m_FillFeedbackContour = false;
    }

  }
  else
  {
    m_FillFeedbackContour = false;
  }

  m_ReferenceSlice = NULL;

  return true;
}
void mitk::RegionGrowingTool::OnMouseMoved(StateMachineAction*, InteractionEvent* interactionEvent )
{
    // Until OnMousePressedInside() implements a behaviour, we're just returning here whenever m_PaintingPixelValue is 0, i.e. when the user clicked inside the segmentation
    if (m_PaintingPixelValue == 0)
    {
        return;
    }

    mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>(interactionEvent);

    if ( m_ReferenceSlice.IsNotNull() && positionEvent)
    {
        // Get geometry and indices
        mitk::BaseGeometry::Pointer workingSliceGeometry;
        workingSliceGeometry = m_WorkingSlice->GetTimeGeometry()->GetGeometryForTimeStep(m_LastEventSender->GetTimeStep());
        itk::Index<2> indexInWorkingSlice2D;
        indexInWorkingSlice2D[0] = m_SeedPoint[0];
        indexInWorkingSlice2D[1] = m_SeedPoint[1];

        m_ScreenYDifference += positionEvent->GetPointerPositionOnScreen()[1] - m_LastScreenPosition[1];
        m_ScreenXDifference += positionEvent->GetPointerPositionOnScreen()[0] - m_LastScreenPosition[0];
        m_LastScreenPosition = positionEvent->GetPointerPositionOnScreen();

        // Moving the mouse up and down adjusts the width of the threshold window, moving it left and right shifts the threshold window
        m_Thresholds[0] = std::min<ScalarType>(m_SeedValue, m_InitialThresholds[0] - (m_ScreenYDifference - m_ScreenXDifference) * m_MouseDistanceScaleFactor);
        m_Thresholds[1] = std::max<ScalarType>(m_SeedValue, m_InitialThresholds[1] + (m_ScreenYDifference + m_ScreenXDifference) * m_MouseDistanceScaleFactor);
        MITK_DEBUG << "Screen difference X: " << m_ScreenXDifference;

        // Perform region growing again and show the result
        mitk::Image::Pointer resultImage = mitk::Image::New();
        AccessFixedDimensionByItk_3(m_ReferenceSlice, StartRegionGrowing, 2, indexInWorkingSlice2D, m_Thresholds, resultImage);
        resultImage->SetGeometry(workingSliceGeometry);

        // Update the contour
        if (resultImage.IsNotNull() && m_ConnectedComponentValue >= 1)
        {
            mitk::ImageToContourModelFilter::Pointer contourExtractor = mitk::ImageToContourModelFilter::New();
            contourExtractor->SetInput(resultImage);
            contourExtractor->SetContourValue(m_ConnectedComponentValue - 0.5);
            contourExtractor->Update();
            ContourModel::Pointer resultContour = ContourModel::New();
            resultContour = contourExtractor->GetOutput();

            // Show contour
            if (resultContour.IsNotNull())
            {
                ContourModel::Pointer resultContourWorld = FeedbackContourTool::BackProjectContourFrom2DSlice(workingSliceGeometry, FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, resultContour));
                FeedbackContourTool::SetFeedbackContour(resultContourWorld);
                FeedbackContourTool::SetFeedbackContourVisible(true);
                mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(positionEvent->GetSender()->GetRenderWindow());
            }
        }
    }
}
/**
 If the feedback contour should be filled, then it is done here. (Contour is NOT filled, when skeletonization is done but no nice cut was found)
*/
bool mitk::RegionGrowingTool::OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent )
{
  if ( FeedbackContourTool::CanHandleEvent(interactionEvent) > 0.0 )
  {
    // 1. If we have a working slice, use the contour to fill a new piece on segmentation on it (or erase a piece that was selected by ipMITKSegmentationGetCutPoints)
    if ( m_WorkingSlice.IsNotNull() && m_OriginalPicSlice )
    {
      mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent );
      //const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
      if (positionEvent)
      {
        // remember parameters for next time
        m_InitialLowerThreshold = m_LowerThreshold;
        m_InitialUpperThreshold = m_UpperThreshold;

        int timestep = positionEvent->GetSender()->GetTimeStep();

        if (m_FillFeedbackContour)
        {
          // 3. use contour to fill a region in our working slice
          ContourModel* feedbackContour( FeedbackContourTool::GetFeedbackContour() );
          if (feedbackContour)
          {
            ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( m_WorkingSlice, feedbackContour, false, false ); // false: don't add any 0.5
                                                                                                                                    // false: don't constrain the contour to the image's inside
            if (projectedContour.IsNotNull())
            {
              FeedbackContourTool::FillContourInSlice( projectedContour, timestep, m_WorkingSlice, m_PaintingPixelValue );

              const PlaneGeometry* planeGeometry( dynamic_cast<const PlaneGeometry*> (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) );

              //MITK_DEBUG << "OnMouseReleased: writing back to dimension " << affectedDimension << ", slice " << affectedSlice << " in working image" << std::endl;

             // 4. write working slice back into image volume
             this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice);
            }
          }
        }

        FeedbackContourTool::SetFeedbackContourVisible(false);
        mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() );
      }
    }
  }

  m_ReferenceSlice = NULL; // don't leak
  m_WorkingSlice = NULL;
  m_OriginalPicSlice = NULL;

  return true;
}
void mitk::SetRegionTool::OnMouseReleased(StateMachineAction *, InteractionEvent *interactionEvent)
{
  auto *positionEvent = dynamic_cast<mitk::InteractionPositionEvent *>(interactionEvent);
  if (!positionEvent)
    return;

  assert(positionEvent->GetSender()->GetRenderWindow());
  // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's
  // working image corresponds to that
  FeedbackContourTool::SetFeedbackContourVisible(false);
  mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());

  int timeStep = positionEvent->GetSender()->GetTimeStep();

  DataNode *workingNode(m_ToolManager->GetWorkingData(0));
  if (!workingNode)
    return;

  auto *image = dynamic_cast<Image *>(workingNode->GetData());
  const PlaneGeometry *planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()));
  if (!image || !planeGeometry)
    return;

  Image::Pointer slice = FeedbackContourTool::GetAffectedImageSliceAs2DImage(positionEvent, image);

  if (slice.IsNull())
  {
    MITK_ERROR << "Unable to extract slice." << std::endl;
    return;
  }

  ContourModel *feedbackContour(FeedbackContourTool::GetFeedbackContour());
  ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice(
    slice, feedbackContour, false, false); // false: don't add 0.5 (done by FillContourInSlice)
  // false: don't constrain the contour to the image's inside
  if (projectedContour.IsNull())
    return;

  auto *labelImage = dynamic_cast<LabelSetImage *>(image);
  int activeColor = 1;
  if (labelImage != nullptr)
  {
    activeColor = labelImage->GetActiveLabel()->GetValue();
  }

  mitk::ContourModelUtils::FillContourInSlice(
    projectedContour, timeStep, slice, image, m_PaintingPixelValue * activeColor);

  this->WriteBackSegmentationResult(positionEvent, slice);
}
/**
  Close the contour, project it to the image slice and fill it in 2D.
*/
bool mitk::ContourTool::OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent )
{
  // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's working image corresponds to that
  FeedbackContourTool::SetFeedbackContourVisible(false);

  mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent );
  //const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
  if (!positionEvent) return false;

  assert( positionEvent->GetSender()->GetRenderWindow() );
  mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() );

  DataNode* workingNode( m_ToolManager->GetWorkingData(0) );
  if (!workingNode) return false;

  Image* image = dynamic_cast<Image*>(workingNode->GetData());
  const PlaneGeometry* planeGeometry( dynamic_cast<const PlaneGeometry*> (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) );
  if ( !image || !planeGeometry ) return false;

  const AbstractTransformGeometry* abstractTransformGeometry( dynamic_cast<const AbstractTransformGeometry*> (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) );
  if ( !image || abstractTransformGeometry ) return false;

    // 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;
    }

    ContourModel* feedbackContour = FeedbackContourTool::GetFeedbackContour();
    ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( slice, feedbackContour, true, false ); // true: actually no idea why this is neccessary, but it works :-(

    if (projectedContour.IsNull()) return false;

    int timestep = positionEvent->GetSender()->GetTimeStep();

    FeedbackContourTool::FillContourInSlice( projectedContour, timestep, slice, m_PaintingPixelValue );

    this->WriteBackSegmentationResult(positionEvent, slice);

    // 4. Make sure the result is drawn again --> is visible then.
    assert( positionEvent->GetSender()->GetRenderWindow() );

  return true;
}
bool mitk::SetRegionTool::OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent )
{
  // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's working image corresponds to that
  FeedbackContourTool::SetFeedbackContourVisible(false);

  mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent );
  if (!positionEvent) return false;

  assert( positionEvent->GetSender()->GetRenderWindow() );
  mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());

  int timeStep = positionEvent->GetSender()->GetTimeStep();

  if (!m_FillContour && !m_StatusFillWholeSlice) return true;

  DataNode* workingNode( m_ToolManager->GetWorkingData(0) );
  if (!workingNode) return false;

  Image* image = dynamic_cast<Image*>(workingNode->GetData());
  const AbstractTransformGeometry* abstractTransformGeometry( dynamic_cast<const AbstractTransformGeometry*> (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) );
  const PlaneGeometry* planeGeometry( dynamic_cast<const PlaneGeometry*> (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) );
  if ( !image || !planeGeometry || abstractTransformGeometry ) return false;

  Image::Pointer slice = FeedbackContourTool::GetAffectedImageSliceAs2DImage( positionEvent, image );

  if ( slice.IsNull() )
  {
      MITK_ERROR << "Unable to extract slice." << std::endl;
      return false;
  }

  ContourModel* feedbackContour( FeedbackContourTool::GetFeedbackContour() );
  ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( slice, feedbackContour, false, false ); // false: don't add 0.5 (done by FillContourInSlice)
  // false: don't constrain the contour to the image's inside
  if (projectedContour.IsNull()) return false;

  FeedbackContourTool::FillContourInSlice( projectedContour, timeStep, slice, m_PaintingPixelValue );

  this->WriteBackSegmentationResult(positionEvent, slice);

  m_WholeImageContourInWorldCoordinates = NULL;
  m_SegmentationContourInWorldCoordinates = NULL;

  return true;
}
Beispiel #9
0
mitk::ContourModel::Pointer mitk::ContourUtils::ProjectContourTo2DSlice(Image* slice, ContourModel* contourIn3D, bool itkNotUsed( correctionForIpSegmentation ), bool constrainToInside)
{
  if ( !slice || !contourIn3D ) return NULL;

  ContourModel::Pointer projectedContour = ContourModel::New();
  projectedContour->Initialize(*contourIn3D);

  const Geometry3D* sliceGeometry = slice->GetGeometry();

  int numberOfTimesteps = contourIn3D->GetTimeGeometry()->CountTimeSteps();

  for(int currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++)
  {
    ContourModel::VertexIterator iter = contourIn3D->Begin(currentTimestep);
    ContourModel::VertexIterator end = contourIn3D->End(currentTimestep);

    while( iter != end)
    {
      Point3D currentPointIn3D = (*iter)->Coordinates;

      Point3D projectedPointIn2D;
      projectedPointIn2D.Fill(0.0);
      sliceGeometry->WorldToIndex( currentPointIn3D, projectedPointIn2D );
      // MITK_INFO << "world point " << currentPointIn3D << " in index is " << projectedPointIn2D;

      if ( !sliceGeometry->IsIndexInside( projectedPointIn2D ) && constrainToInside )
      {
        MITK_INFO << "**" << currentPointIn3D << " is " << projectedPointIn2D << " --> correct it (TODO)" << std::endl;
      }

      projectedContour->AddVertex( projectedPointIn2D, currentTimestep );
      iter++;
    }
  }

  return projectedContour;
}
bool mitk::CorrectorAlgorithm::ImprovedHeimannCorrectionAlgorithm(itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer pic)
{
/*!
Some documentation (not by the original author)

TobiasHeimannCorrectionAlgorithm will be called, when the user has finished drawing a freehand line.

There should be different results, depending on the line's properties:

1. Without any prior segmentation, the start point and the end point of the drawn line will be
connected to a contour and the area enclosed by the contour will be marked as segmentation.

2. When the whole line is inside a segmentation, start and end point will be connected to
a contour and the area of this contour will be subtracted from the segmentation.

3. When the line starts inside a segmentation and ends outside with only a single
transition from segmentation to no-segmentation, nothing will happen.

4. When there are multiple transitions between inside-segmentation and
outside-segmentation, the line will be divided in so called segments. Each segment is
either fully inside or fully outside a segmentation. When it is inside a segmentation, its
enclosed area will be subtracted from the segmentation. When the segment is outside a
segmentation, its enclosed area it will be added to the segmentation.

The algorithm is described in full length in Tobias Heimann's diploma thesis
(MBI Technical Report 145, p. 37 - 40).
*/

  ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice( m_WorkingImage, m_Contour, true, false );

  bool firstPointIsFillingColor = false;

  if (projectedContour.IsNull() ||
    projectedContour->GetNumberOfVertices() < 2 )
  {
    return false;
  }

  // Read the first point of the contour
  ContourModel::VertexIterator contourIter = projectedContour->Begin();
  if (contourIter == projectedContour->End())
    return false;
  itk::Index<2> previousIndex;
  previousIndex[0] = (*contourIter)->Coordinates[0];
  previousIndex[1] = (*contourIter)->Coordinates[1];
  ++contourIter;

  int currentColor = ( pic->GetPixel(previousIndex) == m_FillColor);
  firstPointIsFillingColor = currentColor;
  TSegData currentSegment;
  int countOfSegments = 1;


  bool firstSegment = true;
  ContourModel::VertexIterator contourEnd = projectedContour->End();
  for (; contourIter != contourEnd; ++contourIter)
  {
    // Get current point
    itk::Index<2> currentIndex;
    currentIndex[0] = (*contourIter)->Coordinates[0] +0.5;
    currentIndex[1] = (*contourIter)->Coordinates[1] +0.5;

    // Calculate length and slope
    double slopeX = currentIndex[0] - previousIndex[0];
    double slopeY = currentIndex[1] - previousIndex[1];
    double length = std::sqrt(slopeX * slopeX + slopeY * slopeY);
    double deltaX = slopeX / length;
    double deltaY = slopeY / length;

    for (double i = 0; i <= length && length > 0; i+=1)
    {
      itk::Index<2> temporaryIndex;
      temporaryIndex[0] = previousIndex[0] + deltaX * i;
      temporaryIndex[1] = previousIndex[1] + deltaY * i;
      if ( ! pic->GetLargestPossibleRegion().IsInside(temporaryIndex))
        continue;
      if ( (pic->GetPixel(temporaryIndex) == m_FillColor) != currentColor)
      {
        currentSegment.points.push_back(temporaryIndex);
        if ( ! firstSegment)
        {
          ModifySegment( currentSegment, pic);
        } else
        {
          firstSegment = false;
        }
        currentSegment = TSegData();
        ++countOfSegments;
        currentColor = (pic->GetPixel(temporaryIndex) == m_FillColor);
      }
      currentSegment.points.push_back(temporaryIndex);
    }
    previousIndex = currentIndex;
  }

  // Check if only on Segment
  if (firstSegment && currentSegment.points.size() > 0)
  {
      ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice( m_WorkingImage, m_Contour, true, false );
      projectedContour->Close();
      if (firstPointIsFillingColor)
      {
        ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingImage, m_EraseColor);
      } else
      {
        ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingImage, m_FillColor);
      }
  }
  return true;
}
/**
Uses ipSegmentation algorithms to do the actual region growing. The result (binary image) is first smoothed by a 5x5 circle mask, then
its contour is extracted and converted to MITK coordinates.
*/
mitkIpPicDescriptor* mitk::RegionGrowingTool::PerformRegionGrowingAndUpdateContour(int timestep)
{
  // 1. m_OriginalPicSlice and m_SeedPointMemoryOffset are set to sensitive values, as well as m_LowerThreshold and m_UpperThreshold
  assert (m_OriginalPicSlice);
  if (m_OriginalPicSlice->n[0] != 256 || m_OriginalPicSlice->n[1] != 256) // ???
  assert( (m_SeedPointMemoryOffset < static_cast<int>( m_OriginalPicSlice->n[0] * m_OriginalPicSlice->n[1] )) && (m_SeedPointMemoryOffset >= 0) ); // inside the image

  // 2. ipSegmentation is used to perform region growing
  float ignored;
  int oneContourOffset( 0 );
  mitkIpPicDescriptor* regionGrowerResult = ipMITKSegmentationGrowRegion4N( m_OriginalPicSlice,
                                                                        m_SeedPointMemoryOffset,       // seed point
                                                                        true,              // grayvalue interval relative to seed point gray value?
                                                                        m_LowerThreshold,
                                                                        m_UpperThreshold,
                                                                        0,                  // continue until done (maxIterations == 0)
                                                                        NULL,               // allocate new memory (only this time, on mouse move we'll reuse the old buffer)
                                                                        oneContourOffset,   // a pixel that is near the resulting contour
                                                                        ignored             // ignored by us
                                                                      );

  if (!regionGrowerResult || oneContourOffset == -1)
  {
    ContourModel::Pointer dummyContour = ContourModel::New();
    dummyContour->Initialize();
    FeedbackContourTool::SetFeedbackContour( *dummyContour );

    if (regionGrowerResult) ipMITKSegmentationFree(regionGrowerResult);
    return NULL;
  }

  // 3. We smooth the result a little to reduce contour complexity
  bool smoothResult( true ); // currently fixed, perhaps remove else block
  mitkIpPicDescriptor* smoothedRegionGrowerResult;
  if (smoothResult)
  {
    // Smooth the result (otherwise very detailed contour)
    smoothedRegionGrowerResult = SmoothIPPicBinaryImage( regionGrowerResult, oneContourOffset );

    ipMITKSegmentationFree( regionGrowerResult );
  }
  else
  {
    smoothedRegionGrowerResult = regionGrowerResult;
  }

  // 4. convert the result of region growing into a mitk::Contour
  // At this point oneContourOffset could be useless, if smoothing destroyed a thin bridge. In these
  // cases, we have two or more unconnected segmentation regions, and we don't know, which one is touched by oneContourOffset.
  // In the bad case, the contour is not the one around our seedpoint, so the result looks very strange to the user.
  // -> we remove the point where the contour started so far. Then we look from the bottom of the image for the first segmentation pixel
  //    and start another contour extraction from there. This is done, until the seedpoint is inside the contour
  int numberOfContourPoints( 0 );
  int newBufferSize( 0 );
  float* contourPoints = ipMITKSegmentationGetContour8N( smoothedRegionGrowerResult, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc
  if (contourPoints)
  {
    while ( !ipMITKSegmentationIsInsideContour( contourPoints,                                               // contour
                                                numberOfContourPoints,                                       // points in contour
                                                m_SeedPointMemoryOffset % smoothedRegionGrowerResult->n[0],  // test point x
                                                m_SeedPointMemoryOffset / smoothedRegionGrowerResult->n[0]   // test point y
                                              ) )
    {
      // we decide that this cannot be part of the segmentation because the seedpoint is not contained in the contour (fill the 4-neighborhood with 0)
      ipMITKSegmentationReplaceRegion4N( smoothedRegionGrowerResult, oneContourOffset, 0 );

      // move the contour offset to the last row (x position of the seed point)
      int rowLength = smoothedRegionGrowerResult->n[0]; // number of pixels in a row
      oneContourOffset =    m_SeedPointMemoryOffset % smoothedRegionGrowerResult->n[0]  // x of seed point
                          + rowLength*(smoothedRegionGrowerResult->n[1]-1);              // y of last row

      while (    oneContourOffset >=0
              && (*(static_cast<ipMITKSegmentationTYPE*>(smoothedRegionGrowerResult->data) + oneContourOffset) == 0) )
      {
        oneContourOffset -= rowLength; // if pixel at data+oneContourOffset is 0, then move up one row
      }

      if ( oneContourOffset < 0 )
      {
        break; // just use the last contour we found
      }

      free(contourPoints); // release contour memory
      contourPoints = ipMITKSegmentationGetContour8N( smoothedRegionGrowerResult, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc
    }

    // copy point from float* to mitk::Contour
    ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New();
    contourInImageIndexCoordinates->Expand(timestep + 1);
    contourInImageIndexCoordinates->SetClosed(true, timestep);
    Point3D newPoint;
    for (int index = 0; index < numberOfContourPoints; ++index)
    {
      newPoint[0] = contourPoints[ 2 * index + 0 ] - 0.5;//correction is needed because the output of the algorithm is center based
      newPoint[1] = contourPoints[ 2 * index + 1 ] - 0.5;//and we want our contour displayed corner based.
      newPoint[2] = 0;

      contourInImageIndexCoordinates->AddVertex( newPoint, timestep );
    }

    free(contourPoints);

    ContourModel::Pointer contourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( m_ReferenceSlice->GetGeometry(), contourInImageIndexCoordinates, true );   // true: sub 0.5 for ipSegmentation correctio

    FeedbackContourTool::SetFeedbackContour( *contourInWorldCoordinates );
  }

  // 5. Result HAS TO BE freed by caller, contains the binary region growing result
  return smoothedRegionGrowerResult;
}
Beispiel #12
0
void mitk::PaintbrushTool::UpdateContour(const InteractionPositionEvent* positionEvent)
{
  //MITK_INFO<<"Update...";
  // examine stateEvent and create a contour that matches the pixel mask that we are going to draw
  //mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent );
  //const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
  if (!positionEvent) return;

  // Get Spacing of current Slice
  //mitk::Vector3D vSpacing = m_WorkingSlice->GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing();

  //
  // Draw a contour in Square according to selected brush size
  //
  int radius = (m_Size)/2;
  float fradius = static_cast<float>(m_Size) / 2.0f;

  ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New();


  // estimate center point of the brush ( relative to the pixel the mouse points on )
  // -- left upper corner for even sizes,
  // -- midpoint for uneven sizes
  mitk::Point2D centerCorrection;
  centerCorrection.Fill(0);

  // even --> correction of [+0.5, +0.5]
  bool evenSize = ((m_Size % 2) == 0);
  if( evenSize )
  {
    centerCorrection[0] += 0.5;
    centerCorrection[1] += 0.5;
  }

  // we will compute the control points for the upper left quarter part of a circle contour
  std::vector< mitk::Point2D > quarterCycleUpperRight;
  std::vector< mitk::Point2D > quarterCycleLowerRight;
  std::vector< mitk::Point2D > quarterCycleLowerLeft;
  std::vector< mitk::Point2D > quarterCycleUpperLeft;


  mitk::Point2D curPoint;
  bool curPointIsInside = true;
  curPoint[0] = 0;
  curPoint[1] = radius;
  quarterCycleUpperRight.push_back( upperLeft(curPoint) );

  // to estimate if a pixel is inside the circle, we need to compare against the 'outer radius'
  // i.e. the distance from the midpoint [0,0] to the border of the pixel [0,radius]
  //const float outer_radius = static_cast<float>(radius) + 0.5;

  while (curPoint[1] > 0)
  {
     // Move right until pixel is outside circle
     float curPointX_squared = 0.0f;
     float curPointY_squared = (curPoint[1] - centerCorrection[1] ) * (curPoint[1] - centerCorrection[1] );
     while( curPointIsInside )
     {
        // increment posX and chec
        curPoint[0]++;
        curPointX_squared = (curPoint[0] - centerCorrection[0] ) * (curPoint[0] - centerCorrection[0] );
        const float len = sqrt( curPointX_squared + curPointY_squared);
        if ( len > fradius )
        {
           // found first Pixel in this horizontal line, that is outside the circle
           curPointIsInside = false;
        }
     }
     quarterCycleUpperRight.push_back( upperLeft(curPoint) );

     // Move down until pixel is inside circle
     while( !curPointIsInside )
     {
        // increment posX and chec
        curPoint[1]--;
        curPointY_squared = (curPoint[1] - centerCorrection[1] ) * (curPoint[1] - centerCorrection[1] );
        const float len = sqrt( curPointX_squared + curPointY_squared);
        if ( len <= fradius )
        {
           // found first Pixel in this horizontal line, that is outside the circle
           curPointIsInside = true;
           quarterCycleUpperRight.push_back( upperLeft(curPoint) );
        }

        // Quarter cycle is full, when curPoint y position is 0
        if (curPoint[1] <= 0)
           break;
     }

   }

  // QuarterCycle is full! Now copy quarter cycle to other quarters.

  if( !evenSize )
  {
    std::vector< mitk::Point2D >::const_iterator it = quarterCycleUpperRight.begin();
    while( it != quarterCycleUpperRight.end() )
    {
      mitk::Point2D p;
      p = *it;

      // the contour points in the lower right corner have same position but with negative y values
      p[1] *= -1;
      quarterCycleLowerRight.push_back(p);

      // the contour points in the lower left corner have same position
      // but with both x,y negative
      p[0] *= -1;
      quarterCycleLowerLeft.push_back(p);

      // the contour points in the upper left corner have same position
      // but with x negative
      p[1] *= -1;
      quarterCycleUpperLeft.push_back(p);

      it++;
    }
  }
  else
  {
    std::vector< mitk::Point2D >::const_iterator it = quarterCycleUpperRight.begin();
    while( it != quarterCycleUpperRight.end() )
    {
      mitk::Point2D p,q;
      p = *it;

      q = p;
      // the contour points in the lower right corner have same position but with negative y values
      q[1] *= -1;
      // correct for moved offset if size even = the midpoint is not the midpoint of the current pixel
      // but its upper rigt corner
      q[1] += 1;
      quarterCycleLowerRight.push_back(q);

      q = p;
      // the contour points in the lower left corner have same position
      // but with both x,y negative
      q[1] = -1.0f * q[1] + 1;
      q[0] = -1.0f * q[0] + 1;
      quarterCycleLowerLeft.push_back(q);

      // the contour points in the upper left corner have same position
      // but with x negative
      q = p;
      q[0] *= -1;
      q[0] +=  1;
      quarterCycleUpperLeft.push_back(q);

      it++;
    }
  }

  // fill contour with poins in right ordering, starting with the upperRight block
  mitk::Point3D tempPoint;
  for (unsigned int i=0; i<quarterCycleUpperRight.size(); i++)
  {
     tempPoint[0] = quarterCycleUpperRight[i][0];
     tempPoint[1] = quarterCycleUpperRight[i][1];
     tempPoint[2] = 0;
     contourInImageIndexCoordinates->AddVertex( tempPoint );
  }
  // the lower right has to be parsed in reverse order
  for (int i=quarterCycleLowerRight.size()-1; i>=0; i--)
  {
     tempPoint[0] = quarterCycleLowerRight[i][0];
     tempPoint[1] = quarterCycleLowerRight[i][1];
     tempPoint[2] = 0;
     contourInImageIndexCoordinates->AddVertex( tempPoint );
  }
  for (unsigned int i=0; i<quarterCycleLowerLeft.size(); i++)
  {
     tempPoint[0] = quarterCycleLowerLeft[i][0];
     tempPoint[1] = quarterCycleLowerLeft[i][1];
     tempPoint[2] = 0;
     contourInImageIndexCoordinates->AddVertex( tempPoint );
  }
  // the upper left also has to be parsed in reverse order
  for (int i=quarterCycleUpperLeft.size()-1; i>=0; i--)
  {
     tempPoint[0] = quarterCycleUpperLeft[i][0];
     tempPoint[1] = quarterCycleUpperLeft[i][1];
     tempPoint[2] = 0;
     contourInImageIndexCoordinates->AddVertex( tempPoint );
  }

  m_MasterContour = contourInImageIndexCoordinates;

}
Beispiel #13
0
/**
  Insert the point to the feedback contour,finish to build the contour and at the same time the painting function
  */
void mitk::PaintbrushTool::MouseMoved(mitk::InteractionEvent* interactionEvent, bool leftMouseButtonPressed)
{
  mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent );

  CheckIfCurrentSliceHasChanged( positionEvent );

  if ( m_LastContourSize != m_Size )
  {
    UpdateContour( positionEvent );
    m_LastContourSize = m_Size;
  }

  Point3D worldCoordinates = positionEvent->GetPositionInWorld();
  Point3D indexCoordinates;

  m_WorkingSlice->GetGeometry()->WorldToIndex( worldCoordinates, indexCoordinates );

  MITK_DEBUG << "Mouse at W " << worldCoordinates << std::endl;
  MITK_DEBUG << "Mouse at I " << indexCoordinates << std::endl;

  // round to nearest voxel center (abort if this hasn't changed)
  if ( m_Size % 2 == 0 ) // even
  {
    indexCoordinates[0] = ROUND( indexCoordinates[0]);// /*+ 0.5*/) + 0.5;
    indexCoordinates[1] = ROUND( indexCoordinates[1]);// /*+ 0.5*/ ) + 0.5;
  }
  else // odd
  {
    indexCoordinates[0] = ROUND( indexCoordinates[0]  ) ;
    indexCoordinates[1] = ROUND( indexCoordinates[1] ) ;
  }

  static Point3D lastPos; // uninitialized: if somebody finds out how this can be initialized in a one-liner, tell me
  if ( fabs(indexCoordinates[0] - lastPos[0]) > mitk::eps ||
       fabs(indexCoordinates[1] - lastPos[1]) > mitk::eps ||
       fabs(indexCoordinates[2] - lastPos[2]) > mitk::eps ||
       leftMouseButtonPressed
     )
  {
    lastPos = indexCoordinates;
  }
  else
  {
    MITK_DEBUG << "." << std::flush;
    return;
  }

  MITK_DEBUG << "Mouse at C " << indexCoordinates;

  int timestep = positionEvent->GetSender()->GetTimeStep();

  ContourModel::Pointer contour = ContourModel::New();
  contour->Expand(timestep + 1);
  contour->SetClosed(true, timestep);

  ContourModel::VertexIterator it = m_MasterContour->Begin();
  ContourModel::VertexIterator end = m_MasterContour->End();

  while(it != end)
  {
    Point3D point = (*it)->Coordinates;
    point[0] += indexCoordinates[ 0 ];
    point[1] += indexCoordinates[ 1 ];

    contour->AddVertex( point, timestep );
    it++;
  }


  if (leftMouseButtonPressed)
  {
    FeedbackContourTool::FillContourInSlice( contour, timestep, m_WorkingSlice, m_PaintingPixelValue );
    m_WorkingNode->SetData(m_WorkingSlice);
    m_WorkingNode->Modified();
  }

  // visualize contour
  ContourModel::Pointer displayContour = this->GetFeedbackContour();
  displayContour->Clear();

  ContourModel::Pointer tmp = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), /*displayContour*/contour );

  // copy transformed contour into display contour
  it =  tmp->Begin();
  end = tmp->End();

  while(it != end)
  {
    Point3D point = (*it)->Coordinates;

    displayContour->AddVertex( point, timestep );
    it++;
  }

  m_FeedbackContourNode->GetData()->Modified();

  assert( positionEvent->GetSender()->GetRenderWindow() );

  RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() );
}
bool mitk::SetRegionTool::OnMousePressed ( StateMachineAction*, InteractionEvent* interactionEvent )
{
  mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent );
  //const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
  if (!positionEvent) return false;

  m_LastEventSender = positionEvent->GetSender();
  m_LastEventSlice = m_LastEventSender->GetSlice();
  int timeStep = positionEvent->GetSender()->GetTimeStep();

  // 1. Get the working image
  Image::Pointer workingSlice   = FeedbackContourTool::GetAffectedWorkingSlice( positionEvent );
  if ( workingSlice.IsNull() ) return false; // can't do anything without the segmentation

  // if click was outside the image, don't continue
  const BaseGeometry* sliceGeometry = workingSlice->GetGeometry();
  itk::Index<2> projectedPointIn2D;
  sliceGeometry->WorldToIndex( positionEvent->GetPositionInWorld(), projectedPointIn2D );
  if ( !sliceGeometry->IsIndexInside( projectedPointIn2D ) )
  {
    MITK_ERROR << "point apparently not inside segmentation slice" << std::endl;
    return false; // can't use that as a seed point
  }

    // Convert to ipMITKSegmentationTYPE (because ipMITKSegmentationGetContour8N relys on that data type)
    itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer correctPixelTypeImage;
    CastToItkImage( workingSlice, correctPixelTypeImage );
    assert (correctPixelTypeImage.IsNotNull() );

  // possible bug in CastToItkImage ?
  // direction maxtrix is wrong/broken/not working after CastToItkImage, leading to a failed assertion in
  // mitk/Core/DataStructures/mitkSlicedGeometry3D.cpp, 479:
  // virtual void mitk::SlicedGeometry3D::SetSpacing(const mitk::Vector3D&): Assertion `aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0' failed
  // solution here: we overwrite it with an unity matrix
  itk::Image< ipMITKSegmentationTYPE, 2 >::DirectionType imageDirection;
  imageDirection.SetIdentity();
  correctPixelTypeImage->SetDirection(imageDirection);

    Image::Pointer temporarySlice = Image::New();
  //  temporarySlice = ImportItkImage( correctPixelTypeImage );
    CastToMitkImage( correctPixelTypeImage, temporarySlice );


  // check index positions
  mitkIpPicDescriptor* originalPicSlice = mitkIpPicNew();
  CastToIpPicDescriptor( temporarySlice, originalPicSlice );

  int m_SeedPointMemoryOffset = projectedPointIn2D[1] * originalPicSlice->n[0] + projectedPointIn2D[0];

  if ( m_SeedPointMemoryOffset >= static_cast<int>( originalPicSlice->n[0] * originalPicSlice->n[1] ) ||
       m_SeedPointMemoryOffset < 0 )
  {
    MITK_ERROR << "Memory offset calculation if mitk::SetRegionTool has some serious flaw! Aborting.." << std::endl;
    return false;
  }

  // 2. Determine the contour that surronds the selected "piece of the image"

  // find a contour seed point
  unsigned int oneContourOffset = static_cast<unsigned int>( m_SeedPointMemoryOffset ); // safe because of earlier check if m_SeedPointMemoryOffset < 0

  /**
    * The logic of finding a starting point for the contour is the following:
    *
    *  - If the initial seed point is 0, we are either inside a hole or outside of every segmentation.
    *    We move to the right until we hit a 1, which must be part of a contour.
    *
    *  - If the initial seed point is 1, then ...
    *    we now do the same (running to the right) until we hit a 1
    *
    *  In both cases the found contour point is used to extract a contour and
    *  then a test is applied to find out if the initial seed point is contained
    *  in the contour. If this is the case, filling should be applied, otherwise
    *  nothing is done.
    */
  unsigned int size = originalPicSlice->n[0] * originalPicSlice->n[1];
/*
  unsigned int rowSize = originalPicSlice->n[0];
*/
  ipMITKSegmentationTYPE* data = static_cast<ipMITKSegmentationTYPE*>(originalPicSlice->data);

  if ( data[oneContourOffset] == 0 ) // initial seed 0
  {
    for ( ; oneContourOffset < size; ++oneContourOffset )
    {
      if ( data[oneContourOffset] > 0 ) break;
    }
  }
  else if ( data[oneContourOffset] == 1 ) // initial seed 1
  {
    unsigned int lastValidPixel = size-1; // initialization, will be changed lateron
    bool inSeg = true;    // inside segmentation?
    for ( ; oneContourOffset < size; ++oneContourOffset )
    {
      if ( ( data[oneContourOffset] == 0 ) && inSeg ) // pixel 0 and inside-flag set: this happens at the first pixel outside a filled region
      {
        inSeg = false;
        lastValidPixel = oneContourOffset - 1; // store the last pixel position inside a filled region
        break;
      }
      else // pixel 1, inside-flag doesn't matter: this happens while we are inside a filled region
      {
        inSeg = true; // first iteration lands here
      }

    }
    oneContourOffset = lastValidPixel;
  }
  else
  {
    MITK_ERROR << "Fill/Erase was never intended to work with other than binary images." << std::endl;
    m_FillContour = false;
    return false;
  }

  if (oneContourOffset == size) // nothing found until end of slice
  {
    m_FillContour = false;
    return false;
  }

  int numberOfContourPoints( 0 );
  int newBufferSize( 0 );
  //MITK_INFO << "getting contour from offset " << oneContourOffset << " ("<<oneContourOffset%originalPicSlice->n[0]<<","<<oneContourOffset/originalPicSlice->n[0]<<")"<<std::endl;
  float* contourPoints = ipMITKSegmentationGetContour8N( originalPicSlice, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc

  //MITK_INFO << "contourPoints " << contourPoints << " (N="<<numberOfContourPoints<<")"<<std::endl;
  assert(contourPoints == NULL || numberOfContourPoints > 0);

  bool cursorInsideContour = ipMITKSegmentationIsInsideContour( contourPoints, numberOfContourPoints, projectedPointIn2D[0], projectedPointIn2D[1]);

  // decide if contour should be filled or not
  m_FillContour = cursorInsideContour;

  if (m_FillContour)
  {
    // copy point from float* to mitk::Contour
    ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New();
    contourInImageIndexCoordinates->Expand(timeStep + 1);
    contourInImageIndexCoordinates->SetClosed(true, timeStep);
    Point3D newPoint;
    for (int index = 0; index < numberOfContourPoints; ++index)
    {
      newPoint[0] = contourPoints[ 2 * index + 0 ] - 0.5;
      newPoint[1] = contourPoints[ 2 * index + 1] - 0.5;
      newPoint[2] = 0;

      contourInImageIndexCoordinates->AddVertex(newPoint, timeStep);
    }

    m_SegmentationContourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( workingSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true, correct the result from ipMITKSegmentationGetContour8N

    // 3. Show the contour
    FeedbackContourTool::SetFeedbackContour( *m_SegmentationContourInWorldCoordinates );

    FeedbackContourTool::SetFeedbackContourVisible(true);
    mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
  }

  // always generate a second contour, containing the whole image (used when CTRL is pressed)
  {
    // copy point from float* to mitk::Contour
    ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New();
    contourInImageIndexCoordinates->Expand(timeStep + 1);
    contourInImageIndexCoordinates->SetClosed(true, timeStep);
    Point3D newPoint;
    newPoint[0] = 0; newPoint[1] = 0; newPoint[2] = 0.0;
    contourInImageIndexCoordinates->AddVertex( newPoint, timeStep );
    newPoint[0] = originalPicSlice->n[0]; newPoint[1] = 0; newPoint[2] = 0.0;
    contourInImageIndexCoordinates->AddVertex( newPoint, timeStep );
    newPoint[0] = originalPicSlice->n[0]; newPoint[1] = originalPicSlice->n[1]; newPoint[2] = 0.0;
    contourInImageIndexCoordinates->AddVertex( newPoint, timeStep );
    newPoint[0] = 0; newPoint[1] = originalPicSlice->n[1]; newPoint[2] = 0.0;
    contourInImageIndexCoordinates->AddVertex( newPoint, timeStep );

    m_WholeImageContourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( workingSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true, correct the result from ipMITKSegmentationGetContour8N

    // 3. Show the contour
    FeedbackContourTool::SetFeedbackContour( *m_SegmentationContourInWorldCoordinates );

    FeedbackContourTool::SetFeedbackContourVisible(true);
    mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
  }


  free(contourPoints);

  return true;
}
void mitk::RegionGrowingTool::OnMousePressedOutside(StateMachineAction*, InteractionEvent* interactionEvent)
{
    mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>(interactionEvent);

    if (positionEvent)
    {
        // Get geometry and indices
        mitk::BaseGeometry::Pointer workingSliceGeometry;
        workingSliceGeometry = m_WorkingSlice->GetTimeGeometry()->GetGeometryForTimeStep(m_LastEventSender->GetTimeStep());
        itk::Index<2> indexInWorkingSlice2D;
        indexInWorkingSlice2D[0] = m_SeedPoint[0];
        indexInWorkingSlice2D[1] = m_SeedPoint[1];

        mitk::BaseGeometry::Pointer referenceSliceGeometry;
        referenceSliceGeometry = m_ReferenceSlice->GetTimeGeometry()->GetGeometryForTimeStep(m_LastEventSender->GetTimeStep());
        itk::Index<3> indexInReferenceSlice;
        itk::Index<2> indexInReferenceSlice2D;
        referenceSliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), indexInReferenceSlice);
        indexInReferenceSlice2D[0] = indexInReferenceSlice[0];
        indexInReferenceSlice2D[1] = indexInReferenceSlice[1];

        // Get seed neighborhood
        ScalarType averageValue(0);
        AccessFixedDimensionByItk_3(m_ReferenceSlice, GetNeighborhoodAverage, 2, indexInReferenceSlice2D, &averageValue, 1);
        m_SeedValue = averageValue;
        MITK_DEBUG << "Seed value is " << m_SeedValue;

        // Get level window settings
        LevelWindow lw(0, 500); // default window 0 to 500, can we do something smarter here?
        m_ToolManager->GetReferenceData(0)->GetLevelWindow(lw); // will fill lw if levelwindow property is present, otherwise won't touch it.
        ScalarType currentVisibleWindow = lw.GetWindow();
        MITK_DEBUG << "Level window width is " << currentVisibleWindow;
        m_InitialThresholds[0] = m_SeedValue - currentVisibleWindow / 20.0; // 20 is arbitrary (though works reasonably well), is there a better alternative (maybe option in preferences)?
        m_InitialThresholds[1] = m_SeedValue + currentVisibleWindow / 20.0;
        m_Thresholds[0] = m_InitialThresholds[0];
        m_Thresholds[1] = m_InitialThresholds[1];

        // Perform region growing
        mitk::Image::Pointer resultImage = mitk::Image::New();
        AccessFixedDimensionByItk_3(m_ReferenceSlice, StartRegionGrowing, 2, indexInWorkingSlice2D, m_Thresholds, resultImage);
        resultImage->SetGeometry(workingSliceGeometry);

        // Extract contour
        if (resultImage.IsNotNull() && m_ConnectedComponentValue >= 1)
        {
            mitk::ImageToContourModelFilter::Pointer contourExtractor = mitk::ImageToContourModelFilter::New();
            contourExtractor->SetInput(resultImage);
            contourExtractor->SetContourValue(m_ConnectedComponentValue - 0.5);
            contourExtractor->Update();
            ContourModel::Pointer resultContour = ContourModel::New();
            resultContour = contourExtractor->GetOutput();

            // Show contour
            if (resultContour.IsNotNull())
            {
                ContourModel::Pointer resultContourWorld = FeedbackContourTool::BackProjectContourFrom2DSlice(workingSliceGeometry, FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, resultContour));
                FeedbackContourTool::SetFeedbackContour(resultContourWorld);
                FeedbackContourTool::SetFeedbackContourVisible(true);
                mitk::RenderingManager::GetInstance()->RequestUpdate(m_LastEventSender->GetRenderWindow());
            }
        }
    }
}