VTK notes - use the vtkSplineFilter class to work with 3D space curves

Keywords: vtk

Note before VTK notes - graph correlation - segment smoothing - vtkSplineFilter class I learned that
vtkSplineFilter class is used to refine the input data of multiple line segments and output a set of points with more line segments;
vtkSplineFilter can be used not only for interpolation, but also for sampling;
The interpolation or sampling methods can be set through the setsubdividetospecificed() interface and SetSubdivideToLength() interface of vtkSplineFilter class;
The SetSubdivideToSpecified() interface sets the number of refinements. SetNumberOfSubdivisions is used to set the number of output segments;

spline->SetSubdivideToSpecified();
spline->SetNumberOfSubdivisions(3);	

The SetSubdivideToLength() interface sets the length of each line segment; Divide the total length of the curve by the length of each segment to obtain the final number of segments;

vtkNew<vtkSplineFilter> spline;
spline->SetInputDataObject(polyData);
spline->SetSpline(cardinal_spine);
spline->SetSubdivideToLength();
spline->SetLength(1);

Sample code

Use vtkParametricSpline class to interpolate four points into a curve;

double p0[3] = { 1.0, 0.0, 0.0 };
double p1[3] = { 0.0, 1.0, 0.0 };
double p2[3] = { 0.0, 0.0, 1.0 };
double p3[3] = { 1.0, 2.0, 3.0 };

vtkNew<vtkPoints> points;
points->InsertNextPoint(p0);
points->InsertNextPoint(p1);
points->InsertNextPoint(p2);
points->InsertNextPoint(p3);

vtkNew<vtkParametricSpline> spline;
spline->SetPoints(points);
	
vtkNew<vtkParametricFunctionSource> functionSource;
functionSource->SetParametricFunction(spline);
functionSource->Update();

vtkNew<vtkPolyDataMapper> splineMapper;
splineMapper->SetInputConnection(functionSource->GetOutputPort());

vtkNew<vtkActor> splineActor;
splineActor->SetMapper(splineMapper);

vtkNew<vtkPolyData> resultPolydata;
resultPolydata->SetPoints(points);

vtkNew<vtkVertexGlyphFilter> resultGlyphFilter;
resultGlyphFilter->AddInputData(resultPolydata);
resultGlyphFilter->Update();

vtkNew<vtkPolyDataMapper> resultMapper;
resultMapper->SetInputConnection(resultGlyphFilter->GetOutputPort());
vtkNew<vtkActor> resultActor;
resultActor->SetMapper(resultMapper);
resultActor->GetProperty()->SetPointSize(5);//Defines the size of the point so that it can be displayed on the canvas
resultActor->GetProperty()->SetColor(1, 0.0, 0.0);
	
vtkNew<vtkRenderer> ren1;
ren1->AddActor(splineActor);
ren1->AddActor(resultActor);
ren1->SetBackground(0.1, 0.2, 0.4);

vtkNew<vtkRenderWindow> renWin;
renWin->AddRenderer(ren1);
renWin->SetSize(300, 300);

vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin);
iren->Initialize();
iren->Start();


Or use vtkSplineFilter class to interpolate four points into a curve;

double p0[3] = { 1.0, 0.0, 0.0 };
double p1[3] = { 0.0, 1.0, 0.0 };
double p2[3] = { 0.0, 0.0, 1.0 };
double p3[3] = { 1.0, 2.0, 3.0 };

vtkNew<vtkPoints> points;
points->InsertNextPoint(p0);
points->InsertNextPoint(p1);
points->InsertNextPoint(p2);
points->InsertNextPoint(p3);

vtkNew<vtkCellArray> lines;
lines->InsertNextCell(4);
for (unsigned long i = 0; i < 4; ++i) {
	lines->InsertCellPoint(i);
}
vtkNew<vtkPolyData> polyData;
polyData->SetPoints(points);
polyData->SetLines(lines);

vtkNew<vtkSplineFilter> spline;
vtkNew<vtkCardinalSpline> cardinal_spine;

spline->SetInputDataObject(polyData);
spline->SetSpline(cardinal_spine);
spline->SetSubdivideToLength();
spline->SetLength(0.1);
spline->Update();

vtkNew<vtkPolyDataMapper> splineMapper;
splineMapper->SetInputConnection(spline->GetOutputPort());

vtkNew<vtkActor> splineActor;
splineActor->SetMapper(splineMapper);

vtkNew<vtkPolyData> resultPolydata;
resultPolydata->SetPoints(points);

vtkNew<vtkVertexGlyphFilter> resultGlyphFilter;
resultGlyphFilter->AddInputData(resultPolydata);
resultGlyphFilter->Update();

vtkNew<vtkPolyDataMapper> resultMapper;
resultMapper->SetInputConnection(resultGlyphFilter->GetOutputPort());
vtkNew<vtkActor> resultActor;
resultActor->SetMapper(resultMapper);
resultActor->GetProperty()->SetPointSize(5);//Defines the size of the point so that it can be displayed on the canvas
resultActor->GetProperty()->SetColor(1, 0.0, 0.0);
	
vtkNew<vtkRenderer> ren1;
ren1->AddActor(splineActor);
ren1->AddActor(resultActor);
ren1->SetBackground(0.1, 0.2, 0.4);

vtkNew<vtkRenderWindow> renWin;
renWin->AddRenderer(ren1);
renWin->SetSize(300, 300);

vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin);
iren->Initialize();
iren->Start();
return 0;


Results using SetLength(1):

Note: it can be seen from the above figure that the line segment extracted and interpolated by vtkSplineFilter does not necessarily pass through the position of the input point;
When the number of SetNumberOfSubdivisions is set to 3;

double p0[3] = { 1.0, 0.0, 0.0 };
double p1[3] = { 0.0, 1.0, 0.0 };
double p2[3] = { 0.0, 0.0, 1.0 };
double p3[3] = { 1.0, 2.0, 3.0 };

vtkNew<vtkPoints> points;
points->InsertNextPoint(p0);
points->InsertNextPoint(p1);
points->InsertNextPoint(p2);
points->InsertNextPoint(p3);
vtkNew<vtkCellArray> lines;
lines->InsertNextCell(4);
for (unsigned long i = 0; i < 4; ++i) {
	lines->InsertCellPoint(i);
}
vtkNew<vtkPolyData> polyData;
polyData->SetPoints(points);
polyData->SetLines(lines);

vtkNew<vtkSplineFilter> spline;
vtkNew<vtkCardinalSpline> cardinal_spine;

spline->SetInputDataObject(polyData);
spline->SetSpline(cardinal_spine);
spline->SetSubdivideToSpecified();
spline->SetNumberOfSubdivisions(3);	
spline->Update();

vtkNew<vtkPolyDataMapper> splineMapper;
splineMapper->SetInputConnection(spline->GetOutputPort());

vtkNew<vtkActor> splineActor;
splineActor->SetMapper(splineMapper);

vtkNew<vtkPolyData> resultPolydata;
resultPolydata->SetPoints(points);

vtkNew<vtkVertexGlyphFilter> resultGlyphFilter;
resultGlyphFilter->AddInputData(resultPolydata);
resultGlyphFilter->Update();

vtkNew<vtkPolyDataMapper> resultMapper;
resultMapper->SetInputConnection(resultGlyphFilter->GetOutputPort());
vtkNew<vtkActor> resultActor;
resultActor->SetMapper(resultMapper);
resultActor->GetProperty()->SetPointSize(5);//Defines the size of the point so that it can be displayed on the canvas
resultActor->GetProperty()->SetColor(1, 0.0, 0.0);

vtkNew<vtkRenderer> ren1;
ren1->AddActor(splineActor);
ren1->AddActor(resultActor);
ren1->SetBackground(0.1, 0.2, 0.4);

vtkNew<vtkRenderWindow> renWin;
renWin->AddRenderer(ren1);
renWin->SetSize(300, 300);

vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin);
iren->Initialize();
iren->Start();
return 0;


When the number of SetNumberOfSubdivisions is set to 30;

Further processing of curve data

If our input data is a curve with a lot of interference, there are more high-frequency signals on the curve; For example, the following figure,

We can first perform a point extraction operation on the data (in fact, what should be operated here is low-pass filtering), and then perform interpolation operation to obtain a slightly smooth curve;
The vtkSplineFilter class can implement such a simple operation;
When we move or modify the position of a point on a smooth curve, it may destroy the smoothness of the curve. You can use the vtkSplineFilter class to repair smoothing, but using the vtkSplineFilter class is not the best policy, and the filtering of three-dimensional curves should be a better choice.

Posted by Lagreca on Sat, 20 Nov 2021 18:00:21 -0800