/****************************************************************************
Copyright (C) 2010-2012 Jules Morel, Pondichéry, France.
All rights reserved.
Contact : alexandre.piboule@onf.fr
Developers : Jules Morel
This file is part of Computree version 2.0.
Computree is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Computree is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Computree. If not, see .
*****************************************************************************/
#include "normalsestimator.h"
#ifdef USE_PCL
//DFS visitor, got help from http://stackoverflow.com/questions/14126/how-to-create-a-c-boost-undirected-graph-and-traverse-it-in-depth-first-search
class MSTVisitor : public boost::default_dfs_visitor{
public:
MSTVisitor(std::vector vNames, pcl::PointCloud* pCloud):vertNames(vNames),cloud(pCloud){}
template < typename Edge, typename Graph >
void tree_edge(Edge e, const Graph& g) const {
VertexIndexMap vMap = get(boost::vertex_index,g);
pcl::PointNormal pi = cloud->at(source(e,g));
pcl::PointNormal pj = cloud->at(target(e,g));
float prodScal = pi.normal_x*pj.normal_x+pi.normal_y*pj.normal_y+pi.normal_z*pj.normal_z;
if(prodScal<0.)
{
cloud->at(target(e,g)).normal[0] = - cloud->at(target(e,g)).normal_x;
cloud->at(target(e,g)).normal[1] = - cloud->at(target(e,g)).normal_y;
cloud->at(target(e,g)).normal[2] = - cloud->at(target(e,g)).normal_z;
}
}
private:
std::vector vertNames;
mutable pcl::PointCloud* cloud;
};
normalsEstimator::normalsEstimator(pcl::PointCloud pPointCloud, const IEstimatorObserverAndController *obs):pointCloud(pPointCloud), m_observer((IEstimatorObserverAndController*)obs)
{}
pcl::PointCloud normalsEstimator::getNormals(int nbNeighborsNormals)
{
pcl::PointCloud::Ptr cloud_smoothed (pointCloud.makeShared());
pcl::PointCloud cloud_smoothed_normals;
setProgress(0);
//std::cout<<" Normals : estimation"< ne;
pcl::search::KdTree::Ptr treeNE (new pcl::search::KdTree);
ne.setInputCloud (cloud_smoothed);
ne.setSearchMethod(treeNE);
ne.setKSearch (nbNeighborsNormals);
pcl::PointCloud::Ptr cloud_normals (new pcl::PointCloud());
ne.compute (*cloud_normals);
setProgress(16);
if(mustStop())
return cloud_smoothed_normals;
//std::cout<<" Normals : smoothing ... "< treePtsNormals;
treePtsNormals.setInputCloud(cloud_smoothed_normals.makeShared());
// build a K-nearest neighbors graph
pcl::KdTreeFLANN kdtree;
kdtree.setInputCloud (cloud_smoothed_normals.makeShared());
GraphPoisson cloud_graph;
size_t cloud_smoothed_normals_size = cloud_smoothed_normals.size();
for (size_t point_i = 0; (point_i < cloud_smoothed_normals_size) && !mustStop(); ++point_i)
{
std::vector k_indices (nbNeighborsNormals);
std::vector k_distances (nbNeighborsNormals);
kdtree.nearestKSearch (static_cast (point_i), nbNeighborsNormals, k_indices, k_distances);
for (int k_i = 0; k_i < static_cast (k_indices.size ()); ++k_i){
pcl::PointNormal pi = cloud_smoothed_normals.at(point_i);
pcl::PointNormal pj = cloud_smoothed_normals.at(k_indices[k_i]);
float w = 1 - fabs(pi.normal_x*pj.normal_x+pi.normal_y*pj.normal_y+pi.normal_z*pj.normal_z);
add_edge (point_i, k_indices[k_i], Weight (w), cloud_graph);
}
setProgress(32+((point_i*16)/cloud_smoothed_normals_size));
}
if(mustStop())
return cloud_smoothed_normals;
//const size_t E = num_edges (cloud_graph),V = num_vertices (cloud_graph);
//std::cout<<" Normals : The graph has "< spanning_tree;
kruskal_minimum_spanning_tree(cloud_graph, std::back_inserter(spanning_tree));
setProgress(64);
//std::cout<<" Normals : size of most spanning tree : "< mstNames(num_vertices(cloud_graph));
//Index map for verts in MST, all graphs use an indepenent index system.
VertexIndexMap mstIndexMap = get(boost::vertex_index, MST);
size_t spanning_tree_size = spanning_tree.size();
for(size_t i = 0; (i < spanning_tree_size) && !mustStop(); ++i){
EdgePoisson e = spanning_tree.at(i);
Vertex v1 = source(e,cloud_graph);
Vertex v2 = target(e,cloud_graph);
//insert the edge to the MST graph
// Both graphs will share the vertices in verts list.
std::pair myPair = boost::add_edge(v1,v2,MST);
//get the vertex index in the MST and set the name to that of original graph
// mstNames will be used by the visitor
mstNames.at(mstIndexMap[v1]) = v1;
mstNames.at(mstIndexMap[v2]) = v2;
setProgress(64+((i*16)/spanning_tree_size));
}
if(mustStop())
return cloud_smoothed_normals;
MSTVisitor vis(mstNames,&cloud_smoothed_normals);
//std::cout << " Normals : Depth first search on most spanning tree" << std::endl;
boost::depth_first_search(MST, boost::visitor(vis).root_vertex(0));
setProgress(100);
return cloud_smoothed_normals;
}
void normalsEstimator::setProgress(int p)
{
if(m_observer != NULL)
m_observer->setEstimationProgress(p);
}
bool normalsEstimator::mustStop() const
{
if(m_observer != NULL)
return m_observer->mustStopEstimation();
return false;
}
#endif