dust3d/thirdparty/cgal/CGAL-4.13/include/CGAL/Shape_detection_3/Octree.h

736 lines
24 KiB
C++
Executable File

// Copyright (c) 2015 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$
// SPDX-License-Identifier: GPL-3.0+
//
//
// Author(s) : Sven Oesau, Yannick Verdie, Clément Jamin, Pierre Alliez
//
#ifndef CGAL_SHAPE_DETECTION_3_OCTREE_H
#define CGAL_SHAPE_DETECTION_3_OCTREE_H
#include <CGAL/license/Point_set_shape_detection_3.h>
#include <limits>
#include <stack>
#include <CGAL/Random.h>
#include <CGAL/Bbox_3.h>
#include <CGAL/Shape_detection_3/Shape_base.h>
extern int scoreTime;
namespace CGAL {
namespace Shape_detection_3 {
template<class Traits>
class Efficient_RANSAC;
namespace internal {
const std::size_t size_t_max = (std::numeric_limits<std::size_t>::max)();
template<class Sdt>
class DirectPointAccessor {
public:
typedef Sdt Sd_traits;
typedef typename Sd_traits::Input_range::iterator Input_iterator;
DirectPointAccessor() {}
DirectPointAccessor(const Input_iterator &begin,
const Input_iterator &beyond,
std::size_t offset) : m_first(begin), m_offset(offset) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
}
Input_iterator at(std::size_t i) {
return m_first + i;
}
std::size_t index(std::size_t i) {
return i + m_offset;
}
std::size_t offset() {
return m_offset;
}
std::size_t size() {
return m_beyond - m_first + 1;
}
Input_iterator first() {
return m_first;
}
Input_iterator beyond() {
return m_beyond;
}
void setData(Input_iterator &begin, Input_iterator &beyond) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
}
void swap(std::size_t a, std::size_t b) {
typename std::iterator_traits<Input_iterator>::value_type tmp;
tmp = m_first[a];
m_first[a] = m_first[b];
m_first[b] = tmp;
}
protected:
Input_iterator m_first;
private:
Input_iterator m_beyond;
std::size_t m_offset;
};
template<class Sdt>
class IndexedPointAccessor {
public:
typedef Sdt Sd_traits;
typedef typename Sd_traits::Input_range::iterator Input_iterator;
IndexedPointAccessor() {}
IndexedPointAccessor(const Input_iterator &begin,
const Input_iterator &beyond, std::size_t)
: m_first(begin) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
m_indices.resize(size());
for (std::size_t i = 0;i<size();i++)
m_indices[i] = i;
}
Input_iterator at(std::size_t i) {
return m_first + m_indices[i];
}
std::size_t index(std::size_t i) {
return m_indices[i];
}
std::size_t offset() {
return 0;
}
Input_iterator first() {
return m_first;
}
Input_iterator beyond() {
return m_beyond;
}
void setData(Input_iterator &begin, Input_iterator &beyond) {
m_beyond = (beyond == begin) ? begin : beyond - 1;
m_indices.resize(size());
for (std::size_t i = 0;i<size();i++)
m_indices[i] = i;
}
std::size_t size() {
return m_beyond - m_first + 1;
}
void swap(std::size_t a, std::size_t b) {
std::size_t tmp = m_indices[a];
m_indices[a] = m_indices[b];
m_indices[b] = tmp;
}
protected:
Input_iterator m_first;
private:
std::vector<std::size_t> m_indices;
Input_iterator m_beyond;
};
template<class PointAccessor>
class Octree : public PointAccessor {
typedef typename PointAccessor::Sd_traits Sd_traits;
typedef typename Sd_traits::Input_range::iterator Input_iterator;
typedef Shape_base<Sd_traits> Shape;
typedef typename Sd_traits::Point_3 Point_3;
typedef typename Sd_traits::Vector_3 Vector_3;
typedef typename Sd_traits::FT FT;
typedef typename Sd_traits::Point_map Point_map;
typedef typename Sd_traits::Normal_map Normal_map;
template<class Sd_traits>
friend class ::CGAL::Shape_detection_3::Efficient_RANSAC;
struct Cell {
std::size_t first, last;
Cell *child[8];
Point_3 center;
std::size_t level;
Cell(std::size_t first, std::size_t last, Point_3 center, std::size_t level)
: first(first), last(last), center(center), level(level) {
memset(child, 0, sizeof(Cell *) * 8);
}
bool isLeaf() const {
for (std::size_t i = 0;i<8;i++) {
if (child[i])
return false;
}
return true;
}
std::size_t size() const {
if (first == size_t_max || last == size_t_max)
return 0;
else return (last - first + 1);
}
};
// --------------------------------------------------------------------------
// Utilities
// --------------------------------------------------------------------------
FT get_x(const Vector_3& v){ return m_traits.compute_x_3_object()(v); }
FT get_y(const Vector_3& v){ return m_traits.compute_y_3_object()(v); }
FT get_z(const Vector_3& v){ return m_traits.compute_z_3_object()(v); }
FT get_x(const Point_3& p){ return m_traits.compute_x_3_object()(p); }
FT get_y(const Point_3& p){ return m_traits.compute_y_3_object()(p); }
FT get_z(const Point_3& p){ return m_traits.compute_z_3_object()(p); }
FT get_coord(const Point_3& p, unsigned int d)
{
CGAL_assertion(d >= 0 && d < 3);
switch (d)
{
case 0: return get_x(p);
case 1: return get_y(p);
case 2: return get_z(p);
default: return FT(0);
}
}
Point_3 constr_pt(FT x, FT y, FT z) const
{ return m_traits.construct_point_3_object()(x, y, z); }
Vector_3 constr_vec(const Point_3& p, const Point_3& q) const
{ return m_traits.construct_vector_3_object()(p, q); }
Point_3 transl(const Point_3& p, const Vector_3 &v)
{ return m_traits.construct_translated_point_3_object()(p, v); }
public:
Octree(Sd_traits const& traits)
: m_traits(traits), m_bucket_size(20), m_set_max_level(10), m_root(NULL) {}
Octree(Sd_traits const& traits,
const Input_iterator &first,
const Input_iterator &beyond,
Point_map& point_pmap,
Normal_map& normal_pmap,
std::size_t offset = 0,
std::size_t bucketSize = 20,
std::size_t maxLevel = 10)
: PointAccessor(first, beyond, offset),
m_traits(traits),
m_root(NULL),
m_bucket_size(bucketSize),
m_set_max_level(maxLevel),
m_point_pmap (point_pmap),
m_normal_pmap (normal_pmap) {}
~Octree() {
if (!m_root)
return;
std::stack<Cell *> stack;
stack.push(m_root);
while (!stack.empty()) {
Cell *cell = stack.top();
stack.pop();
for (std::size_t i = 0;i<8;i++)
if (cell->child[i])
stack.push(cell->child[i]);
delete cell;
}
}
// Sorting data in a way such that points of one cell
// are always in one range and ordered child-wise:
// +---+---+
// | 1.| 0.|
// +---+---+
// | 3.| 2.|
// +---+---+
// z max before z min, then y max before y min, then x max before x min
void createTree(double cluster_epsilon_for_max_level_recomputation = -1.) {
buildBoundingCube();
std::size_t count = 0;
m_max_level = 0;
if (cluster_epsilon_for_max_level_recomputation > 0.)
{
FT bbox_diagonal = (FT) CGAL::sqrt(
(m_bBox.xmax() - m_bBox.xmin()) * (m_bBox.xmax() - m_bBox.xmin())
+ (m_bBox.ymax() - m_bBox.ymin()) * (m_bBox.ymax() - m_bBox.ymin())
+ (m_bBox.zmax() - m_bBox.zmin()) * (m_bBox.zmax() - m_bBox.zmin()));
m_set_max_level = std::size_t (std::log (bbox_diagonal
/ cluster_epsilon_for_max_level_recomputation)
/ std::log (2.0));
}
std::stack<Cell *> stack;
m_root = new Cell(0, this->size() - 1, m_center, 0);
stack.push(m_root);
while (!stack.empty()) {
Cell *cell= stack.top();
stack.pop();
m_max_level = std::max<std::size_t>(m_max_level, cell->level);
if (cell->level == m_set_max_level)
continue;
std::size_t zLowYHighXSplit, zLowYLowXSplit, zLowYSplit;
std::size_t zHighYSplit, zHighYHighXSplit, zHighYLowXSplit;
std::size_t zSplit = split(cell->first, cell->last, 2, get_z(cell->center));
if (zSplit != size_t_max) {
zLowYSplit = split(cell->first, zSplit, 1, get_y(cell->center));
if (zLowYSplit != size_t_max) {
zLowYLowXSplit = split(cell->first,
zLowYSplit, 0, get_x(cell->center));
zLowYHighXSplit = split(zLowYSplit + 1,
zSplit, 0, get_x(cell->center));
}
else {
zLowYLowXSplit = size_t_max;
zLowYHighXSplit = split(cell->first, zSplit, 0, get_x(cell->center));
}
zHighYSplit = split(zSplit + 1, cell->last, 1, get_y(cell->center));
if (zHighYSplit != size_t_max) {
zHighYHighXSplit = split(zHighYSplit + 1,
cell->last, 0, get_x(cell->center));
zHighYLowXSplit = split(zSplit + 1,
zHighYSplit, 0, get_x(cell->center));
}
else {
zHighYLowXSplit = size_t_max;
zHighYHighXSplit = split(zSplit + 1,
cell->last, 0, get_x(cell->center));
}
}
else {
zLowYSplit = size_t_max;
zLowYLowXSplit = size_t_max;
zLowYHighXSplit = size_t_max;
zHighYSplit = split(cell->first,
cell->last,
1,
get_y(cell->center));
if (zHighYSplit != size_t_max) {
zHighYHighXSplit = split(zHighYSplit + 1,
cell->last,
0,
get_x(cell->center));
zHighYLowXSplit = split(cell->first,
zHighYSplit,
0,
get_x(cell->center));
}
else {
zHighYLowXSplit = size_t_max;
zHighYHighXSplit = split(cell->first,
cell->last,
0,
get_x(cell->center));
}
}
FT width = m_width / (1<<(cell->level + 1));
if (zSplit != size_t_max) {
if (zLowYSplit != size_t_max) {
if (zLowYLowXSplit != size_t_max) {
if (cell->first <= zLowYLowXSplit) {
//---
cell->child[7] = new Cell(cell->first,
zLowYLowXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width,-width,-width))),
cell->level + 1);
if (cell->child[7]->size() > m_bucket_size)
stack.push(cell->child[7]);
}
}
else zLowYLowXSplit = cell->first - 1;
if (zLowYLowXSplit < zLowYSplit || zLowYLowXSplit == size_t_max) {
//+--
cell->child[6] = new Cell(zLowYLowXSplit + 1,
zLowYSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width,-width,-width))),
cell->level + 1);
if (cell->child[6]->size() > m_bucket_size)
stack.push(cell->child[6]);
}
}
else zLowYSplit = cell->first - 1;
if (zLowYHighXSplit != size_t_max) {
if (zLowYSplit < zLowYHighXSplit || zLowYSplit == size_t_max) {
//-+-
cell->child[5] = new Cell(zLowYSplit + 1,
zLowYHighXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width, width,-width))),
cell->level + 1);
if (cell->child[5]->size() > m_bucket_size)
stack.push(cell->child[5]);
}
}
else zLowYHighXSplit = zLowYSplit;
if (zLowYHighXSplit < zSplit || zLowYHighXSplit == size_t_max) {
//++-
cell->child[4] = new Cell(zLowYHighXSplit + 1,
zSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width, width,-width))),
cell->level + 1);
if (cell->child[4]->size() > m_bucket_size)
stack.push(cell->child[4]);
}
}
else zSplit = cell->first - 1;
if (zHighYSplit != size_t_max) {
if (zHighYLowXSplit != size_t_max) {
if (zSplit < zHighYLowXSplit || zSplit == size_t_max) {
//--+
cell->child[3] = new Cell(zSplit + 1,
zHighYLowXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width,-width, width))),
cell->level + 1);
if (cell->child[3]->size() > m_bucket_size)
stack.push(cell->child[3]);
}
}
else zHighYLowXSplit = zSplit;
if (zHighYLowXSplit < zHighYSplit || zHighYLowXSplit == size_t_max) {
//+-+
cell->child[2] = new Cell(zHighYLowXSplit + 1,
zHighYSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width,-width, width))),
cell->level + 1);
if (cell->child[2]->size() > m_bucket_size)
stack.push(cell->child[2]);
}
}
else zHighYSplit = zSplit;
if (zHighYHighXSplit != size_t_max) {
if (zHighYSplit < zHighYHighXSplit || zHighYSplit == size_t_max) {
//-++
cell->child[1] = new Cell(zHighYSplit + 1,
zHighYHighXSplit,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(-width, width, width))),
cell->level + 1);
if (cell->child[1]->size() > m_bucket_size)
stack.push(cell->child[1]);
}
}
else zHighYHighXSplit = zHighYSplit;
if (zHighYHighXSplit <= cell->last || zHighYHighXSplit == size_t_max) {
if (zHighYHighXSplit < cell->last || zHighYHighXSplit == size_t_max) {
//+++
cell->child[0] = new Cell(zHighYHighXSplit + 1,
cell->last,
transl(cell->center, constr_vec(
ORIGIN, constr_pt(width, width, width))),
cell->level + 1);
if (cell->child[0]->size() > m_bucket_size)
stack.push(cell->child[0]);
}
}
std::size_t sum = 0;
for (std::size_t i = 0;i<8;i++)
sum += (cell->child[i]) ? cell->child[i]->size() : 0;
count++;
}
}
bool drawSamplesFromCellContainingPoint(const Point_3 &p,
std::size_t level,
std::set<std::size_t>& indices,
const std::vector<int>& shapeIndex,
std::size_t requiredSamples) {
bool upperZ, upperY, upperX;
Cell *cur = m_root;
while (cur && cur->level < level) {
upperX = get_x(cur->center) <= get_x(p);
upperY = get_y(cur->center) <= get_y(p);
upperZ = get_z(cur->center) <= get_z(p);
if (upperZ) {
if (upperY)
cur = (upperX) ? cur->child[0] : cur->child[1];
else cur = (upperX) ? cur->child[2] : cur->child[3];
}
else {
if (upperY)
cur = (upperX) ? cur->child[4] : cur->child[5];
else cur = (upperX) ? cur->child[6] : cur->child[7];
}
}
if (cur) {
std::size_t enough = 0;
for (std::size_t i = cur->first;i<=cur->last;i++) {
std::size_t j = this->index(i);
if (shapeIndex[j] == -1) {
enough++;
if (enough >= requiredSamples)
break;
}
}
if (enough >= requiredSamples) {
do {
std::size_t p = CGAL::get_default_random().
uniform_int<std::size_t>(0, cur->size() - 1);
std::size_t j = this->index(cur->first + p);
if (shapeIndex[j] == -1)
indices.insert(j);
} while (indices.size() < requiredSamples);
return true;
}
else return false;
}
else return false;
}
std::size_t maxLevel() {
return m_max_level;
}
std::size_t fullScore(Shape *candidate,
std::vector<int> &shapeIndex,
FT epsilon,
FT normal_threshold) {
std::vector<std::size_t> indices(m_root->size());
for (std::size_t i = 0;i<m_root->size();i++) {
indices[i] = index(m_root->first + i);
}
candidate->cost_function(this->begin() + m_root->first,
this->begin() + m_root->last,
shapeIndex,
epsilon,
normal_threshold,
indices);
return candidate->m_indices.size();
}
std::size_t score(Shape *candidate,
std::vector<int> &shapeIndex,
FT epsilon,
FT normal_threshold) {
std::stack<Cell *> stack;
stack.push(m_root);
while(!stack.empty()) {
Cell *cell = stack.top();
stack.pop();
FT width = m_width / (1<<(cell->level));
FT diag = CGAL::sqrt(FT(3) * width * width) + epsilon;
FT dist = candidate->squared_distance(cell->center);
if (dist > (diag * diag))
continue;
// differ between full or partial overlap?
// if full overlap further traversal of this branch is not necessary
if (cell->isLeaf()) {
std::vector<std::size_t> indices;
indices.reserve(cell->size());
for (std::size_t i = 0;i<cell->size();i++) {
if (shapeIndex[this->index(cell->first + i)] == -1) {
indices.push_back(this->index(cell->first + i));
}
}
candidate->cost_function(epsilon,
normal_threshold,
indices);
}
else {
for (std::size_t i = 0;i<8;i++)
if (cell->child[i])
stack.push(cell->child[i]);
}
}
return candidate->m_indices.size();
}
void setBucketSize(std::size_t bucketSize) {
m_bucket_size = bucketSize;
}
const Bbox_3 &boundingBox() {
return m_bBox;
}
const Bbox_3 &buildBoundingCube() {
FT min[] = {std::numeric_limits<FT>::infinity(),
std::numeric_limits<FT>::infinity(),
std::numeric_limits<FT>::infinity()};
FT max[] = {-std::numeric_limits<FT>::infinity(),
-std::numeric_limits<FT>::infinity(),
-std::numeric_limits<FT>::infinity()};
for (std::size_t i = 0;i<this->size();i++) {
Point_3 p = get(m_point_pmap, *this->at(i));
min[0] = (std::min<FT>)(min[0], get_x(p));
min[1] = (std::min<FT>)(min[1], get_y(p));
min[2] = (std::min<FT>)(min[2], get_z(p));
max[0] = (std::max<FT>)(max[0], get_x(p));
max[1] = (std::max<FT>)(max[1], get_y(p));
max[2] = (std::max<FT>)(max[2], get_z(p));
}
m_bBox = Bbox_3(min[0], min[1], min[2], max[0], max[1], max[2]);
m_width = (std::max)(max[0] - min[0],
(std::max)(max[1] - min[1], max[2] - min[2])) * (FT) 0.5;
m_center = constr_pt((min[0] + max[0]) * (FT) 0.5,
(min[1] + max[1]) * (FT) 0.5,
(min[2] + max[2]) * (FT) 0.5);
return m_bBox;
}
// returns index of last point below threshold
std::size_t split(std::size_t first, std::size_t last, std::size_t dimension, FT threshold) {
if (last == size_t_max || first == size_t_max)
return size_t_max;
if (first > last)
return first - 1;
std::size_t origFirst = first;
while(first < last) {
// find first above threshold
while (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold
&& first < last) {
first++;
}
// check if last has been reached
if (first == last) {
return (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold) ?
first : (first == origFirst) ? size_t_max : first - 1;
}
// find last below threshold
while (get_coord(
get(m_point_pmap, *this->at(last)),
static_cast<unsigned int>(dimension)) >= threshold
&& last > first) {
last--;
}
// check if first has been reached
if (last == first) {
return (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold) ?
first : (first == origFirst) ? size_t_max : first - 1;
}
this->swap(first, last);
first++;
last--;
}
return (get_coord(
get(m_point_pmap, *this->at(first)),
static_cast<unsigned int>(dimension)) < threshold) ?
first : (first == origFirst) ? size_t_max : first - 1;
}
Sd_traits m_traits;
Bbox_3 m_bBox;
Cell *m_root;
Point_3 m_center;
FT m_width;
std::size_t m_bucket_size;
std::size_t m_set_max_level;
std::size_t m_max_level;
Point_map m_point_pmap;
Normal_map m_normal_pmap;
};
}
}
}
#endif