Note 1: point cloud environment construction and normal vector acquisition

1. Point cloud environment

1.1 point cloud
Relationship between point cloud and 3D image: 3D image is a special form of information expression, which is characterized by the data of three dimensions in the expressed space, including depth map (expressing the distance between the object and the camera in gray scale), geometric model (established by CAD software), point cloud model (all reverse engineering equipment sample the object into point cloud). Compared with two-dimensional image, three-dimensional image can realize natural object background decoupling with the help of the information of the third dimension. Point cloud data is the most common and basic 3D model. The point cloud model is often obtained directly from measurement. Each point corresponds to a measurement point without other processing means, so it contains the maximum amount of information. These information is hidden in the point cloud and needs to be extracted by other extraction methods. The process of extracting the information in the point cloud is three-dimensional image processing.
Concept of Point Cloud: a Point Cloud is a massive set of points that express the spatial distribution of the target and the characteristics of the target surface under the same spatial reference system. After obtaining the spatial coordinates of each sampling point on the object surface, a set of points is obtained, which is called "Point Cloud".

1.2 environment configuration

1.2.1 PCL library installation
The first time: failed and could not be solved, so he gave up reinstalling the system.

The second time:

         step1: a grub install fatal error occurred during the installation of ubuntu. It was not resolved in two days, so it was abandoned and the u disk was replaced successfully.

        Step 2: install dependent Libraries

  sudo apt-get update
  sudo apt-get install git build-essential linux-libc-dev
  sudo apt-get install cmake cmake-gui 
  sudo apt-get install libusb-1.0-0-dev libusb-dev libudev-dev
  sudo apt-get install mpi-default-dev openmpi-bin openmpi-common  
  sudo apt-get install libflann1.9 libflann-dev    #1.9 corresponding to Ubuntu 18
  sudo apt-get install libeigen3-dev
  sudo apt-get install libboost-all-dev
  sudo apt-get install libvtk7.1-qt libvtk7.1 libvtk7-dev     #New version 7.1
  sudo apt-get install libqhull* libgtest-dev
  sudo apt-get install freeglut3-dev pkg-config
  sudo apt-get install libxmu-dev libxi-dev 
  sudo apt-get install openjdk-8-jdk openjdk-8-jre      #QT SDK has been discarded, use this code instead

        step3: Download

git clone 

        step4: Compiling

cd pcl 
mkdir release 
cd release

#After compilation
sudo make install

        step5: check

        Create test_pcl folder, create test through vscode_ Pcl.cpp and write

#include <iostream>
#include <pcl/common/common_headers.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/console/parse.h>
using namespace std;
int main(int argc, char **argv) {//Cylindrical point cloud test
  cout << "Test PCL !" << endl;
  pcl::PointCloud<pcl::PointXYZRGB>::Ptr point_cloud_ptr (new pcl::PointCloud<pcl::PointXYZRGB>);
  uint8_t r(255), g(15), b(15);
  for (float z(-1.0); z <= 1.0; z += 0.05) {//Making cylindrical point clouds
  	for (float angle(0.0); angle <= 360.0; angle += 5.0) {
      pcl::PointXYZRGB point;
      point.x = cos (pcl::deg2rad(angle));
      point.y = sin (pcl::deg2rad(angle));
      point.z = z;
      uint32_t rgb = (static_cast<uint32_t>(r) << 16 | static_cast<uint32_t>(g) << 8 | static_cast<uint32_t>(b));
      point.rgb = *reinterpret_cast<float*>(&rgb);
      point_cloud_ptr->points.push_back (point);
    if (z < 0.0) {//Color gradient
      r -= 12;
      g += 12;
    else {
      g -= 12;
      b += 12;
  point_cloud_ptr->width = (int) point_cloud_ptr->points.size ();
  point_cloud_ptr->height = 1;
  pcl::visualization::CloudViewer viewer ("pcl—test test");

  while (!viewer.wasStopped()){ };
  return 0;

           Use touch CMakeLists.txt and write

cmake_minimum_required(VERSION 2.6)
find_package(PCL 1.2 REQUIRED)
add_executable(test_pcl test_pcl.cpp)
target_link_libraries (test_pcl ${PCL_LIBRARIES})

         Then in test_ Create a build folder under the PCL folder.
         Start compilation:
        Open the terminal in the build folder (right click + T under the folder)
        Terminal input

cmake ..


  Except that the file name is garbled, it is basically completed!

1.2.2 installing realsense D455

        Note: install the version suitable for the local kernel from the official website, otherwise it will show that you can't connect.

         Download decompression

cd librealsense
sudo apt-get install libudev-dev pkg-config libgtk-3-dev
sudo apt-get install libusb-1.0-0-dev pkg-config
sudo apt-get install libglfw3-dev
sudo apt-get install libssl-dev
mkdir build
cd build
cmake ../ -DBUILD_EXAMPLES=true
make -j4
sudo make install 



1.2.3 installing openGL

         See Installing opengl under linux_ Csp123258 column - CSDN blog_ linux Installation opengl

1.2.4 installation glfw

         See Installation and use of glfw under Ubuntu_ dxmcu's column - CSDN blog Installation and use of glfw under buntu_ dxmcu Installation and use of glfw under Ubuntu_ dxmcu's column - CSDN blog

         Tsinghua source, China University of science and technology source, and the source code package relying on build dep glfw can not be found without changing the source. Skip direct compilation. I don't know if it's OK.

1.2.5 installing opencv4.5.3

        See Ubuntu 18 installing opencv3.4_zhongqli's blog - CSDN blog       

         There may be    errorE: unable to locate libjasper-dev

Input at terminal     

   sudo add-apt-repository "deb xenial-security main"
   sudo apt update
   sudo apt upgrade
   sudo apt install libjasper1 libjasper-dev

One more time will solve it.

2. Calculation of normal vector
2.1 normal vector
The method based on local surface fitting firstly, Hoppe et al. Proposed the assumption that the sampling plane of the point cloud is smooth everywhere in the surface reconstruction algorithm based on the directed distance function. Therefore, the local neighborhood of any point can be well fitted with the plane; Therefore, for each point P in the point cloud, k nearest adjacent points are obtained, and then a local plane P in the sense of least squares is calculated for these points, and the normal vector is found.



  2.2 PCL processing point cloud data of realsense

Initial idea: there are two kinds of point cloud data obtained by realsense: photo data. ply and recording data. bag, but pcl needs to process pcd data, so. ply should be converted to. pcd (x,y,z,RGB) format, and then imported into pcl for processing.

Wrong thinking: instead of saving data directly from realsense, you should read the data and directly use PCL to process and return the normal vector.

First, get the position and color data from realsense

#ifndef RSDEVICE_H
#define RSDEVICE_H
#include <librealsense/rs.hpp>
#include <QString>

#Encapsulates the operation of realsense
class rsdevice
    rs::context ctx;
    rs::device * dev;
    int devCount;
    QString devName;
    QString devSerial;
    QString devFwVersion;
    QString devCamType;
    QString devIspFwVersion;
    bool streamEnable;

    QString getDevName(){
        return devName;

    QString getDevSerial(){
        return devSerial;

    QString getDevFwVersion(){
        return devFwVersion;

    QString getCamType(){
        return devCamType;

    QString getIspFwVersion(){
        return devIspFwVersion;

    void initRsDevice();
    void enableStream();
    uchar * getFrameData();
    bool isSteamEnable();
    float getDistance(int x,int y);

#endif // RSDEVICE_H

#Initialize camera
void rsdevice::initRsDevice()
 devCount = ctx.get_device_count();

 dev = ctx.get_device(0);

#Turn on the camera data stream. Here, we only turn on two channels of data, one channel of RGB color data and one channel of depth camera data
void rsdevice::enableStream(){
    streamEnable = true;
    dev->enable_stream(rs::stream::color, 640, 480, rs::format::rgb8, 60);
    dev->enable_stream(rs::stream::depth,640,480, rs::format::z16, 60);

#Get RGBC color data
uchar * rsdevice::getFrameData(){
    return (uchar *) dev->get_frame_data(rs::stream::color);

#Get depth data
float rsdevice::getDistance(int x, int y){
    uint16_t *depthImage = (uint16_t *) dev->get_frame_data(rs::stream::depth);
    float scale = dev->get_depth_scale();
    rs::intrinsics depthIntrin = dev->get_stream_intrinsics(rs::stream::depth);
    uint16_t depthValue = depthImage[y * depthIntrin.width + x];
    float depthInMeters = depthValue * scale;
    return depthInMeters;

Then PCL is used to obtain the normal vector

#PCL get normal vector
 #include <pcl/point_types.h>
    #include <pcl/features/normal_3d.h>

  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);

  ... read, pass in or create a point cloud ...

  // Create the normal estimation class, and pass the input dataset to it
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud);

  // Create an empty kdtree representation, and pass it to the normal estimation object.
  // Its content will be filled inside the object, based on the given input dataset (as no other search surface is given).
  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
  ne.setSearchMethod (tree);

  // Output datasets
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);

  // Compute the features
  ne.compute (*cloud_normals);

  // cloud_normals->points.size () should have the same size as the input cloud->points.size ()*

The theory is over, and the assembly of c + + has to be solved! (smile)

Keywords: Linux Ubuntu

Added by dfarrar on Wed, 10 Nov 2021 16:34:44 +0200