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