ATTPCROOT  0.3.0-alpha
A ROOT-based framework for analyzing data from active target detectors
hc.cxx
Go to the documentation of this file.
1 #include "hc.h"
2 
3 #include <boost/core/checked_delete.hpp> // for checked_delete
4 #include <boost/smart_ptr/shared_ptr.hpp> // for shared_ptr
5 #include <pcl/PointIndices.h> // for PointIndicesPtr, PointIndices
6 
7 #include "fastcluster.h"
8 
9 #include <iostream> // for operator<<, ofstream, basi...
10 #include <iterator> // for distance
11 #include <memory> // for allocator_traits<>::value_...
12 #include <string> // for char_traits, string
13 
14 #pragma warning(push, 0)
15 #include <pcl/kdtree/kdtree_flann.h>
16 
17 #include <algorithm>
18 #include <fstream> // IWUYU pragma: keep
19 #pragma warning(pop)
20 
21 namespace hc {
22 std::vector<triplet> generateTriplets(pcl::PointCloud<pcl::PointXYZI>::ConstPtr cloud, size_t k, size_t n, float a)
23 {
24  std::vector<triplet> triplets;
25  pcl::KdTreeFLANN<pcl::PointXYZI> kdtree;
26  kdtree.setInputCloud(cloud);
27 
28  for (size_t pointIndexB = 0; pointIndexB < cloud->size(); ++pointIndexB) {
29  pcl::PointXYZI pointB = (*cloud)[pointIndexB];
30  Eigen::Vector3f pointBEigen(pointB.x, pointB.y, pointB.z);
31 
32  std::vector<triplet> tripletCandidates;
33 
34  std::vector<int> nnIndices;
35  nnIndices.reserve(k);
36  std::vector<float> nnbSquaredDistances;
37  nnbSquaredDistances.reserve(k);
38  int const nnFound = kdtree.nearestKSearch(*cloud, (int)pointIndexB, (int)k, nnIndices, nnbSquaredDistances);
39 
40  for (size_t pointIndexIndexA = 1; pointIndexIndexA < nnFound; ++pointIndexIndexA) {
41  // When the distance is 0, we have the same point as pointB
42  if (nnbSquaredDistances[pointIndexIndexA] == 0)
43  continue;
44  size_t const pointIndexA = nnIndices[pointIndexIndexA];
45  pcl::PointXYZI pointA = (*cloud)[pointIndexA];
46  Eigen::Vector3f pointAEigen(pointA.x, pointA.y, pointA.z);
47 
48  Eigen::Vector3f directionAB = pointBEigen - pointAEigen;
49  directionAB /= directionAB.norm();
50 
51  for (size_t pointIndexIndexC = pointIndexIndexA + 1; pointIndexIndexC < nnFound; ++pointIndexIndexC) {
52  // When the distance is 0, we have the same point as pointB
53  if (nnbSquaredDistances[pointIndexIndexC] == 0)
54  continue;
55  size_t const pointIndexC = nnIndices[pointIndexIndexC];
56  pcl::PointXYZI pointC = (*cloud)[pointIndexC];
57  Eigen::Vector3f pointCEigen(pointC.x, pointC.y, pointC.z);
58 
59  Eigen::Vector3f directionBC = pointCEigen - pointBEigen;
60  directionBC /= directionBC.norm();
61 
62  float const angle = directionAB.dot(directionBC);
63 
64  // calculate error
65  float const error = 1.0f - angle; // removed: -0.5f
66 
67  if (error <= a) {
68  // calculate center
69  Eigen::Vector3f center = (pointAEigen + pointBEigen + pointCEigen) / 3.0f;
70 
71  // calculate direction
72  Eigen::Vector3f direction = pointCEigen - pointBEigen;
73  direction /= direction.norm();
74 
75  triplet newTriplet;
76 
77  newTriplet.pointIndexA = pointIndexA;
78  newTriplet.pointIndexB = pointIndexB;
79  newTriplet.pointIndexC = pointIndexC;
80  newTriplet.center = center;
81  newTriplet.direction = direction;
82  newTriplet.error = error;
83 
84  tripletCandidates.push_back(newTriplet);
85  }
86  }
87  }
88 
89  // order triplet candidates
90  std::sort(tripletCandidates.begin(), tripletCandidates.end());
91 
92  // use the n best candidates
93  for (size_t i = 0; i < std::min(n, tripletCandidates.size()); ++i) {
94  triplets.push_back(tripletCandidates[i]);
95  }
96  }
97 
98  return triplets;
99 }
100 
101 /*
102 @brief computation of condensed distance matrix.
103 
104 This function computes the condensed distance matrix and returns it as an array.
105 
106 @param cloud is the pointcloud
107 @param triplets is a list of triplets
108 @param triplet_metric the metric to compute the distance between triplets
109 @return returns an array of double as condensed distance matrix
110 */
111 double *calculate_distance_matrix(pcl::PointCloud<pcl::PointXYZI>::ConstPtr cloud, std::vector<triplet> const &triplets,
112  ScaleTripletMetric triplet_metric)
113 {
114  size_t const triplet_size = triplets.size();
115  size_t k = 0;
116  auto *result = new double[(triplet_size * (triplet_size - 1)) / 2]; // NOLINT
117 
118  for (size_t i = 0; i < triplet_size; ++i) {
119  for (size_t j = i + 1; j < triplet_size; j++) {
120  result[k++] = triplet_metric(triplets[i], triplets[j], cloud);
121  }
122  }
123  return result;
124 }
125 
126 /*
127 @brief computation of the clustering.
128 
129 This function computes the clustering. It uses the C++ standalone version
130 of fastcluster.
131 
132 @param cloud is the pointcloud
133 @param triplets is a list of triplets
134 @param triplet_metric the metric to compute the distance between triplets
135 @param t the distance for splitting the dendrogram into clusters
136 @param opt_verbose verbosity
137 @return returns a cluster_group
138 */
139 cluster_group compute_hc(pcl::PointCloud<pcl::PointXYZI>::ConstPtr cloud, std::vector<triplet> const &triplets,
140  ScaleTripletMetric triplet_metric, float t, int opt_verbose)
141 {
142  size_t const triplet_size = triplets.size();
143  size_t k, cluster_size, i;
144  cluster_group result;
145 
146  if (!triplet_size) {
147  // if no triplets are generated
148  return result;
149  }
150 
151  double *distance_matrix, *height = new double[triplet_size - 1]; // NOLINT
152  int *merge = new int[2 * (triplet_size - 1)], *labels = new int[triplet_size]; // NOLINT
153 
154  distance_matrix = calculate_distance_matrix(cloud, triplets, triplet_metric);
155 
156  hclust_fast(triplet_size, distance_matrix, HCLUST_METHOD_SINGLE, merge, height);
157 
158  // splitting the dendrogram into clusters
159  for (k = 0; k < (triplet_size - 1); ++k) {
160  if (height[k] >= t) {
161  break;
162  }
163  }
164  cluster_size = triplet_size - k;
165  cutree_k(triplet_size, merge, cluster_size, labels);
166 
167  // generate clusters
168  for (i = 0; i < cluster_size; ++i) {
169  cluster new_cluster;
170  result.clusters.push_back(new_cluster);
171  }
172  result.bestClusterDistance = height[k - 1];
173 
174  for (i = 0; i < triplet_size; ++i) {
175  result.clusters[labels[i]].push_back(i);
176  }
177 
178  if (opt_verbose > 1) {
179  // write debug file
180  const char *fname = "debug_ts.csv";
181  std::ofstream of(fname);
182  if (of.is_open()) {
183  for (size_t iii = 0; i < (triplet_size - 1); ++iii) {
184  of << height[iii] << std::endl;
185  }
186  } else {
187  std::cerr << "Could Not write file '" << fname << "'\n";
188  }
189  of.close();
190  }
191 
192  // cleanup
193  // NOLINTBEGIN
194  delete[] distance_matrix;
195  delete[] height;
196  delete[] merge;
197  delete[] labels;
198  // NOLINTEND
199 
200  return result;
201 }
202 
203 cluster_group cleanupClusterGroup(cluster_group const &clusterGroup, size_t m)
204 {
205  cluster_group cleanedGroup;
206  cleanedGroup.bestClusterDistance = clusterGroup.bestClusterDistance;
207  for (const auto &clust : clusterGroup.clusters) {
208  if (clust.size() >= m)
209  cleanedGroup.clusters.push_back(clust);
210  }
211 
212  return cleanedGroup;
213 }
214 
215 Cluster
216 toCluster(std::vector<hc::triplet> const &triplets, hc::cluster_group const &clusterGroup, size_t pointIndexCount)
217 {
218  std::vector<pcl::PointIndicesPtr> result;
219 
220  for (auto currentCluster = clusterGroup.clusters.begin(); currentCluster < clusterGroup.clusters.end();
221  currentCluster++) {
222  pcl::PointIndicesPtr pointIndices(new pcl::PointIndices());
223 
224  // add point indices
225  for (auto currentTripletIndex = currentCluster->begin(); currentTripletIndex < currentCluster->end();
226  currentTripletIndex++) {
227  hc::triplet const &currentTriplet = triplets[*currentTripletIndex];
228 
229  pointIndices->indices.push_back((int)currentTriplet.pointIndexA);
230  pointIndices->indices.push_back((int)currentTriplet.pointIndexB);
231  pointIndices->indices.push_back((int)currentTriplet.pointIndexC);
232  }
233 
234  // sort point-indices and remove duplicates
235  std::sort(pointIndices->indices.begin(), pointIndices->indices.end());
236  auto newEnd = std::unique(pointIndices->indices.begin(), pointIndices->indices.end());
237  pointIndices->indices.resize(std::distance(pointIndices->indices.begin(), newEnd));
238 
239  result.push_back(pointIndices);
240  }
241 
242  return {result, pointIndexCount};
243 }
244 
246 {
247  this->scale = s;
248 }
249 
250 float ScaleTripletMetric::operator()(triplet const &lhs, triplet const &rhs,
251  pcl::PointCloud<pcl::PointXYZI>::ConstPtr cloud)
252 {
253  float const perpendicularDistanceA =
254  (rhs.center - (lhs.center + lhs.direction.dot(rhs.center - lhs.center) * lhs.direction)).squaredNorm();
255  float const perpendicularDistanceB =
256  (lhs.center - (rhs.center + rhs.direction.dot(lhs.center - rhs.center) * rhs.direction)).squaredNorm();
257 
258  float const angle = std::abs(std::tan(std::acos(lhs.direction.dot(rhs.direction))));
259  return (std::sqrt(std::max(perpendicularDistanceA, perpendicularDistanceB)) / this->scale) + angle;
260 }
261 } // namespace hc
hc::triplet::pointIndexC
size_t pointIndexC
Definition: hc.h:18
hc::triplet::pointIndexA
size_t pointIndexA
Definition: hc.h:16
f
double(* f)(double t, const double *par)
Definition: lmcurve.cxx:21
hc
Definition: AtTrackFinderHC.h:32
hc::cluster_group::clusters
std::vector< cluster > clusters
Definition: hc.h:28
hc::generateTriplets
std::vector< triplet > generateTriplets(pcl::PointCloud< pcl::PointXYZI >::ConstPtr cloud, size_t k, size_t n, float a)
Definition: hc.cxx:22
hc::triplet::direction
Eigen::Vector3f direction
Definition: hc.h:20
fastcluster.h
hc::cluster
std::vector< size_t > cluster
Definition: hc.h:25
Cluster
Definition: cluster.h:9
hc::calculate_distance_matrix
double * calculate_distance_matrix(pcl::PointCloud< pcl::PointXYZI >::ConstPtr cloud, std::vector< triplet > const &triplets, ScaleTripletMetric triplet_metric)
Definition: hc.cxx:111
hc::triplet::error
float error
Definition: hc.h:21
hc::cleanupClusterGroup
cluster_group cleanupClusterGroup(cluster_group const &clusterGroup, size_t m)
Definition: hc.cxx:203
cutree_k
void cutree_k(int n, const int *merge, int nclust, int *labels)
Definition: fastcluster.cxx:38
hc::ScaleTripletMetric::ScaleTripletMetric
ScaleTripletMetric(float s)
Definition: hc.cxx:245
hc::ScaleTripletMetric::operator()
float operator()(triplet const &lhs, triplet const &rhs, pcl::PointCloud< pcl::PointXYZI >::ConstPtr cloud)
Definition: hc.cxx:250
hclust_fast
int hclust_fast(int n, double *distmat, int method, int *merge, double *height)
Definition: fastcluster.cxx:141
hc::triplet::center
Eigen::Vector3f center
Definition: hc.h:19
hc.h
hc::compute_hc
cluster_group compute_hc(pcl::PointCloud< pcl::PointXYZI >::ConstPtr cloud, std::vector< triplet > const &triplets, ScaleTripletMetric triplet_metric, float t, int opt_verbose)
Definition: hc.cxx:139
hc::cluster_group::bestClusterDistance
float bestClusterDistance
Definition: hc.h:29
hc::triplet::pointIndexB
size_t pointIndexB
Definition: hc.h:17
hc::toCluster
Cluster toCluster(std::vector< hc::triplet > const &triplets, hc::cluster_group const &clusterGroup, size_t pointIndexCount)
Definition: hc.cxx:216
HCLUST_METHOD_SINGLE
@ HCLUST_METHOD_SINGLE
Definition: fastcluster.h:66
hc::triplet
Definition: hc.h:15
hc::cluster_group
Definition: hc.h:27
hc::ScaleTripletMetric
Definition: hc.h:37