# The vtk VoxelContoursToSurfaceFilter output is not closed surface

**URL:** https://discourse.vtk.org/t/the-vtk-voxelcontourstosurfacefilter-output-is-not-closed-surface/4258
**Category:** Support
**Created:** [September 21, 2020, 3:36am UTC](https://discourse.vtk.org/t/the-vtk-voxelcontourstosurfacefilter-output-is-not-closed-surface/4258 "2020-09-21T03:36:59Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![guojunjie4SD](https://discourse.vtk.org/user_avatar/discourse.vtk.org/guojunjie4sd/32/2352_2.png) [@guojunjie4SD](https://discourse.vtk.org/u/guojunjie4SD)
#### Post date: [September 21, 2020, 3:36am UTC](https://discourse.vtk.org/t/the-vtk-voxelcontourstosurfacefilter-output-is-not-closed-surface/4258/1 "2020-09-21T03:36:59Z")

</div>

I used closed polygons as input polydata， but the result is not closed。  
Anyone can give me some advice？

How to make the output closed?

```
Mesh reconstruction(const Ring3dList &ring_list, double slice_spacing)
{

// ring3d: {plane, ring2d}; 
// pre-condition: slices in contour list are parallel and equal spacing
Point3dDouble normal_vector = ring_list.front().first.getNormalVector();
Point3dDouble plane_origin = ring_list[0].first.getOrigin();
arma::mat matrix = transformMatrix(plane_origin, normal_vector); // use first plane as the z=0 plane.

vtkSmartPointer<vtkAppendPolyData> appendFilter = vtkSmartPointer<vtkAppendPolyData>::New();
for (size_t i = 0; i < ring_list.size(); i++) {
    Points3dDoubleList pt_list;
    for (auto &point : ring_list[i].second.points) {
        Point3dDouble pt = ring_list[i].first.transfer2DTo3D(point);
        pt_list.push_back(transform(matrix, pt));
    }
    vtkSmartPointer<vtkPolyData> data = vtkSmartPointer<vtkPolyData>::New();
    vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
    vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
    points->SetNumberOfPoints(static_cast<long long>(pt_list.size()));
    cells->Allocate(1, points->GetNumberOfPoints());
    cells->InsertNextCell(static_cast<int>(points->GetNumberOfPoints()));
    for (size_t j = 0; j < pt_list.size(); j++) {
        Point3dDouble pt = pt_list[j];
        points->SetPoint(static_cast<int>(j), pt.x(), pt.y(), pt.z());
        cells->InsertCellPoint(static_cast<int>(j));
    }
    data->Initialize();
    data->SetPolys(cells);
    data->SetPoints(points);
    appendFilter->AddInputData(data);
}
appendFilter->Update();
double bounds[6];
vtkPolyData* contours = appendFilter->GetOutput();
contours->GetBounds( bounds );
double origin[3] = {bounds[0], bounds[2], bounds[4] };
double spacing[3] = {(bounds[1] - bounds[0]) / K_RING_RESOLUTION,
                     (bounds[3] - bounds[2]) / K_RING_RESOLUTION,
                     slice_spacing};

vtkSmartPointer<vtkPolyData> poly = vtkSmartPointer<vtkPolyData>::New();
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
vtkPoints* contourPoints = contours->GetPoints();
int numPoints = static_cast<int>(contourPoints->GetNumberOfPoints());
points->SetNumberOfPoints( numPoints );
for( int i = 0; i < numPoints; ++i )
{
    double pt[3];
    contourPoints->GetPoint( i, pt );
    pt[0] = static_cast<int>( (pt[0] - origin[0]) / spacing[0] + 0.5 );
    pt[1] = static_cast<int>( (pt[1] - origin[1]) / spacing[1] + 0.5 );
    pt[2] = static_cast<int>( (pt[2] - origin[2]) / spacing[2] + 0.5 );
    points->SetPoint( i, pt );
}
poly->SetPolys( contours->GetPolys() );
poly->SetPoints( points );
vtkSmartPointer<vtkVoxelContoursToSurfaceFilter> contoursToSurface =
        vtkSmartPointer<vtkVoxelContoursToSurfaceFilter>::New();
contoursToSurface->SetInputData( poly );
contoursToSurface->SetSpacing( spacing[0], spacing[1], spacing[2] );
contoursToSurface->Update();

double scaleCenter[3];
contoursToSurface->GetOutput()->GetCenter( scaleCenter );
double center[3];
contours->GetCenter(center);

vtkSmartPointer<vtkTransform> translate = vtkSmartPointer<vtkTransform>::New();
translate->Translate(-scaleCenter[0], -scaleCenter[1], -scaleCenter[2]);
translate->Translate(center[0], center[1], center[2]);
translate->Update();

vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter1 = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
transformFilter1->SetInputConnection(contoursToSurface->GetOutputPort());
transformFilter1->SetTransform(translate);
transformFilter1->Update();

vtkSmartPointer<vtkTransform> reverse = vtkSmartPointer<vtkTransform>::New();
arma::mat reverse_matrix = matrix.i();
double m[16];
for (size_t i = 0; i < 16; i++) {
    m[i] = reverse_matrix[(i % 4 ) * 4 + i / 4];
}
reverse->SetMatrix(m);
reverse->Update();

vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter2 = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
transformFilter2->SetInputConnection(transformFilter1->GetOutputPort());
transformFilter2->SetTransform(reverse);
transformFilter2->Update();

if (!isMeshDataValid(transformFilter2)) {
    DLOG("Reconstruction of ring list failed!");
    return Mesh();
}
MeshData mesh_data = cleanMeshData(transformFilter2);
MeshProp mesh_prop = getMeshDataProp(mesh_data);
DLOG("Reconstruction of ring list success!");
return Mesh{mesh_data, mesh_prop};

```

}

The result is

 ![1](https://discourse.vtk.org/uploads/default/original/2X/6/6798ff9977c0bb213fac6d1a32059355db833f0b.png)

Thanks !!!

---

<div class="post-metadata">

### Author: ![lassoan](https://discourse.vtk.org/user_avatar/discourse.vtk.org/lassoan/32/50_2.png) [@lassoan](https://discourse.vtk.org/u/lassoan)
#### Post date: [September 21, 2020, 3:40am UTC](https://discourse.vtk.org/t/the-vtk-voxelcontourstosurfacefilter-output-is-not-closed-surface/4258/2 "2020-09-21T03:40:57Z")

</div>

[vtkVoxelContoursToSurfaceFilter](https://vtk.org/doc/nightly/html/classvtkVoxelContoursToSurfaceFilter.html) has so many restrictions on the input contours that it is hard to find applications where it is usable in practice.

Would you like to use it to rasterize DICOM RT structure sets?

---

<div class="post-metadata">

### Author: ![guojunjie4SD](https://discourse.vtk.org/user_avatar/discourse.vtk.org/guojunjie4sd/32/2352_2.png) [@guojunjie4SD](https://discourse.vtk.org/u/guojunjie4SD)
#### Post date: [September 21, 2020, 3:52am UTC](https://discourse.vtk.org/t/the-vtk-voxelcontourstosurfacefilter-output-is-not-closed-surface/4258/3 "2020-09-21T03:52:44Z")

</div>

In my work, for example， I draw the outline of a tumor in different dicom images，then make them to be a 3d surface so that I can get the contour of tumor in any plane

If the outline has a sharp corner， the result will be not closed in high probability

---

<div class="post-metadata">

### Author: ![lassoan](https://discourse.vtk.org/user_avatar/discourse.vtk.org/lassoan/32/50_2.png) [@lassoan](https://discourse.vtk.org/u/lassoan)
#### Post date: [September 21, 2020, 4:01am UTC](https://discourse.vtk.org/t/the-vtk-voxelcontourstosurfacefilter-output-is-not-closed-surface/4258/4 "2020-09-21T04:01:39Z")

</div>

Reconstructing 3D shape from 2D intersections is a very common but challenging task. There is no general solution but there are lots of solutions for specific cases.

Do you segment the tumor on parallel slices only?  
Do you segment in every slice, regularly (in every n-th slice), or with random spacing between slices?  
Can you have branching between slices?  
Can your 3D shapes be concave (i.e., segmented slice contain holes)?

---

<div class="post-metadata">

### Author: ![guojunjie4SD](https://discourse.vtk.org/user_avatar/discourse.vtk.org/guojunjie4sd/32/2352_2.png) [@guojunjie4SD](https://discourse.vtk.org/u/guojunjie4SD)
#### Post date: [September 21, 2020, 4:06am UTC](https://discourse.vtk.org/t/the-vtk-voxelcontourstosurfacefilter-output-is-not-closed-surface/4258/5 "2020-09-21T04:06:28Z")

</div>

Segment on parallel slices only, with same spacing, in every slice, no holes, but the shape may be concave.

Is any way to remove the boundary of surface as i mention in the pic ? Or I can force the output to be closed?

---

<div class="post-metadata">

### Author: ![lassoan](https://discourse.vtk.org/user_avatar/discourse.vtk.org/lassoan/32/50_2.png) [@lassoan](https://discourse.vtk.org/u/lassoan)
#### Post date: [September 21, 2020, 4:17am UTC](https://discourse.vtk.org/t/the-vtk-voxelcontourstosurfacefilter-output-is-not-closed-surface/4258/6 "2020-09-21T04:17:07Z")

</div>

> [@guojunjie4SD](#):
>
> Segment on parallel slices only, with same spacing, in every slice, no holes, but the shape may be concave.

In this case, you can try [vtkRuledSurfaceFilter implementation](https://github.com/Kitware/VTK/blob/master/Filters/Modeling/vtkRuledSurfaceFilter.cxx) in VTK or 3D Slicer’s [planar contour to closed surface converter](https://github.com/SlicerRt/SlicerRT/blob/master/DicomRtImportExport/ConversionRules/vtkPlanarContourToClosedSurfaceConversionRule.cxx).

---

<div class="post-metadata">

### Author: ![guojunjie4SD](https://discourse.vtk.org/user_avatar/discourse.vtk.org/guojunjie4sd/32/2352_2.png) [@guojunjie4SD](https://discourse.vtk.org/u/guojunjie4SD)
#### Post date: [September 21, 2020, 6:40am UTC](https://discourse.vtk.org/t/the-vtk-voxelcontourstosurfacefilter-output-is-not-closed-surface/4258/7 "2020-09-21T06:40:26Z")

</div>

Sorry.  
Some situation have branching between slices. Some contain holes.  
Can 3D Slicer’s[planar contour to closed surface converter](https://github.com/SlicerRt/SlicerRT/blob/master/DicomRtImportExport/ConversionRules/vtkPlanarContourToClosedSurfaceConversionRule.cxx) can slove it?  
[vtkRuledSurfaceFilter implementation](https://github.com/Kitware/VTK/blob/master/Filters/Modeling/vtkRuledSurfaceFilter.cxx) seems not to handle branching.

---

<div class="post-metadata">

### Author: ![lassoan](https://discourse.vtk.org/user_avatar/discourse.vtk.org/lassoan/32/50_2.png) [@lassoan](https://discourse.vtk.org/u/lassoan)
#### Post date: [September 21, 2020, 7:12pm UTC](https://discourse.vtk.org/t/the-vtk-voxelcontourstosurfacefilter-output-is-not-closed-surface/4258/8 "2020-09-21T19:12:34Z")

</div>

> [@guojunjie4SD](#):
>
> Some situation have branching between slices. Some contain holes.  
> Can 3D Slicer’s[planar contour to closed surface converter](https://github.com/SlicerRt/SlicerRT/blob/master/DicomRtImportExport/ConversionRules/vtkPlanarContourToClosedSurfaceConversionRule.cxx) can slove it?

Yes, 3D Slicer’s converter handles both branching and holes.

Note that if you draw the contour based on images and you have segmentation for all the slices then it may be simpler to represent your segmentation as a 3D image, too. Then, generating a closed surface is trivial (using flying edges or marching cubes filter).
