45#include <gtsam_points/types/frame_traits.hpp>
46#include <gtsam_points/segmentation/min_cut.hpp>
48#include <boost/graph/adjacency_list.hpp>
49#include <boost/graph/graph_traits.hpp>
50#include <boost/graph/one_bit_color_map.hpp>
51#include <boost/graph/stoer_wagner_min_cut.hpp>
52#include <boost/graph/boykov_kolmogorov_max_flow.hpp>
53#include <boost/graph/graph_utility.hpp>
54#include <boost/property_map/property_map.hpp>
56namespace gtsam_points {
58template <
typename Po
intCloud>
59MinCutResult min_cut_(
const PointCloud& points,
const NearestNeighborSearch& search,
const size_t source_pt_index,
const MinCutParams& params) {
60 const size_t INVALID_INDEX = std::numeric_limits<size_t>::max();
63 std::vector<size_t> all_neighbors(frame::size(points) * params.k_neighbors);
64 std::vector<double> all_neighbor_sq_dists(frame::size(points) * params.k_neighbors);
65#pragma omp parallel for num_threads(params.num_threads) schedule(guided, 4)
66 for (
size_t i = 0; i < frame::size(points); i++) {
67 size_t* k_neighbors = all_neighbors.data() + i * params.k_neighbors;
68 double* k_sq_dists = all_neighbor_sq_dists.data() + i * params.k_neighbors;
69 const size_t num_found = search.knn_search(frame::point(points, i).data(), params.k_neighbors, k_neighbors, k_sq_dists);
71 if (num_found != params.k_neighbors) {
72 std::cerr <<
"warning : Not enough neighbors. num_found=" << num_found <<
" k_neighbors=" << params.k_neighbors << std::endl;
73 std::fill(k_neighbors + num_found, k_neighbors + params.k_neighbors, INVALID_INDEX);
78 using Traits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
79 using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
80 boost::property<boost::vertex_index_t, long,
81 boost::property<boost::vertex_color_t, boost::default_color_type,
82 boost::property<boost::vertex_distance_t, long,
83 boost::property<boost::vertex_predecessor_t, Traits::edge_descriptor>>>>,
84 boost::property<boost::edge_capacity_t, double,
85 boost::property<boost::edge_residual_capacity_t, double,
86 boost::property<boost::edge_reverse_t, Traits::edge_descriptor>>>>;
87 using EdgeDescriptor = Traits::edge_descriptor;
91 Graph g(frame::size(points));
92 boost::property_map<Graph, boost::edge_capacity_t>::type capacity = get(boost::edge_capacity, g);
93 boost::property_map<Graph, boost::edge_residual_capacity_t>::type residual_capacity = get(boost::edge_residual_capacity, g);
94 boost::property_map<Graph, boost::edge_reverse_t>::type reverse_edges = get(boost::edge_reverse, g);
95 boost::property_map<Graph, boost::vertex_color_t>::type color = get(boost::vertex_color, g);
97 const auto add_edge = [&](
size_t source,
size_t target,
double weight) {
98 const auto edge = boost::add_edge(source, target, g);
99 const auto reverse_edge = boost::add_edge(target, source, g);
100 if (!edge.second || !reverse_edge.second) {
104 capacity[edge.first] = weight;
105 capacity[reverse_edge.first] = 0.0;
106 reverse_edges[edge.first] = reverse_edge.first;
107 reverse_edges[reverse_edge.first] = edge.first;
110 const double inv_dist_sq_sigma = 1.0 / (params.distance_sigma * params.distance_sigma);
111 const double inv_angle_sq_sigma = 1.0 / (params.angle_sigma * params.angle_sigma);
113 std::vector<size_t> sink_pt_indices;
114 for (
size_t i = 0; i < frame::size(points); i++) {
115 const size_t* neighbors = all_neighbors.data() + i * params.k_neighbors;
116 const double* neighbor_sq_dists = all_neighbor_sq_dists.data() + i * params.k_neighbors;
118 const Eigen::Vector4d& normal = frame::normal(points, i);
120 const double dist_from_source = (frame::point(points, i) - frame::point(points, source_pt_index)).norm();
123 if (i != source_pt_index && dist_from_source < params.foreground_mask_radius) {
124 add_edge(i, source_pt_index, params.foreground_weight);
127 if (dist_from_source > params.background_mask_radius) {
128 sink_pt_indices.emplace_back(i);
132 for (
size_t k = 0; k < params.k_neighbors; k++) {
133 if (neighbors[k] == INVALID_INDEX) {
137 const double weight_dist = std::exp(-neighbor_sq_dists[k] * inv_dist_sq_sigma);
138 const double normal_diff = std::acos(std::abs(normal.dot(frame::normal(points, neighbors[k]))));
139 const double weight_angle = std::exp(-normal_diff * normal_diff * inv_angle_sq_sigma);
140 add_edge(i, neighbors[k], weight_dist * weight_angle);
144 if (sink_pt_indices.empty()) {
145 std::cerr <<
"No sink points" << std::endl;
146 return MinCutResult();
149 size_t sink_pt_index = sink_pt_indices.front();
150 for (
size_t i = 1; i < sink_pt_indices.size(); i++) {
151 add_edge(sink_pt_indices[i], sink_pt_index, params.background_weight);
156 result.source_index = source_pt_index;
157 result.sink_index = sink_pt_index;
158 result.max_flow = boost::boykov_kolmogorov_max_flow(g, source_pt_index, sink_pt_index);
161 const auto source_color = color[source_pt_index];
162 for (
size_t i = 0; i < frame::size(points); i++) {
163 if (color[i] == source_color) {
164 result.cluster_indices.emplace_back(i);
171template <
typename Po
intCloud>
172MinCutResult min_cut_(
const PointCloud& points,
const NearestNeighborSearch& search,
const Eigen::Vector4d& source_pt,
const MinCutParams& params) {
173 size_t source_pt_index;
175 if (!search.knn_search(source_pt.data(), 1, &source_pt_index, &sq_dist)) {
176 std::cerr <<
"Failed to find the source point" << std::endl;
177 return MinCutResult();
180 if (sq_dist > params.foreground_mask_radius * params.foreground_mask_radius) {
181 std::cerr <<
"warning: The source point is too far from the point cloud" << std::endl;
184 return min_cut_(points, search, source_pt_index, params);