velo2cam_utils.h
Go to the documentation of this file.
1 /*
2  velo2cam_calibration - Automatic calibration algorithm for extrinsic
3  parameters of a stereo camera and a velodyne Copyright (C) 2017-2021 Jorge
4  Beltran, Carlos Guindel
5 
6  This file is part of velo2cam_calibration.
7 
8  velo2cam_calibration is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 2 of the License, or
11  (at your option) any later version.
12 
13  velo2cam_calibration is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with velo2cam_calibration. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 /*
23  velo2cam_utils: Helper functions
24 */
25 
26 #ifndef velo2cam_utils_H
27 #define velo2cam_utils_H
28 
29 #define PCL_NO_PRECOMPILE
30 #define DEBUG 0
31 
32 #include <pcl/search/kdtree.h>
33 #include <pcl/segmentation/extract_clusters.h>
34 
35 #define TARGET_NUM_CIRCLES 4
36 #define TARGET_RADIUS 0.12
37 #define GEOMETRY_TOLERANCE 0.06
38 
39 using namespace std;
40 
41 namespace Velodyne {
42 struct Point {
43  PCL_ADD_POINT4D; // quad-word XYZ
44  float intensity;
45  std::uint16_t ring;
46  float range;
47  EIGEN_MAKE_ALIGNED_OPERATOR_NEW // ensure proper alignment
49 
50 void addRange(pcl::PointCloud<Velodyne::Point> &pc) {
51  for (pcl::PointCloud<Point>::iterator pt = pc.points.begin();
52  pt < pc.points.end(); pt++) {
53  pt->range = sqrt(pt->x * pt->x + pt->y * pt->y + pt->z * pt->z);
54  }
55 }
56 
57 vector<vector<Point *>> getRings(pcl::PointCloud<Velodyne::Point> &pc,
58  int rings_count) {
59  vector<vector<Point *>> rings(rings_count);
60  for (pcl::PointCloud<Point>::iterator pt = pc.points.begin();
61  pt < pc.points.end(); pt++) {
62  rings[pt->ring].push_back(&(*pt));
63  }
64  return rings;
65 }
66 
67 // all intensities to range min-max
68 void normalizeIntensity(pcl::PointCloud<Point> &pc, float minv, float maxv) {
69  float min_found = INFINITY;
70  float max_found = -INFINITY;
71 
72  for (pcl::PointCloud<Point>::iterator pt = pc.points.begin();
73  pt < pc.points.end(); pt++) {
74  max_found = max(max_found, pt->intensity);
75  min_found = min(min_found, pt->intensity);
76  }
77 
78  for (pcl::PointCloud<Point>::iterator pt = pc.points.begin();
79  pt < pc.points.end(); pt++) {
80  pt->intensity =
81  (pt->intensity - min_found) / (max_found - min_found) * (maxv - minv) +
82  minv;
83  }
84 }
85 } // namespace Velodyne
86 
88  (float, x, x)(float, y, y)(float, z, z)(
89  float, intensity,
90  intensity)(std::uint16_t, ring,
91  ring)(float, range, range));
92 
93 void sortPatternCenters(pcl::PointCloud<pcl::PointXYZ>::Ptr pc,
94  vector<pcl::PointXYZ> &v) {
95  // 0 -- 1
96  // | |
97  // 3 -- 2
98 
99  if (v.empty()) {
100  v.resize(4);
101  }
102 
103  // Transform points to polar coordinates
104  pcl::PointCloud<pcl::PointXYZ>::Ptr spherical_centers(
105  new pcl::PointCloud<pcl::PointXYZ>());
106  int top_pt = 0;
107  int index = 0; // Auxiliar index to be used inside loop
108  for (pcl::PointCloud<pcl::PointXYZ>::iterator pt = pc->points.begin();
109  pt < pc->points.end(); pt++, index++) {
110  pcl::PointXYZ spherical_center;
111  spherical_center.x = atan2(pt->y, pt->x); // Horizontal
112  spherical_center.y =
113  atan2(sqrt(pt->x * pt->x + pt->y * pt->y), pt->z); // Vertical
114  spherical_center.z =
115  sqrt(pt->x * pt->x + pt->y * pt->y + pt->z * pt->z); // Range
116  spherical_centers->push_back(spherical_center);
117 
118  if (spherical_center.y < spherical_centers->points[top_pt].y) {
119  top_pt = index;
120  }
121  }
122 
123  // Compute distances from top-most center to rest of points
124  vector<double> distances;
125  for (int i = 0; i < 4; i++) {
126  pcl::PointXYZ pt = pc->points[i];
127  pcl::PointXYZ upper_pt = pc->points[top_pt];
128  distances.push_back(sqrt(pow(pt.x - upper_pt.x, 2) +
129  pow(pt.y - upper_pt.y, 2) +
130  pow(pt.z - upper_pt.z, 2)));
131  }
132 
133  // Get indices of closest and furthest points
134  int min_dist = (top_pt + 1) % 4, max_dist = top_pt;
135  for (int i = 0; i < 4; i++) {
136  if (i == top_pt) continue;
137  if (distances[i] > distances[max_dist]) {
138  max_dist = i;
139  }
140  if (distances[i] < distances[min_dist]) {
141  min_dist = i;
142  }
143  }
144 
145  // Second highest point shoud be the one whose distance is the median value
146  int top_pt2 = 6 - (top_pt + max_dist + min_dist); // 0 + 1 + 2 + 3 = 6
147 
148  // Order upper row centers
149  int lefttop_pt = top_pt;
150  int righttop_pt = top_pt2;
151 
152  if (spherical_centers->points[top_pt].x <
153  spherical_centers->points[top_pt2].x) {
154  int aux = lefttop_pt;
155  lefttop_pt = righttop_pt;
156  righttop_pt = aux;
157  }
158 
159  // Swap indices if target is located in the pi,-pi discontinuity
160  double angle_diff = spherical_centers->points[lefttop_pt].x -
161  spherical_centers->points[righttop_pt].x;
162  if (angle_diff > M_PI - spherical_centers->points[lefttop_pt].x) {
163  int aux = lefttop_pt;
164  lefttop_pt = righttop_pt;
165  righttop_pt = aux;
166  }
167 
168  // Define bottom row centers using lefttop == top_pt as hypothesis
169  int leftbottom_pt = min_dist;
170  int rightbottom_pt = max_dist;
171 
172  // If lefttop != top_pt, swap indices
173  if (righttop_pt == top_pt) {
174  leftbottom_pt = max_dist;
175  rightbottom_pt = min_dist;
176  }
177 
178  // Fill vector with sorted centers
179  v[0] = pc->points[lefttop_pt]; // lt
180  v[1] = pc->points[righttop_pt]; // rt
181  v[2] = pc->points[rightbottom_pt]; // rb
182  v[3] = pc->points[leftbottom_pt]; // lb
183 }
184 
185 void colourCenters(const std::vector<pcl::PointXYZ> pc,
186  pcl::PointCloud<pcl::PointXYZI>::Ptr coloured) {
187  float intensity = 0;
188  for (int i = 0; i < 4; i++) {
189  pcl::PointXYZI cc;
190  cc.x = pc[i].x;
191  cc.y = pc[i].y;
192  cc.z = pc[i].z;
193 
194  cc.intensity = intensity;
195  coloured->push_back(cc);
196  intensity += 0.3;
197  }
198 }
199 
200 void getCenterClusters(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in,
201  pcl::PointCloud<pcl::PointXYZ>::Ptr centers_cloud,
202  double cluster_tolerance = 0.10,
203  int min_cluster_size = 15, int max_cluster_size = 200,
204  bool verbosity = true) {
205  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(
206  new pcl::search::KdTree<pcl::PointXYZ>);
207  tree->setInputCloud(cloud_in);
208 
209  std::vector<pcl::PointIndices> cluster_indices;
210  pcl::EuclideanClusterExtraction<pcl::PointXYZ> euclidean_cluster;
211  euclidean_cluster.setClusterTolerance(cluster_tolerance);
212  euclidean_cluster.setMinClusterSize(min_cluster_size);
213  euclidean_cluster.setMaxClusterSize(max_cluster_size);
214  euclidean_cluster.setSearchMethod(tree);
215  euclidean_cluster.setInputCloud(cloud_in);
216  euclidean_cluster.extract(cluster_indices);
217 
218  if (DEBUG && verbosity)
219  cout << cluster_indices.size() << " clusters found from "
220  << cloud_in->points.size() << " points in cloud" << endl;
221 
222  for (std::vector<pcl::PointIndices>::iterator it = cluster_indices.begin();
223  it < cluster_indices.end(); it++) {
224  float accx = 0., accy = 0., accz = 0.;
225  for (vector<int>::iterator it2 = it->indices.begin();
226  it2 < it->indices.end(); it2++) {
227  accx += cloud_in->at(*it2).x;
228  accy += cloud_in->at(*it2).y;
229  accz += cloud_in->at(*it2).z;
230  }
231  // Compute and add center to clouds
232  pcl::PointXYZ center;
233  center.x = accx / it->indices.size();
234  center.y = accy / it->indices.size();
235  center.z = accz / it->indices.size();
236  centers_cloud->push_back(center);
237  }
238 }
239 
240 Eigen::Affine3f getRotationMatrix(Eigen::Vector3f source,
241  Eigen::Vector3f target) {
242  Eigen::Vector3f rotation_vector = target.cross(source);
243  rotation_vector.normalize();
244  double theta = acos(source[2] / sqrt(pow(source[0], 2) + pow(source[1], 2) +
245  pow(source[2], 2)));
246 
247  if (DEBUG)
248  cout << "Rot. vector: " << rotation_vector << " / Angle: " << theta << endl;
249 
250  Eigen::Matrix3f rotation =
251  Eigen::AngleAxis<float>(theta, rotation_vector) * Eigen::Scaling(1.0f);
252  Eigen::Affine3f rot(rotation);
253  return rot;
254 }
255 
256 class Square {
257  private:
258  pcl::PointXYZ _center;
259  std::vector<pcl::PointXYZ> _candidates;
260  float _target_width, _target_height, _target_diagonal;
261 
262  public:
263  Square(std::vector<pcl::PointXYZ> candidates, float width, float height) {
264  _candidates = candidates;
265  _target_width = width;
266  _target_height = height;
267  _target_diagonal = sqrt(pow(width, 2) + pow(height, 2));
268 
269  // Compute candidates centroid
270  for (int i = 0; i < candidates.size(); ++i) {
271  _center.x += candidates[i].x;
272  _center.y += candidates[i].y;
273  _center.z += candidates[i].z;
274  }
275 
276  _center.x /= candidates.size();
277  _center.y /= candidates.size();
278  _center.z /= candidates.size();
279  }
280 
281  float distance(pcl::PointXYZ pt1, pcl::PointXYZ pt2) {
282  return sqrt(pow(pt1.x - pt2.x, 2) + pow(pt1.y - pt2.y, 2) +
283  pow(pt1.z - pt2.z, 2));
284  }
285 
286  float perimeter() { // TODO: It is assumed that _candidates are ordered, it
287  // shouldn't
288  float perimeter = 0;
289  for (int i = 0; i < 4; ++i) {
290  perimeter += distance(_candidates[i], _candidates[(i + 1) % 4]);
291  }
292  return perimeter;
293  }
294 
295  pcl::PointXYZ at(int i) {
296  assert(0 <= i && i < 4);
297  return _candidates[i];
298  }
299 
300  bool is_valid() {
301  pcl::PointCloud<pcl::PointXYZ>::Ptr candidates_cloud(
302  new pcl::PointCloud<pcl::PointXYZ>());
303  // Check if candidates are at 5% of target's diagonal/2 to their centroid
304  for (int i = 0; i < _candidates.size(); ++i) {
305  candidates_cloud->push_back(_candidates[i]);
306  float d = distance(_center, _candidates[i]);
307  if (fabs(d - _target_diagonal / 2.) / (_target_diagonal / 2.) >
309  return false;
310  }
311  }
312  // Check perimeter?
313  std::vector<pcl::PointXYZ> sorted_centers;
314  sortPatternCenters(candidates_cloud, sorted_centers);
315  float perimeter = 0;
316  for (int i = 0; i < sorted_centers.size(); ++i) {
317  float current_distance = distance(
318  sorted_centers[i], sorted_centers[(i + 1) % sorted_centers.size()]);
319  if (i % 2) {
320  if (fabs(current_distance - _target_height) / _target_height >
322  return false;
323  }
324  } else {
325  if (fabs(current_distance - _target_width) / _target_width >
327  return false;
328  }
329  }
330  perimeter += current_distance;
331  }
332  float ideal_perimeter = (2 * _target_width + 2 * _target_height);
333  if (fabs((perimeter - ideal_perimeter) / ideal_perimeter >
335  return false;
336  }
337 
338  // Check width + height?
339  return true;
340  }
341 };
342 
343 void comb(int N, int K, std::vector<std::vector<int>> &groups) {
344  int upper_factorial = 1;
345  int lower_factorial = 1;
346 
347  for (int i = 0; i < K; i++) {
348  upper_factorial *= (N - i);
349  lower_factorial *= (K - i);
350  }
351  int n_permutations = upper_factorial / lower_factorial;
352 
353  if (DEBUG)
354  cout << N << " centers found. Iterating over " << n_permutations
355  << " possible sets of candidates" << endl;
356 
357  std::string bitmask(K, 1); // K leading 1's
358  bitmask.resize(N, 0); // N-K trailing 0's
359 
360  // print integers and permute bitmask
361  do {
362  std::vector<int> group;
363  for (int i = 0; i < N; ++i) // [0..N-1] integers
364  {
365  if (bitmask[i]) {
366  group.push_back(i);
367  }
368  }
369  groups.push_back(group);
370  } while (std::prev_permutation(bitmask.begin(), bitmask.end()));
371 
372  assert(groups.size() == n_permutations);
373 }
374 
375 const std::string currentDateTime() {
376  time_t now = time(0);
377  struct tm tstruct;
378  char buf[80];
379  tstruct = *localtime(&now);
380  strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);
381 
382  return buf;
383 }
384 
385 #endif
d
f
struct Velodyne::Point EIGEN_ALIGN16
float intensity
laser intensity reading
Square(std::vector< pcl::PointXYZ > candidates, float width, float height)
pcl::PointXYZ at(int i)
bool is_valid()
TFSIMD_FORCE_INLINE tfScalar distance(const Vector3 &v) const
TFSIMD_FORCE_INLINE const tfScalar & y() const
POINT_CLOUD_REGISTER_POINT_STRUCT(Velodyne::Point,(float, x, x)(float, y, y)(float, z, z)(float, intensity, intensity)(std::uint16_t, ring, ring)(float, range, range))
#define DEBUG
void normalizeIntensity(pcl::PointCloud< Point > &pc, float minv, float maxv)
float distance(pcl::PointXYZ pt1, pcl::PointXYZ pt2)
float perimeter()
vector< vector< Point * > > getRings(pcl::PointCloud< Velodyne::Point > &pc, int rings_count)
#define GEOMETRY_TOLERANCE
void addRange(pcl::PointCloud< Velodyne::Point > &pc)
TFSIMD_FORCE_INLINE const tfScalar & x() const
double min(double a, double b)
std::vector< pcl::PointXYZ > _candidates
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
void colourCenters(const std::vector< pcl::PointXYZ > pc, pcl::PointCloud< pcl::PointXYZI >::Ptr coloured)
Eigen::Affine3f getRotationMatrix(Eigen::Vector3f source, Eigen::Vector3f target)
void getCenterClusters(pcl::PointCloud< pcl::PointXYZ >::Ptr cloud_in, pcl::PointCloud< pcl::PointXYZ >::Ptr centers_cloud, double cluster_tolerance=0.10, int min_cluster_size=15, int max_cluster_size=200, bool verbosity=true)
TFSIMD_FORCE_INLINE const tfScalar & z() const
INLINE Rall1d< T, V, S > pow(const Rall1d< T, V, S > &arg, double m)
void sortPatternCenters(pcl::PointCloud< pcl::PointXYZ >::Ptr pc, vector< pcl::PointXYZ > &v)
std::uint16_t ring
laser ring number
INLINE Rall1d< T, V, S > acos(const Rall1d< T, V, S > &x)
pcl::PointXYZ _center
const std::string currentDateTime()
INLINE Rall1d< T, V, S > atan2(const Rall1d< T, V, S > &y, const Rall1d< T, V, S > &x)
float _target_width
double max(double a, double b)
void comb(int N, int K, std::vector< std::vector< int >> &groups)


velo2cam_calibration
Author(s): Jorge Beltran , Carlos Guindel
autogenerated on Fri Feb 26 2021 03:40:57