Ejemplo n.º 1
0
/**
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;
}
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;
}