[vtkusers] Once more -- how do I get the outline contours of regions in a mask/binary image

kent williams nkwmailinglists at gmail.com
Thu Mar 27 15:17:26 EDT 2008


Here's what I want:

Input: Image where pixel == 0 is outside mask pixel != 0 is inside mask
Output: Polygon/Polyline outline of all regions of nonzero pixels in image.

Reason: My program uses vtkContourWidget to trace anatomical features.
 I save the contours in an ITK xml file. I can also save a binary
image -- i.e. an image file the same dimensions as the image being
traced.  If a voxel is inside the traced contour, then it has a value
of 255, otherwise zero.

What I want to do is to read a binary image, and then find all the
contours around the set pixels.  Basically the inverse of the
operation above -- rather than using outline contours to generate a
binary image, I want to use a binary image to get a set of outline
contours.

I've written what I think should work, below, but it looks really,
really wrong when I load it -- it seems to be some woven agglomeration
of many too many conour outlines.

#include "MaskToContoursCLP.h"
#include "itkBrains2MaskImageIO.h"
#include "itkBrains2MaskImageIOFactory.h"
#include "itkIO.h"
#include "vtkKWImage.h"
#include <vtkAppendPolyData.h>
#include <vtkContourFilter.h>
#include <vtkXMLPolyDataWriter.h>
#include <vtkPolyData.h>
#include <vtkImageData.h>
#include <vtkDataObject.h>
#include <vtkMath.h>
#include <vtkIdTypeArray.h>
#include <vtkCellArray.h>
#include <itkExtractImageFilter.h>

//
// ConverPointSequenceToPolyData
// does what the name says -- code borrowed from someon
// on the VTK Mailing list
//
// logically what you end up is a collection of lines; at some point
// we might want something different. At which point I'll have to dive
// in and learn what the hell is going on with vtkCells.
void ConvertPointSequenceToPolyData(vtkPoints* inPts,
                                    const int& closed,
                                    vtkPolyData* outPoly)
{
  if ( !inPts || !outPoly )
    {
    return;
    }

  int npts = inPts->GetNumberOfPoints();

  if ( npts < 2 )
    {
    return;
    }

  double p0[3];
  double p1[3];
  inPts->GetPoint( 0, p0 );
  inPts->GetPoint( npts - 1, p1 );
  if ( p0[0] == p1[0] && p0[1] == p1[1] && p0[2] == p1[2] && closed )
    {
    --npts;
    }

  vtkPoints* temp = vtkPoints::New();
  temp->SetNumberOfPoints( npts );
  for ( int i = 0; i < npts; ++i )
    {
    temp->SetPoint( i, inPts->GetPoint( i ) );
    }

  vtkCellArray *cells = vtkCellArray::New();
  cells->Allocate( cells->EstimateSize( npts + closed, 2 ) );

  cells->InsertNextCell( npts + closed );

  for ( int i = 0; i < npts; ++i )
    {
    cells->InsertCellPoint( i );
    }

  if ( closed )
    {
    cells->InsertCellPoint( 0 );
    }

  outPoly->SetPoints( temp );
  temp->Delete();
  outPoly->SetLines( cells );
  cells->Delete();
}
//--------------------------------------------------------------------------
// assumes a piecwise linear polyline with points at discrete locations
//
int
ReducePolyData2D(vtkPolyData* inPoly,
                 vtkPolyData* outPoly, const int& closed )
{
  if ( !inPoly || !outPoly )
    {
    return 0;
    }

  vtkPoints* inPts = inPoly->GetPoints();
  if ( !inPts )
    {
    return 0;
    }
  int n = inPts->GetNumberOfPoints();
  if ( n < 3 )
    {
    return 0;
    }

  double p0[3];
  inPts->GetPoint( 0, p0 );
  double p1[3];
  inPts->GetPoint( n - 1, p1 );
  bool minusNth = ( p0[0] == p1[0] && p0[1] == p1[1] && p0[2] == p1[2] );
  if ( minusNth && closed )
    {
    --n;
    }

  struct frenet
  {
    double t[3];   // unit tangent vector
    bool   state; // state of kappa: zero or non-zero  T/F
  };

  frenet* f;
  f = new frenet[n];
  double tL;
  // calculate the tangent vector by forward differences
  for ( int i = 0; i < n; ++i )
    {
    inPts->GetPoint( i, p0 );
    inPts->GetPoint( ( i + 1 ) % n, p1 );
    tL = sqrt( vtkMath::Distance2BetweenPoints( p0, p1 ) );
    if ( tL == 0.0 ){ tL = 1.0; }
    for ( int j = 0 ; j < 3; ++j )
      {
      f[i].t[j] = (p1[j] - p0[j]) / tL;
      }
    }

  // calculate kappa from tangent vectors by forward differences
  // mark those points that have very low curvature
  double eps = 1.e-10;

  for ( int i = 0; i < n; ++i )
    {
    f[i].state = ( fabs( vtkMath::Dot( f[i].t, f[( i + 1 ) % n].t ) - 1.0 )
                   < eps );
    }

  vtkPoints* tempPts = vtkPoints::New();

  // mark keepers
  vtkIdTypeArray* ids = vtkIdTypeArray::New();

  // for now, insist on keeping the first point for closure
  ids->InsertNextValue( 0 );

  for ( int i = 1; i < n; ++i )
    {
    bool pre = f[( i - 1 + n ) % n].state; // means fik != 1
    bool cur = f[i].state;  // means fik = 1
    bool nex = f[( i + 1 ) % n].state;

    // possible vertex bend patterns for keep: pre cur nex
    // 0 0 1
    // 0 1 1
    // 0 0 0
    // 0 1 0

    // definite delete pattern
    // 1 1 1

    bool keep = false;

    if      (  pre &&  cur &&  nex ) { keep = false; }
    else if ( !pre && !cur &&  nex ) { keep = true; }
    else if ( !pre &&  cur &&  nex ) { keep = true; }
    else if ( !pre && !cur && !nex ) { keep = true; }
    else if ( !pre &&  cur && !nex ) { keep = true; }

    if ( keep  ) // not a current sure thing but the preceding delete was
      {
      ids->InsertNextValue( i );
      }
    }

  for ( int i = 0; i < ids->GetNumberOfTuples(); ++i )
    {
    tempPts->InsertNextPoint( inPts->GetPoint( ids->GetValue( i ) ) );
    }

  if ( closed )
    {
    tempPts->InsertNextPoint( inPts->GetPoint( ids->GetValue( 0 ) ) );
    }

  ConvertPointSequenceToPolyData( tempPts, closed, outPoly );

  ids->Delete();
  tempPts->Delete();
  delete [] f;
  return 1;
}

/*This program generates a signed distance image for a binary image.
The intensity values inside the structues will be positive and outside
the structure will be negative in the signed distance image. */

int main( int argc, char * argv[] )
{
  PARSE_ARGS;
  itk::Brains2MaskImageIOFactory::RegisterOneFactory();

  const     unsigned int   Dimension = 3;
  typedef float PixelType;
  typedef itk::Image< PixelType,  Dimension >   ImageType;
  typedef ImageType SliceImageType;
  //  typedef itk::Image<PixelType,2> SliceImageType;
  typedef itk::ExtractImageFilter<ImageType,SliceImageType> ExtractFilterType;

  ImageType::Pointer inputImage(itkUtil::ReadImage<ImageType>(InputImage));

  ImageType::SizeType size = inputImage->GetLargestPossibleRegion().GetSize();
  //
  // accumulate PolyData
  vtkAppendPolyData *accumulator = vtkAppendPolyData::New();

  //
  // for each slice
  for(unsigned i = 0; i < size[2]; i++)
    {
    //
    // extract a slice from the image.
    ImageType::RegionType extractRegion;

    ImageType::SizeType extractSize(size);
    extractSize[2] = 1;

    ImageType::IndexType extractIndex;
    extractIndex[0] = 0;
    extractIndex[1] = 0;
    extractIndex[2] = i;

    extractRegion.SetSize(extractSize);
    extractRegion.SetIndex(extractIndex);

    ExtractFilterType::Pointer extractFilter = ExtractFilterType::New();
    extractFilter->SetInput(inputImage);
    extractFilter->SetExtractionRegion(extractRegion);
    extractFilter->Update();

    SliceImageType::Pointer sliceImage(extractFilter->GetOutput());

    vtkKWImage *adapter = vtkKWImage::New();
    adapter->SetITKImageBase(sliceImage);
    vtkContourFilter *contourFilter = vtkContourFilter::New();
    contourFilter->SetValue(0,0.0);
    contourFilter->SetValue(1,1.0);
    contourFilter->SetInput(vtkDataObject::SafeDownCast(adapter->GetVTKImage()));
    contourFilter->Update();
    vtkPolyData *pd = contourFilter->GetOutput();
    vtkPolyData *pd2 = vtkPolyData::New();
    ReducePolyData2D(pd,pd2,true);
    accumulator->AddInput(pd2);
    adapter->Delete();
    contourFilter->Delete();
    }
  accumulator->Update();
  vtkXMLPolyDataWriter *writer = vtkXMLPolyDataWriter::New();
  writer->SetInput(accumulator->GetOutput());
  writer->SetDataModeToAscii();
  writer->SetFileName(OutputContours.c_str());
  writer->Write();
  writer->Delete();

  return EXIT_SUCCESS;

}



More information about the vtkusers mailing list