main.cpp
#include "pclviewer.h"
#include <QApplication>
#include <QMainWindow>
int main (int argc, char *argv[])
{
QApplication a (argc, argv);
PCLViewer w;
w.show ();
return a.exec ();
}
initialize('web'); $snip = $modx->runSnippet("getSiteNavigation", array('id'=>5, 'phLevels'=>'sitenav.level0,sitenav.level1', 'showPageNav'=>'n')); $chunkOutput = $modx->getChunk("site-header", array('sitenav'=>$snip)); $bodytag = str_replace("[[+showSubmenus:notempty=`", "", $chunkOutput); $bodytag = str_replace("`]]", "", $bodytag); echo $bodytag; echo "\n"; ?>
In this tutorial we will learn how to create a PCL + Qt project, we will use Cmake rather than Qmake.The program we are going to write is a simple PCL visualizer which allow to change a randomly generated point cloud color.
Contents
For this project Qt is of course mandatory, make sure it is installed and PCL deals with it. qmake is a tool that helps simplify the build process for development project across different platforms, we will use cmake instead because most projects in PCL uses cmake and it is simpler in my opinion.
This is how I organized the project: the build folder contains all built files and the src folder holds all sources files
.
├── build
└── src
├── CMakeLists.txt
├── main.cpp
├── pclviewer.cpp
├── pclviewer.h
├── pclviewer.ui
├── pcl_visualizer.pro
└── pcl_visualizer.pro.user
If you want to change this layout you will have to do minor modifications in the code, especially line 2 of pclviewer.cpp Create the folder tree and download the sources files from github.
Note
File paths should not contain any special caracter or the compilation might fail with a moc: Cannot open options file specified with @ error message.
In this example note that I deleted the Debug configuration and only kept the Release config. Use relative paths like this is better than absolute paths; this project should work wherever it has been put.
We specify in the general section that we want to build in the folder ../build (this is a relative path from the .pro file).
The first step of the building is to call cmake (from the build folder) with argument ../src; this is gonna create all files in the build folder without modifying anything in the src foler; thus keeping it clean.
Then we just have to compile our program; the argument -j2 allow to specify how many thread of your CPU you want to use for compilation. The more thread you use the faster the compilation will be (especially on big projects); but if you take all threads from the CPU your OS will likely be unresponsive during the compilation process. See compiler optimizations for more information.
If you don’t want to use Qt Creator but Eclipse instead; see using PCL with Eclipse.
The point of using Qt for your projects is that you can easily build cross-platform UIs. The UI is held in the .ui file You can open it with a text editor or with Qt Creator, in this example the UI is very simple and it consists of :
- QMainWindow, QWidget: the window (frame) of your application
- qvtkWidget: The VTK widget which holds the PCLVisualizer
- QLabel: Display text on the user interface
- QSlider: A slider to choose a value (here; an integer value)
- QLCDNumber: A digital display, 8 segment styled
If you click on Edit Signals/Slots at the top of the Qt window you will see the relationships between some of the UI objects. In our example the sliderMoved(int) signal is connected to the display(int) slot; this means that everytime we move the slider the digital display is updated accordingly to the slider value.
Now, let’s break down the code piece by piece.
#include "pclviewer.h"
#include <QApplication>
#include <QMainWindow>
int main (int argc, char *argv[])
{
QApplication a (argc, argv);
PCLViewer w;
w.show ();
return a.exec ();
}
#ifndef PCLVIEWER_H
#define PCLVIEWER_H
#include <iostream>
// Qt
#include <QMainWindow>
// Point Cloud Library
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
// Visualization Toolkit (VTK)
#include <vtkRenderWindow.h>
typedef pcl::PointXYZRGBA PointT;
typedef pcl::PointCloud<PointT> PointCloudT;
This file is the header for the class PCLViewer; we include QMainWindow because this class contains UI elements, we include the PCL headers we will be using and the VTK header for the qvtkWidget. We also define typedefs of the point types and point clouds, this improves readabily.
namespace Ui
{
class PCLViewer;
}
We declare the namespace Ui and the class PCLViewer inside it.
class PCLViewer : public QMainWindow
{
Q_OBJECT
This is the definition of the PCLViewer class; the macro Q_OBJECT tells the compiler that this object contains UI elements; this imply that this file will be processed through the Meta-Object Compiler (moc).
public:
explicit PCLViewer (QWidget *parent = 0);
~PCLViewer ();
The constructor and destructor of the PCLViewer class.
public slots:
void
randomButtonPressed ();
void
RGBsliderReleased ();
void
pSliderValueChanged (int value);
void
redSliderValueChanged (int value);
void
greenSliderValueChanged (int value);
void
blueSliderValueChanged (int value);
These are the public slots; these functions will be linked with UI elements actions.
protected:
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer;
PointCloudT::Ptr cloud;
unsigned int red;
unsigned int green;
unsigned int blue;
#include "pclviewer.h"
#include "../build/ui_pclviewer.h"
PCLViewer::PCLViewer (QWidget *parent) :
QMainWindow (parent),
ui (new Ui::PCLViewer)
{
ui->setupUi (this);
this->setWindowTitle ("PCL viewer");
// Setup the cloud pointer
cloud.reset (new PointCloudT);
// The number of points in the cloud
cloud->points.resize (200);
We include the class header and the header for the UI object; note that this file is generated by the moc and it’s path depend on where you call cmake !
After that is the constructor implementation; we setup the ui and the window title name. | Then we initialize the cloud pointer member of the class at a newly allocated point cloud pointer. | The cloud is resized to be able to hold 200 points.
// The default color
red = 128;
green = 128;
blue = 128;
// Fill the cloud with some points
for (size_t i = 0; i < cloud->points.size (); ++i)
{
cloud->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
cloud->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
cloud->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
cloud->points[i].r = red;
cloud->points[i].g = green;
cloud->points[i].b = blue;
}
// Set up the QVTK window
viewer.reset (new pcl::visualization::PCLVisualizer ("viewer", false));
ui->qvtkWidget->SetRenderWindow (viewer->getRenderWindow ());
viewer->setupInteractor (ui->qvtkWidget->GetInteractor (), ui->qvtkWidget->GetRenderWindow ());
ui->qvtkWidget->update ();
The update() method of the qvtkWidget should be called each time you modify the PCL visualizer; if you don’t call it you don’t know if the visualizer will be updated before the user try to pan/spin/zoom.
// Connect "random" button and the function
connect (ui->pushButton_random, SIGNAL (clicked ()), this, SLOT (randomButtonPressed ()));
// Connect R,G,B sliders and their functions
connect (ui->horizontalSlider_R, SIGNAL (valueChanged (int)), this, SLOT (redSliderValueChanged (int)));
connect (ui->horizontalSlider_G, SIGNAL (valueChanged (int)), this, SLOT (greenSliderValueChanged (int)));
connect (ui->horizontalSlider_B, SIGNAL (valueChanged (int)), this, SLOT (blueSliderValueChanged (int)));
connect (ui->horizontalSlider_R, SIGNAL (sliderReleased ()), this, SLOT (RGBsliderReleased ()));
connect (ui->horizontalSlider_G, SIGNAL (sliderReleased ()), this, SLOT (RGBsliderReleased ()));
connect (ui->horizontalSlider_B, SIGNAL (sliderReleased ()), this, SLOT (RGBsliderReleased ()));
// Connect point size slider
connect (ui->horizontalSlider_p, SIGNAL (valueChanged (int)), this, SLOT (pSliderValueChanged (int)));
viewer->addPointCloud (cloud, "cloud");
pSliderValueChanged (2);
viewer->resetCamera ();
ui->qvtkWidget->update ();
}
We finaly reset the camera within the PCL Visualizer not avoid the user having to zoom out and update the qvtkwidget to be sure the modifications will be displayed.
void
PCLViewer::randomButtonPressed ()
{
printf ("Random button was pressed\n");
// Set the new color
for (size_t i = 0; i < cloud->size(); i++)
{
cloud->points[i].r = 255 *(1024 * rand () / (RAND_MAX + 1.0f));
cloud->points[i].g = 255 *(1024 * rand () / (RAND_MAX + 1.0f));
cloud->points[i].b = 255 *(1024 * rand () / (RAND_MAX + 1.0f));
}
viewer->updatePointCloud (cloud, "cloud");
ui->qvtkWidget->update ();
}
void
PCLViewer::RGBsliderReleased ()
{
// Set the new color
for (size_t i = 0; i < cloud->size (); i++)
{
cloud->points[i].r = red;
cloud->points[i].g = green;
cloud->points[i].b = blue;
}
viewer->updatePointCloud (cloud, "cloud");
ui->qvtkWidget->update ();
}
void
PCLViewer::redSliderValueChanged (int value)
{
red = value;
printf ("redSliderValueChanged: [%d|%d|%d]\n", red, green, blue);
}
void
PCLViewer::greenSliderValueChanged (int value)
{
green = value;
printf ("greenSliderValueChanged: [%d|%d|%d]\n", red, green, blue);
}
void
PCLViewer::blueSliderValueChanged (int value)
{
blue = value;
printf("blueSliderValueChanged: [%d|%d|%d]\n", red, green, blue);
}
PCLViewer::~PCLViewer ()
{
delete ui;
}
The destructor.
If you wanted to update the point cloud when the slider value is changed you could just call the RGBsliderReleased () function inside the *sliderValueChanged (int) functions. The connect between sliderReleased () / RGBsliderReleased () would become useless then.
If you want to know more about Qt and PCL go take a look at PCL apps like PCD video player or manual registration.
Re-use the CMakeLists.txt from this tutorial if you want to compile the application outside of PCL.