Merge branch 'Point_set_classification-GF-old' into Point_set_classification-GF

This commit is contained in:
Simon Giraudot 2016-07-22 11:58:16 +02:00
commit 4a03949eec
41 changed files with 29654 additions and 4 deletions

View File

@ -0,0 +1,26 @@
namespace CGAL {
/*!
\mainpage User Manual
\anchor Chapter_Data_Classification
\cgalAutoToc
\author Simon Giraudot
This component implements an algorithm that classifies a point cloud into a user-defined set of classes (such as ground, vegetation, buildings, etc.). A flexible API is provided so that the user can compute its own local attributes on the point cloud and define its own classes based on these attributes.
\section Data_classification_attributes Attributes
\section Data_classification_types Classification types
\section Data_classification_psc Point set classification
\section Data_classification_custom Defining custom attributes and types
\section Data_classification_example Example
The following example reads an input file (LIDAR point set in PLY format); computes 4 attributes (scatter, elevation, horizontality and non-planarity) from this input; defines 3 classification types (vegetation, ground and roof) along with the effects of attributes on them; classifies the point set; recovers the segmented input in 3 separated arrays.
\cgalExample{Data_classification/example_point_set_classification.cpp}
*/
} /* namespace CGAL */

View File

@ -0,0 +1,5 @@
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}
PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Data Classification"
INPUT = ${CMAKE_SOURCE_DIR}/Data_classification/doc/Data_classification/ \
${CMAKE_SOURCE_DIR}/Data_classification/include/CGAL/Data_classification/ \
${CMAKE_SOURCE_DIR}/Data_classification/include/CGAL/Point_set_classification.h

View File

@ -0,0 +1,46 @@
/*!
\defgroup PkgDataClassification Point Set Shape Detection Reference
\defgroup PkgDataClassificationConcepts Concepts
\ingroup PkgDataClassification
\defgroup PkgDataClassificationShapes Shapes
\ingroup PkgDataClassification
\addtogroup PkgDataClassification
\cgalPkgDescriptionBegin{Data Classification, PkgDataClassificationSummary}
\cgalPkgPicture{data_classif.png}
\cgalPkgSummaryBegin
\cgalPkgAuthors{Simon Giraudot}
\cgalPkgDesc{This component implements an algorithm that classifies a point cloud into a user-defined set of classes (such as ground, vegetation, buildings, etc.). A flexible API is provided so that the user can compute its own local attributes on the point cloud and define its own classes based on these attributes.}
\cgalPkgManuals{Chapter_Data_Classification, PkgDataClassification}
\cgalPkgSummaryEnd
\cgalPkgShortInfoBegin
\cgalPkgSince{4.9}
\cgalPkgBib{cgal:ovja-pssd}
\cgalPkgDependsOn{\ref PkgDEPENDENCY}
\cgalPkgLicense{\ref licensesGPL "GPL"}
\cgalPkgDemo{Operations on Polyhedra,polyhedron_3.zip}
\cgalPkgShortInfoEnd
\cgalPkgDescriptionEnd
\cgalClassifedRefPages
## Main Classes ##
- `CGAL::Point_set_classification<Kernel>`
- `CGAL::Classification_type`
## Segmentation attributes ##
- `CGAL::Segmentation_attribute`
- `CGAL::Segmentation_attribute_color<Kernel>`
- `CGAL::Segmentation_attribute_elevation<Kernel>`
- `CGAL::Segmentation_attribute_horizontality<Kernel>`
- `CGAL::Segmentation_attribute_nonplanarity<Kernel>`
- `CGAL::Segmentation_attribute_scatter<Kernel>`
*/

View File

@ -0,0 +1,6 @@
Manual
Kernel_23
STL_Extension
Algebraic_foundations
Circulator
Stream_support

View File

@ -0,0 +1,3 @@
/*!
\example Data_classification/example_point_set_classification.cpp
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,100 @@
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Point_set_classification.h>
#include <CGAL/IO/read_ply_points.h>
typedef CGAL::Simple_cartesian<double> Kernel;
typedef CGAL::Point_set_classification<Kernel> PSC;
typedef CGAL::Segmentation_attribute_scatter<Kernel> Scatter;
typedef CGAL::Segmentation_attribute_elevation<Kernel> Elevation;
typedef CGAL::Segmentation_attribute_horizontality<Kernel> Horizontality;
typedef CGAL::Segmentation_attribute_nonplanarity<Kernel> NonPlanarity;
typedef Kernel::Point_3 Point;
int main (int argc, char** argv)
{
std::string filename (argc > 1 ? argv[1] : "data/b9.ply");
std::ifstream in (filename.c_str());
std::vector<Point> pts;
std::cerr << "Reading input" << std::endl;
if (!in
|| !(CGAL::read_ply_points (in, std::back_inserter (pts))))
{
std::cerr << "Error: cannot read " << filename << std::endl;
return EXIT_FAILURE;
}
std::cerr << "Initializing" << std::endl;
PSC psc (pts.begin (), pts.end(), 0.8);
psc.initialization();
std::cerr << "Computing attributes" << std::endl;
// Attributes with user-defined weights
Scatter scat (psc, 0.26);
Elevation elev (psc, 0.08);
Horizontality horiz (psc, 0.13);
NonPlanarity plan (psc, 0.72);
// Add attributes to PSC
psc.add_segmentation_attribute (&scat);
psc.add_segmentation_attribute (&elev);
psc.add_segmentation_attribute (&horiz);
psc.add_segmentation_attribute (&plan);
// Create classification type and define how attributes affect them
CGAL::Classification_type ground ("ground");
ground.set_attribute_effect (&scat, CGAL::Classification_type::PENALIZED_ATT);
ground.set_attribute_effect (&elev, CGAL::Classification_type::PENALIZED_ATT);
ground.set_attribute_effect (&horiz, CGAL::Classification_type::PENALIZED_ATT);
ground.set_attribute_effect (&plan, CGAL::Classification_type::PENALIZED_ATT);
CGAL::Classification_type vege ("vegetation");
vege.set_attribute_effect (&scat, CGAL::Classification_type::FAVORED_ATT);
vege.set_attribute_effect (&elev, CGAL::Classification_type::NEUTRAL_ATT);
vege.set_attribute_effect (&horiz, CGAL::Classification_type::NEUTRAL_ATT);
vege.set_attribute_effect (&plan, CGAL::Classification_type::PENALIZED_ATT);
CGAL::Classification_type roof ("roof");
roof.set_attribute_effect (&scat, CGAL::Classification_type::NEUTRAL_ATT);
roof.set_attribute_effect (&elev, CGAL::Classification_type::FAVORED_ATT);
roof.set_attribute_effect (&horiz, CGAL::Classification_type::NEUTRAL_ATT);
roof.set_attribute_effect (&plan, CGAL::Classification_type::NEUTRAL_ATT);
// Add types to PSC
psc.add_classification_type (&vege);
psc.add_classification_type (&ground);
psc.add_classification_type (&roof);
psc.classify (1); // Run with method=1 (global regularization with graphcut)
// Recover output
std::vector<Point> pts_ground, pts_vege, pts_roof;
for (std::size_t i = 0; i < pts.size(); ++ i)
{
Classification_type* type = psc.classification_type_of (i);
switch (type)
{
case &ground:
pts_ground.push_back (i);
break;
case &vege:
pts_vege.push_back (i);
break;
case &roof:
pts_roof.push_back (i);
break;
default:
std::cerr << "Error: unknown classification type" << std::endl;
}
}
std::cerr << "All done" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,122 @@
// Copyright (c) 2016 INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// 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.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
// Author(s) : Simon Giraudot
#ifndef CGAL_DATA_CLASSIFICATION_COLOR_H
#define CGAL_DATA_CLASSIFICATION_COLOR_H
#include <CGAL/array.h>
namespace CGAL {
namespace Data_classification {
/// \cond SKIP_IN_MANUAL
typedef CGAL::cpp11::array<unsigned char, 3> RGB_Color;
typedef CGAL::cpp11::array<double, 3> HSV_Color;
inline HSV_Color rgb_to_hsv (const RGB_Color& c)
{
double r = (double)(c[0]) / 255.;
double g = (double)(c[1]) / 255.;
double b = (double)(c[2]) / 255.;
double Cmax = (std::max) (r, (std::max) (g, b));
double Cmin = (std::min) (r, (std::min) (g, b));
double delta = Cmax - Cmin;
double H = 0.;
if (delta != 0.)
{
if (Cmax == r)
H = 60. * (((int)((g - b) / delta)) % 6);
else if (Cmax == g)
H = 60. * (((b - r) / delta) + 2.);
else
H = 60. * (((r - g) / delta) + 4.);
}
if (H < 0.) H += 360.;
double S = (Cmax == 0. ? 0. : 100. * (delta / Cmax));
double V = 100. * Cmax;
HSV_Color out = {{ H, S, V }};
return out;
}
inline RGB_Color hsv_to_rgb (const HSV_Color& c)
{
double h = c[0];
double s = c[1];
double v = c[2];
s /= 100.;
v /= 100.;
double C = v*s;
int hh = (int)(h/60.);
double X = C * (1-CGAL::abs (hh % 2 - 1));
double r = 0, g = 0, b = 0;
if( hh>=0 && hh<1 )
{
r = C;
g = X;
}
else if( hh>=1 && hh<2 )
{
r = X;
g = C;
}
else if( hh>=2 && hh<3 )
{
g = C;
b = X;
}
else if( hh>=3 && hh<4 )
{
g = X;
b = C;
}
else if( hh>=4 && hh<5 )
{
r = X;
b = C;
}
else
{
r = C;
b = X;
}
double m = v-C;
r += m;
g += m;
b += m;
r *= 255.0;
g *= 255.0;
b *= 255.0;
RGB_Color out = {{ (unsigned char)r, (unsigned char)g, (unsigned char)b }};
return out;
}
/// \endcond
} // namespace Data_classification
} // namespace CGAL
#endif // CGAL_DATA_CLASSIFICATION_COLOR_H

View File

@ -0,0 +1,110 @@
// Copyright (c) 2016 INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// 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.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
// Author(s) : Simon Giraudot
#ifndef CGAL_DATA_CLASSIFICATION_IMAGE_H
#define CGAL_DATA_CLASSIFICATION_IMAGE_H
namespace CGAL {
namespace Data_classification {
/// \cond SKIP_IN_MANUAL
template <typename Type>
class Image
{
std::size_t m_width;
std::size_t m_height;
Type* m_raw;
public:
Image () : m_width(0), m_height(0), m_raw (NULL)
{
}
Image (std::size_t width, std::size_t height)
: m_width (width),
m_height (height)
{
if (m_width * m_height > 0)
m_raw = new Type[width * height];
else
m_raw = NULL;
}
~Image ()
{
if (m_raw != NULL)
delete[] m_raw;
}
Image (const Image& other)
: m_width (other.width()),
m_height (other.height())
{
if (m_width * m_height > 0)
{
m_raw = new Type[m_width * m_height];
std::copy (other.m_raw, other.m_raw + (m_width * m_height), this->m_raw);
}
else
m_raw = NULL;
}
Image& operator= (const Image& other)
{
if (m_raw != NULL)
delete[] m_raw;
m_raw = NULL;
m_width = other.width();
m_height = other.height();
if (m_width * m_height > 0)
{
m_raw = new Type[m_width * m_height];
std::copy (other.m_raw, other.m_raw + (m_width * m_height), this->m_raw);
}
return *this;
}
std::size_t width() const { return m_width; }
std::size_t height() const { return m_height; }
Type& operator() (const std::size_t& x, const std::size_t& y)
{
return m_raw[y * m_width + x];
}
const Type& operator() (const std::size_t& x, const std::size_t& y) const
{
return m_raw[y * m_width + x];
}
};
/// \endcond
} // namespace Data_classification
} // namespace CGAL
#endif // CGAL_DATA_CLASSIFICATION_IMAGE_H

View File

@ -0,0 +1,68 @@
#ifndef CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_H
#define CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_H
#include <vector>
namespace CGAL {
/*!
\ingroup PkgDataClassification
\brief Abstract class describing a segmentation attribute.
A segmentation attribute must have a unique ID and associate a
scalar value to each point index of the classification input.
*/
class Segmentation_attribute
{
public:
/// \cond SKIP_IN_MANUAL
virtual ~Segmentation_attribute() { }
/// \endcond
/*!
\brief Get the ID of the attribute.
\return the ID of the attribute
*/
virtual std::string id() { return "abstract_attribute"; }
/*!
\brief Value taken by the attribute on the given point.
This method must be implemented by inherited classes.
\param pt_index Index of the point in the classification input
\return the value of the attribute on the point of index `pt_index`
*/
virtual double value (std::size_t pt_index) = 0;
/// \cond SKIP_IN_MANUAL
virtual double favored (std::size_t pt_index) { return (1. - value (pt_index)); }
virtual double penalized (std::size_t pt_index) { return value (pt_index); }
// virtual double ignored (std::size_t pt_index) { return std::min (favored(pt_index), penalized(pt_index)); }
virtual double ignored (std::size_t) { return 0.5; }
void compute_mean_max (std::vector<double>& vect, double& mean, double& max)
{
mean = 0.;
max = 0.;
for (std::size_t i = 0; i < vect.size(); ++ i)
{
mean += vect[i];
if (vect[i] > max)
max = vect[i];
}
mean /= vect.size();
}
/// \endcond
};
}
#endif // CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_H

View File

@ -0,0 +1,95 @@
#ifndef CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_COLOR_H
#define CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_COLOR_H
#include <vector>
#include <CGAL/Point_set_classification.h>
namespace CGAL {
/*!
\ingroup PkgDataClassification
\brief Segmentation attribute based on colorimetric information.
If the input point cloud comes with colorimetric information, it
can be useful for classification purposes. This attribute computes
a distance between the color of a point and a user-defined color
region in the HSV color-space, defined by a Gaussian
distribution with user-defined mean and standard deviation values.
\tparam Kernel The geometric kernel used.
*/
template <typename Kernel>
class Segmentation_attribute_color : public Segmentation_attribute
{
typedef Point_set_classification<Kernel> PSC;
typedef typename PSC::Image_float Image_float;
typedef typename Data_classification::RGB_Color RGB_Color;
typedef typename Data_classification::HSV_Color HSV_Color;
std::vector<double> color_attribute;
double mean_h, mean_s, mean_v, sd_h, sd_s, sd_v;
public:
/// \cond SKIP_IN_MANUAL
double weight;
double mean;
double max;
/// \endcond
/*!
\brief Constructs an attribute based on the given color.
\param M The point set classification object
\param weight The relative weight of this attribute
\param mean_h Mean hue of the selected color
\param mean_s Mean saturation of the selected color
\param mean_v Mean value of the selected color
\param sd_h Standard deviation of the hue of the selected color
\param sd_s Standard deviation of the saturation of the selected color
\param sd_v Standard deviation of the value of the selected color
\note The default values describe a gray color region
corresponding to the color of a concrete road.
*/
Segmentation_attribute_color (Point_set_classification<Kernel>& M,
double weight,
double mean_h = 156., double mean_s = 5., double mean_v = 76.,
double sd_h = 70., double sd_s = 12., double sd_v = 8.4)
: mean_h (mean_h)
, mean_s (mean_s)
, mean_v (mean_v)
, sd_h (sd_h)
, sd_s (sd_s)
, sd_v (sd_v)
, weight (weight)
{
std::cerr << "Using colors " << mean_h << " " << mean_s << " " << mean_v << std::endl;
for(std::size_t i=0; i < M.HPS.size();i++)
{
HSV_Color c = Data_classification::rgb_to_hsv (M.HPS[i].color);
color_attribute.push_back (std::exp (-(c[0] - mean_h) * (c[0] - mean_h) / (2. * sd_h * sd_h))
* std::exp (-(c[1] - mean_s) * (c[1] - mean_s) / (2. * sd_s * sd_s))
* std::exp (-(c[2] - mean_v) * (c[2] - mean_v) / (2. * sd_v * sd_v)));
}
this->compute_mean_max (color_attribute, mean, max);
}
virtual double value (std::size_t pt_index)
{
return std::max (0., std::min (1., color_attribute[pt_index] / weight));
}
virtual std::string id() { return "color"; }
};
}
#endif // CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_COLOR_H

View File

@ -0,0 +1,411 @@
#ifndef CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_ELEVATION_H
#define CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_ELEVATION_H
#include <vector>
#include <CGAL/Point_set_classification.h>
namespace CGAL {
/*!
\ingroup PkgDataClassification
\brief Segmentation attribute based on local elevation.
The local position of the ground can be computed for urban
scenes. This attribute computes the distance to the local
estimation of the ground.
\tparam Kernel The geometric kernel used.
*/
template <typename Kernel>
class Segmentation_attribute_elevation : public Segmentation_attribute
{
typedef Point_set_classification<Kernel> PSC;
typedef typename PSC::Image_float Image_float;
std::vector<double> elevation_attribute;
public:
/// \cond SKIP_IN_MANUAL
double weight;
double mean;
double max;
/// \endcond
/*!
\brief Constructs the attribute.
\param M The point set classification object
\param weight The relative weight of this attribute
\param on_groups Select if the attribute is computed point-wise of group-wise
*/
Segmentation_attribute_elevation (Point_set_classification<Kernel>& M,
double weight,
bool on_groups = false) : weight (weight)
{
if (on_groups)
{
std::vector<std::vector<typename Kernel::Point_3> > pts (M.groups.size());
for (std::size_t i = 0; i < M.HPS.size(); ++ i)
{
std::size_t g = M.HPS[i].group;
if (g == (std::size_t)(-1))
continue;
pts[g].push_back (M.HPS[i].position);
}
std::vector<typename Kernel::Point_3> centroids;
for (std::size_t i = 0; i < pts.size(); ++ i)
centroids.push_back (CGAL::centroid (pts[i].begin(), pts[i].end()));
for (std::size_t i = 0; i < M.HPS.size(); ++ i)
{
std::size_t g = M.HPS[i].group;
if (g == (std::size_t)(-1))
elevation_attribute.push_back (0.);
else
elevation_attribute.push_back (centroids[g].z());
}
this->compute_mean_max (elevation_attribute, mean, max);
return;
}
//DEM
Image_float DEM(M.grid_HPS.width(),M.grid_HPS.height());
Image_float DEMtemp(M.grid_HPS.width(),M.grid_HPS.height());
for (int j=0;j<(int)DEM.height();j++){
for (int i=0;i<(int)DEM.width();i++){
double mean_height=0;
std::vector < double > list_Z;
for(std::size_t k=0;k<M.grid_HPS(i,j).size();k++) list_Z.push_back(M.HPS[M.grid_HPS(i,j)[k]].position.z());
if(list_Z.size()>0){
std::sort(list_Z.begin(),list_Z.end());
int ind_k= (int)floor((double)(list_Z.size()*0.9)-0.5);
mean_height=list_Z[ind_k];
}
DEM(i,j)=(float)mean_height;
DEMtemp(i,j)=DEM(i,j);
}
}
std::size_t square = (std::size_t)(M.m_radius_neighbors / M.m_grid_resolution) + 1;
for (std::size_t j = 0; j < DEM.height(); j++){
for (std::size_t i = 0; i < DEM.width(); i++){
if((M.Mask(i,j))&&(DEM(i,j)==0)){
double distance_tot=0;
double val=0;
std::size_t squareXmin = (i < square ? 0 : i - square);
std::size_t squareXmax = std::min(DEM.width() - 1, i + square);
std::size_t squareYmin = (j < square ? 0 : j - square);
std::size_t squareYmax = std::min(DEM.height() - 1, j + square);
for(std::size_t k = squareXmin; k <= squareXmax; k++){
for(std::size_t l = squareYmin; l <= squareYmax; l++){
double distance=sqrt(pow((double)i-k,2)+pow((double)j-l,2))*M.m_grid_resolution;
if((distance<=M.m_radius_neighbors)&&(DEM(k,l)>0)&&(distance!=0)){
double dista=distance*distance;
val=val+DEM(k,l)/dista;
distance_tot=distance_tot+1/dista;
}
}
}
val=val/distance_tot;
DEMtemp(i,j)=(float)val;
}
}
}
float scale_byte_min_dem = M.BBox_scan.zmax();
float scale_byte_max_dem = M.BBox_scan.zmin();
for (std::size_t j = 0; j < DEM.height(); j++){
for (std::size_t i = 0; i < DEM.width(); i++){
DEM(i,j)=DEMtemp(i,j);
if(M.Mask(i,j)){
if(DEM(i,j)>scale_byte_max_dem) scale_byte_max_dem=DEM(i,j);
if(DEM(i,j)<scale_byte_min_dem) scale_byte_min_dem=DEM(i,j);
}
}
}
//TODO: smooth the DEM
for (std::size_t j = 1; j < DEM.height()-1; j++){
for (std::size_t i = 1; i < DEM.width()-1; i++){
if(M.Mask(i,j)){
//DEM(i,j)=
}
}
}
//DTM computation
std::size_t step=15;
square = (std::size_t)(M.m_radius_dtm/M.m_grid_resolution)+1;
Image_float toto(M.grid_HPS.width(),M.grid_HPS.height());
Image_float im_Zfront(M.grid_HPS.width(),M.grid_HPS.height());
M.DTM=toto;
//round 1
for (std::size_t j = 0; j < M.DTM.height(); j++){
for (std::size_t i = 0; i < M.DTM.width(); i++){
M.DTM(i,j)=0;
im_Zfront(i,j)=0;
}
}
for (std::size_t j = step/2+1; j < M.DTM.height(); j = j+step){
for (std::size_t i = step+1/2; i < M.DTM.width(); i = i+step){
//storage of the points in the disk
std::vector<float> list_pointsZ;
std::size_t squareXmin = (i < square ? 0 : i - square);
std::size_t squareXmax = std::min(M.DTM.width() - 1, i + square);
std::size_t squareYmin = (j < square ? 0 : j - square);
std::size_t squareYmax = std::min(M.DTM.height() - 1, j + square);
for(std::size_t k = squareXmin; k <= squareXmax; k++){
for(std::size_t l = squareYmin; l <= squareYmax; l++){
double distance=sqrt(pow((double)i-k,2)+pow((double)j-l,2))*M.m_grid_resolution;
if(distance<=M.m_radius_dtm){
for(int nb=0;nb<(int)M.grid_HPS(k,l).size();nb++) list_pointsZ.push_back(M.HPS[M.grid_HPS(k,l)[nb]].position.z());
}
}
}
//ordering of the points
float G1=0;
float G2=0;
if(list_pointsZ.size()>0){
std::sort(list_pointsZ.begin(),list_pointsZ.end());
int ind_k2= (int)floor((double)(list_pointsZ.size()*0.6)-0.5);
int ind_k1= (int)floor((double)(list_pointsZ.size()*0.3)-0.5);
G1=list_pointsZ[ind_k1];
G2=list_pointsZ[ind_k2];
}
float Gfront=(G1+G2)/2;
for(int iter=0;iter<3;iter++){
float G1_temp=0;
float G2_temp=0;
int count1=0;
for (std::size_t j=0;j<list_pointsZ.size();j++){
if(list_pointsZ[j]<=Gfront){
G1_temp+=list_pointsZ[j];
count1++;
}
else{G2_temp+=list_pointsZ[j];}
}
if(count1>0) G1=(float)G1_temp/count1;
if(count1<(int)list_pointsZ.size()) G2=(float)G2_temp/(list_pointsZ.size()-count1);
Gfront=(G1+G2)/2;
}
M.DTM(i,j)=G1;
im_Zfront(i,j)=Gfront;
//extension by duplication
std::size_t IsquareXmin = (i < step/2 ? 0 : i-step/2);
std::size_t IsquareXmax = std::min(M.DTM.width()-1,i+step/2);
std::size_t JsquareYmin = (j < step/2 ? 0 : j-step/2);
std::size_t JsquareYmax = std::min(M.DTM.height()-1,j+step/2);
if(M.DTM.width()-1-IsquareXmax<step) IsquareXmax=M.DTM.width()-1;
if(M.DTM.height()-1-JsquareYmax<step) JsquareYmax=M.DTM.height()-1;
if(IsquareXmin<step) IsquareXmin=0;
if(JsquareYmin<step) JsquareYmin=0;
for(std::size_t k = IsquareXmin; k <= IsquareXmax; k++){
for(std::size_t l = JsquareYmin; l <= JsquareYmax; l++){
if(M.Mask(k,l)){
M.DTM(k,l)=G1;
im_Zfront(k,l)=Gfront;
}
}
}
}
}
//Gaussian smoothness
for(std::size_t ik = 0; ik < 10; ik++) {
for(std::size_t j = 0; j < M.DTM.height(); j++){
for (std::size_t i = 0; i < M.DTM.width(); i++) {
std::size_t IsquareXmin = (i < 1 ? 0 : i-1);
std::size_t IsquareXmax = std::min(M.DTM.width()-1,i+1);
std::size_t JsquareYmin = (j < 1 ? 0 : j-1);
std::size_t JsquareYmax = std::min(M.DTM.height()-1,j+1);
int count=0;
for(std::size_t k = IsquareXmin; k <= IsquareXmax; k++){
for(std::size_t l = JsquareYmin; l <= JsquareYmax; l++){
if(M.Mask(k,l)) count++;
}
}
if(count==9) im_Zfront(i,j)=(im_Zfront(i-1,j-1)+2*im_Zfront(i,j-1)+im_Zfront(i+1,j-1)+2*im_Zfront(i-1,j)+4*im_Zfront(i,j)+2*im_Zfront(i+1,j)+im_Zfront(i-1,j+1)+2*im_Zfront(i,j+1)+im_Zfront(i+1,j+1))/16;
}
}
}
//round 2
std::vector < bool > test_ground;
for(int i=0;i<(int)M.HPS.size();i++){
int I=M.HPS[i].ind_x;
int J=M.HPS[i].ind_y;
bool test=false;
if(M.HPS[i].position.z()-im_Zfront(I,J)<=0) {test=true; test_ground.push_back(test);}
else{test_ground.push_back(test);}
}
for (int j=0;j<(int)M.DTM.height();j++){
for (int i=0;i<(int)M.DTM.width();i++){
M.DTM(i,j)=0;
im_Zfront(i,j)=0;
}
}
for (std::size_t j = step/2+1; j < M.DTM.height(); j = j+step){
for (std::size_t i = step/2+1; i < M.DTM.width(); i = i+step){
//stockage des nouveaux points
std::vector < float > list_pointsZ;
std::size_t squareXmin = (i < square ? 0 : i-square);
std::size_t squareXmax = std::min(M.DTM.width()-1,i+square);
std::size_t squareYmin = (j < square ? 0 : j-square);
std::size_t squareYmax = std::min(M.DTM.height()-1,j+square);
for(std::size_t k = squareXmin; k <= squareXmax; k++){
for(std::size_t l = squareYmin; l <= squareYmax; l++){
double distance=sqrt(pow((double)i-k,2)+pow((double)j-l,2))*M.m_grid_resolution;
if(distance<=M.m_radius_dtm){
for(int nb=0;nb<(int)M.grid_HPS(k,l).size();nb++){
if(test_ground[M.grid_HPS(k,l)[nb]]) list_pointsZ.push_back(M.HPS[M.grid_HPS(k,l)[nb]].position.z());
}
}
}
}
//ordering the new points
float G1=0;
float G2=0;
if(list_pointsZ.size()>0){
std::sort(list_pointsZ.begin(),list_pointsZ.end());
int ind_k2= (int)floor((double)(list_pointsZ.size()*0.6)-0.5);
int ind_k1= (int)floor((double)(list_pointsZ.size()*0.3)-0.5);
G1=list_pointsZ[ind_k1];
G2=list_pointsZ[ind_k2];
}
float Gfront=(G1+G2)/2;
for(int iter=0;iter<3;iter++){
float G1_temp=0;
float G2_temp=0;
int count1=0;
for (int j=0;j<(int)list_pointsZ.size();j++){
if(list_pointsZ[j]<=Gfront){G1_temp+=list_pointsZ[j]; count1++;}
else{G2_temp+=list_pointsZ[j];}
}
if(count1>0) G1=(float)G1_temp/count1;
if(count1<(int)list_pointsZ.size()) G2=(float)G2_temp/(list_pointsZ.size()-count1);
Gfront=(G1+G2)/2;
}
M.DTM(i,j)=G1;
im_Zfront(i,j)=Gfront;
//extension by duplication
std::size_t IsquareXmin = (i < step/2 ? 0 : i-step/2);
std::size_t IsquareXmax = std::min(M.DTM.width()-1,i+step/2);
std::size_t JsquareYmin = (j < step/2 ? 0 : j-step/2);
std::size_t JsquareYmax = std::min(M.DTM.height()-1,j+step/2);
if(M.DTM.width()-1-IsquareXmax<step) IsquareXmax=M.DTM.width()-1;
if(M.DTM.height()-1-JsquareYmax<step) JsquareYmax=M.DTM.height()-1;
if(IsquareXmin<step) IsquareXmin=0;
if(JsquareYmin<step) JsquareYmin=0;
for(std::size_t k = IsquareXmin; k <= IsquareXmax; k++){
for(std::size_t l = JsquareYmin; l <= JsquareYmax; l++){
if(M.Mask(k,l)){
M.DTM(k,l)=G1;
im_Zfront(k,l)=Gfront;
}
}
}
}
}
//Gaussian smoothness
for(std::size_t ik = 0; ik < 10; ik++) {
for(std::size_t j = 0; j < M.DTM.height(); j++){
for (std::size_t i = 0; i < M.DTM.width(); i++) {
std::size_t IsquareXmin = (i < 1 ? 0 : i - 1);
std::size_t IsquareXmax = std::min(M.DTM.width()-1,i+1);
std::size_t JsquareYmin = (j < 1 ? 0 : j - 1);
std::size_t JsquareYmax = std::min(M.DTM.height()-1,j+1);
int count=0;
for(std::size_t k=IsquareXmin; k<=IsquareXmax; k++){
for(std::size_t l=JsquareYmin; l<=JsquareYmax; l++){
if(M.Mask(k,l)) count++;
}
}
if(count==9) M.DTM(i,j)=(M.DTM(i-1,j-1)+2*M.DTM(i,j-1)+M.DTM(i+1,j-1)+2*M.DTM(i-1,j)+4*M.DTM(i,j)+2*M.DTM(i+1,j)+M.DTM(i-1,j+1)+2*M.DTM(i,j+1)+M.DTM(i+1,j+1))/16;
}
}
}
//ranger les valeurs dans scans lidars
for(int i=0;i<(int)M.HPS.size();i++){
int I=M.HPS[i].ind_x;
int J=M.HPS[i].ind_y;
elevation_attribute.push_back ((double)(M.HPS[i].position.z()-M.DTM(I,J)));
}
this->compute_mean_max (elevation_attribute, mean, max);
// max *= 5;
}
virtual double value (std::size_t pt_index)
{
return std::max (0., std::min (1., elevation_attribute[pt_index] / weight));
}
virtual std::string id() { return "elevation"; }
};
}
#endif // CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_ELEVATION_H

View File

@ -0,0 +1,89 @@
#ifndef CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_HORIZONTALITY_H
#define CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_HORIZONTALITY_H
#include <vector>
#include <CGAL/Point_set_classification.h>
namespace CGAL {
/*!
\ingroup PkgDataClassification
\brief Segmentation attribute based on local horizontality.
The orientation of the best fitting plane of a local neighborhood
of the considered point can be useful to disciminate facades from
the ground.
\tparam Kernel The geometric kernel used.
*/
template <typename Kernel>
class Segmentation_attribute_horizontality : public Segmentation_attribute
{
typedef Point_set_classification<Kernel> PSC;
std::vector<double> horizontality_attribute;
public:
/// \cond SKIP_IN_MANUAL
double weight;
double mean;
double max;
/// \endcond
/*!
\brief Constructs the attribute.
\param M The point set classification object
\param weight The relative weight of this attribute
\param on_groups Select if the attribute is computed point-wise of group-wise
*/
Segmentation_attribute_horizontality (Point_set_classification<Kernel>& M, double weight,
bool on_groups = false) : weight (weight)
{
typename Kernel::Vector_3 vertical (0., 0., 1.);
if (on_groups)
{
std::vector<double> values;
values.reserve (M.groups.size());
for (std::size_t i = 0; i < M.groups.size(); ++ i)
{
typename Kernel::Vector_3 normal = M.groups[i].orthogonal_vector();
normal = normal / std::sqrt (normal * normal);
values.push_back (1. - std::fabs(normal * vertical));
}
for (std::size_t i = 0; i < M.HPS.size(); ++ i)
if (M.HPS[i].group == (std::size_t)(-1))
horizontality_attribute.push_back (0.5);
else
horizontality_attribute.push_back (values[M.HPS[i].group]);
}
else
{
for(int i=0; i<(int)M.HPS.size(); i++)
{
typename Kernel::Vector_3 normal = M.planes[i].orthogonal_vector();
normal = normal / std::sqrt (normal * normal);
horizontality_attribute.push_back (1. - std::fabs(normal * vertical));
}
}
this->compute_mean_max (horizontality_attribute, mean, max);
// max *= 2;
}
virtual double value (std::size_t pt_index)
{
return std::max (0., std::min (1., horizontality_attribute[pt_index] / weight));
}
virtual std::string id() { return "horizontality"; }
};
}
#endif // CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_HORIZONTALITY_H

View File

@ -0,0 +1,92 @@
#ifndef CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_NONPLANARITY_H
#define CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_NONPLANARITY_H
#include <vector>
#include <CGAL/Point_set_classification.h>
namespace CGAL {
/*!
\ingroup PkgDataClassification
\brief Segmentation attribute based on local non-planarity.
Characterizing a level of non-planarity can help identify noisy
parts of the input such as vegetation. This attribute computes the
distance of a point to a locally fitted plane.
\tparam Kernel The geometric kernel used.
*/
template <typename Kernel>
class Segmentation_attribute_nonplanarity : public Segmentation_attribute
{
typedef Point_set_classification<Kernel> PSC;
std::vector<double> distance_to_plane_attribute;
public:
/// \cond SKIP_IN_MANUAL
double weight;
double mean;
double max;
/// \endcond
/*!
\brief Constructs the attribute.
\param M The point set classification object
\param weight The relative weight of this attribute
\param on_groups Select if the attribute is computed point-wise of group-wise
*/
Segmentation_attribute_nonplanarity (Point_set_classification<Kernel>& M, double weight, bool on_groups = false) : weight (weight)
{
if (on_groups)
{
std::vector<double> sq_dist (M.groups.size(), 0.);
std::vector<std::size_t> nb (M.groups.size(), 0);
for (std::size_t i = 0; i < M.HPS.size(); ++ i)
{
std::size_t g = M.HPS[i].group;
if (g == (std::size_t)(-1))
continue;
sq_dist[g] += CGAL::squared_distance (M.groups[g], M.HPS[i].position);
nb[g] ++;
}
for (std::size_t i = 0; i < sq_dist.size(); ++ i)
if (nb[i] > 0)
// sq_dist[i] = std::sqrt (sq_dist[i] / nb[i]);
sq_dist[i] = 1. / nb[i];
for (std::size_t i = 0; i < M.HPS.size(); ++ i)
{
std::size_t g = M.HPS[i].group;
if (g == (std::size_t)(-1))
distance_to_plane_attribute.push_back (0.);
else
distance_to_plane_attribute.push_back (sq_dist[g]);
}
}
else
{
for(int i=0; i<(int)M.HPS.size(); i++)
distance_to_plane_attribute.push_back (std::sqrt (CGAL::squared_distance (M.HPS[i].position, M.planes[i])));
}
this->compute_mean_max (distance_to_plane_attribute, mean, max);
// max *= 2;
}
virtual double value (std::size_t pt_index)
{
return std::max (0., std::min (1., distance_to_plane_attribute[pt_index] / weight));
}
virtual std::string id() { return "nonplanarity"; }
};
}
#endif // CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_NONPLANARITY_H

View File

@ -0,0 +1,268 @@
#ifndef CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_SCATTER_H
#define CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_SCATTER_H
#include <vector>
#include <CGAL/Point_set_classification.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Alpha_shape_2.h>
namespace CGAL {
/*!
\ingroup PkgDataClassification
\brief Segmentation attribute based on local vertical dispersion of points.
Urban scenes can usually be described as a set of 2D regions with
different heights. While these heights are usually piecewise
constant or piecewise linear, on some specific parts of the scene
such as vegetation, they can become extremely unstable. This
attribute quantifies the vertical dispersion of the points on a local
Z-cylinder around the points.
\tparam Kernel The geometric kernel used.
*/
template <typename Kernel>
class Segmentation_attribute_scatter : public Segmentation_attribute
{
typedef Point_set_classification<Kernel> PSC;
typedef typename PSC::Image_float Image_float;
std::vector<double> vegetation_attribute;
public:
/// \cond SKIP_IN_MANUAL
double weight;
double mean;
double max;
/// \endcond
/*!
\brief Constructs the attribute.
\param M The point set classification object
\param weight The relative weight of this attribute
\param on_groups Select if the attribute is computed point-wise of group-wise
*/
Segmentation_attribute_scatter (Point_set_classification<Kernel>& M, double weight, bool on_groups = false) : weight (weight)
{
if (on_groups)
scatter_on_groups(M);
else if(M.is_echo_given)
scatter_with_echo(M);
else
scatter_with_vertical_dispersion(M);
this->compute_mean_max (vegetation_attribute, mean, max);
// max *= 2;
}
// \cond SKIP_IN_MANUAL
typename Kernel::Point_2 to_2d (typename Kernel::Point_3 point,
typename Kernel::Plane_3 plane) const
{
typename Kernel::Vector_3 base1 = plane.base1() / std::sqrt (plane.base1() * plane.base1 ());
typename Kernel::Vector_3 base2 = plane.base2() / std::sqrt (plane.base2() * plane.base2 ());
typename Kernel::Point_3 origin = plane.projection (CGAL::ORIGIN);
typename Kernel::Vector_3 v (origin, point);
return typename Kernel::Point_2 (v * base1, v * base2);
}
void scatter_on_groups (PSC& M)
{
std::vector<std::vector<typename Kernel::Point_2> > pts (M.groups.size());
for (std::size_t i = 0; i < M.HPS.size(); ++i)
{
if (M.HPS[i].group == (std::size_t)(-1))
continue;
std::size_t g = M.HPS[i].group;
const typename Kernel::Plane_3& plane = M.groups[g];
pts[g].push_back (to_2d (M.HPS[i].position, plane));
}
std::vector<double> values;
// Alpha shapes
typedef CGAL::Alpha_shape_vertex_base_2<Kernel> Vb;
typedef CGAL::Alpha_shape_face_base_2<Kernel> Fb;
typedef CGAL::Triangulation_data_structure_2<Vb,Fb> Tds;
typedef CGAL::Delaunay_triangulation_2<Kernel,Tds> Triangulation_2;
typedef CGAL::Alpha_shape_2<Triangulation_2> Alpha_shape_2;
for (std::size_t i = 0; i < M.groups.size(); ++ i)
{
Alpha_shape_2 ashape (pts[i].begin (), pts[i].end (), M.m_grid_resolution);
double perimeter = 0.;
for (typename Alpha_shape_2::Finite_edges_iterator it = ashape.finite_edges_begin ();
it != ashape.finite_edges_end (); ++ it)
if (ashape.classify(*it) == Alpha_shape_2::REGULAR)
perimeter +=
(std::sqrt (CGAL::squared_distance (it->first->vertex ((it->second + 1) % 3)->point(),
it->first->vertex ((it->second + 2) % 3)->point())));
double area = 0.;
for (typename Alpha_shape_2::Finite_faces_iterator it = ashape.finite_faces_begin ();
it != ashape.finite_faces_end (); ++ it)
if (ashape.classify(it) == Alpha_shape_2::INTERIOR)
area +=
(std::fabs (CGAL::area (it->vertex(0)->point(), it->vertex(1)->point(), it->vertex(2)->point())));
// values.push_back (ashape.number_of_solid_components() / (double)(pts[i].size()));
if (area == 0)
values.push_back (0.);
else
values.push_back (perimeter / pts[i].size());
}
for (std::size_t i = 0; i < M.HPS.size(); ++i)
if (M.HPS[i].group == (std::size_t)(-1))
vegetation_attribute.push_back (0.);
else
vegetation_attribute.push_back (values[M.HPS[i].group]);
}
void scatter_with_echo(PSC& M)
{
Image_float Vegetation(M.grid_HPS.width(), M.grid_HPS.height());
for (int j=0;j<(int)M.DTM.height();j++)
for (int i=0;i<(int)M.DTM.width();i++)
Vegetation(i,j)=0;
std::size_t square = (std::size_t)(0.5 * M.m_radius_neighbors / M.m_grid_resolution) + 1;
std::cerr << "Echo given" << std::endl;
for (std::size_t j = 0; j < M.grid_HPS.height(); j++){
for (std::size_t i = 0; i < M.grid_HPS.width(); i++){
if(M.Mask(i,j)){
std::size_t squareXmin = (i < square ? 0 : i - square);
std::size_t squareXmax = std::min (M.grid_HPS.width()-1, i + square);
std::size_t squareYmin = (j < square ? 0 : j - square);
std::size_t squareYmax = std::min (M.grid_HPS.height()-1, j + square);
int NB_echo_sup=0;
int NB_echo_total=0;
for(std::size_t k = squareXmin; k <= squareXmax; k++){
for(std::size_t l = squareYmin; l <= squareYmax; l++){
if(sqrt(pow((double)k-i,2)+pow((double)l-j,2))<=(double)0.5*M.m_radius_neighbors/M.m_grid_resolution){
if(M.grid_HPS(k,l).size()>0){
for(int t=0; t<(int)M.grid_HPS(k,l).size();t++){
int ip = M.grid_HPS(k,l)[t];
if(M.HPS[ip].echo>1) NB_echo_sup++;
}
NB_echo_total=NB_echo_total+M.grid_HPS(k,l).size();
}
}
}
}
Vegetation(i,j)=(float)NB_echo_sup/NB_echo_total;
}
}
}
for(int i=0;i<(int)M.HPS.size();i++){
int I= M.HPS[i].ind_x;
int J= M.HPS[i].ind_y;
vegetation_attribute.push_back((double)Vegetation(I,J));
}
}
void scatter_with_vertical_dispersion(PSC& M)
{
Image_float Vegetation(M.grid_HPS.width(), M.grid_HPS.height());
for (int j=0;j<(int)M.DTM.height();j++)
for (int i=0;i<(int)M.DTM.width();i++)
Vegetation(i,j)=0;
std::size_t square = (std::size_t)(0.5 * M.m_radius_neighbors / M.m_grid_resolution) + 1;
typename Kernel::Vector_3 verti (0., 0., 1.);
for (std::size_t j = 0; j < M.grid_HPS.height(); j++){
for (std::size_t i = 0; i < M.grid_HPS.width(); i++){
if(!(M.Mask(i,j)))
continue;
std::vector<double> hori;
std::size_t squareXmin = (i < square ? 0 : i - square);
std::size_t squareXmax = std::min (M.grid_HPS.width()-1, i + square);
std::size_t squareYmin = (j < square ? 0 : j - square);
std::size_t squareYmax = std::min (M.grid_HPS.height()-1, j + square);
for(std::size_t k = squareXmin; k <= squareXmax; k++)
for(std::size_t l = squareYmin; l <= squareYmax; l++)
if(sqrt(pow((double)k-i,2)+pow((double)l-j,2))
<=(double)0.5*M.m_radius_neighbors/M.m_grid_resolution
&& (M.grid_HPS(k,l).size()>0))
for(int t=0; t<(int)M.grid_HPS(k,l).size();t++)
{
int ip = M.grid_HPS(k,l)[t];
hori.push_back (M.HPS[ip].position.z());
}
if (hori.empty())
continue;
std::sort (hori.begin(), hori.end());
std::size_t nb_layers = 1;
std::vector<bool> occupy (1 + (std::size_t)((hori.back() - hori.front()) / M.m_grid_resolution), false);
std::size_t last_index = 0;
for (std::size_t k = 0; k < hori.size(); ++ k)
{
std::size_t index = (std::size_t)((hori[k] - hori.front()) / M.m_grid_resolution);
occupy[index] = true;
if (index > last_index + 1)
++ nb_layers;
last_index = index;
}
std::size_t nb_occ = 0;
for (std::size_t k = 0; k < occupy.size(); ++ k)
if (occupy[k])
++ nb_occ;
Vegetation(i,j)= 1.f - (nb_occ / (float)(occupy.size()));
}
}
for(int i=0;i<(int)M.HPS.size();i++){
int I= M.HPS[i].ind_x;
int J= M.HPS[i].ind_y;
vegetation_attribute.push_back((double)Vegetation(I,J));
}
}
/// \endcond
virtual double value (std::size_t pt_index)
{
return std::max (0., std::min (1., vegetation_attribute[pt_index] / weight));
}
virtual std::string id() { return "scatter"; }
};
}
#endif // CGAL_DATA_CLASSIFICATION_SEGMENTATION_ATTRIBUTE_SCATTER_H

File diff suppressed because it is too large Load Diff

View File

@ -95,6 +95,6 @@ Barycentric_coordinates_2
Surface_mesh
Surface_mesh_shortest_path
Polygon_mesh_processing
Data_classification

View File

@ -118,6 +118,7 @@ h1 {
\package_listing{Point_set_processing_3}
\package_listing{Point_set_shape_detection_3}
\package_listing{Stream_lines_2}
\package_listing{Data_classification}
\section PartSearchStructures Spatial Searching and Sorting

View File

@ -399,6 +399,13 @@ and <code>src/</code> directories).
<code>CGAL::write_ply_points()</code>
and <code>CGAL::write_ply_points_and_normals()</code>.</li>
</ul>
<h3>Point Set Shape Detection</h3>
<ul>
<li>New post-processing
algorithm: <code>CGAL::regularize_planes()</code>. This allows the user
to favor parallelism, orthogonality, coplanarity and/or axial
symmetry between detected planes.</li>
</ul>
<h3>Surface Mesh Parameterization</h3>
<ul>
<li><code>LSCM_parameterizer_3</code> now uses by default Eigen

View File

@ -24,6 +24,7 @@
#include <CGAL/value_type_traits.h>
#include <CGAL/point_set_processing_assertions.h>
#include <CGAL/Kernel_traits.h>
#include <CGAL/IO/io.h>
#include <boost/version.hpp>
#include <boost/cstdint.hpp>
@ -82,7 +83,17 @@ namespace internal {
stream >> s;
c = static_cast<unsigned char>(s);
}
void read_ascii (std::istream& stream, float& t) const
{
stream >> iformat(t);
}
void read_ascii (std::istream& stream, double& t) const
{
stream >> iformat(t);
}
// Default template when Type is not a char type
template <typename Type>
void read_ascii (std::istream& stream, Type& t) const
@ -90,6 +101,7 @@ namespace internal {
stream >> t;
}
template <typename Type>
Type read (std::istream& stream) const
{

View File

@ -242,6 +242,9 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
add_item(scene_polyhedron_selection_item Scene_polyhedron_selection_item.cpp)
target_link_libraries(scene_polyhedron_selection_item scene_polyhedron_item_decorator scene_polyhedron_item_k_ring_selection)
add_item(scene_point_set_classification_item Scene_point_set_classification_item.cpp)
target_link_libraries(scene_point_set_classification_item scene_points_with_normal_item scene_color_ramp)
add_item(scene_polyhedron_shortest_path_item Plugins/Surface_mesh/Scene_polyhedron_shortest_path_item.cpp)
add_item(scene_surface_mesh_item Scene_surface_mesh_item.cpp)
@ -270,9 +273,6 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
target_link_libraries( demo_framework gl_splat)
foreach( lib
demo_framework
scene_basic_objects

View File

@ -39,6 +39,10 @@ endif()
polyhedron_demo_plugin(point_set_selection_plugin Point_set_selection_plugin ${point_set_selectionUI_FILES})
target_link_libraries(point_set_selection_plugin scene_points_with_normal_item scene_polylines_item)
qt5_wrap_ui( point_set_classificationUI_FILES Point_set_classification_widget.ui)
polyhedron_demo_plugin(point_set_classification_plugin Point_set_classification_plugin ${point_set_classificationUI_FILES})
target_link_libraries(point_set_classification_plugin scene_point_set_classification_item scene_points_with_normal_item scene_polylines_item scene_polygon_soup_item)
qt5_wrap_ui(point_set_shape_detectionUI_FILES Point_set_shape_detection_plugin.ui)
polyhedron_demo_plugin(point_set_shape_detection_plugin Point_set_shape_detection_plugin ${point_set_shape_detectionUI_FILES})
target_link_libraries(point_set_shape_detection_plugin scene_polyhedron_item scene_points_with_normal_item scene_polygon_soup_item)

View File

@ -0,0 +1,733 @@
#include <QtCore/qglobal.h>
#include <QFileDialog>
#include <QColorDialog>
#include <fstream>
#include "opengl_tools.h"
#include "Messages_interface.h"
#include "Scene_points_with_normal_item.h"
#include "Scene_point_set_classification_item.h"
#include "Scene_polylines_item.h"
#include "Scene_polygon_soup_item.h"
#include <CGAL/Three/Scene_interface.h>
#include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
#include <CGAL/Random.h>
#include "ui_Point_set_classification_widget.h"
#include <QAction>
#include <QMainWindow>
#include <QApplication>
#include <QCheckBox>
#include <map>
#include <boost/graph/adjacency_list.hpp>
#include <CGAL/boost/graph/split_graph_into_polylines.h>
using namespace CGAL::Three;
class Polyhedron_demo_point_set_classification_plugin :
public QObject,
public Polyhedron_demo_plugin_helper
{
Q_OBJECT
Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0")
public:
bool applicable(QAction*) const {
return true;
}
void print_message(QString message) { messages->information(message); }
QList<QAction*> actions() const { return QList<QAction*>() << actionPointSetClassification; }
using Polyhedron_demo_plugin_helper::init;
void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface* m) {
mw = mainWindow;
scene = scene_interface;
messages = m;
actionPointSetClassification = new QAction(tr("Point Set Classification"), mw);
connect(actionPointSetClassification, SIGNAL(triggered()), this, SLOT(point_set_classification_action()));
dock_widget = new QDockWidget("Point Set Classification", mw);
dock_widget->setVisible(false);
ui_widget.setupUi(dock_widget);
addDockWidget(dock_widget);
add_class_rows();
color_att = QColor (75, 75, 77);
connect(ui_widget.create_from_file, SIGNAL(clicked()), this,
SLOT(on_create_from_file_button_clicked()));
connect(ui_widget.create_from_item, SIGNAL(clicked()), this,
SLOT(on_create_from_item_button_clicked()));
connect(ui_widget.estimate_parameters, SIGNAL(clicked()), this,
SLOT(on_estimate_parameters_button_clicked()));
connect(ui_widget.compute_features, SIGNAL(clicked()), this,
SLOT(on_compute_features_button_clicked()));
connect(ui_widget.compute_ransac, SIGNAL(clicked()), this,
SLOT(on_compute_ransac_button_clicked()));
connect(ui_widget.random_weights, SIGNAL(clicked()), this,
SLOT(on_random_weights_button_clicked()));
connect(ui_widget.display, SIGNAL(currentIndexChanged(int)), this,
SLOT(on_display_button_clicked(int)));
connect(ui_widget.run, SIGNAL(clicked()), this,
SLOT(on_run_button_clicked()));
connect(ui_widget.scatterSlider, SIGNAL(sliderReleased()), this,
SLOT(on_run_button_clicked()));
connect(ui_widget.planaritySlider, SIGNAL(sliderReleased()), this,
SLOT(on_run_button_clicked()));
connect(ui_widget.horizontalitySlider, SIGNAL(sliderReleased()), this,
SLOT(on_run_button_clicked()));
connect(ui_widget.elevationSlider, SIGNAL(sliderReleased()), this,
SLOT(on_run_button_clicked()));
connect(ui_widget.colorSlider, SIGNAL(sliderReleased()), this,
SLOT(on_run_button_clicked()));
connect(ui_widget.save_config, SIGNAL(clicked()), this,
SLOT(on_save_config_button_clicked()));
connect(ui_widget.load_config, SIGNAL(clicked()), this,
SLOT(on_load_config_button_clicked()));
connect(ui_widget.colorButton, SIGNAL(clicked()), this,
SLOT(on_colorButton_clicked()));
connect(ui_widget.run_with_smoothing, SIGNAL(clicked()), this,
SLOT(on_run_with_smoothing_button_clicked()));
connect(ui_widget.run_with_ransac, SIGNAL(clicked()), this,
SLOT(on_run_with_ransac_button_clicked()));
connect(ui_widget.compute_clusters, SIGNAL(clicked()), this,
SLOT(on_compute_clusters_button_clicked()));
connect(ui_widget.save, SIGNAL(clicked()), this,
SLOT(on_save_button_clicked()));
connect(ui_widget.generate_point_set_items, SIGNAL(clicked()), this,
SLOT(on_generate_point_set_items_button_clicked()));
connect(ui_widget.extract_facades, SIGNAL(clicked()), this,
SLOT(on_extract_facades_button_clicked()));
connect(ui_widget.extract_2d_outline, SIGNAL(clicked()), this,
SLOT(on_extract_2d_outline_button_clicked()));
}
virtual void closure()
{
dock_widget->hide();
}
void add_class_rows()
{
class_rows.push_back (ClassRow (dock_widget, "Ground", true, 2, 2, 2, 2, 1));
class_rows.push_back (ClassRow (dock_widget, "Vegetation", true, 0, 0, 1, 0, 1));
class_rows.push_back (ClassRow (dock_widget, "Road", false, 2, 2, 2, 2, 0));
class_rows.push_back (ClassRow (dock_widget, "Roof", true, 2, 1, 2, 0, 1));
class_rows.push_back (ClassRow (dock_widget, "Facade", true, 1, 2, 0, 0, 1));
class_rows.push_back (ClassRow (dock_widget, "Building", false, 2, 1, 0, 1, 1));
for (std::size_t i = 0; i < class_rows.size(); ++ i)
{
ui_widget.gridLayout->addWidget (class_rows[i].label, 2 + (int)i, 0);
ui_widget.gridLayout->addWidget (class_rows[i].checkbox, 2 + (int)i, 1);
for (std::size_t j = 0; j < class_rows[i].combo.size(); ++ j)
ui_widget.gridLayout->addWidget (class_rows[i].combo[j], 2 + (int)i, 2 + (int)j);
}
}
public Q_SLOTS:
void point_set_classification_action()
{
dock_widget->show();
dock_widget->raise();
}
void on_create_from_file_button_clicked()
{
CGAL::Random rand(time(0));
QString filename = QFileDialog::getOpenFileName(mw,
tr("Open PLY point set"),
".",
"PLY point set (*.ply);;All Files (*)");
if (filename == QString())
return;
QFileInfo fi(filename);
QString name = fi.fileName();
name.chop(4);
std::ifstream in(filename.toUtf8(), std::ios_base::binary);
QApplication::setOverrideCursor(Qt::WaitCursor);
Scene_point_set_classification_item* new_item
= new Scene_point_set_classification_item ();
new_item->read_ply_point_set (in, ui_widget.gridResolutionDoubleSpinBox->value());
std::vector<Scene_point_set_classification_item*> items;
if (new_item->segment_point_set (ui_widget.max_nb_points->value(), std::back_inserter (items)))
{
for (std::size_t i = 0; i < items.size(); ++ i)
{
unsigned char r, g, b;
r = static_cast<unsigned char>(64 + rand.get_int(0, 192));
g = static_cast<unsigned char>(64 + rand.get_int(0, 192));
b = static_cast<unsigned char>(64 + rand.get_int(0, 192));
items[i]->setRbgColor (r, g, b);
items[i]->setName(QString("%1 %2 (classification)").arg(name).arg (i+1));
int item_id = scene->addItem(items[i]);
scene->setSelectedItem(item_id);
}
delete new_item;
}
else
{
new_item->setName(QString("%1 (classification)").arg(name));
int item_id = scene->addItem(new_item);
scene->setSelectedItem(item_id);
}
QApplication::restoreOverrideCursor();
}
void on_create_from_item_button_clicked()
{
Scene_points_with_normal_item* points_item = getSelectedItem<Scene_points_with_normal_item>();
if(!points_item)
{
print_message("Error: there is no selected point set item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
Scene_point_set_classification_item* new_item
= new Scene_point_set_classification_item (points_item, ui_widget.gridResolutionDoubleSpinBox->value());
int item_id = scene->addItem(new_item);
new_item->setName(QString("%1 (classification)").arg(points_item->name()));
scene->setSelectedItem(item_id);
QApplication::restoreOverrideCursor();
}
void on_estimate_parameters_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
QTime time;
time.start();
double grid_resolution = 0., radius_neighbors = 0., radius_dtm = 0.;
classification_item->estimate_parameters (grid_resolution, radius_neighbors, radius_dtm);
std::cerr << "Parameters computed in " << time.elapsed() / 1000 << " second(s)" << std::endl;
ui_widget.gridResolutionDoubleSpinBox->setValue(grid_resolution);
ui_widget.radiusNeighborsDoubleSpinBox->setValue(radius_neighbors);
ui_widget.radiusDTMDoubleSpinBox->setValue(radius_dtm);
QApplication::restoreOverrideCursor();
}
void run (Scene_point_set_classification_item* classification_item, int method)
{
classification_item->run (ui_widget.scatterSlider->value() /
(double)(ui_widget.scatterSlider->maximum()+1),
ui_widget.planaritySlider->value() /
(double)(ui_widget.planaritySlider->maximum()+1),
ui_widget.horizontalitySlider->value() /
(double)(ui_widget.horizontalitySlider->maximum()+1),
ui_widget.elevationSlider->value() /
(double)(ui_widget.elevationSlider->maximum()+1),
ui_widget.colorSlider->value() /
(double)(ui_widget.colorSlider->maximum()+1),
ui_widget.multiply_weights->isChecked (),
class_rows,
method,
ui_widget.smoothingDoubleSpinBox->value());
}
void on_compute_features_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
QTime time;
time.start();
classification_item->compute_features (ui_widget.gridResolutionDoubleSpinBox->value(),
ui_widget.radiusNeighborsDoubleSpinBox->value(),
ui_widget.radiusDTMDoubleSpinBox->value(),
color_att);
run (classification_item, 0);
std::cerr << "Features computed in " << time.elapsed() / 1000 << " second(s)" << std::endl;
QApplication::restoreOverrideCursor();
scene->itemChanged(classification_item);
}
void on_compute_ransac_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
QTime time;
time.start();
classification_item->compute_ransac (ui_widget.gridResolutionDoubleSpinBox->value());
std::cerr << "RANSAC computed in " << time.elapsed() / 1000 << " second(s)" << std::endl;
QApplication::restoreOverrideCursor();
scene->itemChanged(classification_item);
}
void on_save_config_button_clicked()
{
QString filename = QFileDialog::getSaveFileName(mw,
tr("Save classification configuration"),
QString("default.conf"),
"Config file (*.conf);;");
if (filename == QString())
return;
std::ofstream out(filename.toUtf8());
QApplication::setOverrideCursor(Qt::WaitCursor);
out << ui_widget.gridResolutionDoubleSpinBox->value() << " "
<< ui_widget.radiusNeighborsDoubleSpinBox->value() << " "
<< ui_widget.radiusDTMDoubleSpinBox->value() << " " << std::endl
<< ui_widget.scatterSlider->value() << " "
<< ui_widget.planaritySlider->value() << " "
<< ui_widget.horizontalitySlider->value() << " "
<< ui_widget.elevationSlider->value() << " "
<< ui_widget.colorSlider->value() << std::endl;
for (std::size_t i = 0; i < class_rows.size(); ++ i)
{
out << (class_rows[i].checkbox->isChecked() ? "1" : "0") << " ";
for (std::size_t j = 0; j < class_rows[j].combo.size(); ++ j)
out << class_rows[i].combo[j]->currentIndex() << " ";
out << std::endl;
}
QApplication::restoreOverrideCursor();
out.close();
}
void on_load_config_button_clicked()
{
QString filename = QFileDialog::getOpenFileName(mw,
tr("Open classification configuration"),
".",
"Config file (*.conf);;All Files (*)");
if (filename == QString())
return;
std::ifstream in(filename.toUtf8());
QApplication::setOverrideCursor(Qt::WaitCursor);
double gridResolution = 0., radiusNeighbors = 0., radiusDTM = 0.;
if (!(in >> gridResolution >> radiusNeighbors >> radiusDTM))
{
std::cerr << "Error: can't read config file." << std::endl;
return;
}
int scatter = 0, planarity = 0, horizontality = 0, elevation = 0, color = 0;
if (!(in >> scatter >> planarity >> horizontality >> elevation >> color))
{
std::cerr << "Error: can't read config file." << std::endl;
return;
}
ui_widget.gridResolutionDoubleSpinBox->setValue(gridResolution);
ui_widget.radiusNeighborsDoubleSpinBox->setValue(radiusNeighbors);
ui_widget.radiusDTMDoubleSpinBox->setValue(radiusDTM);
ui_widget.scatterSlider->setValue (scatter);
ui_widget.planaritySlider->setValue (planarity);
ui_widget.horizontalitySlider->setValue (horizontality);
ui_widget.elevationSlider->setValue (elevation);
ui_widget.colorSlider->setValue (color);
for (std::size_t i = 0; i < class_rows.size(); ++ i)
{
int checked = 0;
if (!(in >> checked))
{
std::cerr << "Error: can't read config file." << std::endl;
return;
}
class_rows[i].checkbox->setChecked (checked);
for (std::size_t j = 0; j < class_rows[j].combo.size(); ++ j)
{
int index = 0;
if (!(in >> index))
{
std::cerr << "Error: can't read config file." << std::endl;
return;
}
class_rows[i].combo[j]->setCurrentIndex (index);
}
}
QApplication::restoreOverrideCursor();
in.close();
}
void on_colorButton_clicked()
{
color_att = QColorDialog::getColor (QColor (33, 37, 33), (QWidget*)mw,
QString ("Select favored color"));
}
void on_random_weights_button_clicked()
{
ui_widget.scatterSlider->setValue (rand() % ui_widget.scatterSlider->maximum());
ui_widget.planaritySlider->setValue (rand() % ui_widget.planaritySlider->maximum());
ui_widget.horizontalitySlider->setValue (rand() % ui_widget.horizontalitySlider->maximum());
ui_widget.elevationSlider->setValue (rand() % ui_widget.elevationSlider->maximum());
ui_widget.colorSlider->setValue (rand() % ui_widget.colorSlider->maximum());
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
run (classification_item, 0);
QApplication::restoreOverrideCursor();
scene->itemChanged(classification_item);
}
void on_display_button_clicked(int index)
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
return;
classification_item->change_color (index);
scene->itemChanged(classification_item);
}
void on_run_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
run (classification_item, 0);
QApplication::restoreOverrideCursor();
scene->itemChanged(classification_item);
}
void on_run_with_smoothing_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
QTime time;
time.start();
run (classification_item, 1);
std::cerr << "Smoothed classification computed in " << time.elapsed() / 1000 << " second(s)" << std::endl;
QApplication::restoreOverrideCursor();
scene->itemChanged(classification_item);
}
void on_run_with_ransac_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
QTime time;
time.start();
run (classification_item, 2);
std::cerr << "RANSAC-guided classification computed in " << time.elapsed() / 1000 << " second(s)" << std::endl;
QApplication::restoreOverrideCursor();
scene->itemChanged(classification_item);
}
void on_compute_clusters_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
QTime time;
time.start();
classification_item->compute_clusters (ui_widget.radiusNeighborsDoubleSpinBox->value());
std::cerr << "Clusters computed in " << time.elapsed() / 1000 << " second(s)" << std::endl;
QApplication::restoreOverrideCursor();
scene->itemChanged(classification_item);
}
void on_save_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QString filename = QFileDialog::getSaveFileName(mw,
tr("Save PLY classified point set"),
QString("%1.ply").arg(classification_item->name()),
"PLY point set (*.ply);;");
if (filename == QString())
return;
std::ofstream out(filename.toUtf8());
QApplication::setOverrideCursor(Qt::WaitCursor);
classification_item->write_ply_point_set (out);
QApplication::restoreOverrideCursor();
out.close();
}
void on_generate_point_set_items_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
std::vector<Scene_points_with_normal_item*> new_items;
for (std::size_t i = 0; i < 6; ++ i)
new_items.push_back (new Scene_points_with_normal_item);
classification_item->generate_point_sets (new_items);
for (std::size_t i = 0; i < new_items.size(); ++ i)
{
if (new_items[i]->point_set()->empty())
delete new_items[i];
else
{
if (i == 0) // Vegetation
{
new_items[i]->setName(QString("%1 (vegetation)")
.arg(classification_item->name()));
new_items[i]->setColor(QColor(0, 255, 27));
}
else if (i == 1) // Ground
{
new_items[i]->setName(QString("%1 (ground)")
.arg(classification_item->name()));
new_items[i]->setColor(QColor(245, 180, 0));
}
else if (i == 2) // Road
{
new_items[i]->setName(QString("%1 (road)")
.arg(classification_item->name()));
new_items[i]->setColor(QColor(200, 200, 200));
}
else if (i == 3) // Roof
{
new_items[i]->setName(QString("%1 (roofs)")
.arg(classification_item->name()));
new_items[i]->setColor(QColor(255, 0, 170));
}
else if (i == 4) // Facade
{
new_items[i]->setName(QString("%1 (facades)")
.arg(classification_item->name()));
new_items[i]->setColor(QColor(100, 0, 255));
}
else if (i == 5) // Building
{
new_items[i]->setName(QString("%1 (buildings)")
.arg(classification_item->name()));
new_items[i]->setColor(QColor(0, 114, 225));
}
scene->addItem (new_items[i]);
}
}
QApplication::restoreOverrideCursor();
}
void on_extract_2d_outline_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
std::vector<Kernel::Triangle_3> faces;
classification_item->extract_building_map (ui_widget.radiusNeighborsDoubleSpinBox->value(),
faces);
Scene_polygon_soup_item* new_item
= new Scene_polygon_soup_item ();
new_item->setColor(Qt::magenta);
new_item->setRenderingMode(FlatPlusEdges);
new_item->init_polygon_soup(faces.size() * 3, faces.size());
for (std::size_t i = 0; i < faces.size(); ++ i)
{
for (std::size_t j = 0; j < 3; ++ j)
new_item->new_vertex (faces[i][j].x(), faces[i][j].y(), faces[i][j].z());
new_item->new_triangle (3 * i, 3 * i + 1, 3 * i + 2);
}
scene->addItem (new_item);
// std::vector<Kernel::Point_3> outline;
// classification_item->extract_2d_outline(ui_widget.radiusNeighborsDoubleSpinBox->value(),
// outline);
// Scene_polylines_item* item = new Scene_polylines_item;
// for (std::size_t i = 0; i < outline.size(); i += 2)
// {
// item->polylines.push_back (std::vector<Kernel::Point_3>());
// item->polylines.back().push_back (outline[i]);
// item->polylines.back().push_back (outline[i+1]);
// }
// item->setName(QString("%1 (2D outline)")
// .arg(classification_item->name()));
// item->setColor(Qt::black);
// item->invalidateOpenGLBuffers();
// scene->addItem (item);
QApplication::restoreOverrideCursor();
}
void on_extract_facades_button_clicked()
{
Scene_point_set_classification_item* classification_item
= getSelectedItem<Scene_point_set_classification_item>();
if(!classification_item)
{
print_message("Error: there is no point set classification item!");
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
std::vector<Kernel::Triangle_3> faces;
classification_item->extract_facades (ui_widget.radiusNeighborsDoubleSpinBox->value(),
faces);
Scene_polygon_soup_item* new_item
= new Scene_polygon_soup_item ();
new_item->setColor(Qt::cyan);
new_item->setRenderingMode(FlatPlusEdges);
new_item->init_polygon_soup(faces.size() * 3, faces.size());
for (std::size_t i = 0; i < faces.size(); ++ i)
{
for (std::size_t j = 0; j < 3; ++ j)
new_item->new_vertex (faces[i][j].x(), faces[i][j].y(), faces[i][j].z());
new_item->new_triangle (3 * i, 3 * i + 1, 3 * i + 2);
}
scene->addItem (new_item);
QApplication::restoreOverrideCursor();
}
private:
Messages_interface* messages;
QAction* actionPointSetClassification;
QDockWidget* dock_widget;
struct ClassRow
{
QLabel* label;
QCheckBox* checkbox;
std::vector<QComboBox*> combo;
ClassRow (QWidget* parent, const char* name, bool checked,
int scat, int plan, int hori, int elev, int colo)
{
label = new QLabel (name, parent);
checkbox = new QCheckBox (parent);
checkbox->setChecked(checked);
for (std::size_t i = 0; i < 5; ++ i)
{
combo.push_back (new QComboBox (parent));
combo.back()->addItem ("Favored");
combo.back()->addItem ("Neutral");
combo.back()->addItem ("Penalized");
if (i == 0)
combo.back()->setCurrentIndex (scat);
else if (i == 1)
combo.back()->setCurrentIndex (plan);
else if (i == 2)
combo.back()->setCurrentIndex (hori);
else if (i == 3)
combo.back()->setCurrentIndex (elev);
else if (i == 4)
combo.back()->setCurrentIndex (colo);
}
}
~ClassRow ()
{
}
};
std::vector<ClassRow> class_rows;
Ui::PointSetClassification ui_widget;
QColor color_att;
}; // end Polyhedron_demo_point_set_classification_plugin
#include "Point_set_classification_plugin.moc"

View File

@ -0,0 +1,781 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PointSetClassification</class>
<widget class="QDockWidget" name="PointSetClassification">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>511</width>
<height>572</height>
</rect>
</property>
<property name="windowTitle">
<string>Point set classification</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>2</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>I/O</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Input</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Segment point sets larger than</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="max_nb_points">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string/>
</property>
<property name="minimum">
<number>100</number>
</property>
<property name="maximum">
<number>100000000</number>
</property>
<property name="singleStep">
<number>100000</number>
</property>
<property name="value">
<number>5000000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>points</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="create_from_file">
<property name="text">
<string>Create items from PLY</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="create_from_item">
<property name="text">
<string>Create item from point set item</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Classification parameters</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QPushButton" name="load_config">
<property name="text">
<string>Load config</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="save_config">
<property name="text">
<string>Save config</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Output</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="save">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Save colored point set</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="generate_point_set_items">
<property name="text">
<string>Generate point set items</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QPushButton" name="extract_facades">
<property name="text">
<string>Extract facades</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="extract_2d_outline">
<property name="text">
<string>Extract 2D outline</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_8">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Features</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<spacer name="verticalSpacer_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="estimate_parameters">
<property name="text">
<string>Estimate parameters</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="gridResolutionLabel">
<property name="text">
<string>Grid resolution</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="gridResolutionDoubleSpinBox">
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.001000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="radiusNeighborsLabel">
<property name="text">
<string>Radius neighbors</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="radiusNeighborsDoubleSpinBox">
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.001000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.500000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="radiusDTMLabel">
<property name="text">
<string>Radius DTM</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="radiusDTMDoubleSpinBox">
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.001000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>2.500000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="compute_features">
<property name="text">
<string>Compute features</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="compute_ransac">
<property name="text">
<string>Compute RANSAC</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Classification</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<spacer name="verticalSpacer_9">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="multiply_weights">
<property name="text">
<string>Multiply weights</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="random_weights">
<property name="text">
<string>Try random weights</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="display">
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Real colors</string>
</property>
</item>
<item>
<property name="text">
<string>Classification</string>
</property>
</item>
<item>
<property name="text">
<string>Confidence</string>
</property>
</item>
<item>
<property name="text">
<string>Scatter</string>
</property>
</item>
<item>
<property name="text">
<string>Planarity</string>
</property>
</item>
<item>
<property name="text">
<string>Horizontality</string>
</property>
</item>
<item>
<property name="text">
<string>Elevation</string>
</property>
</item>
<item>
<property name="text">
<string>Color</string>
</property>
</item>
<item>
<property name="text">
<string>RANSAC</string>
</property>
</item>
<item>
<property name="text">
<string>Clusters</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="4" alignment="Qt::AlignHCenter">
<widget class="QDial" name="horizontalitySlider">
<property name="maximumSize">
<size>
<width>75</width>
<height>16777215</height>
</size>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="sliderPosition">
<number>999</number>
</property>
<property name="notchTarget">
<double>50.000000000000000</double>
</property>
<property name="notchesVisible">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="5" alignment="Qt::AlignHCenter">
<widget class="QDial" name="elevationSlider">
<property name="maximumSize">
<size>
<width>75</width>
<height>16777215</height>
</size>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="sliderPosition">
<number>999</number>
</property>
<property name="notchTarget">
<double>50.000000000000000</double>
</property>
<property name="notchesVisible">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="3" alignment="Qt::AlignHCenter">
<widget class="QDial" name="planaritySlider">
<property name="maximumSize">
<size>
<width>75</width>
<height>16777215</height>
</size>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="sliderPosition">
<number>999</number>
</property>
<property name="wrapping">
<bool>false</bool>
</property>
<property name="notchTarget">
<double>50.000000000000000</double>
</property>
<property name="notchesVisible">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2" alignment="Qt::AlignHCenter">
<widget class="QDial" name="scatterSlider">
<property name="maximumSize">
<size>
<width>75</width>
<height>16777215</height>
</size>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="sliderPosition">
<number>999</number>
</property>
<property name="notchTarget">
<double>50.000000000000000</double>
</property>
<property name="notchesVisible">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="4" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="horizontalityLabel">
<property name="text">
<string>Horizontality</string>
</property>
</widget>
</item>
<item row="1" column="1" alignment="Qt::AlignHCenter|Qt::AlignBottom">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Active</string>
</property>
</widget>
</item>
<item row="0" column="5" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="elevationLabel">
<property name="text">
<string>Elevation</string>
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QPushButton" name="colorButton">
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item row="1" column="0" alignment="Qt::AlignHCenter|Qt::AlignBottom">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Class</string>
</property>
</widget>
</item>
<item row="0" column="2" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="scatterLabel">
<property name="text">
<string>Scatter</string>
</property>
</widget>
</item>
<item row="0" column="3" alignment="Qt::AlignHCenter">
<widget class="QLabel" name="planarityLabel">
<property name="text">
<string>Non-planarity</string>
</property>
</widget>
</item>
<item row="1" column="6" alignment="Qt::AlignHCenter">
<widget class="QDial" name="colorSlider">
<property name="maximumSize">
<size>
<width>75</width>
<height>16777215</height>
</size>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="value">
<number>999</number>
</property>
<property name="notchTarget">
<double>50.000000000000000</double>
</property>
<property name="notchesVisible">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="run">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Run (quick)</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QPushButton" name="run_with_smoothing">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Run with smoothing</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="smoothingDoubleSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Smoothing strenght</string>
</property>
<property name="maximum">
<double>10.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.500000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QPushButton" name="run_with_ransac">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Run with RANSAC</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="compute_clusters">
<property name="text">
<string>Compute clusters</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_10">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,648 @@
#ifndef POINT_SET_CLASSIFICATION_ITEM_H
#define POINT_SET_CLASSIFICATION_ITEM_H
#include <CGAL/Three/Scene_item.h>
#include <CGAL/IO/read_ply_points.h>
#include <CGAL/Point_set_classification.h>
#include <CGAL/Data_classification/Segmentation_attribute_scatter.h>
#include <CGAL/Data_classification/Segmentation_attribute_elevation.h>
#include <CGAL/Data_classification/Segmentation_attribute_horizontality.h>
#include <CGAL/Data_classification/Segmentation_attribute_nonplanarity.h>
#include <CGAL/Data_classification/Segmentation_attribute_color.h>
#include <CGAL/Shape_detection_3/Shape_base.h>
#include "Scene_point_set_classification_item_config.h"
#include "Scene_points_with_normal_item.h"
#include "Polyhedron_type_fwd.h"
#include "Kernel_type.h"
#include "Point_set_3.h"
#include <iostream>
typedef CGAL::Point_set_classification<Kernel> PSC;
typedef CGAL::Segmentation_attribute_scatter<Kernel> Scatter;
typedef CGAL::Segmentation_attribute_elevation<Kernel> Elevation;
typedef CGAL::Segmentation_attribute_horizontality<Kernel> Horizontality;
typedef CGAL::Segmentation_attribute_nonplanarity<Kernel> NonPlanarity;
typedef CGAL::Segmentation_attribute_color<Kernel> ColorSeg;
typedef CGAL::Data_classification::RGB_Color Color;
class QMenu;
class QAction;
// This class represents a point set in the OpenGL scene
class SCENE_POINT_SET_CLASSIFICATION_ITEM_EXPORT Scene_point_set_classification_item
: public CGAL::Three::Scene_item
{
Q_OBJECT
public:
Scene_point_set_classification_item(PSC* psc = NULL);
Scene_point_set_classification_item(const Scene_points_with_normal_item* points,
double grid_resolution);
Scene_point_set_classification_item(const Scene_point_set_classification_item& toCopy);
~Scene_point_set_classification_item();
Scene_point_set_classification_item* clone() const;
struct Region
{
std::vector<std::size_t> indices;
double x_min, x_max, y_min, y_max;
Region () : indices(),
x_min(std::numeric_limits<double>::max()),
x_max(-std::numeric_limits<double>::max()),
y_min(std::numeric_limits<double>::max()),
y_max(-std::numeric_limits<double>::max()) { }
};
template <bool use_y>
struct Sort_by_coordinate
{
std::vector<PSC::HPoint>& m_hps;
Sort_by_coordinate (std::vector<PSC::HPoint>& hps) : m_hps (hps) { }
bool operator() (const std::size_t& a, const std::size_t& b)
{
if (use_y)
return m_hps[a].position.y() < m_hps[b].position.y();
else
return m_hps[a].position.x() < m_hps[b].position.x();
}
};
template <typename OutputIterator>
bool segment_point_set (std::size_t nb_max_pt, OutputIterator output)
{
if (m_psc->HPS.size() < nb_max_pt)
return false;
std::list<Region> queue;
queue.push_front (Region());
for (std::size_t i = 0; i < m_psc->HPS.size(); ++ i)
{
queue.front().indices.push_back (i);
if (m_psc->HPS[i].position.x() < queue.front().x_min)
queue.front().x_min = m_psc->HPS[i].position.x();
if (m_psc->HPS[i].position.x() > queue.front().x_max)
queue.front().x_max = m_psc->HPS[i].position.x();
if (m_psc->HPS[i].position.y() < queue.front().y_min)
queue.front().y_min = m_psc->HPS[i].position.y();
if (m_psc->HPS[i].position.y() > queue.front().y_max)
queue.front().y_max = m_psc->HPS[i].position.y();
}
while (!(queue.empty()))
{
Region& current = queue.front();
if (current.indices.size() < nb_max_pt)
{
std::vector<Kernel::Point_3> dummy;
PSC* psc = new PSC (dummy.begin(), dummy.end(), m_psc->m_grid_resolution);
for (std::size_t i = 0; i < current.indices.size(); ++ i)
{
psc->HPS.push_back (PSC::HPoint());
psc->HPS.back().position = m_psc->HPS[current.indices[i]].position;
psc->HPS.back().echo = m_psc->HPS[current.indices[i]].echo;
psc->HPS.back().ind_x = m_psc->HPS[current.indices[i]].ind_x;
psc->HPS.back().ind_y = m_psc->HPS[current.indices[i]].ind_y;
psc->HPS.back().group = m_psc->HPS[current.indices[i]].group;
psc->HPS.back().AE_label = m_psc->HPS[current.indices[i]].AE_label;
psc->HPS.back().neighbor = m_psc->HPS[current.indices[i]].neighbor;
psc->HPS.back().confidence = m_psc->HPS[current.indices[i]].confidence;
psc->HPS.back().color = m_psc->HPS[current.indices[i]].color;
}
*(output ++) = new Scene_point_set_classification_item (psc);
}
else
{
if (current.x_max - current.x_min > current.y_max - current.y_min)
{
std::sort (current.indices.begin(), current.indices.end(),
Sort_by_coordinate<false>(m_psc->HPS));
queue.push_back(Region());
queue.push_back(Region());
std::list<Region>::iterator it = queue.end();
Region& positive = *(-- it);
Region& negative = *(-- it);
negative.y_min = current.y_min;
positive.y_min = current.y_min;
negative.y_max = current.y_max;
positive.y_max = current.y_max;
negative.x_min = current.x_min;
positive.x_max = current.x_max;
std::size_t i = 0;
for (; i < current.indices.size() / 2; ++ i)
negative.indices.push_back (current.indices[i]);
double med_x = 0.5 * (m_psc->HPS[current.indices[i]].position.x()
+ m_psc->HPS[current.indices[i+1]].position.x());
negative.x_max = med_x;
positive.x_min = med_x;
for (; i < current.indices.size(); ++ i)
positive.indices.push_back (current.indices[i]);
}
else
{
std::sort (current.indices.begin(), current.indices.end(),
Sort_by_coordinate<true>(m_psc->HPS));
queue.push_back(Region());
queue.push_back(Region());
std::list<Region>::iterator it = queue.end();
Region& positive = *(-- it);
Region& negative = *(-- it);
negative.x_min = current.x_min;
positive.x_min = current.x_min;
negative.x_max = current.x_max;
positive.x_max = current.x_max;
negative.y_min = current.y_min;
positive.y_max = current.y_max;
std::size_t i = 0;
for (; i < current.indices.size() / 2; ++ i)
negative.indices.push_back (current.indices[i]);
double med_y = 0.5 * (m_psc->HPS[current.indices[i]].position.y()
+ m_psc->HPS[current.indices[i+1]].position.y());
negative.y_max = med_y;
positive.y_min = med_y;
for (; i < current.indices.size(); ++ i)
positive.indices.push_back (current.indices[i]);
}
}
queue.pop_front ();
}
return true;
}
// Function to override the context menu
QMenu* contextMenu();
// IO
bool read_ply_point_set(std::istream& in, double grid_resolution);
bool write_ply_point_set(std::ostream& out) const;
// Function for displaying meta-data of the item
virtual QString toolTip() const;
virtual void invalidateOpenGLBuffers();
// Indicate if rendering mode is supported
virtual bool supportsRenderingMode(RenderingMode m) const;
virtual void drawEdges(CGAL::Three::Viewer_interface* viewer) const;
virtual void drawPoints(CGAL::Three::Viewer_interface*) const;
virtual void drawSplats(CGAL::Three::Viewer_interface*) const;
// Gets dimensions
virtual bool isFinite() const { return true; }
virtual bool isEmpty() const;
virtual void compute_bbox() const;
virtual void setRenderingMode(RenderingMode m);
void change_color (int index);
int real_index_color() const;
void estimate_parameters (double& grid_resolution,
double& radius_neighbors,
double& radius_dtm);
void compute_features (const double& grid_resolution,
const double& radius_neighbors,
const double& radius_dtm,
const QColor& c);
void compute_ransac (const double& radius_neighbors);
void compute_clusters (const double& radius_neighbors);
void save_2d_image ();
template <typename Classes>
bool run (double weight_scat, double weight_plan,
double weight_hori, double weight_elev, double weight_colo,
bool multiply, Classes& classes, int method, double weight)
{
if (m_scat == NULL || m_plan == NULL || m_hori == NULL || m_elev == NULL || m_colo == NULL)
return false;
m_psc->m_grid_resolution = weight;
m_scat->weight = std::tan ((1. - weight_scat) * (CGAL_PI/2));
m_plan->weight = std::tan ((1. - weight_plan) * (CGAL_PI/2));
m_hori->weight = std::tan ((1. - weight_hori) * (CGAL_PI/2));
m_elev->weight = std::tan ((1. - weight_elev) * (CGAL_PI/2));
m_colo->weight = std::tan ((1. - weight_colo) * (CGAL_PI/2));
// m_scat->weight = 2 * (1. - weight_scat) * m_scat->max;
// m_plan->weight = 2 * (1. - weight_plan) * m_plan->max;
// m_hori->weight = 2 * (1. - weight_hori) * m_hori->max;
// m_elev->weight = 2 * (1. - weight_elev) * m_elev->mean;
// m_colo->weight = 2 * (1. - weight_colo) * m_colo->max;
if (!(m_psc->segmentation_classes.empty()))
{
for (std::size_t i = 0; i < m_psc->segmentation_classes.size(); ++ i)
delete m_psc->segmentation_classes[i];
m_psc->segmentation_classes.clear();
}
std::cerr << "Running with:" << std::endl;
for (std::size_t i = 0; i < classes.size(); ++ i)
{
if (!(classes[i].checkbox->isChecked()))
continue;
m_psc->segmentation_classes.push_back (new CGAL::Classification_type
(classes[i].label->text().toLower().toStdString().c_str()));
m_psc->segmentation_classes.back()->set_attribute_effect (m_scat, (CGAL::Classification_type::Attribute_side)(classes[i].combo[0]->currentIndex()));
m_psc->segmentation_classes.back()->set_attribute_effect (m_plan, (CGAL::Classification_type::Attribute_side)(classes[i].combo[1]->currentIndex()));
m_psc->segmentation_classes.back()->set_attribute_effect (m_hori, (CGAL::Classification_type::Attribute_side)(classes[i].combo[2]->currentIndex()));
m_psc->segmentation_classes.back()->set_attribute_effect (m_elev, (CGAL::Classification_type::Attribute_side)(classes[i].combo[3]->currentIndex()));
m_psc->segmentation_classes.back()->set_attribute_effect (m_colo, (CGAL::Classification_type::Attribute_side)(classes[i].combo[4]->currentIndex()));
std::cerr << " * ";
m_psc->segmentation_classes.back()->info();
}
std::cerr << "Weights: " << m_scat->weight << " " << m_plan->weight << " " << m_hori->weight
<< " " << m_elev->weight << " " << m_colo->weight << std::endl;
m_psc->m_multiplicative = multiply;
m_psc->classify(method);
// if (method != 0)
// save_2d_image();
invalidateOpenGLBuffers();
return false;
}
template <typename ItemContainer>
void generate_point_sets (ItemContainer& items)
{
for (std::size_t i = 0; i < m_psc->HPS.size(); ++ i)
{
int c = m_psc->HPS[i].AE_label;
if (c == -1)
continue;
if (m_psc->segmentation_classes[c]->id() == "vegetation")
items[0]->point_set()->push_back (m_psc->HPS[i].position);
else if (m_psc->segmentation_classes[c]->id() == "ground")
items[1]->point_set()->push_back (m_psc->HPS[i].position);
else if (m_psc->segmentation_classes[c]->id() == "road")
items[2]->point_set()->push_back (m_psc->HPS[i].position);
else if (m_psc->segmentation_classes[c]->id() == "roof")
items[3]->point_set()->push_back (m_psc->HPS[i].position);
else if (m_psc->segmentation_classes[c]->id() == "facade")
items[4]->point_set()->push_back (m_psc->HPS[i].position);
else if (m_psc->segmentation_classes[c]->id() == "building")
items[5]->point_set()->push_back (m_psc->HPS[i].position);
}
}
void extract_2d_outline (double radius,
std::vector<Kernel::Point_3>& outline);
void extract_building_map (double radius,
std::vector<Kernel::Triangle_3>& faces);
void extract_facades (double radius,
std::vector<Kernel::Triangle_3>& faces);
public Q_SLOTS:
// Data
private:
PSC* m_psc;
Scatter* m_scat;
Elevation* m_elev;
Horizontality* m_hori;
NonPlanarity* m_plan;
ColorSeg* m_colo;
std::vector<Color> m_real_colors;
int m_index_color;
enum VAOs {
Edges=0,
ThePoints,
NbOfVaos = ThePoints+1
};
enum VBOs {
Edges_vertices = 0,
Points_vertices,
Points_colors,
NbOfVbos = Points_colors+1
};
mutable std::vector<double> positions_lines;
mutable std::vector<double> positions_points;
mutable std::vector<double> colors_points;
mutable std::size_t nb_points;
mutable std::size_t nb_lines;
mutable QOpenGLShaderProgram *program;
using CGAL::Three::Scene_item::initializeBuffers;
void initializeBuffers(CGAL::Three::Viewer_interface *viewer) const;
void compute_normals_and_vertices() const;
}; // end class Scene_point_set_classification_item
template <typename Floating>
class My_ply_interpreter
{
std::vector<Kernel::Point_3>& points;
std::vector<Color>& colors;
std::vector<unsigned char>& echo;
bool echo_float;
public:
My_ply_interpreter (std::vector<Kernel::Point_3>& points,
std::vector<Color>& colors,
std::vector<unsigned char>& echo)
: points (points), colors (colors), echo (echo), echo_float (false)
{ }
// Init and test if input file contains the right properties
bool is_applicable (CGAL::Ply_reader& reader)
{
if (reader.does_tag_exist<float> ("scalar_Return_Number"))
echo_float = true;
return reader.does_tag_exist<Floating> ("x")
&& reader.does_tag_exist<Floating> ("y")
&& reader.does_tag_exist<Floating> ("z");
}
// Describes how to process one line (= one point object)
void process_line (CGAL::Ply_reader& reader)
{
Floating x = (Floating)0., y = (Floating)0., z = (Floating)0.;
Color c = {{ 0, 0, 0 }};
unsigned char e = 0;
reader.assign (x, "x");
reader.assign (y, "y");
reader.assign (z, "z");
reader.assign (c[0], "red");
reader.assign (c[1], "green");
reader.assign (c[2], "blue");
if (echo_float)
{
float f = 0.;
reader.assign (f, "scalar_Return_Number");
e = (unsigned char)f;
}
else
reader.assign (e, "echo");
points.push_back (Kernel::Point_3 (x, y, z));
colors.push_back (c);
echo.push_back (e);
}
};
template <typename HPS>
struct HPS_property_map{
const std::vector<HPS>* points;
typedef Kernel::Point_3 value_type;
typedef const value_type& reference;
typedef std::size_t key_type;
typedef boost::lvalue_property_map_tag category;
HPS_property_map () : points(NULL)
{
}
HPS_property_map (std::vector<HPS>* pts) : points(pts)
{
}
~HPS_property_map()
{
}
HPS_property_map (const HPS_property_map& pmap) : points(pmap.points)
{
}
HPS_property_map& operator= (const HPS_property_map& pmap)
{
this->points = pmap.points;
return *this;
}
reference operator[](key_type k) const
{
return (*points)[k].position;
}
friend reference get(const HPS_property_map& ppmap,key_type i)
{return ppmap[i];}
};
template <class Traits>
class My_plane : public CGAL::Shape_detection_3::Shape_base<Traits> {
public:
/// \cond SKIP_IN_MANUAL
typedef typename Traits::Point_map Point_map;
///< property map to access the location of an input point.
typedef typename Traits::Normal_map Normal_map;
///< property map to access the unoriented normal of an input point.
typedef typename Traits::FT FT; ///< number type.
typedef typename Traits::Point_3 Point_3; ///< point type.
typedef typename Traits::Vector_3 Vector_3;
/// \endcond
typedef typename Traits::Plane_3 Plane_3;///< plane type for conversion operator.
public:
My_plane() : CGAL::Shape_detection_3::Shape_base<Traits>() {}
/*!
Conversion operator to `Plane_3` type.
*/
operator Plane_3() const {
return Plane_3(this->get_x(m_normal), this->get_y(m_normal), this->get_z(m_normal), m_d);
}
/*!
Normal vector of the plane.
*/
Vector_3 plane_normal() const {
return m_normal;
}
/*!
Signed distance from the origin.
*/
FT d() const {
return m_d;
}
/// \cond SKIP_IN_MANUAL
/*!
Computes squared Euclidean distance from query point to the shape.
*/
FT squared_distance(const Point_3 &p) const {
FT d = this->scalar_pdct(
this->constr_vec(p, m_point_on_primitive), m_normal);
return d * d;
}
/*!
Helper function to write the plane equation and
number of assigned points into a string.
*/
std::string info() const {
std::stringstream sstr;
sstr << "Type: plane (" << this->get_x(m_normal) << ", " << this->get_y(m_normal)
<< ", " << this->get_z(m_normal) << ")x - " << m_d << "= 0"
<< " #Pts: " << this->m_indices.size();
return sstr.str();
}
/// \endcond
protected:
/// \cond SKIP_IN_MANUAL
virtual void create_shape(const std::vector<std::size_t> &indices) {
Point_3 p1 = this->point(indices[0]);
Point_3 p2 = this->point(indices[1]);
Point_3 p3 = this->point(indices[2]);
m_normal = this->cross_pdct(
this->constr_vec(p2, p1), this->constr_vec(p3, p1));
if (std::fabs (m_normal * Vector_3 (0., 0., 1.)) > 0.5)
m_normal = Vector_3 (0., 0., 1.);
else
m_normal = Vector_3 (m_normal.x(), m_normal.y(), 0.);
FT length = CGAL::sqrt(this->sqlen(m_normal));
// Are the points almost singular?
if (length < (FT)0.0001) {
return;
}
m_normal = this->scale(m_normal, (FT)1.0 / length);
m_d = -(this->get_x(p1) * this->get_x(m_normal)
+ this->get_y(p1) * this->get_y(m_normal)
+ this->get_z(p1) * this->get_z(m_normal));
//check deviation of the 3 normal
Vector_3 l_v = this->constr_vec();
for (std::size_t i = 0;i<3;i++) {
l_v = this->normal(indices[i]);
if (CGAL::abs(this->scalar_pdct(l_v, m_normal))
< this->m_normal_threshold * CGAL::sqrt(this->sqlen(l_v))) {
this->m_is_valid = false;
return;
}
m_point_on_primitive = p1;
m_base1 = this->cross_pdct(this->constr_vec(p2, p1), m_normal);
m_base1 = this->scale(m_base1, ((FT)1.0 / CGAL::sqrt(this->sqlen(m_base1))));
m_base2 = this->cross_pdct(m_base1, m_normal);
m_base2 = this->scale(m_base2, ((FT)1.0 / CGAL::sqrt(this->sqlen(m_base2))));
}
this->m_is_valid = true;
}
virtual void parameters(const std::vector<std::size_t> &indices,
std::vector<std::pair<FT, FT> > &parameterSpace,
FT &,
FT min[2],
FT max[2]) const {
// Transform first point before to initialize min/max
Vector_3 p = this->constr_vec(
m_point_on_primitive, this->point(indices[0]));
FT u = this->scalar_pdct(p, m_base1);
FT v = this->scalar_pdct(p, m_base2);
parameterSpace[0] = std::pair<FT, FT>(u, v);
min[0] = max[0] = u;
min[1] = max[1] = v;
for (std::size_t i = 1;i<indices.size();i++) {
p = this->constr_vec(m_point_on_primitive, this->point(indices[i]));
u = this->scalar_pdct(p, m_base1);
v = this->scalar_pdct(p, m_base2);
min[0] = (std::min<FT>)(min[0], u);
max[0] = (std::max<FT>)(max[0], u);
min[1] = (std::min<FT>)(min[1], v);
max[1] = (std::max<FT>)(max[1], v);
parameterSpace[i] = std::pair<FT, FT>(u, v);
}
}
virtual void squared_distance(const std::vector<std::size_t> &indices,
std::vector<FT> &dists) const {
for (std::size_t i = 0;i<indices.size();i++) {
const FT d = this->scalar_pdct(
this->constr_vec(m_point_on_primitive, this->point(indices[i])),
m_normal);
dists[i] = d * d;
}
}
virtual void cos_to_normal(const std::vector<std::size_t> &indices,
std::vector<FT> &angles) const {
for (std::size_t i = 0;i<indices.size();i++) {
angles[i] = CGAL::abs(
this->scalar_pdct(this->normal(indices[i]), m_normal));
}
}
FT cos_to_normal(const Point_3 &, const Vector_3 &n) const{
return CGAL::abs(this->scalar_pdct(n, m_normal));
}
virtual std::size_t minimum_sample_size() const {
return 3;
}
virtual bool supports_connected_component() const {
return true;
}
private:
Point_3 m_point_on_primitive;
Vector_3 m_base1, m_base2, m_normal;
FT m_d;
/// \endcond
};
#endif // POINT_SET_CLASSIFICATION_ITEM_H

View File

@ -0,0 +1,10 @@
#ifndef SCENE_POINT_SET_CLASSIFICATION_ITEM_CONFIG_H
#define SCENE_POINT_SET_CLASSIFICATION_ITEM_CONFIG_H
#ifdef scene_point_set_classification_item_EXPORTS
# define SCENE_POINT_SET_CLASSIFICATION_ITEM_EXPORT Q_DECL_EXPORT
#else
# define SCENE_POINT_SET_CLASSIFICATION_ITEM_EXPORT Q_DECL_IMPORT
#endif
#endif // SCENE_POINT_SET_CLASSIFICATION_ITEM_CONFIG_H

View File

@ -177,6 +177,59 @@ public:
return is;
}
};
template <>
class Input_rep<float> {
float& t;
public:
//! initialize with a reference to \a t.
Input_rep( float& tt) : t(tt) {}
std::istream& operator()( std::istream& is) const
{
typedef std::istream istream;
typedef istream::char_type char_type;
typedef istream::int_type int_type;
typedef istream::traits_type traits_type;
std::string buffer;
buffer.reserve(32);
char_type c;
do {
const int_type i = is.get();
if(i == traits_type::eof()) {
return is;
}
c = static_cast<char_type>(i);
}while (std::isspace(c));
if(c == '-'){
buffer += '-';
} else if(c != '+'){
is.unget();
}
do {
const int_type i = is.get();
if(i == traits_type::eof()) {
is.clear(is.rdstate() & ~std::ios_base::failbit);
break;
}
c = static_cast<char_type>(i);
if(std::isdigit(c) || (c =='.') || (c =='E') || (c =='e') || (c =='+') || (c =='-')){
buffer += c;
}else{
is.unget();
break;
}
}while(true);
if(sscanf(buffer.c_str(), "%f", &t) != 1) {
// if a 'buffer' does not contain a double, set the fail bit.
is.setstate(std::ios_base::failbit);
}
return is;
}
};
#endif
/*! \relates Input_rep