[vtk-developers] Use vtkContour to find outline of mask in vtkImageData?

dean.inglis at camris.ca dean.inglis at camris.ca
Mon Jan 14 19:45:41 EST 2008


Hi Kent, 

if you have extracted a set of points that are coincident with pixel
centers, you can try the routine below for reducing the number
of points...

Dean

// SAMPLE USE TO REDUCE CONTOUR POINTS COINCIDENT
// WITH PIXEL CENTERS

vtkPolyData* orig = const_cast<vtkPolyData*>( this->ContourRep->GetContourRepresentationAsPolyData() );

  vtkPolyData* temp = vtkPolyData::New();
  int closed = this->ContourRep->GetClosedLoop();

  if( vtkGeometryUtilities::ReducePolyData2D( orig, temp, closed ) )
    {
    this->ContourWidget->Initialize( temp );
    }

  temp->Delete();

// END SAMPLE USE


int vtkGeometryUtilities::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;
}

//---------------------------------------------------------------------------
// guarantees that only the minimum required number of points
// is used for open or closed line polydata
//
void vtkGeometryUtilities::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();
}
> 
> Kent:
> 
> Sorry for the long overdue reply.  Yes there is a way to do this.
> 
> The method you're looking for is
> 
>   vtkContourWidget::Initialize( polydata_containing_contour_control_points,
> StartingWidgetState )
> 
> What this does is to take a vtkPolyData (that contains 2 or more points and
> just one cell) and populate a contour from it.
> 
> StartingWidgetState as the documentation states can be
> vtkContourWidget::Define or vtkContourWidget::Manipulate. The former will
> let you go about adding new points. The latter puts it into manipulate
> state, (the same state you have on a vtkContourWidget, after you add the
> last point or close the loop).
> 
> ----------
> Now for the caveat:
> 
> I had a similar situation a while ago where I needed to go from a binary
> representation to a parametric representation, for editing purposes.
> If you run a vtkContourFilter and get a polydata out, you will have a
> polydata with several cells. You'll need to extract out each of those cells
> into seperate vtkPolydata. Take one of these cells. It will contain several
> points. If you initialize a contour widget from it, it will work, but it
> will have a few hundred control points and that doesn't look pretty. You can
> try it out :)
> 
> What you need is a downsampler, that picks every nth point, or better still
> a smart one that picks the ones that lie on high curvature.
> ----------
> 
> I just added a test that demonstrates initializing a contour widget from a
> user-supplied polydata. (a closed circle in this case).
> 
> Please let us know if you have issues.
> 
> --
> Karthik Krishnan
> R&D Engineer,
> Kitware Inc.
> 
> 
> On 1/9/08, kent williams <nkwmailinglists at gmail.com> wrote:
> >
> > I posted this to VTKUsers and got no suggestions.  Anyone here want to
> > comment?
> >
> > I need to go from a binary image, representing a 3D mask, to a set of
> > outlines on each slice of the binary image.
> >
> > It seems like something like this would work:
> >
> > Use vtkImageReslice to extract 2D slices from the 3D volume
> > For each slice, use vtkContourFilter to extract contours around areas
> > of set pixels.
> >
> > Right now I have a tracer that uses one or more vtkContourWidgets to
> > interactively generate a set of contours defining a mask.  I just
> > finished code that iterates through the contour set a slice at a time,
> > and generates a binary image volume where a non-zero pixel value is
> > inside the mask, and zero pixel value is outside the mask.  What I
> > want to do is reverse this process -- given a binary image, generate
> > the countour set.
> >
> > It looks like I should maybe use GenerateValues to generate contours,
> > but I don't understand the relationship between the output data of
> > vtkContour filter and the data i would need to initialize a
> > vtkContourWidget.  The few examples extant don't make that clear to me
> > either.
> >
> > Thanks!
> > _______________________________________________
> > vtk-developers mailing list
> > vtk-developers at vtk.org
> > http://www.vtk.org/mailman/listinfo/vtk-developers
> >
> 
> 



More information about the vtk-developers mailing list