代码拉取完成,页面将自动刷新
同步操作将从 jerry.xu/CloudViewer 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
#include "cloudviewer.h"
CloudViewer::CloudViewer(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
/***** Slots connection of QMenuBar and QToolBar *****/
// File (connect)
QObject::connect(ui.openAction, &QAction::triggered, this, &CloudViewer::open);
QObject::connect(ui.addAction, &QAction::triggered, this, &CloudViewer::add);
QObject::connect(ui.clearAction, &QAction::triggered, this, &CloudViewer::clear);
QObject::connect(ui.saveAction, &QAction::triggered, this, &CloudViewer::save);
QObject::connect(ui.saveBinaryAction, &QAction::triggered, this, &CloudViewer::saveBinary);
QObject::connect(ui.changeAction, &QAction::triggered, this, &CloudViewer::change);
QObject::connect(ui.exitAction, &QAction::triggered, this, &CloudViewer::exit);
// Display (connect)
QObject::connect(ui.pointcolorAction, &QAction::triggered, this, &CloudViewer::pointcolorChanged);
QObject::connect(ui.bgcolorAction, &QAction::triggered, this, &CloudViewer::bgcolorChanged);
QObject::connect(ui.mainviewAction, &QAction::triggered, this, &CloudViewer::mainview);
QObject::connect(ui.leftviewAction, &QAction::triggered, this, &CloudViewer::leftview);
QObject::connect(ui.topviewAction, &QAction::triggered, this, &CloudViewer::topview);
// View (connect)
QObject::connect(ui.dataAction, &QAction::triggered, this, &CloudViewer::data);
QObject::connect(ui.propertyAction, &QAction::triggered, this, &CloudViewer::properties);
QObject::connect(ui.consoleAction, &QAction::triggered, this, &CloudViewer::console);
QObject::connect(ui.RGBAction, &QAction::triggered, this, &CloudViewer::rgbDock);
// Generate (connect)
QObject::connect(ui.cubeAction, &QAction::triggered, this, &CloudViewer::cube);
QObject::connect(ui.sphereAction, &QAction::triggered, this, &CloudViewer::createSphere);
QObject::connect(ui.cylinderAction, &QAction::triggered, this, &CloudViewer::createCylinder);
// Process (connect)
QObject::connect(ui.meshsurfaceAction, &QAction::triggered, this, &CloudViewer::convertSurface);
QObject::connect(ui.wireframeAction, &QAction::triggered, this, &CloudViewer::convertWireframe);
// Option (connect)
QObject::connect(ui.windowsThemeAction, &QAction::triggered, this, &CloudViewer::windowsTheme);
QObject::connect(ui.darculaThemeAction, &QAction::triggered, this, &CloudViewer::darculaTheme);
QObject::connect(ui.englishAction, &QAction::triggered, this, &CloudViewer::langEnglish);
QObject::connect(ui.chineseAction, &QAction::triggered, this, &CloudViewer::langChinese);
// About (connect)
QObject::connect(ui.aboutAction, &QAction::triggered, this, &CloudViewer::about);
QObject::connect(ui.helpAction, &QAction::triggered, this, &CloudViewer::help);
/***** Slots connection of RGB widget *****/
// Random color (connect)
connect(ui.colorBtn, SIGNAL(clicked()), this, SLOT(colorBtnPressed()));
// Connection between RGB slider and RGB value (connect)
connect(ui.rSlider, SIGNAL(valueChanged(int)), this, SLOT(rSliderChanged(int)));
connect(ui.gSlider, SIGNAL(valueChanged(int)), this, SLOT(gSliderChanged(int)));
connect(ui.bSlider, SIGNAL(valueChanged(int)), this, SLOT(bSliderChanged(int)));
// RGB slider released (connect)
connect(ui.rSlider, SIGNAL(sliderReleased()), this, SLOT(RGBsliderReleased()));
connect(ui.gSlider, SIGNAL(sliderReleased()), this, SLOT(RGBsliderReleased()));
connect(ui.bSlider, SIGNAL(sliderReleased()), this, SLOT(RGBsliderReleased()));
// Change size of cloud (connect)
connect(ui.pSlider, SIGNAL(valueChanged(int)), this, SLOT(pSliderChanged(int)));
connect(ui.pSlider, SIGNAL(sliderReleased()), this, SLOT(psliderReleased()));
// Checkbox for coordinate and background color (connect)
connect(ui.cooCbx, SIGNAL(stateChanged(int)), this, SLOT(cooCbxChecked(int)));
connect(ui.bgcCbx, SIGNAL(stateChanged(int)), this, SLOT(bgcCbxChecked(int)));
/***** Slots connection of dataTree(QTreeWidget) widget *****/
// Item in dataTree is left-clicked (connect)
connect(ui.dataTree, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(itemSelected(QTreeWidgetItem*, int)));
// Item in dataTree is right-clicked
connect(ui.dataTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(popMenu(const QPoint&)));
connect(ui.consoleTable, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(popMenuInConsole(const QPoint&)));
// Initialization
initial();
}
CloudViewer::~CloudViewer()
{
}
// Open point cloud
void CloudViewer::open()
{
QStringList filenames = QFileDialog::getOpenFileNames(this, tr("Open point cloud file"), QString::fromLocal8Bit(mycloud.dirname.c_str()), tr("Point cloud data(*.pcd *.ply *.obj);;All file(*.*)"));
//Return if filenames is empty
if (filenames.isEmpty())
return;
// Clear cache
mycloud_vec.clear();
total_points = 0;
ui.dataTree->clear();
viewer->removeAllPointClouds();
// Open point cloud one by one
for (int i = 0; i != filenames.size(); i++){
// time start
timeStart();
mycloud.cloud.reset(new PointCloudT); // Reset cloud
QString filename = filenames[i];
std::string file_name = filename.toStdString();
std::string subname = getFileName(file_name); //提取全路径中的文件名(带后缀)
//更新状态栏
ui.statusBar->showMessage(QString::fromLocal8Bit(subname.c_str()) + ": " + QString::number(i) + "/" + QString::number(filenames.size()) + " point cloud loading...");
int status = -1;
if (filename.endsWith(".pcd", Qt::CaseInsensitive))
{
status = pcl::io::loadPCDFile(file_name, *(mycloud.cloud));
if (mycloud.cloud->points[0].r == 0 && mycloud.cloud->points[0].g == 0 && mycloud.cloud->points[0].b == 0)
{
setCloudColor(255, 255, 255);
}
}
else if (filename.endsWith(".ply", Qt::CaseInsensitive))
{
status = pcl::io::loadPLYFile(file_name, *(mycloud.cloud));
if (mycloud.cloud->points[0].r == 0 && mycloud.cloud->points[0].g == 0 && mycloud.cloud->points[0].b == 0)
{
setCloudColor(255, 255, 255);
}
}
else if (filename.endsWith(".obj", Qt::CaseInsensitive))
{
status = pcl::io::loadOBJFile(file_name, *(mycloud.cloud));
if (mycloud.cloud->points[0].r == 0 && mycloud.cloud->points[0].g == 0 && mycloud.cloud->points[0].b == 0)
{
setCloudColor(255, 255, 255);
}
}
else
{
//提示:无法读取除了.ply .pcd .obj以外的文件
QMessageBox::information(this, tr("File format error"),
tr("Can't open files except .ply .pcd .obj"));
return;
}
//提示:后缀没问题,但文件内容无法读取
if (status != 0)
{
QMessageBox::critical(this, tr("Reading file error"), tr("We can not open the file"));
return;
}
setA(255); //设置点云为不透明
// 最后导入的点云的信息
mycloud.filename = file_name;
mycloud.subname = subname;
mycloud.dirname = file_name.substr(0, file_name.size() - subname.size());
mycloud_vec.push_back(mycloud); //将点云导入点云容器
// time off
time_cost = timeOff();
// 输出窗口
consoleLog("Open", QString::fromLocal8Bit(mycloud.subname.c_str()), QString::fromLocal8Bit(mycloud.filename.c_str()), "Time cost: " + time_cost + " s, Points: " + QString::number(mycloud.cloud->points.size()));
//更新资源管理树
QTreeWidgetItem *cloudName = new QTreeWidgetItem(QStringList()
<< QString::fromLocal8Bit(subname.c_str()));
cloudName->setIcon(0, QIcon(":/Resources/images/icon.png"));
ui.dataTree->addTopLevelItem(cloudName);
//setWindowTitle(filename + " - CloudViewer"); //更新标题
total_points += mycloud.cloud->points.size();
}
ui.statusBar->showMessage("");
showPointcloudAdd(); //更新视图窗口
setPropertyTable();
}
// Add Point Cloud
void CloudViewer::add()
{
QStringList filenames = QFileDialog::getOpenFileNames(this, tr("Open point cloud file"), QString::fromLocal8Bit(mycloud.dirname.c_str()), tr("Point cloud data(*.pcd *.ply *.obj);;All file(*.*)"));
if (filenames.isEmpty())
return;
for (int i = 0; i != filenames.size(); i++){
// time start
timeStart();
mycloud.cloud.reset(new PointCloudT);
QString filename = filenames[i];
std::string file_name = filename.toStdString();
std::string subname = getFileName(file_name);
// 更新状态栏
ui.statusBar->showMessage(QString::fromLocal8Bit(subname.c_str()) + ": " + QString::number(i) + "/" + QString::number(filenames.size()) + " point cloud loading...");
int status = -1;
if (filename.endsWith(".pcd", Qt::CaseInsensitive))
{
status = pcl::io::loadPCDFile(file_name, *(mycloud.cloud));
if (mycloud.cloud->points[0].r == 0 && mycloud.cloud->points[0].g == 0 && mycloud.cloud->points[0].b == 0)
{
setCloudColor(255, 255, 255);
}
}
else if (filename.endsWith(".ply", Qt::CaseInsensitive))
{
status = pcl::io::loadPLYFile(file_name, *(mycloud.cloud));
if (mycloud.cloud->points[0].r == 0 && mycloud.cloud->points[0].g == 0 && mycloud.cloud->points[0].b == 0)
{
setCloudColor(255, 255, 255);
}
}
else if (filename.endsWith(".obj", Qt::CaseInsensitive))
{
status = pcl::io::loadOBJFile(file_name, *(mycloud.cloud));
if (mycloud.cloud->points[0].r == 0 && mycloud.cloud->points[0].g == 0 && mycloud.cloud->points[0].b == 0)
{
setCloudColor(255, 255, 255);
}
}
else
{
//提示:无法读取除了.ply .pcd .obj以外的文件
QMessageBox::information(this, tr("File format error"), tr("Can't open files except .ply .pcd .obj"));
return;
}
//提示:后缀没问题,但文件内容无法读取
if (status != 0)
{
QMessageBox::critical(this, tr("Reading file error"), tr("We can not open the file"));
return;
}
setA(255); //设置点云为不透明
mycloud.filename = file_name;
mycloud.subname = subname;
mycloud.dirname = file_name.substr(0, file_name.size() - subname.size());
mycloud_vec.push_back(mycloud); //将点云导入点云容器
// time of
time_cost = timeOff();
//输出窗口
consoleLog("Add", QString::fromLocal8Bit(mycloud.subname.c_str()), QString::fromLocal8Bit(mycloud.filename.c_str()), "Time cost: " + time_cost + " s, Points: " + QString::number(mycloud.cloud->points.size()));
//设置资源管理器
QTreeWidgetItem *cloudName = new QTreeWidgetItem(QStringList() << QString::fromLocal8Bit(subname.c_str()));
cloudName->setIcon(0, QIcon(":/Resources/images/icon.png"));
ui.dataTree->addTopLevelItem(cloudName);
//setWindowTitle("CloudViewer");
total_points += mycloud.cloud->points.size();
}
ui.statusBar->showMessage("");
showPointcloudAdd();
setPropertyTable();
}
// Clear all point clouds
void CloudViewer::clear()
{
mycloud_vec.clear(); //从点云容器中移除所有点云
viewer->removeAllPointClouds(); //从viewer中移除所有点云
viewer->removeAllShapes(); //这个remove更彻底
ui.dataTree->clear(); //将dataTree清空
ui.propertyTable->clear(); //清空属性窗口propertyTable
QStringList header;
header << "Property" << "Value";
ui.propertyTable->setHorizontalHeaderLabels(header);
//输出窗口
consoleLog("Clear", "All point clouds", "", "");
setWindowTitle("CloudViewer"); //更新窗口标题
showPointcloud(); //更新显示
}
// Save point cloud
void CloudViewer::save()
{
save_filename = QFileDialog::getSaveFileName(this, tr("Save point cloud"),
QString::fromLocal8Bit(mycloud.dirname.c_str()), tr("Point cloud data(*.pcd *.ply);;Allfile(*.*)"));
std::string file_name = save_filename.toStdString();
std::string subname = getFileName(file_name);
//文件名为空直接返回
if (save_filename.isEmpty())
return;
if (mycloud_vec.size() > 1)
{
savemulti();
return;
}
int status = -1;
if (save_filename.endsWith(".pcd", Qt::CaseInsensitive))
{
status = pcl::io::savePCDFile(file_name, *(mycloud.cloud));
}
else if (save_filename.endsWith(".ply", Qt::CaseInsensitive))
{
status = pcl::io::savePLYFile(file_name, *(mycloud.cloud));
}
else //提示:无法保存为除了.ply .pcd以外的文件
{
QMessageBox::information(this, tr("File format error"),
tr("Can't save files except .ply .pcd"));
return;
}
//提示:后缀没问题,但是无法保存
if (status != 0)
{
QMessageBox::critical(this, tr("Saving file error"),
tr("We can not save the file"));
return;
}
//输出窗口
consoleLog("Save", QString::fromLocal8Bit(subname.c_str()), save_filename, "Single save");
setWindowTitle(save_filename + " - CloudViewer");
QMessageBox::information(this, tr("save point cloud file"),
QString::fromLocal8Bit(("Save " + subname + " successfully!").c_str()));
}
// Save point cloud as binary file
void CloudViewer::saveBinary()
{
save_filename = QFileDialog::getSaveFileName(this, tr("Save point cloud as binary file"),
QString::fromLocal8Bit(mycloud.dirname.c_str()), tr("Point cloud data(*.pcd *.ply);;Allfile(*.*)"));
std::string file_name = save_filename.toStdString();
std::string subname = getFileName(file_name);
//文件名为空直接返回
if (save_filename.isEmpty())
return;
if (mycloud_vec.size() > 1)
{
savemulti();
return;
}
int status = -1;
if (save_filename.endsWith(".pcd", Qt::CaseInsensitive))
{
status = pcl::io::savePCDFileBinary(file_name, *(mycloud.cloud));
}
else if (save_filename.endsWith(".ply", Qt::CaseInsensitive))
{
status = pcl::io::savePLYFileBinary(file_name, *(mycloud.cloud));
}
else //提示:无法保存为除了.ply .pcd以外的文件
{
QMessageBox::information(this, tr("File format error"),
tr("Can't save files except .ply .pcd"));
return;
}
//提示:后缀没问题,但是无法保存
if (status != 0)
{
QMessageBox::critical(this, tr("Saving file error"),
tr("We can not save the file"));
return;
}
//输出窗口
consoleLog("Save as binary", QString::fromLocal8Bit(subname.c_str()), save_filename, "Single save (binary)");
setWindowTitle(save_filename + " - CloudViewer");
QMessageBox::information(this, tr("save point cloud file"),
QString::fromLocal8Bit(("Save " + subname + " successfully!").c_str()));
}
// Save multi point cloud
void CloudViewer::savemulti()
{
std::string subname = getFileName(save_filename.toStdString());
PointCloudT::Ptr multi_cloud;
multi_cloud.reset(new PointCloudT);
multi_cloud->height = 1;
int sum = 0;
for (auto c : mycloud_vec)
{
sum += c.cloud->points.size();
}
multi_cloud->width = sum;
multi_cloud->resize(multi_cloud->height * multi_cloud->width);
int k = 0;
for (int i = 0; i != mycloud_vec.size(); i++)
{
for (int j = 0; j != mycloud_vec[i].cloud->points.size(); j++) //注意cloudvec[i]->points.size()和cloudvec[i]->size()的区别
{
multi_cloud->points[k].x = mycloud_vec[i].cloud->points[j].x;
multi_cloud->points[k].y = mycloud_vec[i].cloud->points[j].y;
multi_cloud->points[k].z = mycloud_vec[i].cloud->points[j].z;
multi_cloud->points[k].r = mycloud_vec[i].cloud->points[j].r;
multi_cloud->points[k].g = mycloud_vec[i].cloud->points[j].g;
multi_cloud->points[k].b = mycloud_vec[i].cloud->points[j].b;
k++;
}
}
//保存multi_cloud
int status = -1;
if (save_filename.endsWith(".pcd", Qt::CaseInsensitive))
{
if (save_as_binary){
status = pcl::io::savePCDFileBinary(save_filename.toStdString(), *multi_cloud);
}
else{
status = pcl::io::savePCDFile(save_filename.toStdString(), *multi_cloud);
}
}
else if (save_filename.endsWith(".ply", Qt::CaseInsensitive))
{
if (save_as_binary){
status = pcl::io::savePLYFileBinary(save_filename.toStdString(), *multi_cloud);
}
else{
status = pcl::io::savePLYFile(save_filename.toStdString(), *multi_cloud);
}
}
else //提示:无法保存为除了.ply .pcd以外的文件
{
QMessageBox::information(this, tr("File format error"), tr("Can't save files except .ply .pcd"));
return;
}
//提示:后缀没问题,但是无法保存
if (status != 0)
{
QMessageBox::critical(this, tr("Saving file error"), tr("We can not save the file"));
return;
}
// 输出窗口
if (save_as_binary){
consoleLog("Save as binary", QString::fromLocal8Bit(subname.c_str()), save_filename, "Multi save (binary)");
}
else{
consoleLog("Save", QString::fromLocal8Bit(subname.c_str()), save_filename, "Multi save");
}
save_as_binary = false;
//将保存后的 multi_cloud 设置为当前 mycloud,以便保存之后直接进行操作
mycloud.cloud = multi_cloud;
mycloud.filename = save_filename.toStdString();
mycloud.subname = subname;
setWindowTitle(save_filename + " - CloudViewer");
QMessageBox::information(this, tr("save point cloud file"), QString::fromLocal8Bit(("Save " + subname + " successfully!").c_str()));
}
//格式转换
void CloudViewer::change()
{
}
//退出程序
void CloudViewer::exit()
{
this->close();
}
// Generate cube
void CloudViewer::cube()
{
mycloud.cloud.reset(new PointCloudT);
total_points = 0;
ui.dataTree->clear(); //清空资源管理器的item
viewer->removeAllPointClouds(); //从viewer中移除所有点云
mycloud_vec.clear(); //清空点云容器
mycloud.cloud->width = 50000; // 设置点云宽
mycloud.cloud->height = 1; // 设置点云高,高为1,说明为无组织点云
mycloud.cloud->is_dense = false;
mycloud.cloud->resize(mycloud.cloud->width * mycloud.cloud->height); // 重置点云大小
for (size_t i = 0; i != mycloud.cloud->size(); ++i)
{
mycloud.cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f);
mycloud.cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f);
mycloud.cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f);
mycloud.cloud->points[i].r = red;
mycloud.cloud->points[i].g = green;
mycloud.cloud->points[i].b = blue;
}
//设置资源管理器
QTreeWidgetItem *cloudName = new QTreeWidgetItem(QStringList() << QString::fromLocal8Bit("cube"));
cloudName->setIcon(0, QIcon(":/Resources/images/icon.png"));
ui.dataTree->addTopLevelItem(cloudName);
// 输出窗口
consoleLog("Generate cube", "cube", "cube", "");
mycloud_vec.push_back(mycloud);
showPointcloudAdd();
}
//初始化
void CloudViewer::initial()
{
//界面初始化
setWindowIcon(QIcon(tr(":/Resources/images/icon.png")));
setWindowTitle(tr("CloudViewer"));
//点云初始化
mycloud.cloud.reset(new PointCloudT);
mycloud.cloud->resize(1);
viewer.reset(new pcl::visualization::PCLVisualizer("viewer", false));
//viewer->addPointCloud(cloud, "cloud");
ui.screen->SetRenderWindow(viewer->getRenderWindow());
viewer->setupInteractor(ui.screen->GetInteractor(), ui.screen->GetRenderWindow());
ui.screen->update();
ui.propertyTable->setSelectionMode(QAbstractItemView::NoSelection); // 禁止点击属性管理器的 item
ui.consoleTable->setSelectionMode(QAbstractItemView::NoSelection); // 禁止点击输出窗口的 item
ui.dataTree->setSelectionMode(QAbstractItemView::ExtendedSelection); // 允许 dataTree 进行多选
// 设置默认主题
QString qss = darcula_qss;
qApp->setStyleSheet(qss);
setPropertyTable();
setConsoleTable();
// 输出窗口
consoleLog("Software start", "CloudViewer", "Welcome to use CloudViewer", "Nightn");
// 设置背景颜色为 dark
viewer->setBackgroundColor(30 / 255.0, 30 / 255.0, 30 / 255.0);
}
//显示点云,不重置相机角度
void CloudViewer::showPointcloud()
{
for (int i = 0; i != mycloud_vec.size(); i++)
{
viewer->updatePointCloud(mycloud_vec[i].cloud, "cloud" + QString::number(i).toStdString());
}
//viewer->resetCamera();
ui.screen->update();
}
//添加点云到viewer,并显示点云
void CloudViewer::showPointcloudAdd()
{
for (int i = 0; i != mycloud_vec.size(); i++)
{
viewer->addPointCloud(mycloud_vec[i].cloud, "cloud" + QString::number(i).toStdString());
viewer->updatePointCloud(mycloud_vec[i].cloud, "cloud" + QString::number(i).toStdString());
}
viewer->resetCamera();
ui.screen->update();
}
void CloudViewer::setCloudColor(unsigned int r, unsigned int g, unsigned int b)
{
// Set the new color
for (size_t i = 0; i < mycloud.cloud->size(); i++)
{
mycloud.cloud->points[i].r = r;
mycloud.cloud->points[i].g = g;
mycloud.cloud->points[i].b = b;
mycloud.cloud->points[i].a = 255;
}
}
void CloudViewer::setA(unsigned int a)
{
for (size_t i = 0; i < mycloud.cloud->size(); i++)
{
mycloud.cloud->points[i].a = a;
}
}
//关于
void CloudViewer::about()
{
AboutWin *aboutwin = new AboutWin(this);
aboutwin->setModal(true);
aboutwin->show();
// 输出窗口
consoleLog("About", "Nightn", "http://nightn.com", "Welcome to my blog!");
}
//帮助
void CloudViewer::help()
{
QDesktopServices::openUrl(QUrl(QLatin1String("http://nightn.com/cloudviewer")));
// 输出窗口
consoleLog("Help", "Cloudviewer help", "http://nightn.com/cloudviewer", "");
//QMessageBox::information(this, "Help", "we are building help widget...");
}
//设置停靠窗口的显示与隐藏
void CloudViewer::data()
{
if (ui.dataAction->isChecked())
{
ui.dataDock->setVisible(true);
}
else
{
ui.dataDock->setVisible(false);
}
}
void CloudViewer::properties()
{
if (ui.propertyAction->isChecked())
{
ui.propertyDock->setVisible(true);
}
else
{
ui.propertyDock->setVisible(false);
}
}
void CloudViewer::console()
{
if (ui.consoleAction->isChecked())
{
ui.consoleDock->setVisible(true);
}
else
{
ui.consoleDock->setVisible(false);
}
}
void CloudViewer::rgbDock()
{
if (ui.RGBAction->isChecked())
{
ui.RGBDock->setVisible(true);
}
else
{
ui.RGBDock->setVisible(false);
}
}
//绘制基本图形
void CloudViewer::createSphere()
{
mycloud.cloud.reset(new PointCloudT);
ui.dataTree->clear(); //清空资源管理器的item
viewer->removeAllShapes();
mycloud_vec.clear(); //清空点云容器
pcl::PointXYZ p;
p.x = 0; p.y = 0; p.z = 0;
viewer->addSphere(p, 100, "sphere1");
viewer->resetCamera();
ui.screen->update();
// 输出窗口
consoleLog("Create sphere", "Sphere", "", "Succeeded");
}
void CloudViewer::createCylinder()
{
mycloud.cloud.reset(new PointCloudT);
ui.dataTree->clear(); //清空资源管理器的item
viewer->removeAllShapes();
mycloud_vec.clear(); //清空点云容器
viewer->addCylinder(*(new pcl::ModelCoefficients()), "cylinder");
viewer->resetCamera();
ui.screen->update();
// 输出窗口
consoleLog("Create cylinder", "Cylinder", "", "Failed");
}
// Change theme: Windows/Darcula
void CloudViewer::windowsTheme(){
/*
QFile qssFile("Resources/qss/Windows.qss"); //资源文件":/Darcula.qss"
qssFile.open(QFile::ReadOnly);
if (qssFile.isOpen())
{
QString qss = QLatin1String(qssFile.readAll());
//consoleLog("", "", "", qss);
qApp->setStyleSheet(qss);
qssFile.close();
}
*/
QString qss = windows_qss;
qApp->setStyleSheet(qss);
//改变 dataTree 字体颜色,以适应主题的笨办法
QColor light_color(241, 241, 241, 255);
QColor dark_color(0, 0, 0, 255);
for (int i = 0; i != mycloud_vec.size(); i++){
if (ui.dataTree->topLevelItem(i)->textColor(0) == light_color){
ui.dataTree->topLevelItem(i)->setTextColor(0, dark_color);
}
}
// 输出窗口
consoleLog("Change theme", "Windows theme", "", "");
theme_id = 0;
}
void CloudViewer::darculaTheme(){
/*
QFile qssFile("Resources/qss/Darcula.qss"); //资源文件":/Darcula.qss"
qssFile.open(QFile::ReadOnly);
if (qssFile.isOpen())
{
QString qss = QLatin1String(qssFile.readAll());
//cout << qss.toStdString();
consoleLog("", "", "", qss);
qApp->setStyleSheet(qss);
qssFile.close();
}
*/
QString qss = darcula_qss;
qApp->setStyleSheet(qss);
//改变 dataTree 字体颜色,以适应主题的笨办法
QColor light_color(241, 241, 241, 255);
QColor dark_color(0, 0, 0, 255);
for (int i = 0; i != mycloud_vec.size(); i++){
if (ui.dataTree->topLevelItem(i)->textColor(0) == dark_color){
ui.dataTree->topLevelItem(i)->setTextColor(0, light_color);
}
}
// 输出窗口
consoleLog("Change theme", "Darcula theme", "", "");
theme_id = 1;
}
// Change language: English/Chinese
void CloudViewer::langEnglish(){
// 输出窗口
consoleLog("Change language", "English", "", "");
}
void CloudViewer::langChinese(){
// 输出窗口
consoleLog("Change language", "Chinese", "Doesn't support Chinese temporarily", "");
}
/*********************************************/
/*****************界面槽函数*****************/
/********************************************/
void CloudViewer::colorBtnPressed()
{
QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();
int selected_item_count = ui.dataTree->selectedItems().size();
// 如果未选中任何点云,则对视图窗口中的所有点云进行着色
if (selected_item_count == 0){
for (int i = 0; i != mycloud_vec.size(); i++){
for (int j = 0; j != mycloud_vec[i].cloud->points.size(); j++){
mycloud_vec[i].cloud->points[j].r = 255 * (1024 * rand() / (RAND_MAX + 1.0f));
mycloud_vec[i].cloud->points[j].g = 255 * (1024 * rand() / (RAND_MAX + 1.0f));
mycloud_vec[i].cloud->points[j].b = 255 * (1024 * rand() / (RAND_MAX + 1.0f));
}
}
// 输出窗口
consoleLog("Random color", "All point clous", "", "");
}
else{
for (int i = 0; i != selected_item_count; i++){
int cloud_id = ui.dataTree->indexOfTopLevelItem(itemList[i]);
for (int j = 0; j != mycloud_vec[cloud_id].cloud->size(); j++){
mycloud_vec[cloud_id].cloud->points[j].r = red;
mycloud_vec[cloud_id].cloud->points[j].g = 255 * (1024 * rand() / (RAND_MAX + 1.0f));
mycloud_vec[cloud_id].cloud->points[j].b = 255 * (1024 * rand() / (RAND_MAX + 1.0f));
}
}
// 输出窗口
consoleLog("Random color", "Point clouds selected", "", "");
}
showPointcloud();
}
void CloudViewer::RGBsliderReleased()
{
QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();
int selected_item_count = ui.dataTree->selectedItems().size();
// 如果未选中任何点云,则对视图窗口中的所有点云进行着色
if (selected_item_count == 0){
for (int i = 0; i != mycloud_vec.size(); i++){
for (int j = 0; j != mycloud_vec[i].cloud->points.size(); j++){
mycloud_vec[i].cloud->points[j].r = red;
mycloud_vec[i].cloud->points[j].g = green;
mycloud_vec[i].cloud->points[j].b = blue;
}
}
// 输出窗口
consoleLog("Change cloud color", "All point clouds", QString::number(red) + " " + QString::number(green) + " " + QString::number(blue), "");
}
else{
for (int i = 0; i != selected_item_count; i++){
int cloud_id = ui.dataTree->indexOfTopLevelItem(itemList[i]);
for (int j = 0; j != mycloud_vec[cloud_id].cloud->size(); j++){
mycloud_vec[cloud_id].cloud->points[j].r = red;
mycloud_vec[cloud_id].cloud->points[j].g = green;
mycloud_vec[cloud_id].cloud->points[j].b = blue;
}
}
// 输出窗口
consoleLog("Change cloud color", "Point clouds selected", QString::number(red) + " " + QString::number(green) + " " + QString::number(blue), "");
}
showPointcloud();
}
//设置所有点云的尺寸
void CloudViewer::psliderReleased()
{
QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();
int selected_item_count = ui.dataTree->selectedItems().size();
if (selected_item_count == 0){
for (int i = 0; i != mycloud_vec.size(); i++){
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,
p, "cloud" + QString::number(i).toStdString());
}
// 输出窗口
consoleLog("Change cloud size", "All point clouds", "Size: " + QString::number(p), "");
}
else{
for (int i = 0; i != selected_item_count; i++){
int cloud_id = ui.dataTree->indexOfTopLevelItem(itemList[i]);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,
p, "cloud" + QString::number(cloud_id).toStdString());
}
// 输出窗口
consoleLog("Change cloud size", "Point clouds selected", "Size: " + QString::number(p), "");
}
ui.screen->update();
}
void CloudViewer::pSliderChanged(int value)
{
p = value;
ui.sizeLCD->display(value);
}
void CloudViewer::rSliderChanged(int value)
{
red = value;
ui.rLCD->display(value);
}
void CloudViewer::gSliderChanged(int value)
{
green = value;
ui.gLCD->display(value);
}
void CloudViewer::bSliderChanged(int value)
{
blue = value;
ui.bLCD->display(value);
}
void CloudViewer::cooCbxChecked(int value)
{
switch (value)
{
case 0:
viewer->removeCoordinateSystem(); //移除坐标系
// 输出窗口
consoleLog("Remove coordinate system", "Remove", "", "");
break;
case 2:
viewer->addCoordinateSystem(); //添加坐标系
// 输出窗口
consoleLog("Add coordinate system", "Add", "", "");
break;
}
//viewer->updatePointCloud(cloud, "cloud");
ui.screen->update();
}
void CloudViewer::bgcCbxChecked(int value)
{
switch (value)
{
case 0:
viewer->setBackgroundColor(30 / 255.0, 30 / 255.0, 30 / 255.0);
// 输出窗口
consoleLog("Change bg color", "Background", "30 30 30", "");
break;
case 2:
//!注意:setBackgroundColor()接收的是0-1的double型参数
viewer->setBackgroundColor(240 / 255.0, 240 / 255.0, 240 / 255.0);
// 输出窗口
consoleLog("Change bg color", "Background", "240 240 240", "");
break;
}
//viewer->updatePointCloud(cloud, "cloud");
ui.screen->update();
}
//通过颜色对话框改变点云颜色
void CloudViewer::pointcolorChanged()
{
QColor color = QColorDialog::getColor(Qt::white, this, "Select color for point cloud");
if (color.isValid()) //判断所选的颜色是否有效
{
//QAction* action = dynamic_cast<QAction*>(sender());
//if (action != ui.pointcolorAction) //改变颜色的信号来自于 dataTree
QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();
int selected_item_count = ui.dataTree->selectedItems().size();
if (selected_item_count == 0){
for (int i = 0; i != mycloud_vec.size(); i++){
for (int j = 0; j != mycloud_vec[i].cloud->points.size(); j++){
mycloud_vec[i].cloud->points[j].r = color.red();
mycloud_vec[i].cloud->points[j].g = color.green();
mycloud_vec[i].cloud->points[j].b = color.blue();
}
}
// 输出窗口
consoleLog("Change cloud color", "All point clouds", QString::number(color.red()) + " " + QString::number(color.green()) + " " + QString::number(color.blue()), "");
}
else{
for (int i = 0; i != selected_item_count; i++){
int cloud_id = ui.dataTree->indexOfTopLevelItem(itemList[i]);
for (int j = 0; j != mycloud_vec[cloud_id].cloud->size(); j++){
mycloud_vec[cloud_id].cloud->points[j].r = color.red();
mycloud_vec[cloud_id].cloud->points[j].g = color.green();
mycloud_vec[cloud_id].cloud->points[j].b = color.blue();
}
}
// 输出窗口
consoleLog("Change cloud color", "Point clouds selected", QString::number(color.red()) + " " + QString::number(color.green()) + " " + QString::number(color.blue()), "");
}
//颜色的改变同步至RGB停靠窗口
ui.rSlider->setValue(color.red());
ui.gSlider->setValue(color.green());
ui.bSlider->setValue(color.blue());
showPointcloud();
}
}
//通过颜色对话框改变背景颜色
void CloudViewer::bgcolorChanged()
{
QColor color = QColorDialog::getColor(Qt::white, this,
"Select color for point cloud");
if (color.isValid())
{
viewer->setBackgroundColor(color.red() / 255.0,
color.green() / 255.0, color.blue() / 255.0);
// 输出窗口
consoleLog("Change bg color", "Background", QString::number(color.red()) + " " + QString::number(color.green()) + " " + QString::number(color.blue()), "");
showPointcloud();
}
}
//三视图
void CloudViewer::mainview()
{
viewer->setCameraPosition(0, -1, 0, 0.5, 0.5, 0.5, 0, 0, 1);
ui.screen->update();
}
void CloudViewer::leftview()
{
viewer->setCameraPosition(-1, 0, 0, 0, 0, 0, 0, 0, 1);
ui.screen->update();
}
void CloudViewer::topview()
{
viewer->setCameraPosition(0, 0, 1, 0, 0, 0, 0, 1, 0);
ui.screen->update();
}
//设置属性管理窗口
void CloudViewer::setPropertyTable(){
QStringList header;
header << "Property" << "Value";
ui.propertyTable->setHorizontalHeaderLabels(header);
ui.propertyTable->setItem(0, 0, new QTableWidgetItem("Clouds"));
ui.propertyTable->setItem(1, 0, new QTableWidgetItem("Points"));
ui.propertyTable->setItem(2, 0, new QTableWidgetItem("Total points"));
ui.propertyTable->setItem(3, 0, new QTableWidgetItem("RGB"));
ui.propertyTable->setItem(0, 1, new QTableWidgetItem(QString::number(mycloud_vec.size())));
ui.propertyTable->setItem(1, 1, new QTableWidgetItem(""));
ui.propertyTable->setItem(2, 1, new QTableWidgetItem(QString::number(total_points)));
ui.propertyTable->setItem(4, 1, new QTableWidgetItem(""));
}
void CloudViewer::setConsoleTable(){
// 设置输出窗口
QStringList header2;
header2 << "Time" << "Operation" << "Operation obeject" << "Details" << "Note";
ui.consoleTable->setHorizontalHeaderLabels(header2);
ui.consoleTable->setColumnWidth(0, 150);
ui.consoleTable->setColumnWidth(1, 200);
ui.consoleTable->setColumnWidth(2, 200);
ui.consoleTable->setColumnWidth(3, 300);
//ui.consoleTable->setEditTriggers(QAbstractItemView::NoEditTriggers); //设置不可编辑
ui.consoleTable->verticalHeader()->setDefaultSectionSize(22); //设置行距
ui.consoleTable->setContextMenuPolicy(Qt::CustomContextMenu);
}
//
void CloudViewer::consoleLog(QString operation, QString subname, QString filename, QString note)
{
if (enable_console == false){
return;
}
int rows = ui.consoleTable->rowCount();
ui.consoleTable->setRowCount(++rows);
QDateTime time = QDateTime::currentDateTime();//获取系统现在的时间
QString time_str = time.toString("MM-dd hh:mm:ss"); //设置显示格式
ui.consoleTable->setItem(rows - 1, 0, new QTableWidgetItem(time_str));
ui.consoleTable->setItem(rows - 1, 1, new QTableWidgetItem(operation));
ui.consoleTable->setItem(rows - 1, 2, new QTableWidgetItem(subname));
ui.consoleTable->setItem(rows - 1, 3, new QTableWidgetItem(filename));
ui.consoleTable->setItem(rows - 1, 4, new QTableWidgetItem(note));
ui.consoleTable->scrollToBottom(); // 滑动自动滚到最底部
}
//QTreeWidget的item的点击相应函数
void CloudViewer::itemSelected(QTreeWidgetItem* item, int count)
{
count = ui.dataTree->indexOfTopLevelItem(item); //获取item的行号
for (int i = 0; i != mycloud_vec.size(); i++)
{
viewer->updatePointCloud(mycloud_vec[i].cloud, "cloud" + QString::number(i).toStdString());
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud" + QString::number(i).toStdString());
}
//提取当前点云的RGB,点云数量等信息
int cloud_size = mycloud_vec[count].cloud->points.size();
unsigned int cloud_r = mycloud_vec[count].cloud->points[0].r;
unsigned int cloud_g = mycloud_vec[count].cloud->points[0].g;
unsigned int cloud_b = mycloud_vec[count].cloud->points[0].b;
bool multi_color = true;
if (mycloud_vec[count].cloud->points.begin()->r == (mycloud_vec[count].cloud->points.end() - 1)->r) //判断点云单色多色的条件(不是很严谨)
multi_color = false;
ui.propertyTable->setItem(0, 1, new QTableWidgetItem(QString::number(mycloud_vec.size())));
ui.propertyTable->setItem(1, 1, new QTableWidgetItem(QString::number(cloud_size)));
ui.propertyTable->setItem(2, 1, new QTableWidgetItem(QString::number(total_points)));
ui.propertyTable->setItem(3, 1, new QTableWidgetItem(multi_color ? "Multi Color" : (QString::number(cloud_r) + " " + QString::number(cloud_g) + " " + QString::number(cloud_b))));
//选中item所对应的点云尺寸变大
QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();
int selected_item_count = ui.dataTree->selectedItems().size();
for (int i = 0; i != selected_item_count; i++){
int cloud_id = ui.dataTree->indexOfTopLevelItem(itemList[i]);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,
2, "cloud" + QString::number(cloud_id).toStdString());
}
//mycloud = mycloud_vec[count];
ui.screen->update();
}
// consoleTable 右击响应事件
void CloudViewer::popMenuInConsole(const QPoint&){
QAction clearConsoleAction("Clear console", this);
QAction enableConsoleAction("Enable console", this);
QAction disableConsoleAction("Disable console", this);
connect(&clearConsoleAction, &QAction::triggered, this, &CloudViewer::clearConsole);
connect(&enableConsoleAction, &QAction::triggered, this, &CloudViewer::enableConsole);
connect(&disableConsoleAction, &QAction::triggered, this, &CloudViewer::disableConsole);
QPoint pos;
QMenu menu(ui.dataTree);
menu.addAction(&clearConsoleAction);
menu.addAction(&enableConsoleAction);
menu.addAction(&disableConsoleAction);
if (enable_console == true){
menu.actions()[1]->setVisible(false);
menu.actions()[2]->setVisible(true);
}
else{
menu.actions()[1]->setVisible(true);
menu.actions()[2]->setVisible(false);
}
menu.exec(QCursor::pos()); //在当前鼠标位置显示
}
// 清空 consoleTable
void CloudViewer::clearConsole(){
ui.consoleTable->clearContents();
ui.consoleTable->setRowCount(0);
}
// 允许使用 consoleTable
void CloudViewer::enableConsole(){
enable_console = true;
}
// 禁用 consoleTable
void CloudViewer::disableConsole(){
clearConsole();
enable_console = false;
}
//QTreeWidget的item的右击响应函数
void CloudViewer::popMenu(const QPoint&)
{
QTreeWidgetItem* curItem = ui.dataTree->currentItem(); //获取当前被点击的节点
if (curItem == NULL)return; //这种情况是右键的位置不在treeItem的范围内,即在空白位置右击
QString name = curItem->text(0);
int id = ui.dataTree->indexOfTopLevelItem(curItem);
string cloud_id = "cloud" + QString::number(id).toStdString();
QAction hideItemAction("Hide", this);
QAction showItemAction("Show", this);
QAction deleteItemAction("Delete", this);
QAction changeColorAction("Change color", this);
connect(&hideItemAction, &QAction::triggered, this, &CloudViewer::hideItem);
connect(&showItemAction, &QAction::triggered, this, &CloudViewer::showItem);
connect(&deleteItemAction, &QAction::triggered, this, &CloudViewer::deleteItem);
connect(&changeColorAction, &QAction::triggered, this, &CloudViewer::pointcolorChanged);
QPoint pos;
QMenu menu(ui.dataTree);
menu.addAction(&hideItemAction);
menu.addAction(&showItemAction);
menu.addAction(&deleteItemAction);
menu.addAction(&changeColorAction);
if (mycloud_vec[id].visible == true){
menu.actions()[1]->setVisible(false);
menu.actions()[0]->setVisible(true);
}
else{
menu.actions()[1]->setVisible(true);
menu.actions()[0]->setVisible(false);
}
menu.exec(QCursor::pos()); //在当前鼠标位置显示
}
void CloudViewer::hideItem()
{
QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();
for (int i = 0; i != ui.dataTree->selectedItems().size(); i++){
//TODO hide之后,item变成灰色,再次右击item时,“hideItem” 选项变成 “showItem”
//QTreeWidgetItem* curItem = ui.dataTree->currentItem();
QTreeWidgetItem* curItem = itemList[i];
QString name = curItem->text(0);
int id = ui.dataTree->indexOfTopLevelItem(curItem);
string cloud_id = "cloud" + QString::number(id).toStdString();
//QMessageBox::information(this, "cloud_id", QString::fromLocal8Bit(cloud_id.c_str()));
// 将cloud_id所对应的点云设置成透明
viewer->setPointCloudRenderingProperties(pcl::visualization::RenderingProperties::PCL_VISUALIZER_OPACITY, 0.0, cloud_id, 0);
QColor item_color = QColor(112, 122, 132, 255);
curItem->setTextColor(0, item_color);
mycloud_vec[id].visible = false;
}
// 输出窗口
consoleLog("Hide point clouds", "Point clouds selected", "", "");
ui.screen->update(); //刷新视图窗口,不能省略
}
void CloudViewer::showItem()
{
QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();
for (int i = 0; i != ui.dataTree->selectedItems().size(); i++){
//QTreeWidgetItem* curItem = ui.dataTree->currentItem();
QTreeWidgetItem* curItem = itemList[i];
QString name = curItem->text(0);
int id = ui.dataTree->indexOfTopLevelItem(curItem);
string cloud_id = "cloud" + QString::number(id).toStdString();
// 将cloud_id所对应的点云设置成透明
viewer->setPointCloudRenderingProperties(pcl::visualization::RenderingProperties::PCL_VISUALIZER_OPACITY, 1.0, cloud_id, 0);
QColor item_color;
if (theme_id == 0){
item_color = QColor(0, 0, 0, 255);
}
else{
item_color = QColor(241, 241, 241, 255);
}
curItem->setTextColor(0, item_color);
mycloud_vec[id].visible = true;
}
// 输出窗口
consoleLog("Show point clouds", "Point clouds selected", "", "");
ui.screen->update(); //刷新视图窗口,不能省略
}
void CloudViewer::deleteItem()
{
QList<QTreeWidgetItem*> itemList = ui.dataTree->selectedItems();
// ui.dataTree->selectedItems().size() 随着迭代次数而改变,因此循环条件要设置为固定大小的 selected_item_count
int selected_item_count = ui.dataTree->selectedItems().size();
for (int i = 0; i != selected_item_count; i++){
//QTreeWidgetItem* curItem = ui.dataTree->currentItem();
//QMessageBox::information(this, "itemList's size", QString::number(ui.dataTree->selectedItems().size()));
QTreeWidgetItem* curItem = itemList[i];
QString name = curItem->text(0);
int id = ui.dataTree->indexOfTopLevelItem(curItem);
//QMessageBox::information(this, "information", "curItem: " + name + " " + QString::number(id));
auto it = mycloud_vec.begin() + ui.dataTree->indexOfTopLevelItem(curItem);
// 删除点云之前,将其点的数目保存
int delete_points = (*it).cloud->points.size();
it = mycloud_vec.erase(it);
//QMessageBox::information(this, "information", QString::number(delete_points) + " " + QString::number(mycloud_vec.size()));
total_points -= delete_points;
setPropertyTable();
ui.dataTree->takeTopLevelItem(ui.dataTree->indexOfTopLevelItem(curItem));
}
// 移除之后再添加,避免 id 和资源管理树行号不一致的情况
viewer->removeAllPointClouds();
for (int i = 0; i != mycloud_vec.size(); i++)
{
viewer->addPointCloud(mycloud_vec[i].cloud, "cloud" + QString::number(i).toStdString());
viewer->updatePointCloud(mycloud_vec[i].cloud, "cloud" + QString::number(i).toStdString());
}
// 输出窗口
consoleLog("Delete point clouds", "Point clouds selected", "", "");
ui.screen->update();
}
//法线估计、曲面重建、网格面片显示
int CloudViewer::convertSurface()
{
/* 问题
好像该方法只能处理PointXYZ的点云,用PointXZYRGBA的点云编译会报错
调用boost::this_thread::sleep好像也会编译出错
*/
pcl::PointXYZ point;
cloud_xyz.reset(new pcl::PointCloud<pcl::PointXYZ>);
for (size_t i = 0; i < mycloud.cloud->size(); i++)
{
point.x = mycloud.cloud->points[i].x;
point.y = mycloud.cloud->points[i].y;
point.z = mycloud.cloud->points[i].z;
cloud_xyz->push_back(point);
}
if (!cloud_xyz)
{
return -1;
}
/****** 法向估计模块 ******/
//创建法线估计对象 n
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;
//创建法向数据指针 normals
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
//创建 kdtree 用于法向计算时近邻搜索
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud(cloud_xyz); //为 kdtree 输入点云
n.setInputCloud(cloud_xyz); //为法向估计对象输入点云
n.setSearchMethod(tree); //设置法向估计时所采取的搜索方式为kdtree
n.setKSearch(20); //设置法向估计时,k近邻搜索的点数
n.compute(*normals); //进行法向估计
QMessageBox::information(this, "information", "Normal estimation finished");
/****** 点云数据与法向数据拼接 ******/
//从这之后出现问题
//创建同时包含点和法线的数据结构的指针
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
//将已获得的点数据和法向数据拼接
pcl::concatenateFields(*cloud_xyz, *normals, *cloud_with_normals); //这里编译出错,与cloud的类型有关?改成PointXYZ的点云就没有报错了
//创建另一个kdtree用于重建
pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(new pcl::search::KdTree<pcl::PointNormal>);
//为kdtree输入点云数据,该点云数据类型为点和法向
tree2->setInputCloud(cloud_with_normals);
/****** 曲面重建模块 ******/
//创建贪婪三角形投影重建对象
pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3;
//创建多边形网格对象,用来存储重建结果
pcl::PolygonMesh triangles;
//设置参数
gp3.setSearchRadius(25); //设置连接点之间最大距离,用于确定k近邻的球半径
gp3.setMu(2.5); //设置最近邻距离的乘子,以得到每个点的最终搜索半径
gp3.setMaximumNearestNeighbors(100); //设置搜索的最近邻点的最大数量
gp3.setMaximumSurfaceAngle(M_PI / 2); //45度 最大平面角
gp3.setMinimumAngle(M_PI / 18); //10度 每个三角的最大角度?
gp3.setMaximumAngle(2 * M_PI / 3); //120度
gp3.setNormalConsistency(false); //若法向量一致,设为true
//设置点云数据和搜索方式
gp3.setInputCloud(cloud_with_normals);
gp3.setSearchMethod(tree2);
// 开始重建
gp3.reconstruct(triangles);
QMessageBox::information(this, "informaiton", "Reconstruction finished");
//将重建结果保存到硬盘文件中,重建结果以VTK格式存储
pcl::io::saveVTKFile("mymesh.vtk", triangles);
/*
//Additional vertex information
std::vector<int> parts = gp3.getPartIDs();
std::vector<int> states = gp3.getPointStates();
fstream fs;
fs.open("partsID.txt", ios::out);
if (!fs)
{
return -2;
}
fs << "number of point clouds:" << parts.size() << "\n";
for (int i = 0; i < parts.size(); i++)
{
if (parts[i] != 0)
{
fs << parts[i] << "\n";
}
}
*/
/****** 图形显示模块 ******/
QMessageBox::information(this, "informaiton", "Start to show");
viewer->addPolygonMesh(triangles, "my"); //设置要显示的网格对象
//设置网格模型显示模式
viewer->setRepresentationToSurfaceForAllActors(); //网格模型以面片形式显示
//viewer->setRepresentationToPointsForAllActors(); //网格模型以点形式显示
//viewer->setRepresentationToWireframeForAllActors(); //网格模型以线框图模式显示
// 输出窗口
consoleLog("Convert surface", "", "", "");
viewer->removeAllShapes();
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
//boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
int CloudViewer::convertWireframe()
{
/* 问题
好像该方法只能处理PointXYZ的点云,用PointXZYRGBA的点云编译会报错
调用boost::this_thread::sleep好像也会编译出错
*/
pcl::PointXYZ point;
cloud_xyz.reset(new pcl::PointCloud<pcl::PointXYZ>);
for (size_t i = 0; i < mycloud.cloud->size(); i++)
{
point.x = mycloud.cloud->points[i].x;
point.y = mycloud.cloud->points[i].y;
point.z = mycloud.cloud->points[i].z;
cloud_xyz->push_back(point);
}
if (!cloud_xyz)
{
return -1;
}
/****** 法向估计模块 ******/
//创建法线估计对象 n
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;
//创建法向数据指针 normals
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
//创建 kdtree 用于法向计算时近邻搜索
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud(cloud_xyz); //为 kdtree 输入点云
n.setInputCloud(cloud_xyz); //为法向估计对象输入点云
n.setSearchMethod(tree); //设置法向估计时所采取的搜索方式为kdtree
n.setKSearch(20); //设置法向估计时,k近邻搜索的点数
n.compute(*normals); //进行法向估计
QMessageBox::information(this, "information", "Normal estimation finished");
/****** 点云数据与法向数据拼接 ******/
//从这之后出现问题
//创建同时包含点和法线的数据结构的指针
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
//将已获得的点数据和法向数据拼接
pcl::concatenateFields(*cloud_xyz, *normals, *cloud_with_normals); //这里编译出错,与cloud的类型有关?改成PointXYZ的点云就没有报错了
//创建另一个kdtree用于重建
pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(new pcl::search::KdTree<pcl::PointNormal>);
//为kdtree输入点云数据,该点云数据类型为点和法向
tree2->setInputCloud(cloud_with_normals);
/****** 曲面重建模块 ******/
//创建贪婪三角形投影重建对象
pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3;
//创建多边形网格对象,用来存储重建结果
pcl::PolygonMesh triangles;
//设置参数
gp3.setSearchRadius(25); //设置连接点之间最大距离,用于确定k近邻的球半径
gp3.setMu(2.5); //设置最近邻距离的乘子,以得到每个点的最终搜索半径
gp3.setMaximumNearestNeighbors(100); //设置搜索的最近邻点的最大数量
gp3.setMaximumSurfaceAngle(M_PI / 2); //45度 最大平面角
gp3.setMinimumAngle(M_PI / 18); //10度 每个三角的最大角度?
gp3.setMaximumAngle(2 * M_PI / 3); //120度
gp3.setNormalConsistency(false); //若法向量一致,设为true
//设置点云数据和搜索方式
gp3.setInputCloud(cloud_with_normals);
gp3.setSearchMethod(tree2);
// 开始重建
gp3.reconstruct(triangles);
QMessageBox::information(this, "informaiton", "Reconstruction finished");
//将重建结果保存到硬盘文件中,重建结果以VTK格式存储
pcl::io::saveVTKFile("mymesh.vtk", triangles);
/*
//Additional vertex information
std::vector<int> parts = gp3.getPartIDs();
std::vector<int> states = gp3.getPointStates();
fstream fs;
fs.open("partsID.txt", ios::out);
if (!fs)
{
return -2;
}
fs << "number of point clouds:" << parts.size() << "\n";
for (int i = 0; i < parts.size(); i++)
{
if (parts[i] != 0)
{
fs << parts[i] << "\n";
}
}
*/
/****** 图形显示模块 ******/
QMessageBox::information(this, "informaiton", "Start to show");
viewer->addPolygonMesh(triangles, "my"); //设置要显示的网格对象
//设置网格模型显示模式
//viewer->setRepresentationToSurfaceForAllActors(); //网格模型以面片形式显示
//viewer->setRepresentationToPointsForAllActors(); //网格模型以点形式显示
viewer->setRepresentationToWireframeForAllActors(); //网格模型以线框图模式显示
// 输出窗口
consoleLog("Convert wireframe", "", "", "");
viewer->removeAllShapes();
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
//boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。