1163 lines
43 KiB
C++
1163 lines
43 KiB
C++
/*
|
|
* SceneReconstruct.cpp
|
|
*
|
|
* Copyright (c) 2014-2015 SEACAVE
|
|
*
|
|
* Author(s):
|
|
*
|
|
* cDc <cdc.seacave@gmail.com>
|
|
*
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*
|
|
* Additional Terms:
|
|
*
|
|
* You are required to preserve legal notices and author attributions in
|
|
* that material or in the Appropriate Legal Notices displayed by works
|
|
* containing it.
|
|
*/
|
|
|
|
#include "Common.h"
|
|
#include "Scene.h"
|
|
// Delaunay: mesh reconstruction
|
|
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
|
#include <CGAL/Delaunay_triangulation_3.h>
|
|
#include <CGAL/Triangulation_vertex_base_with_info_3.h>
|
|
#include <CGAL/Triangulation_cell_base_with_info_3.h>
|
|
#include <CGAL/Spatial_sort_traits_adapter_3.h>
|
|
#include <CGAL/AABB_tree.h>
|
|
#include <CGAL/AABB_traits.h>
|
|
#include <CGAL/AABB_triangle_primitive.h>
|
|
#include <CGAL/Polyhedron_3.h>
|
|
|
|
using namespace MVS;
|
|
|
|
|
|
// D E F I N E S ///////////////////////////////////////////////////
|
|
|
|
// uncomment to enable multi-threading based on OpenMP
|
|
#ifdef _USE_OPENMP
|
|
#define DELAUNAY_USE_OPENMP
|
|
#endif
|
|
|
|
// uncomment to enable reconstruction algorithm of weakly supported surfaces
|
|
#define DELAUNAY_WEAKSURF
|
|
|
|
// uncomment to use IBFS algorithm for max-flow
|
|
// (faster, but not clear license policy)
|
|
#define DELAUNAY_MAXFLOW_IBFS
|
|
|
|
|
|
// S T R U C T S ///////////////////////////////////////////////////
|
|
|
|
#ifdef DELAUNAY_MAXFLOW_IBFS
|
|
#include "../Math/IBFS/IBFS.h"
|
|
template <typename NType, typename VType>
|
|
class MaxFlow
|
|
{
|
|
public:
|
|
// Type-Definitions
|
|
typedef NType node_type;
|
|
typedef VType value_type;
|
|
typedef IBFS::IBFSGraph graph_type;
|
|
|
|
public:
|
|
MaxFlow(size_t numNodes) {
|
|
graph.initSize((int)numNodes, (int)numNodes*2);
|
|
}
|
|
|
|
inline void AddNode(node_type n, value_type source, value_type sink) {
|
|
ASSERT(ISFINITE(source) && source >= 0 && ISFINITE(sink) && sink >= 0);
|
|
graph.addNode((int)n, source, sink);
|
|
}
|
|
|
|
inline void AddEdge(node_type n1, node_type n2, value_type capacity, value_type reverseCapacity) {
|
|
ASSERT(ISFINITE(capacity) && capacity >= 0 && ISFINITE(reverseCapacity) && reverseCapacity >= 0);
|
|
graph.addEdge((int)n1, (int)n2, capacity, reverseCapacity);
|
|
}
|
|
|
|
value_type ComputeMaxFlow() {
|
|
graph.initGraph();
|
|
return graph.computeMaxFlow();
|
|
}
|
|
|
|
inline bool IsNodeOnSrcSide(node_type n) const {
|
|
return graph.isNodeOnSrcSide((int)n);
|
|
}
|
|
|
|
protected:
|
|
graph_type graph;
|
|
};
|
|
#else
|
|
#include <boost/graph/graph_traits.hpp>
|
|
#include <boost/graph/one_bit_color_map.hpp>
|
|
#include <boost/property_map/property_map.hpp>
|
|
#include <boost/graph/adjacency_list.hpp>
|
|
#include <boost/graph/boykov_kolmogorov_max_flow.hpp>
|
|
template <typename NType, typename VType>
|
|
class MaxFlow
|
|
{
|
|
public:
|
|
// Type-Definitions
|
|
typedef NType node_type;
|
|
typedef VType value_type;
|
|
typedef boost::vecS out_edge_list_t;
|
|
typedef boost::vecS vertex_list_t;
|
|
typedef boost::adjacency_list_traits<out_edge_list_t, vertex_list_t, boost::directedS> graph_traits;
|
|
typedef typename graph_traits::edge_descriptor edge_descriptor;
|
|
typedef typename graph_traits::vertex_descriptor vertex_descriptor;
|
|
typedef typename graph_traits::vertices_size_type vertex_size_type;
|
|
struct Edge {
|
|
value_type capacity;
|
|
value_type residual;
|
|
edge_descriptor reverse;
|
|
};
|
|
typedef boost::adjacency_list<out_edge_list_t, vertex_list_t, boost::directedS, size_t, Edge> graph_type;
|
|
typedef typename boost::graph_traits<graph_type>::edge_iterator edge_iterator;
|
|
typedef typename boost::graph_traits<graph_type>::out_edge_iterator out_edge_iterator;
|
|
|
|
public:
|
|
MaxFlow(size_t numNodes) : graph(numNodes+2), S(node_type(numNodes)), T(node_type(numNodes+1)) {}
|
|
|
|
void AddNode(node_type n, value_type source, value_type sink) {
|
|
ASSERT(ISFINITE(source) && source >= 0 && ISFINITE(sink) && sink >= 0);
|
|
if (source > 0) {
|
|
edge_descriptor e(boost::add_edge(S, n, graph).first);
|
|
edge_descriptor er(boost::add_edge(n, S, graph).first);
|
|
graph[e].capacity = source;
|
|
graph[e].reverse = er;
|
|
graph[er].reverse = e;
|
|
}
|
|
if (sink > 0) {
|
|
edge_descriptor e(boost::add_edge(n, T, graph).first);
|
|
edge_descriptor er(boost::add_edge(T, n, graph).first);
|
|
graph[e].capacity = sink;
|
|
graph[e].reverse = er;
|
|
graph[er].reverse = e;
|
|
}
|
|
}
|
|
|
|
void AddEdge(node_type n1, node_type n2, value_type capacity, value_type reverseCapacity) {
|
|
ASSERT(ISFINITE(capacity) && capacity >= 0 && ISFINITE(reverseCapacity) && reverseCapacity >= 0);
|
|
edge_descriptor e(boost::add_edge(n1, n2, graph).first);
|
|
edge_descriptor er(boost::add_edge(n2, n1, graph).first);
|
|
graph[e].capacity = capacity;
|
|
graph[er].capacity = reverseCapacity;
|
|
graph[e].reverse = er;
|
|
graph[er].reverse = e;
|
|
}
|
|
|
|
value_type ComputeMaxFlow() {
|
|
vertex_size_type n_verts(boost::num_vertices(graph));
|
|
color.resize(n_verts);
|
|
std::vector<edge_descriptor> pred(n_verts);
|
|
std::vector<vertex_size_type> dist(n_verts);
|
|
return boost::boykov_kolmogorov_max_flow(graph,
|
|
boost::get(&Edge::capacity, graph),
|
|
boost::get(&Edge::residual, graph),
|
|
boost::get(&Edge::reverse, graph),
|
|
&pred[0],
|
|
&color[0],
|
|
&dist[0],
|
|
boost::get(boost::vertex_index, graph),
|
|
S, T
|
|
);
|
|
}
|
|
|
|
inline bool IsNodeOnSrcSide(node_type n) const {
|
|
return (color[n] != boost::white_color);
|
|
}
|
|
|
|
protected:
|
|
graph_type graph;
|
|
std::vector<boost::default_color_type> color;
|
|
const node_type S;
|
|
const node_type T;
|
|
};
|
|
#endif
|
|
/*----------------------------------------------------------------*/
|
|
|
|
|
|
// S T R U C T S ///////////////////////////////////////////////////
|
|
|
|
// construct the mesh out of the dense point cloud using Delaunay tetrahedralization & graph-cut method
|
|
// see "Exploiting Visibility Information in Surface Reconstruction to Preserve Weakly Supported Surfaces", Jancosek and Pajdla, 2015
|
|
namespace DELAUNAY {
|
|
typedef CGAL::Exact_predicates_inexact_constructions_kernel kernel_t;
|
|
typedef kernel_t::Point_3 point_t;
|
|
typedef kernel_t::Vector_3 vector_t;
|
|
typedef kernel_t::Direction_3 direction_t;
|
|
typedef kernel_t::Segment_3 segment_t;
|
|
typedef kernel_t::Plane_3 plane_t;
|
|
typedef kernel_t::Triangle_3 triangle_t;
|
|
typedef kernel_t::Ray_3 ray_t;
|
|
|
|
typedef uint32_t vert_size_t;
|
|
typedef uint32_t cell_size_t;
|
|
|
|
typedef float edge_cap_t;
|
|
|
|
#ifdef DELAUNAY_WEAKSURF
|
|
struct view_info_t;
|
|
#endif
|
|
struct vert_info_t {
|
|
typedef edge_cap_t Type;
|
|
struct view_t {
|
|
PointCloud::View idxView; // view index
|
|
Type weight; // point's weight
|
|
inline view_t() {}
|
|
inline view_t(PointCloud::View _idxView, Type _weight) : idxView(_idxView), weight(_weight) {}
|
|
inline bool operator <(const view_t& v) const { return idxView < v.idxView; }
|
|
inline operator PointCloud::View() const { return idxView; }
|
|
};
|
|
typedef SEACAVE::cList<view_t,const view_t&,0,4,uint32_t> view_vec_t;
|
|
view_vec_t views; // faces' weight from the cell outwards
|
|
#ifdef DELAUNAY_WEAKSURF
|
|
view_info_t* viewsInfo; // each view caches the two faces from the point towards the camera and the end (used only by the weakly supported surfaces)
|
|
inline vert_info_t() : viewsInfo(NULL) {}
|
|
~vert_info_t();
|
|
void AllocateInfo();
|
|
#else
|
|
inline vert_info_t() {}
|
|
#endif
|
|
void InsertViews(const PointCloud& pc, PointCloud::Index idxPoint) {
|
|
const PointCloud::ViewArr& _views = pc.pointViews[idxPoint];
|
|
ASSERT(!_views.IsEmpty());
|
|
const PointCloud::WeightArr* pweights(pc.pointWeights.IsEmpty() ? NULL : pc.pointWeights.Begin()+idxPoint);
|
|
ASSERT(pweights == NULL || _views.GetSize() == pweights->GetSize());
|
|
FOREACH(i, _views) {
|
|
const PointCloud::View viewID(_views[i]);
|
|
const PointCloud::Weight weight(pweights ? (*pweights)[i] : PointCloud::Weight(1));
|
|
// insert viewID in increasing order
|
|
const uint32_t idx(views.FindFirstEqlGreater(viewID));
|
|
if (idx < views.GetSize() && views[idx] == viewID) {
|
|
// the new view is already in the array
|
|
ASSERT(views.FindFirst(viewID) == idx);
|
|
// update point's weight
|
|
views[idx].weight += weight;
|
|
} else {
|
|
// the new view is not in the array,
|
|
// insert it
|
|
views.InsertAt(idx, view_t(viewID, weight));
|
|
ASSERT(views.IsSorted());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
struct cell_info_t {
|
|
typedef edge_cap_t Type;
|
|
Type f[4]; // faces' weight from the cell outwards
|
|
Type s; // cell's weight towards s-source
|
|
Type t; // cell's weight towards t-sink
|
|
inline const Type* ptr() const { return f; }
|
|
inline Type* ptr() { return f; }
|
|
};
|
|
|
|
typedef CGAL::Triangulation_vertex_base_with_info_3<vert_info_t, kernel_t> vertex_base_t;
|
|
typedef CGAL::Triangulation_cell_base_with_info_3<cell_size_t, kernel_t> cell_base_t;
|
|
typedef CGAL::Triangulation_data_structure_3<vertex_base_t, cell_base_t> triangulation_data_structure_t;
|
|
typedef CGAL::Delaunay_triangulation_3<kernel_t, triangulation_data_structure_t, CGAL::Compact_location> delaunay_t;
|
|
typedef delaunay_t::Vertex_handle vertex_handle_t;
|
|
typedef delaunay_t::Cell_handle cell_handle_t;
|
|
typedef delaunay_t::Facet facet_t;
|
|
typedef delaunay_t::Edge edge_t;
|
|
|
|
#ifdef DELAUNAY_WEAKSURF
|
|
struct view_info_t {
|
|
cell_handle_t cell2Cam;
|
|
cell_handle_t cell2End;
|
|
};
|
|
vert_info_t::~vert_info_t() {
|
|
delete[] viewsInfo;
|
|
}
|
|
void vert_info_t::AllocateInfo() {
|
|
ASSERT(!views.IsEmpty());
|
|
viewsInfo = new view_info_t[views.GetSize()];
|
|
#ifndef _RELEASE
|
|
memset(viewsInfo, 0, sizeof(view_info_t)*views.GetSize());
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
struct camera_cell_t {
|
|
cell_handle_t cell; // cell containing the camera
|
|
std::vector<facet_t> facets; // all facets on the convex-hull in view of the camera (ordered by importance)
|
|
};
|
|
|
|
struct adjacent_vertex_back_inserter_t {
|
|
const delaunay_t& delaunay;
|
|
const point_t& p;
|
|
vertex_handle_t& v;
|
|
inline adjacent_vertex_back_inserter_t(const delaunay_t& _delaunay, const point_t& _p, vertex_handle_t& _v) : delaunay(_delaunay), p(_p), v(_v) {}
|
|
inline adjacent_vertex_back_inserter_t& operator*() { return *this; }
|
|
inline adjacent_vertex_back_inserter_t& operator++(int) { return *this; }
|
|
inline void operator=(const vertex_handle_t& w) {
|
|
ASSERT(!delaunay.is_infinite(v));
|
|
if (!delaunay.is_infinite(w) && delaunay.geom_traits().compare_distance_3_object()(p, w->point(), v->point()) == CGAL::SMALLER)
|
|
v = w;
|
|
}
|
|
};
|
|
|
|
typedef TPoint3<kernel_t::RT> DPoint3;
|
|
template <typename TYPE>
|
|
inline TPoint3<TYPE> CGAL2MVS(const point_t& p) {
|
|
return TPoint3<TYPE>((TYPE)p.x(), (TYPE)p.y(), (TYPE)p.z());
|
|
}
|
|
template <typename TYPE>
|
|
inline point_t MVS2CGAL(const TPoint3<TYPE>& p) {
|
|
return point_t((kernel_t::RT)p.x, (kernel_t::RT)p.y, (kernel_t::RT)p.z);
|
|
}
|
|
|
|
// Given a facet, compute the plane containing it
|
|
inline Plane getFacetPlane(const facet_t& facet)
|
|
{
|
|
const point_t& v0(facet.first->vertex((facet.second+1)%4)->point());
|
|
const point_t& v1(facet.first->vertex((facet.second+2)%4)->point());
|
|
const point_t& v2(facet.first->vertex((facet.second+3)%4)->point());
|
|
return Plane(CGAL2MVS<REAL>(v0), CGAL2MVS<REAL>(v1), CGAL2MVS<REAL>(v2));
|
|
}
|
|
|
|
|
|
// Check if a point (p) is coplanar with a triangle (a, b, c);
|
|
// return orientation type
|
|
#if _PLATFORM_X86 && defined(__GNUC__)
|
|
#pragma GCC push_options
|
|
#pragma GCC target ("no-fma")
|
|
#endif
|
|
static inline int orientation(const point_t& a, const point_t& b, const point_t& c, const point_t& p)
|
|
{
|
|
#if 0
|
|
return CGAL::orientation(a, b, c, p);
|
|
#else
|
|
// inexact_orientation
|
|
const double& px = a.x(); const double& py = a.y(); const double& pz = a.z();
|
|
const double pqx(b.x()-px); const double prx(c.x()-px); const double psx(p.x()-px);
|
|
const double pqy(b.y()-py); const double pry(c.y()-py); const double psy(p.y()-py);
|
|
#if 1
|
|
const double det((pqx*pry-prx*pqy)*(p.z()-pz) - (pqx*psy-psx*pqy)*(c.z()-pz) + (prx*psy-psx*pry)*(b.z()-pz));
|
|
const double eps(1e-12);
|
|
#else // very slow due to ABS()
|
|
const double pqz(b.z()-pz); const double prz(c.z()-pz); const double psz(p.z()-pz);
|
|
const double det(CGAL::determinant(
|
|
pqx, pqy, pqz,
|
|
prx, pry, prz,
|
|
psx, psy, psz));
|
|
const double max0(MAXF3(ABS(pqx), ABS(pqy), ABS(pqz)));
|
|
const double max1(MAXF3(ABS(prx), ABS(pry), ABS(prz)));
|
|
const double eps(5.1107127829973299e-15 * MAXF(max0, max1));
|
|
#endif
|
|
if (det > eps) return CGAL::POSITIVE;
|
|
if (det < -eps) return CGAL::NEGATIVE;
|
|
return CGAL::COPLANAR;
|
|
#endif
|
|
}
|
|
#if _PLATFORM_X86 && defined(__GNUC__)
|
|
#pragma GCC pop_options
|
|
#endif
|
|
|
|
// Check if a point (p) is inside a frustum
|
|
// given the four corners (a, b, c, d) and the origin (o) of the frustum
|
|
inline bool checkPointInside(const point_t& a, const point_t& b, const point_t& c, const point_t& d, const point_t& o, const point_t& p)
|
|
{
|
|
return (
|
|
orientation(o, a, b, p) == CGAL::POSITIVE &&
|
|
orientation(o, b, c, p) == CGAL::POSITIVE &&
|
|
orientation(o, c, d, p) == CGAL::POSITIVE &&
|
|
orientation(o, d, a, p) == CGAL::POSITIVE
|
|
);
|
|
}
|
|
|
|
// Given a cell and a camera inside it, if the cell is infinite,
|
|
// find all facets on the convex-hull and inside the camera frustum,
|
|
// else return all four cell's facets
|
|
template <int FacetOrientation>
|
|
void fetchCellFacets(const delaunay_t& Tr, const std::vector<facet_t>& hullFacets, const cell_handle_t& cell, const Image& imageData, std::vector<facet_t>& facets)
|
|
{
|
|
if (!Tr.is_infinite(cell)) {
|
|
// store all 4 facets of the cell
|
|
for (int i=0; i<4; ++i) {
|
|
const facet_t f(cell, i);
|
|
ASSERT(!Tr.is_infinite(f));
|
|
facets.push_back(f);
|
|
}
|
|
return;
|
|
}
|
|
// find all facets on the convex-hull in camera's view
|
|
// create the 4 frustum planes
|
|
ASSERT(facets.empty());
|
|
const TFrustum<REAL,4> frustum(imageData.camera.P, imageData.width, imageData.height, 0, 1);
|
|
// loop over all cells
|
|
const point_t ptOrigin(MVS2CGAL(imageData.camera.C));
|
|
for (const facet_t& face: hullFacets) {
|
|
// add face if visible
|
|
const triangle_t verts(Tr.triangle(face));
|
|
if (orientation(verts[0], verts[1], verts[2], ptOrigin) != FacetOrientation)
|
|
continue;
|
|
AABB3 ab(CGAL2MVS<REAL>(verts[0]));
|
|
for (int i=1; i<3; ++i)
|
|
ab.Insert(CGAL2MVS<REAL>(verts[i]));
|
|
if (frustum.Classify(ab) == CULLED)
|
|
continue;
|
|
facets.push_back(face);
|
|
}
|
|
}
|
|
|
|
|
|
// information about an intersection between a segment and a facet
|
|
struct intersection_t {
|
|
enum Type {FACET, EDGE, VERTEX};
|
|
cell_handle_t ncell; // cell neighbor to the last intersected facet
|
|
vertex_handle_t v1; // vertex for vertex intersection, 1st edge vertex for edge intersection
|
|
vertex_handle_t v2; // 2nd edge vertex for edge intersection
|
|
facet_t facet; // intersected facet
|
|
Type type; // type of intersection (inside facet, on edge, or vertex)
|
|
REAL dist; // distance from starting point (camera) to this facet
|
|
bool bigger; // are we advancing away or towards the starting point?
|
|
const Ray3 ray; // the ray from starting point into the direction of the end point (point -> camera/end-point)
|
|
inline intersection_t() {}
|
|
inline intersection_t(const Point3& pt, const Point3& dir) : dist(-FLT_MAX), bigger(true), ray(pt, dir) {}
|
|
};
|
|
|
|
// Check if a segment (p, q) is coplanar with edges of a triangle (a, b, c):
|
|
// coplanar [in,out] : pointer to the 3 int array of indices of the edges coplanar with pq
|
|
// return number of entries in coplanar
|
|
inline int checkEdges(const point_t& a, const point_t& b, const point_t& c, const point_t& p, const point_t& q, int coplanar[3])
|
|
{
|
|
int nCoplanar(0);
|
|
switch (orientation(p,q,a,b)) {
|
|
case CGAL::POSITIVE: return -1;
|
|
case CGAL::COPLANAR: coplanar[nCoplanar++] = 0;
|
|
}
|
|
switch (orientation(p,q,b,c)) {
|
|
case CGAL::POSITIVE: return -1;
|
|
case CGAL::COPLANAR: coplanar[nCoplanar++] = 1;
|
|
}
|
|
switch (orientation(p,q,c,a)) {
|
|
case CGAL::POSITIVE: return -1;
|
|
case CGAL::COPLANAR: coplanar[nCoplanar++] = 2;
|
|
}
|
|
return nCoplanar;
|
|
}
|
|
|
|
// Check intersection between a facet (f) and a segment (s)
|
|
// (derived from CGAL::do_intersect in CGAL/Triangle_3_Segment_3_do_intersect.h)
|
|
// coplanar [out] : pointer to the 3 int array of indices of the edges coplanar with (s)
|
|
// return -1 if there is no intersection or
|
|
// the number of edges coplanar with the segment (0 = intersection inside the triangle)
|
|
int intersect(const triangle_t& t, const segment_t& s, int coplanar[3])
|
|
{
|
|
const point_t& a = t.vertex(0);
|
|
const point_t& b = t.vertex(1);
|
|
const point_t& c = t.vertex(2);
|
|
const point_t& p = s.source();
|
|
const point_t& q = s.target();
|
|
|
|
switch (orientation(a,b,c,p)) {
|
|
case CGAL::POSITIVE:
|
|
switch (orientation(a,b,c,q)) {
|
|
case CGAL::POSITIVE:
|
|
// the segment lies in the positive open halfspaces defined by the
|
|
// triangle's supporting plane
|
|
return -1;
|
|
case CGAL::COPLANAR:
|
|
// q belongs to the triangle's supporting plane
|
|
// p sees the triangle in counterclockwise order
|
|
return checkEdges(a,b,c,p,q,coplanar);
|
|
case CGAL::NEGATIVE:
|
|
// p sees the triangle in counterclockwise order
|
|
return checkEdges(a,b,c,p,q,coplanar);
|
|
default:
|
|
break;
|
|
}
|
|
case CGAL::NEGATIVE:
|
|
switch (orientation(a,b,c,q)) {
|
|
case CGAL::POSITIVE:
|
|
// q sees the triangle in counterclockwise order
|
|
return checkEdges(a,b,c,q,p,coplanar);
|
|
case CGAL::COPLANAR:
|
|
// q belongs to the triangle's supporting plane
|
|
// p sees the triangle in clockwise order
|
|
return checkEdges(a,b,c,q,p,coplanar);
|
|
case CGAL::NEGATIVE:
|
|
// the segment lies in the negative open halfspaces defined by the
|
|
// triangle's supporting plane
|
|
return -1;
|
|
default:
|
|
break;
|
|
}
|
|
case CGAL::COPLANAR: // p belongs to the triangle's supporting plane
|
|
switch (orientation(a,b,c,q)) {
|
|
case CGAL::POSITIVE:
|
|
// q sees the triangle in counterclockwise order
|
|
return checkEdges(a,b,c,q,p,coplanar);
|
|
case CGAL::COPLANAR:
|
|
// the segment is coplanar with the triangle's supporting plane
|
|
// as we know that it is inside the tetrahedron it intersects the face
|
|
//coplanar[0] = coplanar[1] = coplanar[2] = 3;
|
|
return 3;
|
|
case CGAL::NEGATIVE:
|
|
// q sees the triangle in clockwise order
|
|
return checkEdges(a,b,c,p,q,coplanar);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
ASSERT("should not happen" == NULL);
|
|
return -1;
|
|
}
|
|
|
|
// Find which facet is intersected by the segment (seg) and return next facets to check:
|
|
// in_facets [in] : vector of facets to check
|
|
// out_facets [out] : vector of facets to check at next step (can be in_facets)
|
|
// out_inter [out] : kind of intersection
|
|
// return false if no intersection found and the end of the segment was not reached
|
|
bool intersect(const delaunay_t& Tr, const segment_t& seg, const std::vector<facet_t>& in_facets, std::vector<facet_t>& out_facets, intersection_t& inter)
|
|
{
|
|
ASSERT(!in_facets.empty());
|
|
static const int facet_vertex_order[] = {2,1,3,2,2,3,0,2,0,3,1,0,0,1,2,0};
|
|
int coplanar[3];
|
|
const REAL prevDist(inter.dist);
|
|
for (const facet_t& in_facet: in_facets) {
|
|
ASSERT(!Tr.is_infinite(in_facet));
|
|
const int nb_coplanar(intersect(Tr.triangle(in_facet), seg, coplanar));
|
|
if (nb_coplanar >= 0) {
|
|
// skip this cell if the intersection is not in the desired direction
|
|
const REAL interDist(inter.ray.IntersectsDist(getFacetPlane(in_facet)));
|
|
if ((interDist > prevDist) != inter.bigger)
|
|
continue;
|
|
// vertices of facet i: j = 4 * i, vertices = facet_vertex_order[j,j+1,j+2] negative orientation
|
|
inter.facet = in_facet;
|
|
inter.dist = interDist;
|
|
switch (nb_coplanar) {
|
|
case 0: {
|
|
// face intersection
|
|
inter.type = intersection_t::FACET;
|
|
// now find next facets to be checked as
|
|
// the three faces in the neighbor cell different than the origin face
|
|
out_facets.clear();
|
|
const cell_handle_t nc(inter.facet.first->neighbor(inter.facet.second));
|
|
ASSERT(!Tr.is_infinite(nc));
|
|
for (int i=0; i<4; ++i)
|
|
if (nc->neighbor(i) != inter.facet.first)
|
|
out_facets.push_back(facet_t(nc, i));
|
|
return true; }
|
|
case 1: {
|
|
// coplanar with 1 edge = intersect edge
|
|
const int j(4 * inter.facet.second);
|
|
const int i1(j + coplanar[0]);
|
|
inter.type = intersection_t::EDGE;
|
|
inter.v1 = inter.facet.first->vertex(facet_vertex_order[i1+0]);
|
|
inter.v2 = inter.facet.first->vertex(facet_vertex_order[i1+1]);
|
|
// now find next facets to be checked as
|
|
// the two faces in this cell opposing this edge
|
|
out_facets.clear();
|
|
const edge_t out_edge(inter.facet.first, facet_vertex_order[i1+0], facet_vertex_order[i1+1]);
|
|
const typename delaunay_t::Cell_circulator efc(Tr.incident_cells(out_edge));
|
|
typename delaunay_t::Cell_circulator ifc(efc);
|
|
do {
|
|
const cell_handle_t c(ifc);
|
|
if (c == inter.facet.first) continue;
|
|
const facet_t f1(c, c->index(inter.v1));
|
|
if (!Tr.is_infinite(f1))
|
|
out_facets.push_back(f1);
|
|
const facet_t f2(c, c->index(inter.v2));
|
|
if (!Tr.is_infinite(f2))
|
|
out_facets.push_back(f2);
|
|
} while (++ifc != efc);
|
|
return true; }
|
|
case 2: {
|
|
// coplanar with 2 edges = hit a vertex
|
|
// find vertex index
|
|
const int j(4 * inter.facet.second);
|
|
const int i1(j + coplanar[0]);
|
|
const int i2(j + coplanar[1]);
|
|
int i;
|
|
if (facet_vertex_order[i1] == facet_vertex_order[i2] || facet_vertex_order[i1] == facet_vertex_order[i2+1]) {
|
|
i = facet_vertex_order[i1];
|
|
} else
|
|
if (facet_vertex_order[i1+1] == facet_vertex_order[i2] || facet_vertex_order[i1+1] == facet_vertex_order[i2+1]) {
|
|
i = facet_vertex_order[i1+1];
|
|
} else {
|
|
ASSERT("2 edges intersections without common vertex" == NULL);
|
|
}
|
|
inter.type = intersection_t::VERTEX;
|
|
inter.v1 = inter.facet.first->vertex(i);
|
|
ASSERT(!Tr.is_infinite(inter.v1));
|
|
if (inter.v1->point() == seg.target()) {
|
|
// target reached
|
|
out_facets.clear();
|
|
return false;
|
|
}
|
|
// now find next facets to be checked as
|
|
// the faces in the cells around opposing this common vertex
|
|
out_facets.clear();
|
|
struct cell_back_inserter_t {
|
|
const delaunay_t& Tr;
|
|
const vertex_handle_t v;
|
|
const cell_handle_t current_cell;
|
|
std::vector<facet_t>& out_facets;
|
|
inline cell_back_inserter_t(const delaunay_t& _Tr, const intersection_t& inter, std::vector<facet_t>& _out_facets)
|
|
: Tr(_Tr), v(inter.v1), current_cell(inter.facet.first), out_facets(_out_facets) {}
|
|
inline cell_back_inserter_t& operator*() { return *this; }
|
|
inline cell_back_inserter_t& operator++(int) { return *this; }
|
|
inline void operator=(cell_handle_t c) {
|
|
if (c == current_cell)
|
|
return;
|
|
const facet_t f(c, c->index(v));
|
|
if (Tr.is_infinite(f))
|
|
return;
|
|
out_facets.push_back(f);
|
|
}
|
|
};
|
|
Tr.finite_incident_cells(inter.v1, cell_back_inserter_t(Tr, inter, out_facets));
|
|
return true; }
|
|
}
|
|
// coplanar with 3 edges = tangent = impossible?
|
|
break;
|
|
}
|
|
}
|
|
// Bad end: no intersection found and we are not at the end of the segment (very rarely, but it happens)!
|
|
out_facets.clear();
|
|
return false;
|
|
}
|
|
|
|
// same as above, but simplified only to find face intersection (otherwise terminate);
|
|
// terminate if cell containing the segment endpoint is found or if an infinite cell is encountered
|
|
bool intersectFace(const delaunay_t& Tr, const segment_t& seg, const std::vector<facet_t>& in_facets, std::vector<facet_t>& out_facets, intersection_t& inter)
|
|
{
|
|
int coplanar[3];
|
|
for (std::vector<facet_t>::const_iterator it=in_facets.cbegin(); it!=in_facets.cend(); ++it) {
|
|
ASSERT(!Tr.is_infinite(*it));
|
|
if (intersect(Tr.triangle(*it), seg, coplanar) == 0) {
|
|
// face intersection
|
|
inter.facet = *it;
|
|
inter.type = intersection_t::FACET;
|
|
// now find next facets to be checked as
|
|
// the three faces in the neighbor cell different than the origin face
|
|
out_facets.clear();
|
|
inter.ncell = inter.facet.first->neighbor(inter.facet.second);
|
|
if (Tr.is_infinite(inter.ncell))
|
|
return false;
|
|
for (int i=0; i<4; ++i)
|
|
if (inter.ncell->neighbor(i) != inter.facet.first)
|
|
out_facets.push_back(facet_t(inter.ncell, i));
|
|
return true;
|
|
}
|
|
}
|
|
out_facets.clear();
|
|
return false;
|
|
}
|
|
// same as above, but starts from a known vertex and incident cell
|
|
inline bool intersectFace(const delaunay_t& Tr, const segment_t& seg, const vertex_handle_t& v, const cell_handle_t& cell, std::vector<facet_t>& out_facets, intersection_t& inter)
|
|
{
|
|
if (cell == cell_handle_t())
|
|
return false;
|
|
if (Tr.is_infinite(cell)) {
|
|
inter.ncell = inter.facet.first = cell;
|
|
return true;
|
|
}
|
|
std::vector<facet_t>& in_facets = out_facets;
|
|
ASSERT(in_facets.empty());
|
|
in_facets.push_back(facet_t(cell, cell->index(v)));
|
|
return intersectFace(Tr, seg, in_facets, out_facets, inter);
|
|
}
|
|
|
|
|
|
// Given a cell, compute the free-space support for it
|
|
edge_cap_t freeSpaceSupport(const delaunay_t& Tr, const std::vector<cell_info_t>& infoCells, const cell_handle_t& cell)
|
|
{
|
|
// sum up all 4 incoming weights
|
|
// (corresponding to the 4 facets of the neighbor cells)
|
|
edge_cap_t wf(0);
|
|
for (int i=0; i<4; ++i) {
|
|
const facet_t& mfacet(Tr.mirror_facet(facet_t(cell, i)));
|
|
wf += infoCells[mfacet.first->info()].f[mfacet.second];
|
|
}
|
|
return wf;
|
|
}
|
|
|
|
// Fetch the triangle formed by the facet vertices,
|
|
// making sure the facet orientation is kept (as in CGAL::Triangulation_3::triangle())
|
|
// return the vertex handles of the triangle
|
|
struct triangle_vhandles_t {
|
|
vertex_handle_t verts[3];
|
|
triangle_vhandles_t() {}
|
|
triangle_vhandles_t(vertex_handle_t _v0, vertex_handle_t _v1, vertex_handle_t _v2)
|
|
#ifdef _SUPPORT_CPP11
|
|
: verts{_v0,_v1,_v2} {}
|
|
#else
|
|
{ verts[0] = _v0; verts[1] = _v1; verts[2] = _v2; }
|
|
#endif
|
|
};
|
|
inline triangle_vhandles_t getTriangle(cell_handle_t cell, int i)
|
|
{
|
|
ASSERT(i >= 0 && i <= 3);
|
|
if ((i&1) == 0)
|
|
return triangle_vhandles_t(
|
|
cell->vertex((i+2)&3),
|
|
cell->vertex((i+1)&3),
|
|
cell->vertex((i+3)&3) );
|
|
return triangle_vhandles_t(
|
|
cell->vertex((i+1)&3),
|
|
cell->vertex((i+2)&3),
|
|
cell->vertex((i+3)&3) );
|
|
}
|
|
|
|
// Compute the angle between the plane containing the given facet and the cell's circumscribed sphere
|
|
// return cosines of the angle
|
|
float computePlaneSphereAngle(const delaunay_t& Tr, const facet_t& facet)
|
|
{
|
|
// compute facet normal
|
|
if (Tr.is_infinite(facet.first))
|
|
return 1.f;
|
|
const triangle_vhandles_t tri(getTriangle(facet.first, facet.second));
|
|
const Point3f v0(CGAL2MVS<float>(tri.verts[0]->point()));
|
|
const Point3f v1(CGAL2MVS<float>(tri.verts[1]->point()));
|
|
const Point3f v2(CGAL2MVS<float>(tri.verts[2]->point()));
|
|
const Point3f fn((v1-v0).cross(v2-v0));
|
|
const float fnLenSq(normSq(fn));
|
|
if (fnLenSq == 0.f)
|
|
return 0.5f;
|
|
|
|
// compute the co-tangent to the circumscribed sphere in one of the vertices
|
|
#if CGAL_VERSION_NR < 1041101000
|
|
const Point3f cc(CGAL2MVS<float>(facet.first->circumcenter(Tr.geom_traits())));
|
|
#else
|
|
struct Tools {
|
|
static point_t circumcenter(const delaunay_t& Tr, const facet_t& facet) {
|
|
return Tr.geom_traits().construct_circumcenter_3_object()(
|
|
facet.first->vertex(0)->point(),
|
|
facet.first->vertex(1)->point(),
|
|
facet.first->vertex(2)->point(),
|
|
facet.first->vertex(3)->point()
|
|
);
|
|
}
|
|
};
|
|
const Point3f cc(CGAL2MVS<float>(Tools::circumcenter(Tr, facet)));
|
|
#endif
|
|
const Point3f ct(cc-v0);
|
|
const float ctLenSq(normSq(ct));
|
|
if (ctLenSq == 0.f)
|
|
return 0.5f;
|
|
|
|
// compute the angle between the two vectors
|
|
return CLAMP((fn.dot(ct))/SQRT(fnLenSq*ctLenSq), -1.f, 1.f);
|
|
}
|
|
} // namespace DELAUNAY
|
|
|
|
// First, iteratively create a Delaunay triangulation of the existing point-cloud by inserting point by point,
|
|
// iif the point to be inserted is not closer than distInsert pixels in at least one of its views to
|
|
// the projection of any of already inserted points.
|
|
// Next, the score is computed for all the edges of the directed graph composed of points as vertices.
|
|
// Finally, graph-cut algorithm is used to split the tetrahedrons in inside and outside,
|
|
// and the surface is such extracted.
|
|
bool Scene::ReconstructMesh(float distInsert, bool bUseFreeSpaceSupport, bool bUseOnlyROI, unsigned nItersFixNonManifold,
|
|
float kSigma, float kQual, float kb,
|
|
float kf, float kRel, float kAbs, float kOutl,
|
|
float kInf
|
|
)
|
|
{
|
|
using namespace DELAUNAY;
|
|
ASSERT(!pointcloud.IsEmpty());
|
|
mesh.Release();
|
|
|
|
// create the Delaunay triangulation
|
|
delaunay_t delaunay;
|
|
std::vector<cell_info_t> infoCells;
|
|
std::vector<camera_cell_t> camCells;
|
|
std::vector<facet_t> hullFacets;
|
|
{
|
|
TD_TIMER_STARTD();
|
|
|
|
std::vector<point_t> vertices(pointcloud.points.GetSize());
|
|
std::vector<std::ptrdiff_t> indices(pointcloud.points.GetSize());
|
|
// fetch points
|
|
if (bUseOnlyROI && !IsBounded())
|
|
bUseOnlyROI = false;
|
|
FOREACH(i, pointcloud.points) {
|
|
const PointCloud::Point& X(pointcloud.points[i]);
|
|
if (bUseOnlyROI && !obb.Intersects(X))
|
|
continue;
|
|
vertices[i] = point_t(X.x, X.y, X.z);
|
|
indices[i] = i;
|
|
}
|
|
// sort vertices
|
|
typedef CGAL::Spatial_sort_traits_adapter_3<delaunay_t::Geom_traits, point_t*> Search_traits;
|
|
CGAL::spatial_sort(indices.begin(), indices.end(), Search_traits(&vertices[0], delaunay.geom_traits()));
|
|
// insert vertices
|
|
Util::Progress progress(_T("Points inserted"), indices.size());
|
|
const float distInsertSq(SQUARE(distInsert));
|
|
vertex_handle_t hint;
|
|
delaunay_t::Locate_type lt;
|
|
int li, lj;
|
|
std::for_each(indices.cbegin(), indices.cend(), [&](size_t idx) {
|
|
const point_t& p = vertices[idx];
|
|
const PointCloud::Point& point = pointcloud.points[idx];
|
|
const PointCloud::ViewArr& views = pointcloud.pointViews[idx];
|
|
ASSERT(!views.IsEmpty());
|
|
if (hint == vertex_handle_t()) {
|
|
// this is the first point,
|
|
// insert it
|
|
hint = delaunay.insert(p);
|
|
ASSERT(hint != vertex_handle_t());
|
|
} else
|
|
if (distInsert <= 0) {
|
|
// insert all points
|
|
hint = delaunay.insert(p, hint);
|
|
ASSERT(hint != vertex_handle_t());
|
|
} else {
|
|
// locate cell containing this point
|
|
const cell_handle_t c(delaunay.locate(p, lt, li, lj, hint->cell()));
|
|
if (lt == delaunay_t::VERTEX) {
|
|
// duplicate point, nothing to insert,
|
|
// just update its visibility info
|
|
hint = c->vertex(li);
|
|
ASSERT(hint != delaunay.infinite_vertex());
|
|
} else {
|
|
// locate the nearest vertex
|
|
vertex_handle_t nearest;
|
|
if (delaunay.dimension() < 3) {
|
|
// use a brute-force algorithm if dimension < 3
|
|
delaunay_t::Finite_vertices_iterator vit = delaunay.finite_vertices_begin();
|
|
nearest = vit;
|
|
++vit;
|
|
adjacent_vertex_back_inserter_t inserter(delaunay, p, nearest);
|
|
for (delaunay_t::Finite_vertices_iterator end = delaunay.finite_vertices_end(); vit != end; ++vit)
|
|
inserter = vit;
|
|
} else {
|
|
// - start with the closest vertex from the located cell
|
|
// - repeatedly take the nearest of its incident vertices if any
|
|
// - if not, we're done
|
|
ASSERT(c != cell_handle_t());
|
|
nearest = delaunay.nearest_vertex_in_cell(p, c);
|
|
while (true) {
|
|
const vertex_handle_t v(nearest);
|
|
delaunay.adjacent_vertices(nearest, adjacent_vertex_back_inserter_t(delaunay, p, nearest));
|
|
if (v == nearest)
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(nearest == delaunay.nearest_vertex(p, hint->cell()));
|
|
hint = nearest;
|
|
// check if point is far enough to all existing points
|
|
FOREACHPTR(pViewID, views) {
|
|
const Image& imageData = images[*pViewID];
|
|
const Point3f pn(imageData.camera.ProjectPointP3(point));
|
|
const Point3f pe(imageData.camera.ProjectPointP3(CGAL2MVS<float>(nearest->point())));
|
|
if (!IsDepthSimilar(pn.z, pe.z) || normSq(Point2f(pn)-Point2f(pe)) > distInsertSq) {
|
|
// point far enough to an existing point,
|
|
// insert as a new point
|
|
hint = delaunay.insert(p, lt, c, li, lj);
|
|
ASSERT(hint != vertex_handle_t());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// update point visibility info
|
|
hint->info().InsertViews(pointcloud, idx);
|
|
++progress;
|
|
});
|
|
progress.close();
|
|
pointcloud.Release();
|
|
// init cells weights and
|
|
// loop over all cells and store the finite facet of the infinite cells
|
|
const size_t numNodes(delaunay.number_of_cells());
|
|
infoCells.resize(numNodes);
|
|
memset(&infoCells[0], 0, sizeof(cell_info_t)*numNodes);
|
|
cell_size_t ciID(0);
|
|
for (delaunay_t::All_cells_iterator ci=delaunay.all_cells_begin(), eci=delaunay.all_cells_end(); ci!=eci; ++ci, ++ciID) {
|
|
ci->info() = ciID;
|
|
// skip the finite cells
|
|
if (!delaunay.is_infinite(ci))
|
|
continue;
|
|
// find the finite face
|
|
for (int f=0; f<4; ++f) {
|
|
const facet_t facet(ci, f);
|
|
if (!delaunay.is_infinite(facet)) {
|
|
// store face
|
|
hullFacets.push_back(facet);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// find all cells containing a camera
|
|
camCells.resize(images.GetSize());
|
|
FOREACH(i, images) {
|
|
const Image& imageData = images[i];
|
|
if (!imageData.IsValid())
|
|
continue;
|
|
const Camera& camera = imageData.camera;
|
|
camera_cell_t& camCell = camCells[i];
|
|
camCell.cell = delaunay.locate(MVS2CGAL(camera.C));
|
|
ASSERT(camCell.cell != cell_handle_t());
|
|
fetchCellFacets<CGAL::POSITIVE>(delaunay, hullFacets, camCell.cell, imageData, camCell.facets);
|
|
// link all cells contained by the camera to the source
|
|
for (const facet_t& f: camCell.facets)
|
|
infoCells[f.first->info()].s = kInf;
|
|
}
|
|
|
|
DEBUG_EXTRA("Delaunay tetrahedralization completed: %u points -> %u vertices, %u (+%u) cells, %u (+%u) faces (%s)", indices.size(), delaunay.number_of_vertices(), delaunay.number_of_finite_cells(), delaunay.number_of_cells()-delaunay.number_of_finite_cells(), delaunay.number_of_finite_facets(), delaunay.number_of_facets()-delaunay.number_of_finite_facets(), TD_TIMER_GET_FMT().c_str());
|
|
}
|
|
|
|
// for every camera-point ray intersect it with the tetrahedrons and
|
|
// add alpha_vis(point) to cell's directed edge in the graph
|
|
{
|
|
TD_TIMER_STARTD();
|
|
|
|
// estimate the size of the smallest reconstructible object
|
|
FloatArr distsSq(0, delaunay.number_of_edges());
|
|
for (delaunay_t::Finite_edges_iterator ei=delaunay.finite_edges_begin(), eei=delaunay.finite_edges_end(); ei!=eei; ++ei) {
|
|
const cell_handle_t& c(ei->first);
|
|
distsSq.Insert(normSq(CGAL2MVS<float>(c->vertex(ei->second)->point()) - CGAL2MVS<float>(c->vertex(ei->third)->point())));
|
|
}
|
|
const float sigma(SQRT(distsSq.GetMedian())*kSigma);
|
|
const float inv2SigmaSq(0.5f/(sigma*sigma));
|
|
distsSq.Release();
|
|
|
|
std::vector<facet_t> facets;
|
|
|
|
// compute the weights for each edge
|
|
{
|
|
TD_TIMER_STARTD();
|
|
Util::Progress progress(_T("Points weighted"), delaunay.number_of_vertices());
|
|
#ifdef DELAUNAY_USE_OPENMP
|
|
delaunay_t::Vertex_iterator vertexIter(delaunay.vertices_begin());
|
|
const int64_t nVerts(delaunay.number_of_vertices()+1);
|
|
#pragma omp parallel for private(facets)
|
|
for (int64_t i=0; i<nVerts; ++i) {
|
|
delaunay_t::Vertex_iterator vi;
|
|
#pragma omp critical
|
|
vi = vertexIter++;
|
|
#else
|
|
for (delaunay_t::Vertex_iterator vi=delaunay.vertices_begin(), vie=delaunay.vertices_end(); vi!=vie; ++vi) {
|
|
#endif
|
|
vert_info_t& vert(vi->info());
|
|
if (vert.views.IsEmpty())
|
|
continue;
|
|
#ifdef DELAUNAY_WEAKSURF
|
|
vert.AllocateInfo();
|
|
#endif
|
|
const point_t& p(vi->point());
|
|
const Point3 pt(CGAL2MVS<REAL>(p));
|
|
FOREACH(v, vert.views) {
|
|
const typename vert_info_t::view_t view(vert.views[v]);
|
|
const uint32_t imageID(view.idxView);
|
|
const edge_cap_t alpha_vis(view.weight);
|
|
const Image& imageData = images[imageID];
|
|
ASSERT(imageData.IsValid());
|
|
const Camera& camera = imageData.camera;
|
|
const camera_cell_t& camCell = camCells[imageID];
|
|
// compute the ray used to find point intersection
|
|
const Point3 vecCamPoint(pt-camera.C);
|
|
const REAL invLenCamPoint(REAL(1)/norm(vecCamPoint));
|
|
intersection_t inter(pt, Point3(vecCamPoint*invLenCamPoint));
|
|
// find faces intersected by the camera-point segment
|
|
const segment_t segCamPoint(MVS2CGAL(camera.C), p);
|
|
if (!intersect(delaunay, segCamPoint, camCell.facets, facets, inter))
|
|
continue;
|
|
do {
|
|
// assign score, weighted by the distance from the point to the intersection
|
|
const edge_cap_t w(alpha_vis*(1.f-EXP(-SQUARE((float)inter.dist)*inv2SigmaSq)));
|
|
edge_cap_t& f(infoCells[inter.facet.first->info()].f[inter.facet.second]);
|
|
#ifdef DELAUNAY_USE_OPENMP
|
|
#pragma omp atomic
|
|
#endif
|
|
f += w;
|
|
} while (intersect(delaunay, segCamPoint, facets, facets, inter));
|
|
ASSERT(facets.empty() && inter.type == intersection_t::VERTEX && inter.v1 == vi);
|
|
#ifdef DELAUNAY_WEAKSURF
|
|
ASSERT(vert.viewsInfo[v].cell2Cam == NULL);
|
|
vert.viewsInfo[v].cell2Cam = inter.facet.first;
|
|
#endif
|
|
// find faces intersected by the endpoint-point segment
|
|
inter.dist = FLT_MAX; inter.bigger = false;
|
|
const Point3 endPoint(pt+vecCamPoint*(invLenCamPoint*sigma));
|
|
const segment_t segEndPoint(MVS2CGAL(endPoint), p);
|
|
const cell_handle_t endCell(delaunay.locate(segEndPoint.source(), vi->cell()));
|
|
ASSERT(endCell != cell_handle_t());
|
|
fetchCellFacets<CGAL::NEGATIVE>(delaunay, hullFacets, endCell, imageData, facets);
|
|
edge_cap_t& t(infoCells[endCell->info()].t);
|
|
#ifdef DELAUNAY_USE_OPENMP
|
|
#pragma omp atomic
|
|
#endif
|
|
t += alpha_vis;
|
|
while (intersect(delaunay, segEndPoint, facets, facets, inter)) {
|
|
// assign score, weighted by the distance from the point to the intersection
|
|
const facet_t& mf(delaunay.mirror_facet(inter.facet));
|
|
const edge_cap_t w(alpha_vis*(1.f-EXP(-SQUARE((float)inter.dist)*inv2SigmaSq)));
|
|
edge_cap_t& f(infoCells[mf.first->info()].f[mf.second]);
|
|
#ifdef DELAUNAY_USE_OPENMP
|
|
#pragma omp atomic
|
|
#endif
|
|
f += w;
|
|
}
|
|
ASSERT(facets.empty() && inter.type == intersection_t::VERTEX && inter.v1 == vi);
|
|
#ifdef DELAUNAY_WEAKSURF
|
|
ASSERT(vert.viewsInfo[v].cell2End == NULL);
|
|
vert.viewsInfo[v].cell2End = inter.facet.first;
|
|
#endif
|
|
}
|
|
++progress;
|
|
}
|
|
progress.close();
|
|
DEBUG_ULTIMATE("\tweighting completed in %s", TD_TIMER_GET_FMT().c_str());
|
|
}
|
|
camCells.clear();
|
|
|
|
#ifdef DELAUNAY_WEAKSURF
|
|
// enforce t-edges for each point-camera pair with free-space support weights
|
|
if (bUseFreeSpaceSupport) {
|
|
TD_TIMER_STARTD();
|
|
#ifdef DELAUNAY_USE_OPENMP
|
|
delaunay_t::Vertex_iterator vertexIter(delaunay.vertices_begin());
|
|
const int64_t nVerts(delaunay.number_of_vertices()+1);
|
|
#pragma omp parallel for private(facets)
|
|
for (int64_t i=0; i<nVerts; ++i) {
|
|
delaunay_t::Vertex_iterator vi;
|
|
#pragma omp critical
|
|
vi = vertexIter++;
|
|
#else
|
|
for (delaunay_t::Vertex_iterator vi=delaunay.vertices_begin(), vie=delaunay.vertices_end(); vi!=vie; ++vi) {
|
|
#endif
|
|
const vert_info_t& vert(vi->info());
|
|
if (vert.views.IsEmpty())
|
|
continue;
|
|
const point_t& p(vi->point());
|
|
const Point3f pt(CGAL2MVS<float>(p));
|
|
FOREACH(v, vert.views) {
|
|
const uint32_t imageID(vert.views[(vert_info_t::view_vec_t::IDX)v]);
|
|
const Image& imageData = images[imageID];
|
|
ASSERT(imageData.IsValid());
|
|
const Camera& camera = imageData.camera;
|
|
// compute the ray used to find point intersection
|
|
const Point3f vecCamPoint(pt-Cast<float>(camera.C));
|
|
const float invLenCamPoint(1.f/norm(vecCamPoint));
|
|
// find faces intersected by the point-camera segment and keep the max free-space support score
|
|
const Point3f bgnPoint(pt-vecCamPoint*(invLenCamPoint*sigma*kf));
|
|
const segment_t segPointBgn(p, MVS2CGAL(bgnPoint));
|
|
intersection_t inter;
|
|
if (!intersectFace(delaunay, segPointBgn, vi, vert.viewsInfo[v].cell2Cam, facets, inter))
|
|
continue;
|
|
edge_cap_t beta(0);
|
|
do {
|
|
const edge_cap_t fs(freeSpaceSupport(delaunay, infoCells, inter.facet.first));
|
|
if (beta < fs)
|
|
beta = fs;
|
|
} while (intersectFace(delaunay, segPointBgn, facets, facets, inter));
|
|
// find faces intersected by the point-endpoint segment
|
|
const Point3f endPoint(pt+vecCamPoint*(invLenCamPoint*sigma*kb));
|
|
const segment_t segPointEnd(p, MVS2CGAL(endPoint));
|
|
if (!intersectFace(delaunay, segPointEnd, vi, vert.viewsInfo[v].cell2End, facets, inter))
|
|
continue;
|
|
edge_cap_t gammaMin(FLT_MAX), gammaMax(0);
|
|
do {
|
|
const edge_cap_t fs(freeSpaceSupport(delaunay, infoCells, inter.facet.first));
|
|
if (gammaMin > fs)
|
|
gammaMin = fs;
|
|
if (gammaMax < fs)
|
|
gammaMax = fs;
|
|
} while (intersectFace(delaunay, segPointEnd, facets, facets, inter));
|
|
const edge_cap_t gamma((gammaMin+gammaMax)*0.5f);
|
|
// if the point can be considered an interface point,
|
|
// enforce the t-edge weight of the end cell
|
|
const edge_cap_t epsAbs(beta-gamma);
|
|
const edge_cap_t epsRel(gamma/beta);
|
|
if (epsRel < kRel && epsAbs > kAbs && gamma < kOutl) {
|
|
edge_cap_t& t(infoCells[inter.ncell->info()].t);
|
|
#ifdef DELAUNAY_USE_OPENMP
|
|
#pragma omp atomic
|
|
#endif
|
|
t *= epsAbs;
|
|
}
|
|
}
|
|
}
|
|
DEBUG_ULTIMATE("\tt-edge reinforcement completed in %s", TD_TIMER_GET_FMT().c_str());
|
|
}
|
|
#endif
|
|
|
|
DEBUG_EXTRA("Delaunay tetrahedras weighting completed: %u cells, %u faces (%s)", delaunay.number_of_cells(), delaunay.number_of_facets(), TD_TIMER_GET_FMT().c_str());
|
|
}
|
|
|
|
// run graph-cut and extract the mesh
|
|
{
|
|
TD_TIMER_STARTD();
|
|
|
|
// create graph
|
|
MaxFlow<cell_size_t,edge_cap_t> graph(delaunay.number_of_cells());
|
|
// set weights
|
|
constexpr edge_cap_t maxCap(3.402823466e+34f/*FLT_MAX*0.0001f*/);
|
|
for (delaunay_t::All_cells_iterator ci=delaunay.all_cells_begin(), ce=delaunay.all_cells_end(); ci!=ce; ++ci) {
|
|
const cell_size_t ciID(ci->info());
|
|
const cell_info_t& ciInfo(infoCells[ciID]);
|
|
graph.AddNode(ciID, ciInfo.s, MINF(ciInfo.t, maxCap));
|
|
for (int i=0; i<4; ++i) {
|
|
const cell_handle_t cj(ci->neighbor(i));
|
|
const cell_size_t cjID(cj->info());
|
|
if (cjID < ciID) continue;
|
|
const cell_info_t& cjInfo(infoCells[cjID]);
|
|
const int j(cj->index(ci));
|
|
const edge_cap_t q((1.f - MINF(computePlaneSphereAngle(delaunay, facet_t(ci,i)), computePlaneSphereAngle(delaunay, facet_t(cj,j))))*kQual);
|
|
graph.AddEdge(ciID, cjID, ciInfo.f[i]+q, cjInfo.f[j]+q);
|
|
}
|
|
}
|
|
infoCells.clear();
|
|
// find graph-cut solution
|
|
const float maxflow(graph.ComputeMaxFlow());
|
|
// extract surface formed by the facets between inside/outside cells
|
|
const size_t nEstimatedNumVerts(delaunay.number_of_vertices());
|
|
std::unordered_map<void*,Mesh::VIndex> mapVertices;
|
|
#if defined(_MSC_VER) && (_MSC_VER > 1600)
|
|
mapVertices.reserve(nEstimatedNumVerts);
|
|
#endif
|
|
mesh.vertices.Reserve((Mesh::VIndex)nEstimatedNumVerts);
|
|
mesh.faces.Reserve((Mesh::FIndex)nEstimatedNumVerts*2);
|
|
for (delaunay_t::All_cells_iterator ci=delaunay.all_cells_begin(), ce=delaunay.all_cells_end(); ci!=ce; ++ci) {
|
|
const cell_size_t ciID(ci->info());
|
|
for (int i=0; i<4; ++i) {
|
|
if (delaunay.is_infinite(ci, i)) continue;
|
|
const cell_handle_t cj(ci->neighbor(i));
|
|
const cell_size_t cjID(cj->info());
|
|
if (ciID < cjID) continue;
|
|
const bool ciType(graph.IsNodeOnSrcSide(ciID));
|
|
if (ciType == graph.IsNodeOnSrcSide(cjID)) continue;
|
|
Mesh::Face& face = mesh.faces.AddEmpty();
|
|
const triangle_vhandles_t tri(getTriangle(ci, i));
|
|
for (int v=0; v<3; ++v) {
|
|
const vertex_handle_t vh(tri.verts[v]);
|
|
ASSERT(vh->point() == delaunay.triangle(ci,i)[v]);
|
|
const auto pairItID(mapVertices.insert(std::make_pair(vh.for_compact_container(), (Mesh::VIndex)mesh.vertices.GetSize())));
|
|
if (pairItID.second)
|
|
mesh.vertices.Insert(CGAL2MVS<Mesh::Vertex::Type>(vh->point()));
|
|
ASSERT(pairItID.first->second < mesh.vertices.GetSize());
|
|
face[v] = pairItID.first->second;
|
|
}
|
|
// correct face orientation
|
|
if (!ciType)
|
|
std::swap(face[0], face[2]);
|
|
}
|
|
}
|
|
delaunay.clear();
|
|
|
|
DEBUG_EXTRA("Delaunay tetrahedras graph-cut completed (%g flow): %u vertices, %u faces (%s)", maxflow, mesh.vertices.GetSize(), mesh.faces.GetSize(), TD_TIMER_GET_FMT().c_str());
|
|
}
|
|
|
|
// fix non-manifold vertices and edges
|
|
mesh.FixNonManifold();
|
|
return true;
|
|
}
|
|
/*----------------------------------------------------------------*/
|