Merge branch 'Point_set_classification-GF-old' into Point_set_classification-GF
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
|
|
@ -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>`
|
||||
|
||||
|
||||
*/
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
Manual
|
||||
Kernel_23
|
||||
STL_Extension
|
||||
Algebraic_foundations
|
||||
Circulator
|
||||
Stream_support
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
/*!
|
||||
\example Data_classification/example_point_set_classification.cpp
|
||||
*/
|
||||
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -95,6 +95,6 @@ Barycentric_coordinates_2
|
|||
Surface_mesh
|
||||
Surface_mesh_shortest_path
|
||||
Polygon_mesh_processing
|
||||
|
||||
Data_classification
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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>
|
||||
|
|
@ -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> > ¶meterSpace,
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||