在vtk中,读取ply或stl文件使用ICP进行点云配准时,需要注意不要直接使用vtkPolyData进行配准,这种方式会报错。
vtkPLYReader读取的文件可能仅包含点数据,而没有显式的顶点定义。例如,PLY文件可能只列出点的坐标,而没有指定每个点是一个顶点。这时候,vtkPolyData中的Cells部分是空的,导致ICP无法找到单元,进而报错。
vtkVertexGlyphFilter的作用是将点数据转换为顶点单元,每个点生成一个顶点单元。这样,vtkPolyData就包含了必要的单元信息,满足ICP算法的输入要求。因此,使用这个过滤器是必要的步骤。
可参照如下代码完成配准
#include <vtkActor.h>
#include <vtkPLYReader.h>
#include <vtkPolyDataMapper.h>
#include <vtkIterativeClosestPointTransform.h>
#include <vtkTransformPolyDataFilter.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include <vtkVertexGlyphFilter.h> // 关键修复:添加顶点单元int main() {// 读取源PLY文件vtkSmartPointer<vtkPLYReader> readerSource = vtkSmartPointer<vtkPLYReader>::New();readerSource->SetFileName("source.ply");readerSource->Update();// 读取目标PLY文件vtkSmartPointer<vtkPLYReader> readerTarget = vtkSmartPointer<vtkPLYReader>::New();readerTarget->SetFileName("target.ply");readerTarget->Update();// --- 关键修复:为点云添加顶点单元 ---vtkSmartPointer<vtkVertexGlyphFilter> glyphFilterSource = vtkSmartPointer<vtkVertexGlyphFilter>::New();glyphFilterSource->SetInputConnection(readerSource->GetOutputPort());glyphFilterSource->Update();vtkSmartPointer<vtkVertexGlyphFilter> glyphFilterTarget = vtkSmartPointer<vtkVertexGlyphFilter>::New();glyphFilterTarget->SetInputConnection(readerTarget->GetOutputPort());glyphFilterTarget->Update();// 获取带顶点单元的点云数据vtkPolyData* source = glyphFilterSource->GetOutput();vtkPolyData* target = glyphFilterTarget->GetOutput();// 创建ICP配准器vtkSmartPointer<vtkIterativeClosestPointTransform> icp = vtkSmartPointer<vtkIterativeClosestPointTransform>::New();icp->SetSource(source);icp->SetTarget(target);icp->GetLandmarkTransform()->SetModeToRigidBody();icp->SetMaximumNumberOfIterations(100);icp->StartByMatchingCentroidsOn();icp->Modified();icp->Update();// ...(后续可视化代码与之前相同)
}
关键修改说明
添加 vtkVertexGlyphFilter:
vtkVertexGlyphFilter 会将输入的点坐标转换为顶点单元(每个点对应一个VTK_VERTEX单元)。
必须对 源点云 和 目标点云 都进行此操作,确保输入到ICP算法的数据包含顶点信息。
验证数据有效性:
在调用Update()后,可以通过以下代码检查数据是否包含顶点单元:
if (source->GetNumberOfCells() == 0 || target->GetNumberOfCells() == 0) {std::cerr << "错误:点云数据未正确添加顶点单元!" << std::endl;return EXIT_FAILURE;
}