VTK - 在不阻塞交互的情况下更新 renderWindow 中的 vtkPolyData
VTK - update vtkPolyData in renderWindow without blocking interaction
我用VTK显示点云,循环更新。
windows第一次可以鼠标交互我显示云,如果要更新云,我必须按'q'和Window 将显示更新的云。然而,虽然云已更新并正确显示在 window 中,但我失去了对 window 的控制(即无法使用鼠标移动或旋转 window 中的点云)。
以下是我重现问题的最少代码。
我有一个可视化 class 来处理这个,它的 初始化列表 :
points(vtkSmartPointer<vtkPoints>::New())
, vertices(vtkSmartPointer<vtkCellArray>::New())
, pointsPolyData(vtkSmartPointer<vtkPolyData>::New())
, mapper(vtkSmartPointer<vtkPolyDataMapper>::New())
, actor(vtkSmartPointer<vtkActor>::New())
, renderer(vtkSmartPointer<vtkRenderer>::New())
, renderWindow(vtkSmartPointer<vtkRenderWindow>::New())
, renderWindowInteractor(vtkSmartPointer<vtkRenderWindowInteractor>::New())
这是 init 函数:
void Visualization::init(const cv::Mat &point_cloud){
noPoints = point_cloud.cols * point_cloud.rows;
// Preallocate memory
points->SetNumberOfPoints(noPoints);
// Copy
int element_stripe = sizeof(float) * point_cloud.channels();
for (unsigned i = 0; i < noPoints; i++)
{
float x = point_cloud.at<cv::Vec3f>(i)[0];
float y = point_cloud.at<cv::Vec3f>(i)[1];
float z = point_cloud.at<cv::Vec3f>(i)[2];
vtkIdType pid[1] = {i};
points->SetPoint(i, x, y, z);
vertices->InsertNextCell(1, pid);
}
// Push data to polydata
pointsPolyData->SetPoints(points);
pointsPolyData->SetVerts(vertices);
// Set visualization pipeline
mapper->SetInputData(pointsPolyData);
actor->SetMapper(mapper);
actor->GetProperty()->SetPointSize(2);
renderWindow->AddRenderer(renderer);
renderer->SetBackground(.3,.6,.3);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderer->AddActor(actor);
isPipelineInit = true;
renderWindow->Render();
renderWindowInteractor->Start();
}
这里是显示函数:
void Visualization::display(const cv::Mat &point_cloud){
if(!isPipelineInit)
init(point_cloud);
else
{
// Copy
int element_stripe = sizeof(float) * point_cloud.channels();
for (unsigned i = 0; i < noPoints; i++)
{
points->SetPoint(i, (float*)(point_cloud.data + i*element_stripe));
}
pointsPolyData->Modified();
mapper->Update();
renderWindowInteractor->GetRenderWindow()->Render();
}
}
我还有一个函数运行线程中的无限循环:
void Visualization::run()
{
while (1) // infinite while loop
{
// Update m_clouds and display
display(m_clouds);
}
}
更新:
正如 mirni 提醒我 'q' 键本质上退出当前渲染 Window,我后来看到的实际上是来自 display
函数而不是 init
。在 display
中,我没有调用 renderWindowInteractor->Start()
,因此我无法与 window 进行交互。但是,renderWindowInteractor->Start()
卡住了我当前的线程,因此我无法继续我的程序并更新我的 vtkPolyData。
我想问题会变成:
如何同时显示和更新?
我应该在不同的线程中进行,一个用于显示,另一个用于更新吗?
谢谢!
您问题的答案是:您只需更新数据并调用 vtkRenderWindow::Render()
方法即可。 VTK 渲染管道自行发现数据已更改,需要更新。
您的代码确实需要重新设计——在数据更改时(仅一次)更新可视化更有意义,而不是继续探测数据 "has anything changed?"(每毫秒)。
此外,通常您不需要单独的线程用于 VTK 可视化管道。保持简单:
- 不要使可视化对象成为线程。 (如果你有 100 个对象怎么办?你会为每个对象创建 100 个单独的线程吗?)。
- 摆脱无限循环。
- 将
display(...)
方法重命名为
void Visualization::update(const cv::Mat &point_cloud);
使其成为 public 并在数据以适合您的场景的任何方式发生变化时从外部调用它(回调?Qt 信号?消息?...)。确保在最后保留 vtkRenderWindow::Render() 调用。
您可能需要考虑直接从 cv::Mat 结构复制数据,使用指向由 vtkPoints::GetVoidPointer(int)
公开的点数据的原始指针并执行 memcopy 而不是 for循环,但在您准备就绪并且您的测量显示需要优化之前不要这样做。 (过早的优化是根源...)
vtkRenderWindowInteractor::Start()
启动事件循环并在您发现时劫持线程。而不是 Start()
只需调用 Initialize()
来启用交互器但让控制流继续。
HTH,
米罗
我用VTK显示点云,循环更新。 windows第一次可以鼠标交互我显示云,如果要更新云,我必须按'q'和Window 将显示更新的云。然而,虽然云已更新并正确显示在 window 中,但我失去了对 window 的控制(即无法使用鼠标移动或旋转 window 中的点云)。
以下是我重现问题的最少代码。 我有一个可视化 class 来处理这个,它的 初始化列表 :
points(vtkSmartPointer<vtkPoints>::New())
, vertices(vtkSmartPointer<vtkCellArray>::New())
, pointsPolyData(vtkSmartPointer<vtkPolyData>::New())
, mapper(vtkSmartPointer<vtkPolyDataMapper>::New())
, actor(vtkSmartPointer<vtkActor>::New())
, renderer(vtkSmartPointer<vtkRenderer>::New())
, renderWindow(vtkSmartPointer<vtkRenderWindow>::New())
, renderWindowInteractor(vtkSmartPointer<vtkRenderWindowInteractor>::New())
这是 init 函数:
void Visualization::init(const cv::Mat &point_cloud){
noPoints = point_cloud.cols * point_cloud.rows;
// Preallocate memory
points->SetNumberOfPoints(noPoints);
// Copy
int element_stripe = sizeof(float) * point_cloud.channels();
for (unsigned i = 0; i < noPoints; i++)
{
float x = point_cloud.at<cv::Vec3f>(i)[0];
float y = point_cloud.at<cv::Vec3f>(i)[1];
float z = point_cloud.at<cv::Vec3f>(i)[2];
vtkIdType pid[1] = {i};
points->SetPoint(i, x, y, z);
vertices->InsertNextCell(1, pid);
}
// Push data to polydata
pointsPolyData->SetPoints(points);
pointsPolyData->SetVerts(vertices);
// Set visualization pipeline
mapper->SetInputData(pointsPolyData);
actor->SetMapper(mapper);
actor->GetProperty()->SetPointSize(2);
renderWindow->AddRenderer(renderer);
renderer->SetBackground(.3,.6,.3);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderer->AddActor(actor);
isPipelineInit = true;
renderWindow->Render();
renderWindowInteractor->Start();
}
这里是显示函数:
void Visualization::display(const cv::Mat &point_cloud){
if(!isPipelineInit)
init(point_cloud);
else
{
// Copy
int element_stripe = sizeof(float) * point_cloud.channels();
for (unsigned i = 0; i < noPoints; i++)
{
points->SetPoint(i, (float*)(point_cloud.data + i*element_stripe));
}
pointsPolyData->Modified();
mapper->Update();
renderWindowInteractor->GetRenderWindow()->Render();
}
}
我还有一个函数运行线程中的无限循环:
void Visualization::run()
{
while (1) // infinite while loop
{
// Update m_clouds and display
display(m_clouds);
}
}
更新:
正如 mirni 提醒我 'q' 键本质上退出当前渲染 Window,我后来看到的实际上是来自 display
函数而不是 init
。在 display
中,我没有调用 renderWindowInteractor->Start()
,因此我无法与 window 进行交互。但是,renderWindowInteractor->Start()
卡住了我当前的线程,因此我无法继续我的程序并更新我的 vtkPolyData。
我想问题会变成: 如何同时显示和更新? 我应该在不同的线程中进行,一个用于显示,另一个用于更新吗?
谢谢!
您问题的答案是:您只需更新数据并调用 vtkRenderWindow::Render()
方法即可。 VTK 渲染管道自行发现数据已更改,需要更新。
您的代码确实需要重新设计——在数据更改时(仅一次)更新可视化更有意义,而不是继续探测数据 "has anything changed?"(每毫秒)。
此外,通常您不需要单独的线程用于 VTK 可视化管道。保持简单:
- 不要使可视化对象成为线程。 (如果你有 100 个对象怎么办?你会为每个对象创建 100 个单独的线程吗?)。
- 摆脱无限循环。
- 将
display(...)
方法重命名为
void Visualization::update(const cv::Mat &point_cloud);
使其成为 public 并在数据以适合您的场景的任何方式发生变化时从外部调用它(回调?Qt 信号?消息?...)。确保在最后保留 vtkRenderWindow::Render() 调用。
您可能需要考虑直接从 cv::Mat 结构复制数据,使用指向由
vtkPoints::GetVoidPointer(int)
公开的点数据的原始指针并执行 memcopy 而不是 for循环,但在您准备就绪并且您的测量显示需要优化之前不要这样做。 (过早的优化是根源...)vtkRenderWindowInteractor::Start()
启动事件循环并在您发现时劫持线程。而不是Start()
只需调用Initialize()
来启用交互器但让控制流继续。
HTH, 米罗