在cloudcompare的mainwindow.cpp中,用C++定义一个函数void MainWindow::DBH(),基于最小二乘法,通过获取当前选中的实体作为点云文件来源,实现对txt格式的树木点云文件的胸径计算
时间: 2023-11-28 22:47:30 浏览: 34
可以参考以下代码实现:
```cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cmath>
#include <QFileDialog>
#include <QMessageBox>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> PointCloudT;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//连接信号与槽函数
connect(ui->actionOpen, SIGNAL(triggered(bool)), this, SLOT(openFile()));
connect(ui->pushButton, SIGNAL(clicked(bool)), this, SLOT(DBH()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::openFile()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Point Cloud"), "", tr("Point Cloud Files (*.pcd *.txt)"));
if(!fileName.isEmpty())
{
//清空界面
ui->qvtkWidget->update();
ui->textBrowser->clear();
//读取点云文件
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPCDFile<pcl::PointXYZ>(fileName.toStdString(), *cloud) == -1)
{
//尝试读取txt格式文件
std::ifstream ifs(fileName.toStdString());
if (!ifs.is_open())
{
QMessageBox::critical(this, "Error", "Cannot open file!");
return;
}
std::string line;
while (std::getline(ifs, line))
{
pcl::PointXYZ p;
std::stringstream ss(line);
ss >> p.x >> p.y >> p.z;
cloud->push_back(p);
}
ifs.close();
}
//显示点云
pcl::visualization::PCLVisualizer vis("Point Cloud");
vis.setBackgroundColor(0.0, 0.0, 0.0);
vis.addPointCloud(cloud, "cloud");
vis.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud");
ui->qvtkWidget->update();
//输出点云信息
std::stringstream ss;
ss << "Number of points: " << cloud->size() << std::endl;
ui->textBrowser->append(ss.str().c_str());
//保存点云
cloud_ = cloud;
}
}
void MainWindow::DBH()
{
if (!cloud_)
{
QMessageBox::warning(this, "Warning", "No point cloud selected!");
return;
}
//获取选中的点
std::vector<int> indices;
if (ui->qvtkWidget->getRenderWindow()->GetRenderers()->GetFirstRenderer()->GetViewProp("cloud"))
{
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
actor = ui->qvtkWidget->getRenderWindow()->GetRenderers()->GetFirstRenderer()->GetActors()->GetLastActor();
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper());
vtkSmartPointer<vtkDataSet> dataSet = vtkSmartPointer<vtkDataSet>::New();
dataSet = mapper->GetInput();
vtkSmartPointer<vtkUnsignedCharArray> pointColors = vtkSmartPointer<vtkUnsignedCharArray>::New();
pointColors = vtkUnsignedCharArray::SafeDownCast(dataSet->GetPointData()->GetScalars());
for (vtkIdType i = 0; i < pointColors->GetNumberOfTuples(); i++)
{
if (pointColors->GetValue(i) == 255)
{
indices.push_back(i);
}
}
}
if (indices.empty())
{
QMessageBox::warning(this, "Warning", "No point selected!");
return;
}
//计算胸径
double sum_x = 0.0, sum_y = 0.0, sum_z = 0.0, sum_xy = 0.0, sum_xz = 0.0, sum_xx = 0.0;
for (int i : indices)
{
sum_x += cloud_->points[i].x;
sum_y += cloud_->points[i].y;
sum_z += cloud_->points[i].z;
sum_xx += cloud_->points[i].x * cloud_->points[i].x;
sum_xy += cloud_->points[i].x * cloud_->points[i].y;
sum_xz += cloud_->points[i].x * cloud_->points[i].z;
}
double n = static_cast<double>(indices.size());
double a = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x);
double b = (sum_y - a * sum_x) / n;
double c = (n * sum_xz - sum_x * sum_z) / (n * sum_xx - sum_x * sum_x);
double d = (sum_z - c * sum_x) / n;
double D = 2.0 * sqrt(a * a + 1) * 1000.0; //单位:毫米
//输出胸径
std::stringstream ss;
ss << "DBH: " << D << " mm" << std::endl;
ui->textBrowser->append(ss.str().c_str());
}
```
该函数基于最小二乘法,通过获取当前选中的实体作为点云文件来源,实现对txt格式的树木点云文件的胸径计算。