VTK 顯示大量點云數據及交互(點云拾取、著色、測量等)功能

????????VTK (Visualization Toolkit) 是一個強大的開源可視化庫,非常適合處理點云數據。下面將介紹如何使用 VTK 顯示大量點云數據,并實現點云拾取、著色、測量等功能。

基本點云顯示

創建一個基本的點云顯示程序:

cpp

#include <vtkSmartPointer.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkVertexGlyphFilter.h>int main() {// 創建點云數據vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();// 添加大量點(這里示例添加100萬個隨機點)for (int i = 0; i < 1000000; ++i) {points->InsertNextPoint((double)rand() / RAND_MAX * 100.0,(double)rand() / RAND_MAX * 100.0,(double)rand() / RAND_MAX * 100.0);}// 創建PolyData對象并設置點vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();polydata->SetPoints(points);// 使用VertexGlyphFilter將點轉換為可渲染的頂點vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();glyphFilter->SetInputData(polydata);glyphFilter->Update();// 創建mapper和actorvtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();mapper->SetInputConnection(glyphFilter->GetOutputPort());vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();actor->SetMapper(mapper);actor->GetProperty()->SetPointSize(2); // 設置點大小// 創建渲染器、渲染窗口和交互器vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();renderWindow->AddRenderer(renderer);vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();renderWindowInteractor->SetRenderWindow(renderWindow);// 添加actor到渲染器renderer->AddActor(actor);renderer->SetBackground(0.1, 0.2, 0.4); // 設置背景顏色// 開始渲染renderWindow->Render();renderWindowInteractor->Start();return 0;
}

點云拾取功能

要實現點云拾取,可以使用 VTK 的點拾取器 (vtkPointPicker):

cpp

#include <vtkPointPicker.h>// 在main函數中創建交互器后添加以下代碼// 創建點拾取器
vtkSmartPointer<vtkPointPicker> pointPicker = vtkSmartPointer<vtkPointPicker>::New();
renderWindowInteractor->SetPicker(pointPicker);// 添加觀察者處理拾取事件
vtkSmartPointer<vtkCallbackCommand> pickCallback = vtkSmartPointer<vtkCallbackCommand>::New();
pickCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);vtkPointPicker* picker = static_cast<vtkPointPicker*>(interactor->GetPicker());int x = interactor->GetEventPosition()[0];int y = interactor->GetEventPosition()[1];picker->Pick(x, y, 0, interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer());vtkIdType pointId = picker->GetPointId();if (pointId != -1) {double* pos = picker->GetPickPosition();std::cout << "Picked point ID: " << pointId << std::endl;std::cout << "Position: (" << pos[0] << ", " << pos[1] << ", " << pos[2] << ")" << std::endl;}
});renderWindowInteractor->AddObserver(vtkCommand::LeftButtonPressEvent, pickCallback);

點云著色功能

可以為點云添加顏色屬性,實現著色效果:

cpp

#include <vtkUnsignedCharArray.h>// 在創建點數據后添加顏色數組
vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
colors->SetNumberOfComponents(3); // RGB
colors->SetName("Colors");// 為每個點添加隨機顏色
for (int i = 0; i < points->GetNumberOfPoints(); ++i) {unsigned char color[3] = {static_cast<unsigned char>(rand() % 256),static_cast<unsigned char>(rand() % 256),static_cast<unsigned char>(rand() % 256)};colors->InsertNextTypedTuple(color);
}polydata->GetPointData()->SetScalars(colors);// 然后修改mapper以使用這些顏色
mapper->SetScalarVisibility(1); // 啟用標量顏色
mapper->SetScalarModeToUsePointData(); // 使用點數據中的標量

點云測量功能

1. 點對點距離測量

首先實現最基本的點對點距離測量功能:

#include <vtkDistanceWidget.h>
#include <vtkDistanceRepresentation3D.h>
#include <vtkSphereSource.h>// 在main函數中創建交互器后添加以下代碼// 創建距離測量工具
vtkSmartPointer<vtkDistanceWidget> distanceWidget = vtkSmartPointer<vtkDistanceWidget>::New();
distanceWidget->SetInteractor(renderWindowInteractor);
distanceWidget->CreateDefaultRepresentation();// 設置距離表示的樣式
vtkDistanceRepresentation3D* distanceRep = vtkDistanceRepresentation3D::SafeDownCast(distanceWidget->GetRepresentation());
distanceRep->SetLabelFormat("%-#6.3g mm"); // 設置顯示格式
distanceRep->GetAxisProperty()->SetColor(1, 0, 0); // 設置軸線顏色
distanceRep->GetAxisProperty()->SetLineWidth(2); // 設置線寬// 添加標記球體
vtkSmartPointer<vtkSphereSource> sphere1 = vtkSmartPointer<vtkSphereSource>::New();
sphere1->SetRadius(1.0);
vtkSmartPointer<vtkSphereSource> sphere2 = vtkSmartPointer<vtkSphereSource>::New();
sphere2->SetRadius(1.0);distanceRep->SetPoint1Representation(sphere1->GetOutput());
distanceRep->SetPoint2Representation(sphere2->GetOutput());// 啟用距離測量工具
distanceWidget->On();

2. 多點距離測量(折線測量)

如果需要測量多點間的累計距離,可以實現折線測量:

#include <vtkContourWidget.h>
#include <vtkOrientedGlyphContourRepresentation.h>
#include <vtkPolygonalSurfacePointPlacer.h>// 創建折線測量工具
vtkSmartPointer<vtkContourWidget> contourWidget = vtkSmartPointer<vtkContourWidget>::New();
contourWidget->SetInteractor(renderWindowInteractor);
contourWidget->ContinuousDrawOn(); // 啟用連續繪制// 設置表示方式
vtkOrientedGlyphContourRepresentation* contourRep = vtkOrientedGlyphContourRepresentation::SafeDownCast(contourWidget->GetRepresentation());
contourRep->GetLinesProperty()->SetColor(0, 1, 0); // 設置線顏色
contourRep->GetLinesProperty()->SetLineWidth(2); // 設置線寬// 設置點放置器,使點吸附到點云上
vtkSmartPointer<vtkPolygonalSurfacePointPlacer> pointPlacer = vtkSmartPointer<vtkPolygonalSurfacePointPlacer>::New();
pointPlacer->AddProp(actor);
pointPlacer->GetPolys()->AddItem(polydata);
contourRep->SetPointPlacer(pointPlacer);// 添加回調函數計算總長度
vtkSmartPointer<vtkCallbackCommand> contourCallback = vtkSmartPointer<vtkCallbackCommand>::New();
contourCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {vtkContourWidget* widget = static_cast<vtkContourWidget*>(caller);vtkContourRepresentation* rep = widget->GetContourRepresentation();int numPoints = rep->GetNumberOfNodes();if (numPoints > 1) {double totalLength = 0.0;double pos1[3], pos2[3];rep->GetNthNodeWorldPosition(0, pos1);for (int i = 1; i < numPoints; ++i) {rep->GetNthNodeWorldPosition(i, pos2);totalLength += sqrt(vtkMath::Distance2BetweenPoints(pos1, pos2));memcpy(pos1, pos2, 3 * sizeof(double));}std::cout << "Total length: " << totalLength << " mm" << std::endl;}
});contourWidget->AddObserver(vtkCommand::InteractionEvent, contourCallback);
contourWidget->On();

3. 面積測量(多邊形區域)

對于閉合區域的面積測量:

#include <vtkPolygon.h>
#include <vtkCellArray.h>// 修改contourWidget的回調函數以支持面積計算
contourCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {vtkContourWidget* widget = static_cast<vtkContourWidget*>(caller);vtkContourRepresentation* rep = widget->GetContourRepresentation();int numPoints = rep->GetNumberOfNodes();if (numPoints > 2) {// 計算周長double totalLength = 0.0;double pos1[3], pos2[3];rep->GetNthNodeWorldPosition(0, pos1);for (int i = 1; i < numPoints; ++i) {rep->GetNthNodeWorldPosition(i, pos2);totalLength += sqrt(vtkMath::Distance2BetweenPoints(pos1, pos2));memcpy(pos1, pos2, 3 * sizeof(double));}// 閉合路徑rep->GetNthNodeWorldPosition(0, pos2);totalLength += sqrt(vtkMath::Distance2BetweenPoints(pos1, pos2));// 計算面積vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();for (int i = 0; i < numPoints; ++i) {rep->GetNthNodeWorldPosition(i, pos1);points->InsertNextPoint(pos1);}vtkSmartPointer<vtkPolygon> polygon = vtkSmartPointer<vtkPolygon>::New();polygon->Initialize(numPoints, points->GetPoints(), points->GetBounds());double area = polygon->ComputeArea();std::cout << "Perimeter: " << totalLength << " mm" << std::endl;std::cout << "Area: " << area << " mm2" << std::endl;}
});

4. 點云高度差測量

對于三維點云,可以測量高度差:

// 在點拾取回調中添加高度差計算
pickCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {static double lastPos[3] = {0, 0, 0};static bool firstPick = true;vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);vtkPointPicker* picker = static_cast<vtkPointPicker*>(interactor->GetPicker());int x = interactor->GetEventPosition()[0];int y = interactor->GetEventPosition()[1];picker->Pick(x, y, 0, interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer());vtkIdType pointId = picker->GetPointId();if (pointId != -1) {double* pos = picker->GetPickPosition();std::cout << "Picked point ID: " << pointId << std::endl;std::cout << "Position: (" << pos[0] << ", " << pos[1] << ", " << pos[2] << ")" << std::endl;if (!firstPick) {double distance = sqrt(vtkMath::Distance2BetweenPoints(pos, lastPos));double heightDiff = fabs(pos[2] - lastPos[2]);std::cout << "Distance from last point: " << distance << " mm" << std::endl;std::cout << "Height difference: " << heightDiff << " mm" << std::endl;}memcpy(lastPos, pos, 3 * sizeof(double));firstPick = false;}
});

5. 測量結果顯示在3D視圖

將測量結果直接顯示在3D視圖上:

#include <vtkTextActor.h>
#include <vtkTextProperty.h>// 創建文本actor用于顯示測量結果
vtkSmartPointer<vtkTextActor> textActor = vtkSmartPointer<vtkTextActor>::New();
textActor->SetPosition(10, 10);
textActor->GetTextProperty()->SetFontSize(18);
textActor->GetTextProperty()->SetColor(1.0, 1.0, 1.0);
renderer->AddActor2D(textActor);// 修改距離測量的回調函數
distanceWidget->AddObserver(vtkCommand::InteractionEvent, [textActor](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {vtkDistanceWidget* widget = static_cast<vtkDistanceWidget*>(caller);vtkDistanceRepresentation3D* rep = vtkDistanceRepresentation3D::SafeDownCast(widget->GetRepresentation());double distance = rep->GetDistance();std::stringstream ss;ss << "Distance: " << std::fixed << std::setprecision(2) << distance << " mm";textActor->SetInput(ss.str().c_str());
});

6. 測量工具切換

添加鍵盤控制來切換不同的測量工具:

// 添加鍵盤回調
vtkSmartPointer<vtkCallbackCommand> keyCallback = vtkSmartPointer<vtkCallbackCommand>::New();
keyCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);std::string key = interactor->GetKeySym();static vtkDistanceWidget* distanceWidget = static_cast<vtkDistanceWidget*>(clientData);static vtkContourWidget* contourWidget = static_cast<vtkContourWidget*>(clientData);if (key == "d") {// 切換距離測量distanceWidget->SetEnabled(!distanceWidget->GetEnabled());contourWidget->SetEnabled(false);} else if (key == "c") {// 切換輪廓測量contourWidget->SetEnabled(!contourWidget->GetEnabled());distanceWidget->SetEnabled(false);} else if (key == "a") {// 切換面積測量bool enabled = contourWidget->GetEnabled();contourWidget->SetEnabled(!enabled);distanceWidget->SetEnabled(false);if (enabled) {contourWidget->CloseLoop();}}
});renderWindowInteractor->AddObserver(vtkCommand::KeyPressEvent, keyCallback);

性能優化

對于大量點云數據,可以考慮以下優化措施:

1、使用vtkOctreePointLocator?加速點查找

#include <vtkOctreePointLocator.h>vtkSmartPointer<vtkOctreePointLocator> pointLocator = vtkSmartPointer<vtkOctreePointLocator>::New();
pointLocator->SetDataSet(polydata);
pointLocator->BuildLocator();// 拾取時使用locator查找最近點
double searchPoint[3] = {x, y, z};
vtkIdType pointId = pointLocator->FindClosestPoint(searchPoint);

2、使用點云簡化?減少顯示點數

#include <vtkQuadricClustering.h>vtkSmartPointer<vtkQuadricClustering> decimate = vtkSmartPointer<vtkQuadricClustering>::New();
decimate->SetInputData(polydata);
decimate->SetNumberOfDivisions(50, 50, 50); // 調整簡化程度
decimate->Update();

3、使用GPU加速

#include <vtkGPUInfo.h>
#include <vtkGPUInfoList.h>// 檢查GPU信息
vtkSmartPointer<vtkGPUInfoList> infoList = vtkSmartPointer<vtkGPUInfoList>::New();
infoList->Probe();if (infoList->GetNumberOfGPUs() > 0) {mapper->SetUseHardwareShading(true);
}

完整示例

完整示例1(點云顯示+點云拾取+點云著色)

#include <vtkSmartPointer.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkVertexGlyphFilter.h>
#include <vtkUnsignedCharArray.h>
#include <vtkPointPicker.h>
#include <vtkCallbackCommand.h>
#include <vtkOctreePointLocator.h>
#include <iostream>int main() {// 創建點云數據vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();// 添加10萬個隨機點for (int i = 0; i < 100000; ++i) {points->InsertNextPoint((double)rand() / RAND_MAX * 100.0,(double)rand() / RAND_MAX * 100.0,(double)rand() / RAND_MAX * 100.0);}// 創建顏色數組vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();colors->SetNumberOfComponents(3);colors->SetName("Colors");for (int i = 0; i < points->GetNumberOfPoints(); ++i) {unsigned char color[3] = {static_cast<unsigned char>(rand() % 256),static_cast<unsigned char>(rand() % 256),static_cast<unsigned char>(rand() % 256)};colors->InsertNextTypedTuple(color);}// 創建PolyData對象vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();polydata->SetPoints(points);polydata->GetPointData()->SetScalars(colors);// 創建點定位器(用于加速拾取)vtkSmartPointer<vtkOctreePointLocator> pointLocator = vtkSmartPointer<vtkOctreePointLocator>::New();pointLocator->SetDataSet(polydata);pointLocator->BuildLocator();// 將點轉換為可渲染的頂點vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();glyphFilter->SetInputData(polydata);glyphFilter->Update();// 創建mapper和actorvtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();mapper->SetInputConnection(glyphFilter->GetOutputPort());mapper->SetScalarVisibility(1);mapper->SetScalarModeToUsePointData();vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();actor->SetMapper(mapper);actor->GetProperty()->SetPointSize(3);// 創建渲染器和窗口vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();renderWindow->AddRenderer(renderer);renderWindow->SetSize(800, 600);vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();renderWindowInteractor->SetRenderWindow(renderWindow);// 設置點拾取器vtkSmartPointer<vtkPointPicker> pointPicker = vtkSmartPointer<vtkPointPicker>::New();renderWindowInteractor->SetPicker(pointPicker);// 添加拾取回調vtkSmartPointer<vtkCallbackCommand> pickCallback = vtkSmartPointer<vtkCallbackCommand>::New();pickCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);int x = interactor->GetEventPosition()[0];int y = interactor->GetEventPosition()[1];interactor->GetPicker()->Pick(x, y, 0, interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer());vtkPointPicker* picker = static_cast<vtkPointPicker*>(interactor->GetPicker());vtkIdType pointId = picker->GetPointId();if (pointId != -1) {double* pos = picker->GetPickPosition();std::cout << "Picked point ID: " << pointId << std::endl;std::cout << "Position: (" << pos[0] << ", " << pos[1] << ", " << pos[2] << ")" << std::endl;// 高亮顯示選中的點(例如改變顏色)// 這里可以添加代碼修改選中點的顏色}});renderWindowInteractor->AddObserver(vtkCommand::LeftButtonPressEvent, pickCallback);// 添加actor到渲染器renderer->AddActor(actor);renderer->SetBackground(0.1, 0.2, 0.4);// 開始渲染renderWindow->Render();renderWindowInteractor->Start();return 0;
}

完整示例2(點云測量)

#include <vtkSmartPointer.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkVertexGlyphFilter.h>
#include <vtkUnsignedCharArray.h>
#include <vtkPointPicker.h>
#include <vtkCallbackCommand.h>
#include <vtkDistanceWidget.h>
#include <vtkDistanceRepresentation3D.h>
#include <vtkSphereSource.h>
#include <vtkContourWidget.h>
#include <vtkOrientedGlyphContourRepresentation.h>
#include <vtkPolygonalSurfacePointPlacer.h>
#include <vtkTextActor.h>
#include <vtkTextProperty.h>
#include <vtkMath.h>
#include <iostream>
#include <sstream>
#include <iomanip>int main() {// 創建點云數據vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();// 添加隨機點for (int i = 0; i < 50000; ++i) {points->InsertNextPoint((double)rand() / RAND_MAX * 100.0,(double)rand() / RAND_MAX * 100.0,(double)rand() / RAND_MAX * 20.0); // Z軸范圍小一些,便于觀察高度差}// 創建顏色數組vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();colors->SetNumberOfComponents(3);colors->SetName("Colors");for (int i = 0; i < points->GetNumberOfPoints(); ++i) {unsigned char color[3] = {static_cast<unsigned char>(rand() % 256),static_cast<unsigned char>(rand() % 256),static_cast<unsigned char>(rand() % 256)};colors->InsertNextTypedTuple(color);}// 創建PolyData對象vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();polydata->SetPoints(points);polydata->GetPointData()->SetScalars(colors);// 將點轉換為可渲染的頂點vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();glyphFilter->SetInputData(polydata);glyphFilter->Update();// 創建mapper和actorvtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();mapper->SetInputConnection(glyphFilter->GetOutputPort());mapper->SetScalarVisibility(1);mapper->SetScalarModeToUsePointData();vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();actor->SetMapper(mapper);actor->GetProperty()->SetPointSize(3);// 創建渲染器和窗口vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();renderWindow->AddRenderer(renderer);renderWindow->SetSize(800, 600);vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();renderWindowInteractor->SetRenderWindow(renderWindow);// 設置點拾取器vtkSmartPointer<vtkPointPicker> pointPicker = vtkSmartPointer<vtkPointPicker>::New();renderWindowInteractor->SetPicker(pointPicker);// 創建文本actor用于顯示測量結果vtkSmartPointer<vtkTextActor> textActor = vtkSmartPointer<vtkTextActor>::New();textActor->SetPosition(10, 10);textActor->GetTextProperty()->SetFontSize(18);textActor->GetTextProperty()->SetColor(1.0, 1.0, 1.0);renderer->AddActor2D(textActor);// 創建距離測量工具vtkSmartPointer<vtkDistanceWidget> distanceWidget = vtkSmartPointer<vtkDistanceWidget>::New();distanceWidget->SetInteractor(renderWindowInteractor);distanceWidget->CreateDefaultRepresentation();vtkDistanceRepresentation3D* distanceRep = vtkDistanceRepresentation3D::SafeDownCast(distanceWidget->GetRepresentation());distanceRep->SetLabelFormat("%-#6.3g mm");distanceRep->GetAxisProperty()->SetColor(1, 0, 0);distanceRep->GetAxisProperty()->SetLineWidth(2);vtkSmartPointer<vtkSphereSource> sphere1 = vtkSmartPointer<vtkSphereSource>::New();sphere1->SetRadius(1.0);vtkSmartPointer<vtkSphereSource> sphere2 = vtkSmartPointer<vtkSphereSource>::New();sphere2->SetRadius(1.0);distanceRep->SetPoint1Representation(sphere1->GetOutput());distanceRep->SetPoint2Representation(sphere2->GetOutput());distanceWidget->AddObserver(vtkCommand::InteractionEvent, [textActor](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {vtkDistanceWidget* widget = static_cast<vtkDistanceWidget*>(caller);vtkDistanceRepresentation3D* rep = vtkDistanceRepresentation3D::SafeDownCast(widget->GetRepresentation());double distance = rep->GetDistance();std::stringstream ss;ss << "Distance: " << std::fixed << std::setprecision(2) << distance << " mm";textActor->SetInput(ss.str().c_str());});// 創建輪廓測量工具vtkSmartPointer<vtkContourWidget> contourWidget = vtkSmartPointer<vtkContourWidget>::New();contourWidget->SetInteractor(renderWindowInteractor);contourWidget->ContinuousDrawOn();vtkOrientedGlyphContourRepresentation* contourRep = vtkOrientedGlyphContourRepresentation::SafeDownCast(contourWidget->GetRepresentation());contourRep->GetLinesProperty()->SetColor(0, 1, 0);contourRep->GetLinesProperty()->SetLineWidth(2);vtkSmartPointer<vtkPolygonalSurfacePointPlacer> pointPlacer = vtkSmartPointer<vtkPolygonalSurfacePointPlacer>::New();pointPlacer->AddProp(actor);pointPlacer->GetPolys()->AddItem(polydata);contourRep->SetPointPlacer(pointPlacer);contourWidget->AddObserver(vtkCommand::InteractionEvent, [textActor](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {vtkContourWidget* widget = static_cast<vtkContourWidget*>(caller);vtkContourRepresentation* rep = widget->GetContourRepresentation();int numPoints = rep->GetNumberOfNodes();if (numPoints > 1) {// 計算周長double totalLength = 0.0;double pos1[3], pos2[3];rep->GetNthNodeWorldPosition(0, pos1);for (int i = 1; i < numPoints; ++i) {rep->GetNthNodeWorldPosition(i, pos2);totalLength += sqrt(vtkMath::Distance2BetweenPoints(pos1, pos2));memcpy(pos1, pos2, 3 * sizeof(double));}std::stringstream ss;ss << "Perimeter: " << std::fixed << std::setprecision(2) << totalLength << " mm";// 如果閉合,計算面積if (numPoints > 2 && rep->IsClosed()) {vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();for (int i = 0; i < numPoints; ++i) {rep->GetNthNodeWorldPosition(i, pos1);points->InsertNextPoint(pos1);}vtkSmartPointer<vtkPolygon> polygon = vtkSmartPointer<vtkPolygon>::New();polygon->Initialize(numPoints, points->GetPoints(), points->GetBounds());double area = polygon->ComputeArea();ss << "\nArea: " << std::fixed << std::setprecision(2) << area << " mm2";}textActor->SetInput(ss.str().c_str());}});// 添加鍵盤控制vtkSmartPointer<vtkCallbackCommand> keyCallback = vtkSmartPointer<vtkCallbackCommand>::New();keyCallback->SetCallback([distanceWidget, contourWidget, textActor](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);std::string key = interactor->GetKeySym();if (key == "d" || key == "D") {// 切換距離測量distanceWidget->SetEnabled(!distanceWidget->GetEnabled());contourWidget->SetEnabled(false);textActor->SetInput("Distance measurement mode (click two points)");} else if (key == "c" || key == "C") {// 切換輪廓測量contourWidget->SetEnabled(!contourWidget->GetEnabled());distanceWidget->SetEnabled(false);textActor->SetInput("Contour measurement mode (click multiple points)");} else if (key == "a" || key == "A") {// 切換面積測量bool enabled = !contourWidget->GetEnabled();contourWidget->SetEnabled(enabled);distanceWidget->SetEnabled(false);if (enabled) {textActor->SetInput("Area measurement mode (click points to close loop)");contourWidget->CloseLoop();}} else if (key == "Escape") {// 退出所有測量模式distanceWidget->SetEnabled(false);contourWidget->SetEnabled(false);textActor->SetInput("");}});renderWindowInteractor->AddObserver(vtkCommand::KeyPressEvent, keyCallback);// 添加actor到渲染器renderer->AddActor(actor);renderer->SetBackground(0.1, 0.2, 0.4);// 開始渲染renderWindow->Render();// 顯示操作提示std::cout << "Measurement Controls:" << std::endl;std::cout << "  D - Toggle distance measurement" << std::endl;std::cout << "  C - Toggle contour measurement" << std::endl;std::cout << "  A - Toggle area measurement" << std::endl;std::cout << "  ESC - Exit measurement mode" << std::endl;renderWindowInteractor->Start();return 0;
}

功能說明

  1. 距離測量:按 'D' 鍵激活,點擊兩個點測量它們之間的距離

  2. 輪廓測量:按 'C' 鍵激活,點擊多個點測量折線總長度

  3. 面積測量:按 'A' 鍵激活,點擊多個點后閉合區域測量面積

  4. 結果顯示:測量結果實時顯示在3D視圖左上角

  5. 退出測量:按 ESC 鍵退出所有測量模式

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/85395.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/85395.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/85395.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

性能優化 - 高級進階: 性能優化全方位總結

文章目錄 Pre1. 概述&#xff1a;性能優化提綱與使用場景2. 準備階段2.1 明確優化范圍與目標2.2 環境與工具準備 3. 數據收集與指標確認3.1 關鍵資源維度與指標項3.2 監控體系搭建與初始采集3.3 日志與追蹤配置 4. 問題定位思路4.1 從整體到局部的分析流程4.2 常見瓶頸維度檢查…

Mybatis之Integer類型字段為0,入庫為null

背景&#xff1a; 由于項目某個功能用到優先級字段來判斷&#xff0c;需要在mysql表中定義一個字段XX&#xff0c;類型為int&#xff0c;默認為0&#xff0c;具體值由后臺配置&#xff0c;正常入庫即可 問題&#xff1a; 由于后臺配置存量其他類型的數據無需該字段&#xff0c…

上海市計算機學會競賽平臺2022年3月月賽丙組洗牌

題目描述 給定一個整數 nn&#xff0c;表示 nn 張牌&#xff0c;牌的編號為 11 到 nn。 再給定一個洗牌置換 f1,f2,…,fnf1?,f2?,…,fn?&#xff0c;進行一次洗牌操作時&#xff0c;應將第一號位置的牌交換到第 f1f1? 號位置&#xff0c;將第 ii 號位置的牌交換到第 fifi…

DINO-R1:激勵推理能力的視覺基礎模型

摘要 近期&#xff0c;人們對大型語言模型&#xff08;如DeepSeek-R1&#xff09;推理能力的關注呈爆炸式增長&#xff0c;通過基于強化學習的微調框架&#xff08;如組相對策略優化&#xff08;Group Relative Policy Optimization&#xff0c;GRPO&#xff09;方法&#xff…

Linux--LVM邏輯卷擴容

Linux–LVM邏輯卷擴容 文章目錄 Linux--LVM邏輯卷擴容?? LVM 常用命令分類及基本格式? 1. 物理卷(PV)相關命令? 2. 卷組(VG)相關命令? 3. 邏輯卷(LV)相關命令?? 三、查看類命令簡寫說明使用命令及基本格式:lvm邏輯卷擴容步驟:1.添加硬盤設備2.檢測新增硬盤 添加…

C#基礎語法與控制臺操作

1. 控制臺操作基礎 控制臺程序是學習C#的起點。以下是一些常用的控制臺操作方法&#xff1a; 1.1. 清除控制臺 Console.Clear(); // 清除控制臺內容1.2. 輸出字符串 Console.WriteLine("Hello World!"); // 在屏幕的當前位置換行輸出字符串 Console.Write("…

100.Complex[]同時儲存實數和虛數兩組double的數組 C#例子

在信號處理中&#xff0c;IQ 數據&#xff08;In-phase and Quadrature&#xff09;通常表示復數形式的信號&#xff0c;其中實部表示同相分量&#xff0c;虛部表示正交分量。Complex[] data 是一個包含 IQ 數據的數組&#xff0c;每個元素是一個復數&#xff0c;表示一個信號樣…

停止追逐 React 重渲染

大多數開發者都在浪費時間對抗多余的重渲染。真正的 React 架構師根本讓問題無從產生——下面就來揭開他們的思路&#xff0c;以及為何大多數所謂的性能優化技巧反而拖慢了你的應用。 重渲染的無盡輪回 先來直擊痛點&#xff1a;如果還在項目里到處撒 useMemo、useCallback&…

流水線的安全與合規 - 構建可信的交付鏈

流水線的安全與合規 - 構建可信的交付鏈 “安全左移 (Shift-Left Security)”的理念 “安全左移”是 DevSecOps 的核心理念,指的是將安全測試和考量,從軟件開發生命周期 (SDLC) 的末端(發布前),盡可能地向左移動到更早的階段(如編碼、構建、測試階段)。 為何對 SRE 至…

???????神經網絡基礎講解 一

??一.神經網絡 ? ??1. 全連接神經網絡&#xff08;Fully Connected Network, FCN&#xff09;?? ??核心概念&#xff1a;?? ??輸入層??&#xff1a;接收原始數據&#xff08;如數字、圖片像素等&#xff09; 數字矩陣 。??隱藏層??&#xff1a;對數據…

MySQL 8.0 OCP 英文題庫解析(二十二)

Oracle 為慶祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免費考取原價245美元的MySQL OCP 認證。 從今天開始&#xff0c;將英文題庫免費公布出來&#xff0c;并進行解析&#xff0c;幫助大家在一個月之內輕松通過OCP認證。 本期公布試題201~210 試題2…

【大模型推理】PD分離場景下decoder負載均衡,如何選取decoder

https://mp.weixin.qq.com/s?__bizMzg4NTczNzg2OA&mid2247507420&idx1&sn4b32726abd205c7f94144bcb9105330f&chksmce64b9fc7f1d8de04a40b0153302dee52262c6f104c67195e2586e75c8093b8be493f252c8a3#rd 在非 Local 場景下&#xff0c;Prefill 定時獲取 Decode …

【IP地址】IP應用場景的使用方向

網絡安全領域 通過IP地址查詢&#xff0c;安全系統能夠實時監控網絡流量&#xff0c;識別異常訪問行為。例如&#xff0c;當某個IP地址在短時間內頻繁發起大量請求&#xff0c;且訪問模式與正常用戶存在明顯差異時&#xff0c;系統可將其標記為可疑IP&#xff0c;觸發風險預警…

3-18 WPS JS宏 顏色設置實例應用(按條件設置單元格顏色)學習筆記

前面講解了關于單元格的一些格式的設置&#xff0c;本節課再講解一下各種清除方法。 1.函數解析與用法 Range().clear()//清除全部 Range().Value2null //清除內容 Range().ClearContents()//清除內容 Range().ClearFormats()//清除格式 Range().EntireRow.Range()//以Ra…

從零開始的云計算生活——第二十天,腳踏實地,SSH與Rsync服務

目錄 一.故事背景 二.SSH帶外管理 1.概述 2. 配置文件 3.命令解析 4.登錄方式配置 a.用戶名密碼登錄 b.公鑰驗證登錄 5.實操生成密鑰對 三.Rsyncsersync實現數據實時同步 1.rsync概述 2.rsync運行原理 3.rsync部署 4.備份測試 配置備份目錄 5.rsyncsersync 實現…

SpringAI + DeepSeek大模型應用開發 - 初識篇

一、認識AI 1. AI的發展 AI&#xff0c;人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;使機器能像人類一樣思考、學習和解決問題的技術。 2. 大模型及其原理 在自然語言處理&#xff08;Natural Language Processing, NLP&#xff09;中&#xff0c;…

c++第八天-多態

虛函數虛析構函數純虛函數與抽象類 多態實現的條件&#xff1a;&#xff08;1&#xff09;公有繼承 &#xff08;2&#xff09;派生類重寫基類虛函數 &#xff08;3&#xff09;基類指針/引用指向派生類對象 虛函數不能是構造函數&#xff0c;不能是靜態函數&#xff0c;不能…

全景圖渲染Vue3+TS使用Photo Sphere Viewer插件實現

1.Photo Sphere Viewer插件安裝: title=插件安裝 體驗AI代碼助手 代碼解讀復制代碼npm install photo-sphere-viewer -S 或 yarn add photo-sphere-viewer -S 2.原始全景圖展示 初始化標簽容器 體驗AI代碼助手 代碼解讀復制代碼 // 全景圖的根節點必須要具備寬高 TS引用,創建…

Redis之分布式鎖(3)

這篇文章我們來詳細介紹一下如何正確地基于Redis實現分布式鎖。 基于Redis的分布式鎖實現 組件依賴 首先通過Maven引入Jedis開源組件&#xff0c;在pom.xml文件加入下面的代碼&#xff1a; <dependency><groupId>redis.clients</groupId><artifactId&g…

Java課堂筆記11

三個修飾符 一、abstract&#xff08;抽象&#xff09; 1.抽象方法只能在抽象的類里&#xff0c;只有方法的聲明&#xff0c;沒有方法的實現。&#xff08;沒有{}直接&#xff1b;結尾&#xff09;。 2.abstract修飾的類稱為抽象類。 注意&#xff1a;&#xff08;1&#x…