Visualizzazione post con etichetta Pcl. Mostra tutti i post
Visualizzazione post con etichetta Pcl. Mostra tutti i post

martedì 24 novembre 2020

Visualizzatore di nuvole di punti con GLFW e PCL

Per imparare un po' di librerie mi sono fatto il mio visualizzatore di nuvole di punti

Volevo includere ImGui o NanoGui per settare i valori delle variabili ma non riesco a trovare il modo di compilarli

 https://github.com/c1p81/cloudpoint_glfw


La visualizzazione si modifica con i tasti freccia/pg up-down ed il mouse

giovedì 19 novembre 2020

PCL Library e Visual Studio 2019

 Per installare ed usare PCL su Windows la cosa piu' comoda e' utilizzare il pacchetto PCL-1.11.1-AllInOne-msvc2019-win64 

Una volta installato il file exe per esempio in C:\Program Files\PCL 1.11.1


Per creare un progetto in QT con le PCL si devono aggiungere nella path OpenNI2 (stranamente l'installer non la ha aggiunta nella PATH)

Ho sostituito la versione di CMAKE da quella di default di QT con l'installer di CMake  (modificabile dai Kit di Qt) ed come compilatore ho selezionato MSVC2019



il file CMAKE e' il seguente

===============================================

cmake_minimum_required(VERSION 3.5)


project(t3 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(PCL 1.3 REQUIRED COMPONENTS common io features)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})


add_executable(t3 main.cpp)
target_link_libraries(t3 ${PCL_LIBRARIES})

===============================================

mentre un programma di esempio e' 
===============================================
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>

#include <pcl/features/normal_3d.h>

using namespace std;

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

     if (pcl::io::loadPCDFile<pcl::PointXYZ> ("c://gabriele.pcd", *cloud) == -1) //* load the file
     {
       PCL_ERROR ("Couldn't read file test_pcd.pcd \n");
       return (-1);
     }
     //std::cout << "Loaded " << cloud->width * cloud->height << " data points from test_pcd.pcd with the following fields: " << std::endl;
     /*for (size_t i = 0; i < cloud->points.size (); ++i)
       std::cout << "    " << cloud->points[i].x << " "    << cloud->points[i].y << " "    << cloud->points[i].z << std::endl;
    */
     // 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);

       //std::cout << endl << cloud_normals->size() << endl;


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

       int i = 0;
       for (pcl::Normal n : *cloud_normals) {
           //std::cerr << i << " n = " << n.normal_x << ", " << n.normal_y << ", " << n.normal_z << "\n";
           std::cout << n.normal_x << "," << n.normal_y << "," << n.normal_z << "\n";
           i++;
       }



    return 0;
}


sabato 7 novembre 2020

PCL e Tango tablet

Ho ritirato fuori il mio tablet Google Yellowstone Tango per provare il trattamento dati delle nuvole dei punti con PCL

I dati reali sono stati presi presso la cava di Maiano (Fiesole) che e' la palestra di geologia per numerose generazioni di geologi fiorentini 

Ho ripreso con lo scanner del tablet questo dettaglio 


in particolare volevo vedere se riuscivo a misurare l'angolo tra i due piani indicati nella foto sottostante


(quello in blu e' il piano del fronte di scavo, quello in rosso e' relativo ad una frattura)

Usando la app Clino Fieldmove e Innstereo 


a posteriori la scelta delle superfici non e' stata felicissima perche' sono tutte ad alto angolo e si distinguono poco sullo stereoplot

Con il tablet la distanza massima a cui era possibile avere risposta dallo scanner era di circa 3 m. Per prova la superficie e' stata bagnata per vedere se il laser infrarosso era influenzato dall'umidita' della parete ma non si verifiche significative tra superficie asciutta e bagnata

I dati sono stati salvati in PLY e sono stati elaborati con PCL in QtCreator




Il programma dopo aver caricato il file PLY fa un sottocampionamento dei dati con VoxelGrid (passando da oltre 200.000 superfici a poco oltre 50 superfici) e calcola le  normali. I dati sono visualizzati con il visualizzatore interno a PLC

leggermente modificato da https://github.com/jeffdelmerico/pointcloud_tutorial


========================================

cmake_minimum_required(VERSION 3.5)

project(maiano LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(PCL 1.3 REQUIRED COMPONENTS common io features visualization)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

add_executable(maiano main.cpp)

target_link_libraries(maiano ${PCL_LIBRARIES})

========================================

#include <iostream>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/ply_io.h>

#include <pcl/io/pcd_io.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/pcl_visualizer.h>


using namespace std;

void
downsample (pcl::PointCloud<pcl::PointXYZRGB>::Ptr &points, float leaf_size,
            pcl::PointCloud<pcl::PointXYZRGB>::Ptr &downsampled_out)
{
    cout << "PointCloud before filtering: " << points->width * points->height
           << " data points (" << pcl::getFieldsList (*points) << ")." << std::endl;

  pcl::VoxelGrid<pcl::PointXYZRGB> vox_grid;
  vox_grid.setLeafSize (leaf_size, leaf_size, leaf_size);
  vox_grid.setInputCloud (points);
  vox_grid.filter (*downsampled_out);
  cout << "PointCloud after filtering: " << downsampled_out->width * downsampled_out->height
         << " data points (" << pcl::getFieldsList (*downsampled_out) << ")." << std::endl;

}

void compute_surface_normals (pcl::PointCloud<pcl::PointXYZRGB>::Ptr &points, float normal_radius,
                                    pcl::PointCloud<pcl::Normal>::Ptr &normals_out)
{
  pcl::NormalEstimation<pcl::PointXYZRGB, pcl::Normal> norm_est;

  // Use a FLANN-based KdTree to perform neighborhood searches
  norm_est.setSearchMethod (pcl::search::KdTree<pcl::PointXYZRGB>::Ptr
                            (new pcl::search::KdTree<pcl::PointXYZRGB>));

  // Specify the size of the local neighborhood to use when computing the surface normals
  norm_est.setRadiusSearch (normal_radius);

  // Set the input points
  norm_est.setInputCloud (points);

  // Estimate the surface normals and store the result in "normals_out"
  norm_est.compute (*normals_out);
}

void visualize_normals (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr points,
                        const pcl::PointCloud<pcl::PointXYZRGB>::Ptr normal_points,
                        const pcl::PointCloud<pcl::Normal>::Ptr normals)
{
  // Add the points and normals to the vizualizer
  pcl::visualization::PCLVisualizer viz;
  viz.addPointCloud (points, "points");
  viz.addPointCloud (normal_points, "normal_points");

  viz.addPointCloudNormals<pcl::PointXYZRGB, pcl::Normal> (normal_points, normals, 1, 0.01, "normals");

  // Give control over to the visualizer
  viz.spin ();
}


int main (int argc, char** argv)
{
    // Load data from pcd
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>);
    //if (pcl::io::loadPCDFile<pcl::PointXYZRGB> ("../data/robot1.pcd", *cloud) == -1) //* load the file
    if (pcl::io::loadPLYFile<pcl::PointXYZRGB> ("maiano.ply", *cloud) == -1) //* load the file

    {
        PCL_ERROR ("Couldn't read file robot1.pcd \n");
        return (-1);
    }

    // Point Clouds to hold output
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr downsampled (new pcl::PointCloud<pcl::PointXYZRGB>);
    pcl::PointCloud<pcl::Normal>::Ptr normals (new pcl::PointCloud<pcl::Normal>);

    // Downsample the cloud
    const float voxel_grid_leaf_size = 1.0;
    downsample (cloud, voxel_grid_leaf_size, downsampled);

    // Compute surface normals
    const float normal_radius = 1.0;
    compute_surface_normals (downsampled, normal_radius, normals);

    visualize_normals(cloud, downsampled, normals);

    int i = 0;
    for (pcl::Normal n : *normals) {
        std::cout << n.normal_x << "," << n.normal_y << "," << n.normal_z <<"\n";
        i++;
    }

    return(0);
}

========================================

Plottando le normali si ha questo risultato



Al di la' dell'orientamento verso il Nord (che lo scanner non registra in quanto non calibra i dati con la bussola) il programma estra sia le suprfici ad alto angolo che alcun suborizzontali.. non so quanto i dati alla fine siano corretti....i dati delle normali vengono forniti come componenti x,y,z di un versore con origine 0,0,0....non so se ho fatto qualche sbaglio nella conversione in strike e dip



lunedì 2 novembre 2020

Primi passi con Point Cloud Library

Con l'arrivo del sensore lidar sugli IPhone (rip Project Tango) forse e' il caso di ritirare fuori le librerie per trattare le nuvole di punti come PCL 

Per installare la liberia su Debian e' sufficiente

apt-get install libpcl-dev

che si porta dietro un po' di dipendenze come boost

I primi passi sono ovviamente quelli di inserire la libreria in un progetto. Per questo sono partito da QTCreator con le varie opzioni di Make

Compilazione con CMake

per la compilazione si devono aggiungere le righe in giallo al file CMakeLists.txt. Attenzione che in PCL le funzioni sono distribuite in vari moduli che devono essere specificati in REQUIRED COMPONENTS

cmake_minimum_required(VERSION 3.5)

project(nuvola LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(PCL 1.3 REQUIRED COMPONENTS common io features)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

add_executable(nuvola main.cpp)

target_link_libraries(nuvola ${PCL_LIBRARIES})

Compilazione con QMake

Usando qmake, come prima, si devono modificare le righe in giallo.  Si deve anche specificare  c++14 

TEMPLATE = app
CONFIG += console c++14
CONFIG -= app_bundle
CONFIG -= qt
CONFIG += link_pkgconfig
PKGCONFIG += eigen3
INCLUDEPATH += /usr/include/pcl-1.11
LIBS += -L/usr/lib/x86_64-linux-gnu -lpcl_common -lpcl_io -lpcl_features -lpcl_search
QT += widgets

SOURCES += \
        main.cpp

Compilazione con Make

Per verificare quali sono gli switch di compilazione necessari per usare make si puo'usare

pkg-config --cflags --libs pcl_commons-1.7

anche in questo caso si deve ripetere l'operazione per ogni modulo utilizzato

Di seguito un semplice esempio di come leggere un file PCD, calcolare le normali e stampare i dati a schermo

===========================================

#include <iostream>

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>

#include <pcl/features/normal_3d.h>

using namespace std;

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

     if (pcl::io::loadPCDFile<pcl::PointXYZ> ("/home/luca/gabriele.pcd", *cloud) == -1) //* load the file
     {
       PCL_ERROR ("Couldn't read file test_pcd.pcd \n");
       return (-1);
     }
     //std::cout << "Loaded " << cloud->width * cloud->height << " data points from test_pcd.pcd with the following fields: " << std::endl;
     /*for (size_t i = 0; i < cloud->points.size (); ++i)
       std::cout << "    " << cloud->points[i].x << " "    << cloud->points[i].y << " "    << cloud->points[i].z << std::endl;
    */
     // 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);

       //std::cout << endl << cloud_normals->size() << endl;


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

       int i = 0;
       for (pcl::Normal n : *cloud_normals) {
           //std::cerr << i << " n = " << n.normal_x << ", " << n.normal_y << ", " << n.normal_z << "\n";
           std::cout << n.normal_x << "," << n.normal_y << "," << n.normal_z << "\n";
           i++;
       }



    return 0;
}




Geologi

  E so anche espatriare senza praticamente toccare strada asfaltata