Пример #1
0
void mitk::ContourUtils::FillContourInSlice( ContourModel* projectedContour, unsigned int timeStep, Image* sliceImage, int paintingPixelValue )
{
  // 1. Use ipSegmentation to draw a filled(!) contour into a new 8 bit 2D image, which will later be copied back to the slice.
  //    We don't work on the "real" working data, because ipSegmentation would restrict us to 8 bit images

  // convert the projected contour into a ipSegmentation format
  mitkIpInt4_t* picContour = new mitkIpInt4_t[2 * projectedContour->GetNumberOfVertices(timeStep)];
  unsigned int index(0);
  ContourModel::VertexIterator iter = projectedContour->Begin(timeStep);
  ContourModel::VertexIterator end = projectedContour->End(timeStep);

  while( iter != end)
  {
    picContour[ 2 * index + 0 ] = static_cast<mitkIpInt4_t>( (*iter)->Coordinates[0] + 1.0 ); // +0.5 wahrscheinlich richtiger
    picContour[ 2 * index + 1 ] = static_cast<mitkIpInt4_t>( (*iter)->Coordinates[1] + 1.0 );
    //MITK_INFO << "mitk 2d [" << (*iter)[0] << ", " << (*iter)[1] << "]  pic [" << picContour[ 2*index+0] << ", " << picContour[ 2*index+1] << "]";
    iter++;
    index++;
  }

  assert( sliceImage->GetSliceData() );
  mitkIpPicDescriptor* originalPicSlice = mitkIpPicNew();
  CastToIpPicDescriptor( sliceImage, originalPicSlice);
  mitkIpPicDescriptor* picSlice = ipMITKSegmentationNew( originalPicSlice );
  ipMITKSegmentationClear( picSlice );

  assert( originalPicSlice && picSlice );

  // here comes the actual contour filling algorithm (from ipSegmentation/Graphics Gems)
  ipMITKSegmentationCombineRegion ( picSlice, picContour, projectedContour->GetNumberOfVertices(timeStep), NULL, IPSEGMENTATION_OR,  1); // set to 1

  delete[] picContour;

  // 2. Copy the filled contour to the working data slice
  //    copy all pixels that are non-zero to the original image (not caring for the actual type of the working image). perhaps make the replace value a parameter ( -> general painting tool ).
  //    make the pic slice an mitk/itk image (as little ipPic code as possible), call a templated method with accessbyitk, iterate over the original and the modified slice

  Image::Pointer ipsegmentationModifiedSlice = Image::New();
  ipsegmentationModifiedSlice->Initialize( CastToImageDescriptor( picSlice ) );
  ipsegmentationModifiedSlice->SetSlice( picSlice->data );

  AccessFixedDimensionByItk_2( sliceImage, ItkCopyFilledContourToSlice, 2, ipsegmentationModifiedSlice, paintingPixelValue );

  ipsegmentationModifiedSlice = NULL; // free MITK header information
  ipMITKSegmentationFree( picSlice ); // free actual memory
}
void mitk::CorrectorAlgorithm::TobiasHeimannCorrectionAlgorithm(mitkIpPicDescriptor* 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).
*/

  int oaSize = 1000000; // if we need a fixed number, then let it be big
  int* _ofsArray = new int[ oaSize ];
  for (int i=0; i<oaSize; i++) _ofsArray[i] = 0;

  std::vector<TSegData> segData;
  segData.reserve( 16 );
    
  Contour* contour3D = const_cast<Contour*>(m_Contour.GetPointer());
  ContourUtils::Pointer contourUtils = ContourUtils::New();
  Contour::Pointer projectedContour = contourUtils->ProjectContourTo2DSlice( m_WorkingImage, contour3D, true, false ); 
      
  if (projectedContour.IsNull()) 
  {
    delete[] _ofsArray;
    return;
  }

  if (projectedContour->GetNumberOfPoints() < 2) 
  {
    delete[] _ofsArray;
    return;
  }
  
  // convert the projected contour into a ipSegmentation format
  mitkIpInt4_t* _points = new mitkIpInt4_t[2 * projectedContour->GetNumberOfPoints()];
  const Contour::PathType::VertexListType* pointsIn2D = projectedContour->GetContourPath()->GetVertexList();
  unsigned int index(0);
  for ( Contour::PathType::VertexListType::const_iterator iter = pointsIn2D->begin(); 
        iter != pointsIn2D->end();
        ++iter, ++index )
  {
    _points[ 2 * index + 0 ] = static_cast<mitkIpInt4_t>( (*iter)[0] + 0.5 );
    _points[ 2 * index + 1 ] = static_cast<mitkIpInt4_t>( (*iter)[1] + 0.5 );
  }
 
  // store ofsets of the drawn line in array
  int _ofsNum = 0;
  unsigned int num = projectedContour->GetNumberOfPoints();
  int lastOfs = -1;
  for (unsigned int i=0; i<num-1; i++) 
  {
    float x = _points [2*i] + 0.5;
    float y = _points [2*i+1] + 0.5;
    float difX = _points [2*i+2] - x + 0.5;
    float difY = _points [2*i+3] - y + 0.5;
    float length = sqrt( difX*difX + difY*difY );
    float dx = difX / length;
    float dy = difY / length;
    for (int p=0; ((float)p)<length; p++) 
    {
      // if ofs is out of bounds, very nasty things will happen, so better check coordinates:
      if (x<0) x=0.5;
      else if (x>=pic->n[0]) x = pic->n[0]-0.5;
      if (y<0) y=0.5;
      else if (y>=pic->n[1]) y = pic->n[1]-0.5;
      // ok, now store safe ofs
      int ofs = (int)(x) + pic->n[0]*((int)(y));
      x += dx;
      y += dy;
      if (ofs != lastOfs) 
      {
        _ofsArray[_ofsNum++] = ofs;
        lastOfs = ofs;
      }
    }
  }

  if (_ofsNum == 0)
  {
    // contour was completely outside the binary image
    delete[] _ofsArray;
    delete[] _points;
    return;
  }

  ipMITKSegmentationTYPE* picdata = static_cast<ipMITKSegmentationTYPE*>(pic->data);

  // divide line in logical segments:
  int numSegments = 0;
  ipMITKSegmentationTYPE state = *(picdata + _ofsArray[0]);
  int ofsP = 1;
  int modifyStart, modifyEnd;  // start of first and end of last segment
  bool nextSegment;
  segData.clear();
  do 
  {
    nextSegment = false;
    while (ofsP<_ofsNum && *(picdata + _ofsArray[ofsP])==state) ofsP++;
    if (ofsP<_ofsNum) 
    {
      int lineStart = ofsP-1;
      if (numSegments==0) modifyStart = ofsP;
      state = *(picdata + _ofsArray[ofsP]);
      while (ofsP<_ofsNum && *(picdata + _ofsArray[ofsP])==state) ofsP++;
      if (ofsP<_ofsNum) 
      {
        int lineEnd = ofsP;
        modifyEnd = lineEnd;
        nextSegment = true;
        // now we've got a valid segment from lineStart to lineEnd
        TSegData thisSegData;
        thisSegData.lineStart = lineStart;
        thisSegData.lineEnd = lineEnd;
        thisSegData.modified = modifySegment( lineStart, lineEnd, state, pic, _ofsArray );
        segData.push_back( thisSegData );
        numSegments++;
      }
    }
  } while (nextSegment);

  for (int segNr=0; segNr < numSegments; segNr++) 
  {
    // draw line if modified:
    if ( segData[segNr].modified ) 
    {
      for (int i=segData[segNr].lineStart+1; i<segData[segNr].lineEnd; i++) 
      {
        *(picdata + _ofsArray[i]) = 1;
      }
    }
  }

  if (numSegments == 0) 
  {
    if (num <= 1) 
    { // only a single pixel. _ofsArray[_ofsNum-1] in else statement would crash, so don't do anything
      // no movement: delete operation

      // This behaviour would probably confuse users when they use the correction 
      // tool to change a segmentation and it deletes much more than selected
     
      // if (state == 1)  ipMITKSegmentationReplaceRegion4N( pic, _ofsArray[0], 0 );
    }
    else if ( *(picdata + _ofsArray[_ofsNum-1]) == *(picdata + _ofsArray[0]))
    {
      // start point and end point both inside or both outside any segmentation
      // normal paint operation
      mitkIpInt4_t* p = new mitkIpInt4_t[2 * num];
      for (unsigned int i = 0; i < num; i++) 
      {
        p[2 * i] = (mitkIpInt4_t) _points [2 * i];
        p[2 * i + 1] = (mitkIpInt4_t) _points [2 * i + 1];
      }

      if (state == 0) ipMITKSegmentationCombineRegion (pic, p, num, 0, IPSEGMENTATION_OR,  1);
      else            ipMITKSegmentationCombineRegion (pic, p, num, 0, IPSEGMENTATION_AND, 0);

      delete[] p;
    }
  }

  int numberOfContourPoints( 0 );
  int oneContourOffset( 0 );
  int newBufferSize( 0 );

  int imageSize = pic->n[0]*pic->n[1];
  for (oneContourOffset = 0; oneContourOffset < imageSize; oneContourOffset++)
    if ( ((ipMITKSegmentationTYPE*) pic->data)[oneContourOffset]> 0) break;

  float* contourPoints = ipMITKSegmentationGetContour8N( pic, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc
  
  if (contourPoints) 
  {
    
    // copy point from float* to mitk::Contour 
    Contour::Pointer contourInImageIndexCoordinates = Contour::New();
    contourInImageIndexCoordinates->Initialize();
    Point3D newPoint;
    for (int index = 0; index < numberOfContourPoints; ++index)
    {
      newPoint[0] = contourPoints[ 2 * index + 0 ];
      newPoint[1] = contourPoints[ 2 * index + 1];
      newPoint[2] = 0;

      contourInImageIndexCoordinates->AddVertex( newPoint );
    }

    free(contourPoints);
    
    ContourUtils::Pointer contourUtils = ContourUtils::New();
    contourUtils->FillContourInSlice( contourInImageIndexCoordinates, m_WorkingImage );
  }
  
  delete[] _ofsArray;
  delete[] _points;
}