Apollo 11.0
自动驾驶开放平台
ncut.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2019 The Apollo Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
18
19#include <algorithm>
20#include <ctime>
21#include <functional>
22#include <limits>
23#include <queue>
24#include <stack>
25#include <string>
26#include <unordered_map>
27#include <utility>
28#include <vector>
29
30#include <omp.h>
31
32#include "cyber/common/file.h"
33#include "cyber/common/log.h"
35
36namespace apollo {
37namespace perception {
38namespace lidar {
39
40namespace {
41const int OBSTACLE_MINIMUM_NUM_POINTS = 50;
42}
43
46using Eigen::MatrixXf;
47
50 ADEBUG << "NCut destructor done";
51}
52
53bool NCut::Init(const NCutParam &param) {
54 if (!Configure(param)) {
55 AERROR << "failed to load ncut config.";
56 return false;
57 }
58
59 _classifier.reset(new LRClassifier);
60 if (!_classifier->init()) {
61 AERROR << "failed to init FrameClassifierPipeline.";
62 return false;
63 }
64 return true;
65}
66
67bool NCut::Configure(const NCutParam &ncut_param_) {
68 _grid_radius = ncut_param_.grid_radius();
69 _connect_radius = ncut_param_.connect_radius();
70 _super_pixel_cell_size = ncut_param_.super_pixel_cell_size();
71 _num_cuts = ncut_param_.num_cuts();
72 _ncuts_stop_threshold = ncut_param_.ncuts_stop_threshold();
73 _ncuts_enable_classifier_threshold = ncut_param_.ncuts_enable_classifier_threshold();
74 _sigma_space = ncut_param_.sigma_space();
75 _sigma_feature = ncut_param_.sigma_feature();
76 _skeleton_cell_size = ncut_param_.skeleton_cell_size();
77 _patch_size = ncut_param_.patch_size();
78 _overlap_factor = ncut_param_.overlap_factor();
79 _felzenszwalb_sigma = ncut_param_.felzenszwalb_sigma();
80 _felzenszwalb_k = ncut_param_.felzenszwalb_k();
81 _felzenszwalb_min_size = ncut_param_.felzenszwalb_min_size();
82
83 AINFO << "NCut Parameters" << ncut_param_.DebugString();
84 return true;
85}
86
88#ifdef DEBUG_NCUT
89 double start_t = omp_get_wtime();
90#endif
91 // .0 clear everything
92 _segment_pids.clear();
93 _segment_labels.clear();
94 _segment_bbox.clear();
95 _outlier_pids.clear();
96 _cluster_points.clear();
97 _cluster_bounding_box.clear();
98 _cluster_skeleton_points.clear();
99 _cluster_skeleton_features.clear();
100 std::vector<int> pids(cloud->size());
101 for (size_t i = 0; i < cloud->size(); ++i) {
102 pids[i] = static_cast<int>(i);
103 }
104 _cloud_obstacles = base::PointFCloudPtr(new base::PointFCloud(*cloud, pids));
105 AINFO << "cloud obstacle size in ncut segment is " << _cloud_obstacles->size();
106#ifdef DEBUG_NCUT
107 ADEBUG << "segment enter ... input cloud size " << cloud->size();
108// visualize_points(pids);
109#endif
110 // .1 super pixels
111 // super_pixels_felzenszwalb(cloud, _grid_radius, _super_pixel_cell_size,
112 // _cluster_points);
113 SuperPixelsFloodFill(cloud, _grid_radius, _super_pixel_cell_size, &_cluster_points);
114#ifdef DEBUG_NCUT
115 // visualize_segments_from_points(_cluster_points);
116 ADEBUG << "super pixels " << _cluster_points.size();
117 AINFO << "super pixels done " << omp_get_wtime() - start_t;
118 start_t = omp_get_wtime();
119#endif
120
121 // .2 precompute skeleton and bbox
122 PrecomputeAllSkeletonAndBbox();
123#ifdef DEBUG_NCUT
124 AINFO << "precompute skeleton and bbox done " << omp_get_wtime() - start_t;
125 start_t = omp_get_wtime();
126#endif
127 // .3 grach cut
128 std::vector<std::vector<int>> segment_clusters;
129 std::vector<std::string> segment_labels;
130 NormalizedCut(_ncuts_stop_threshold, true, &segment_clusters, &segment_labels);
131#ifdef DEBUG_NCUT
132 AINFO << "normalized_cut done, #segments " << segment_clusters.size() << ", time: " << omp_get_wtime() - start_t;
133 start_t = omp_get_wtime();
134#endif
135 // .4 _segment_pids;
136 for (size_t i = 0; i < segment_clusters.size(); ++i) {
137 std::vector<int> pids;
138 GetClustersPids(segment_clusters[i], &pids);
139 if (pids.size() > 0) {
140 _segment_pids.push_back(pids);
141 _segment_labels.push_back(segment_labels[i]);
142 NcutBoundingBox box;
143 GetComponentBoundingBox(segment_clusters[i], &box);
144 _segment_bbox.push_back(box);
145 }
146 }
147}
148
149void NCut::SuperPixelsFloodFill(
151 float radius,
152 float cell_size,
153 std::vector<std::vector<int>> *super_pixels) {
154 FloodFill ff_grid(radius, cell_size);
155 std::vector<int> num_cells_per_components;
156 ff_grid.GetSegments(cloud, super_pixels, &num_cells_per_components);
157}
158
159void NCut::PrecomputeAllSkeletonAndBbox() {
160 const int num_clusters = static_cast<int>(_cluster_points.size());
161 _cluster_skeleton_points.resize(num_clusters);
162 _cluster_skeleton_features.resize(num_clusters);
163 _cluster_bounding_box.resize(num_clusters);
164 _cluster_labels.resize(num_clusters, "unknown");
165 // skeleton
166 _ff_feature_grid.SetGridRadius(_grid_radius);
167 _ff_feature_grid.SetCellSize(_skeleton_cell_size);
168 _ff_feature_grid.BuildGrid(_cloud_obstacles);
169 std::vector<gridIndex> dummy_indices;
170 BuildAverageHeightMap(_cloud_obstacles, _ff_feature_grid, &_cv_feature_map, &dummy_indices);
171
172 // build_maximum_height_map(_cloud_obstacles, _ff_feature_grid,
173 // _cv_feature_map, dummy_indices);
174 for (int i = 0; i < num_clusters; ++i) {
175 SampleByGrid(_cluster_points[i], &_cluster_skeleton_points[i], &_cluster_skeleton_features[i]);
176 _cluster_bounding_box[i] = ComputeClusterBoundingBox(_cluster_points[i]);
177 }
178 // cluster label
179 for (size_t i = 0; i < _cluster_points.size(); ++i) {
180 base::PointFCloudPtr pc = base::PointFCloudPtr(new base::PointFCloud(*_cloud_obstacles, _cluster_points[i]));
181 _cluster_labels[i] = GetPcLabel(pc);
182 }
183}
184
185void NCut::BuildAverageHeightMap(
187 const FloodFill &ff_map,
188 cv::Mat *cv_height_map_in,
189 std::vector<gridIndex> *point_pixel_indices_in) {
190 cv::Mat &cv_height_map = *cv_height_map_in;
191 std::vector<gridIndex> &point_pixel_indices = *point_pixel_indices_in;
192 const int num_points = static_cast<int>(cloud->size());
193 cv::Mat cv_img = cv::Mat::zeros(ff_map.GetNumRows(), ff_map.GetNumCols(), CV_32F);
194 std::vector<std::vector<int>> counts;
195 counts.resize(cv_img.rows);
196 for (size_t i = 0; i < counts.size(); ++i) {
197 counts[i].resize(cv_img.cols, 0);
198 }
199 point_pixel_indices.resize(num_points);
200 for (int i = 0; i < num_points; ++i) {
201 int irow = -1;
202 int jcol = -1;
203 base::PointF pt = (*cloud)[i];
204 if (ff_map.Pos2d(pt.x, pt.y, &irow, &jcol)) {
205 cv_img.at<float>(irow, jcol) += pt.z;
206 counts[irow][jcol] += 1;
207 point_pixel_indices[i].irow = irow;
208 point_pixel_indices[i].jcol = jcol;
209 } else {
210 point_pixel_indices[i].irow = -1;
211 point_pixel_indices[i].jcol = -1;
212 }
213 }
214 for (int i = 0; i < cv_img.rows; ++i) {
215 for (int j = 0; j < cv_img.cols; ++j) {
216 if (counts[i][j] > 0) {
217 cv_img.at<float>(i, j) /= static_cast<float>(counts[i][j]);
218 }
219 }
220 }
221 cv::Mat cv_height_map_copy;
222 cv::GaussianBlur(cv_img, cv_height_map_copy, cv::Size(3, 3), 0, 0);
223 cv::normalize(cv_height_map_copy, cv_height_map, 0, 255, cv::NORM_MINMAX, CV_8UC1);
224}
225
226void NCut::SampleByGrid(
227 const std::vector<int> &point_gids,
228 MatrixXf *skeleton_coords_in,
229 MatrixXf *skeleton_feature_in) {
230 MatrixXf &skeleton_coords = *skeleton_coords_in;
231 MatrixXf &skeleton_feature = *skeleton_feature_in;
232 FloodFill sampler(_grid_radius, _skeleton_cell_size);
233 base::PointFCloudPtr pc = base::PointFCloudPtr(new base::PointFCloud(*_cloud_obstacles, point_gids));
234 sampler.BuildGrid(pc);
235 std::unordered_map<int, std::pair<base::PointF, float>> centroids;
236 const std::vector<int> &point_idx = sampler.GetPointIdxInGrid();
237 std::unordered_map<int, std::pair<base::PointF, float>>::iterator it;
238 for (size_t i = 0; i < point_idx.size(); ++i) {
239 it = centroids.find(point_idx[i]);
240 if (it != centroids.end()) {
241 it->second.first.x += (*pc)[i].x;
242 it->second.first.y += (*pc)[i].y;
243 it->second.first.z += (*pc)[i].z;
244 it->second.second += 1.f;
245 } else {
246 centroids[point_idx[i]] = std::make_pair((*pc)[i], 1.f);
247 }
248 }
249 int num_skeletons = static_cast<int>(centroids.size());
250 skeleton_coords.resize(num_skeletons, 3);
251 int p = 0;
252 for (it = centroids.begin(); it != centroids.end(); ++it) {
253 skeleton_coords.coeffRef(p, 0) = it->second.first.x / it->second.second;
254 skeleton_coords.coeffRef(p, 1) = it->second.first.y / it->second.second;
255 skeleton_coords.coeffRef(p, 2) = it->second.first.z / it->second.second;
256 p++;
257 }
258 GetPatchFeature(skeleton_coords, &skeleton_feature);
259}
260
261void NCut::GetPatchFeature(const MatrixXf &points, MatrixXf *features_in) {
262 MatrixXf &features = *features_in;
263 const int num_points = static_cast<int>(points.rows());
264 const int dim = _patch_size * _patch_size;
265 features.resize(num_points, dim);
266 for (int i = 0; i < num_points; ++i) {
267 // .1 patch
268 int irow = 0;
269 int jcol = 0;
270 _ff_feature_grid.Pos2d(points.coeffRef(i, 0), points.coeffRef(i, 1), &irow, &jcol);
271 cv::Mat patch;
272 cv::Point2f pt(static_cast<float>(irow), static_cast<float>(jcol));
273 cv::getRectSubPix(_cv_feature_map, cv::Size(_patch_size, _patch_size), pt, patch);
274 // .2 maybe later i will add other features based on patch
275 // .3 add to features
276 assert(dim == patch.rows * patch.cols);
277 int p = 0;
278 for (int r = 0; r < patch.rows; ++r) {
279 for (int c = 0; c < patch.cols; ++c) {
280 float val = patch.at<float>(r, c);
281 features.coeffRef(i, p++) = static_cast<float>((std::isnan(val) || std::isinf(val)) ? 1.e-50 : val);
282 // features.coeffRef(i, p++) = patch.at<float>(r, c);
283 }
284 }
285 }
286}
287
288NCut::NcutBoundingBox NCut::ComputeClusterBoundingBox(const std::vector<int> &point_gids) {
289 // ! Note: do not perform rotation, so just some intuitive guess
290 float x_max = -std::numeric_limits<float>::max();
291 float y_max = -std::numeric_limits<float>::max();
292 float z_max = -std::numeric_limits<float>::max();
293 float x_min = std::numeric_limits<float>::max();
294 float y_min = std::numeric_limits<float>::max();
295 float z_min = std::numeric_limits<float>::max();
296 for (size_t j = 0; j < point_gids.size(); ++j) {
297 int pid = point_gids[j];
298 x_min = std::min(x_min, (*_cloud_obstacles)[pid].x);
299 x_max = std::max(x_max, (*_cloud_obstacles)[pid].x);
300 y_min = std::min(y_min, (*_cloud_obstacles)[pid].y);
301 y_max = std::max(y_max, (*_cloud_obstacles)[pid].y);
302 z_min = std::min(z_min, (*_cloud_obstacles)[pid].z);
303 z_max = std::max(z_max, (*_cloud_obstacles)[pid].z);
304 }
305 NcutBoundingBox box;
306 std::get<0>(box) = x_min;
307 std::get<1>(box) = x_max;
308 std::get<2>(box) = y_min;
309 std::get<3>(box) = y_max;
310 std::get<4>(box) = z_min;
311 std::get<5>(box) = z_max;
312 return box;
313}
314
315std::string NCut::GetPcLabel(const base::PointFCloudPtr &cloud) {
316 if (cloud->size() < OBSTACLE_MINIMUM_NUM_POINTS) {
317 return "unknown";
318 }
320 base::OrientCloud(*cloud, rot_cloud.get(), true);
321 std::string label;
322 // Eigen::VectorXf response = _classifier->classify_and_get_label(rot_cloud,
323 // &label);
324 label = _classifier->GetLabel(rot_cloud);
325 // ./obstacle_detection/classifier/frame_classifier_pipeline.cpp line 466
326 if (label == "background") {
327 label = "unknown";
328 }
329 return label;
330}
331
332void NCut::NormalizedCut(
333 float ncuts_threshold,
334 bool use_classifier,
335 std::vector<std::vector<int>> *segment_clusters_in,
336 std::vector<std::string> *segment_labels_in) {
337 std::vector<std::vector<int>> &segment_clusters = *segment_clusters_in;
338 std::vector<std::string> &segment_labels = *segment_labels_in;
339 const int num_clusters = static_cast<int>(_cluster_points.size());
340 if (num_clusters < 1) {
341 return;
342 }
343 if (num_clusters == 1) {
344 std::vector<int> tmp(1, 0);
345 segment_clusters.push_back(tmp);
346 segment_labels.push_back(_cluster_labels[0]);
347 return;
348 }
349#ifdef DEBUG_NCUT
350 LOG_DEBUG << "\n+++++++++++++++++++++++++++++ input " << num_clusters << " clusters +++++++++++++++++++++++++++\n";
351// visualize_segments_from_cluster(_cluster_points);
352#endif
353 MatrixXf weights;
354 ComputeSkeletonWeights(&weights);
355 std::vector<int> *curr = new std::vector<int>(num_clusters);
356 for (int i = 0; i < num_clusters; ++i) {
357 (*curr)[i] = i;
358 }
359 std::stack<std::vector<int> *> job_stack;
360 job_stack.push(curr);
361 while (!job_stack.empty()) {
362 curr = job_stack.top();
363 job_stack.pop();
364#ifdef DEBUG_NCUT
365 AINFO << "curr size " << curr->size();
366// visualize_cluster(curr);
367#endif
368 std::string seg_label;
369 if (curr->size() == 1) {
370 segment_clusters.push_back(*curr);
371 segment_labels.push_back(_cluster_labels[(*curr)[0]]);
372 } else if (use_classifier && IsMovableObstacle(*curr, &seg_label)) {
373 segment_clusters.push_back(*curr);
374 segment_labels.push_back(seg_label);
375#ifdef DEBUG_NCUT
376 std::cout << "cluster: ";
377 for (size_t i = 0; i < curr->size(); ++i) {
378 std::cout << (*curr)[i] << " ";
379 }
380 std::cout << " as a segment (" << seg_label << ")" << std::endl;
381#endif
382 } else {
383 std::vector<int> *seg1 = new std::vector<int>();
384 std::vector<int> *seg2 = new std::vector<int>();
385 MatrixXf my_weights(curr->size(), curr->size());
386 for (size_t i = 0; i < curr->size(); ++i) {
387 const int ci = curr->at(i);
388 for (size_t j = 0; j < curr->size(); ++j) {
389 const int cj = curr->at(j);
390 my_weights.coeffRef(i, j) = weights.coeffRef(ci, cj);
391 }
392 }
393 double cost = GetMinNcuts(my_weights, curr, seg1, seg2);
394#ifdef DEBUG_NCUT
395 AINFO << "N cut cost is " << cost << ", seg1 size " << seg1->size() << ", seg2 size " << seg2->size();
396 if (curr->size() < 50) {
397 std::cout << "seg1: ";
398 for (size_t i = 0; i < seg1->size(); ++i) {
399 std::cout << (*seg1)[i] << " ";
400 }
401 std::cout << std::endl;
402 std::cout << "seg2: ";
403 for (size_t i = 0; i < seg2->size(); ++i) {
404 std::cout << (*seg2)[i] << " ";
405 }
406 std::cout << std::endl;
407 }
408// visualize_graph_cut(seg1, seg2);
409#endif
410 if (cost > ncuts_threshold || 0 == seg1->size() || 0 == seg2->size()) {
411 std::vector<int> buffer;
412 for (size_t i = 0; i < curr->size(); ++i) {
413 const int cid = (*curr)[i];
414 if (_cluster_labels[cid] != "unknown") {
415#ifdef DEBUG_NCUT
416 std::cout << "cluster: " << cid << " as a segment (hit, " << _cluster_labels[cid] << ")"
417 << std::endl;
418#endif
419 std::vector<int> tmp(1, cid);
420 segment_clusters.push_back(tmp);
421 segment_labels.push_back(_cluster_labels[cid]);
422 } else {
423 buffer.push_back(cid);
424 }
425 }
426 if (buffer.size() > 0) {
427 segment_clusters.push_back(buffer);
428 segment_labels.push_back("unknown");
429 }
430#ifdef DEBUG_NCUT
431 std::cout << "cluster: ";
432 for (size_t i = 0; i < buffer.size(); ++i) {
433 std::cout << buffer[i] << " ";
434 }
435 std::cout << " as a segment (hit, unknown)" << std::endl;
436#endif
437 delete seg1;
438 delete seg2;
439 } else {
440 job_stack.push(seg1);
441 job_stack.push(seg2);
442 }
443 }
444#ifdef DEBUG_NCUT
445 LOG_DEBUG << "============================================================="
446 "================";
447#endif
448 delete curr;
449 } // end of while
450#ifdef DEBUG_NCUT
451 std::cout << "graph cut return segments: " << std::endl;
452 for (size_t i = 0; i < segment_clusters.size(); ++i) {
453 std::cout << "seg " << i << ": ";
454 for (size_t j = 0; j < segment_clusters[i].size(); ++j) {
455 std::cout << segment_clusters[i][j] << " ";
456 }
457 std::cout << ": " << segment_labels[i] << std::endl;
458 }
459 LOG_DEBUG << "normalized_cut: " << segment_clusters.size() << " segments from " << _cluster_points.size()
460 << " clusters";
461// visualize_segments_from_cluster(segment_clusters);
462#endif
463}
464
465void NCut::ComputeSkeletonWeights(Eigen::MatrixXf *weights_in) {
466 Eigen::MatrixXf &weights = *weights_in;
467 const int num_clusters = static_cast<int>(_cluster_points.size());
468 const double hs2 = _sigma_space * _sigma_space;
469 const double hf2 = _sigma_feature * _sigma_feature;
470 const double radius2 = _connect_radius * _connect_radius;
471 weights.resize(num_clusters, num_clusters);
472 for (int i = 0; i < num_clusters; ++i) {
473 weights.coeffRef(i, i) = 1.f;
474 for (int j = i + 1; j < num_clusters; ++j) {
475 float dist_point = std::numeric_limits<float>::max();
476 float dist_feature = std::numeric_limits<float>::max();
477 ComputeSquaredSkeletonDistance(
478 _cluster_skeleton_points[i],
479 _cluster_skeleton_features[i],
480 _cluster_skeleton_points[j],
481 _cluster_skeleton_features[j],
482 &dist_point,
483 &dist_feature);
484 if (dist_point > radius2) {
485 weights.coeffRef(i, j) = 0.f;
486 weights.coeffRef(j, i) = 0.f;
487 } else {
488 weights.coeffRef(i, j) = static_cast<float>(exp(-dist_point / hs2) * exp(-dist_feature / hf2));
489 weights.coeffRef(j, i) = weights.coeffRef(i, j);
490 }
491 }
492 }
493}
494
495float NCut::GetMinNcuts(
496 const Eigen::MatrixXf &in_weights,
497 const std::vector<int> *in_clusters,
498 std::vector<int> *seg1,
499 std::vector<int> *seg2) {
500 // .0 initialization
501 const int num_clusters = static_cast<int>(in_weights.rows());
502 seg1->resize(num_clusters);
503 seg2->resize(num_clusters);
504 // .1 eigen decompostion
505 Eigen::MatrixXf eigenvectors;
506 LaplacianDecomposition(in_weights, &eigenvectors);
507// std::cout << "\nweights: " << in_weights << std::endl;
508// std::cout << "\neigv: " << eigenvectors << std::endl;
509#ifdef DEBUG_NCUT
510// std::cout << "weights:\n " << in_weights << std::endl << std::endl;
511// std::cout << "eigen vectors:\n " << eigenvectors << std::endl << std::endl;
512#endif
513 // .2 search for best split
514 const float minval = eigenvectors.col(1).minCoeff();
515 const float maxval = eigenvectors.col(1).maxCoeff();
516 const float increment = static_cast<float>((maxval - minval) / (static_cast<float>(_num_cuts) + 1.0f));
517 int num_seg1 = 0;
518 int num_seg2 = 0;
519 float opt_split = 0.0;
520 float opt_cost = std::numeric_limits<float>::max();
521 for (int i = 0; i < _num_cuts; ++i) {
522 num_seg1 = 0;
523 num_seg2 = 0;
524 // .2.1 split
525 float split = static_cast<float>(minval + static_cast<float>(i + 1) * increment);
526 for (int j = 0; j < num_clusters; ++j) {
527 if (eigenvectors.coeffRef(j, 1) > split) {
528 (*seg1)[num_seg1++] = j;
529 } else {
530 (*seg2)[num_seg2++] = j;
531 }
532 }
533 // .2.2 compute best normalized_cuts cost
534 double assoc1 = 0.0;
535 double assoc2 = 0.0;
536 double cut = 0.0;
537 for (int j = 0; j < num_seg1; ++j) {
538 assoc1 += in_weights.row(seg1->at(j)).sum();
539 }
540 for (int j = 0; j < num_seg2; ++j) {
541 assoc2 += in_weights.row(seg2->at(j)).sum();
542 }
543 for (int j = 0; j < num_seg1; ++j) {
544 for (int t = 0; t < num_seg2; ++t) {
545 cut += in_weights.coeffRef(seg1->at(j), seg2->at(t));
546 }
547 }
548 float cost = static_cast<float>(cut / assoc1 + cut / assoc2);
549#ifdef DEBUG_NCUT
550 LOG_DEBUG << "seg1: " << num_seg1 << ", seg2: " << num_seg2 << ", split " << split << ", cut " << cut
551 << ", assoc1 " << assoc1 << ", assoc2 " << assoc2 << ", cost " << cost;
552#endif
553 // .2.3 find best cost
554 if (cost < opt_cost) {
555 opt_cost = cost;
556 opt_split = split;
557 }
558 }
559 // .3 split data according to best split
560 num_seg1 = 0;
561 num_seg2 = 0;
562 for (int i = 0; i < num_clusters; ++i) {
563 if (eigenvectors.coeffRef(i, 1) > opt_split) {
564 (*seg1)[num_seg1++] = in_clusters->at(i);
565 } else {
566 (*seg2)[num_seg2++] = in_clusters->at(i);
567 }
568 }
569 seg1->resize(num_seg1);
570 seg2->resize(num_seg2);
571 return opt_cost;
572}
573
574void NCut::LaplacianDecomposition(const Eigen::MatrixXf &weights, Eigen::MatrixXf *eigenvectors_in) {
575#ifdef DEBUG_NCUT
576// std::cout << "laplacian 0:\n " << weights << std::endl << std::endl;
577#endif
578 Eigen::MatrixXf &eigenvectors = *eigenvectors_in;
579 // .1 degree matrix: D = sum(W, 2)
580 Eigen::VectorXf diag(weights.rows());
581 for (int i = 0; i < weights.rows(); ++i) {
582 diag.coeffRef(i) = weights.row(i).sum();
583 }
584 // .2 graph laplacian L = D - W
585 Eigen::MatrixXf laplacian(weights.rows(), weights.cols());
586 for (int i = 0; i < laplacian.rows(); ++i) {
587 for (int j = 0; j < laplacian.cols(); ++j) {
588 if (i == j) {
589 laplacian.coeffRef(i, j) = diag.coeffRef(i) - weights.coeffRef(i, j);
590 } else {
591 laplacian.coeffRef(i, j) = 0.f - weights.coeffRef(i, j);
592 }
593 }
594 }
595#ifdef DEBUG_NCUT
596// std::cout << "laplacian 1:\n " << laplacian << std::endl << std::endl;
597#endif
598 // .3 D^(-1/2)
599 Eigen::VectorXf diag_halfinv(weights.rows());
600 for (int i = 0; i < weights.rows(); ++i) {
601 diag_halfinv.coeffRef(i) = static_cast<float>(1.0 / std::sqrt(diag.coeffRef(i)));
602 }
603 // .4 normalized laplacian D^(-1/2) * L * D^(-1/2)
604 for (int i = 0; i < laplacian.rows(); ++i) {
605 laplacian.row(i) *= diag_halfinv.coeffRef(i);
606 }
607 for (int j = 0; j < laplacian.cols(); ++j) {
608 laplacian.col(j) *= diag_halfinv.coeffRef(j);
609 }
610 // .4.2 for numerical stability, add eps to the diagonal of laplacian
611 float eps = 1e-10f;
612 for (int i = 0; i < laplacian.rows(); ++i) {
613 laplacian.coeffRef(i, i) += eps;
614 }
615#ifdef DEBUG_NCUT
616// std::cout << "laplacian 2:\n " << laplacian << std::endl << std::endl;
617#endif
618 // .5 solve eigen decompostion: TODO: lanczos
619 Eigen::EigenSolver<MatrixXf> eig_solver(laplacian);
620#ifdef DEBUG_NCUT
621// std::cout << "eigvec 1:\n " << eig_solver.eigenvectors() << std::endl <<
622// std::endl;
623#endif
624 // .6 sort eigen values
625 std::vector<std::pair<float, int>> eigval(laplacian.rows());
626 for (size_t i = 0; i < eigval.size(); ++i) {
627 eigval[i] = std::make_pair(eig_solver.eigenvalues()[i].real(), i);
628 }
629 std::sort(eigval.begin(), eigval.end(), std::less<std::pair<float, int>>());
630 // .7 get sorted eigen vectors
631 eigenvectors.resize(weights.rows(), weights.cols());
632 for (int i = 0; i < eigenvectors.cols(); ++i) {
633 eigenvectors.col(i) = eig_solver.eigenvectors().col(eigval[i].second).real();
634 }
635 for (int i = 0; i < eigenvectors.rows(); ++i) {
636 eigenvectors.row(i) *= diag_halfinv.coeffRef(i);
637 }
638}
639
640bool NCut::ComputeSquaredSkeletonDistance(
641 const Eigen::MatrixXf &in1_points,
642 const Eigen::MatrixXf &in1_features,
643 const Eigen::MatrixXf &in2_points,
644 const Eigen::MatrixXf &in2_features,
645 float *dist_point,
646 float *dist_feature) {
647 if (!((in1_points.rows() == in1_features.rows()) && (in2_points.rows() == in2_features.rows()))) {
648 return false;
649 }
650 const int num1 = static_cast<int>(in1_points.rows());
651 const int num2 = static_cast<int>(in2_points.rows());
652 const int dim = static_cast<int>(in1_features.cols());
653 int min_index1 = -1;
654 int min_index2 = -1;
655 float min_dist = std::numeric_limits<float>::max();
656 for (int i = 0; i < num1; ++i) {
657 for (int j = 0; j < num2; ++j) {
658 const float diff_x = in1_points.coeffRef(i, 0) - in2_points.coeffRef(j, 0);
659 const float diff_y = in1_points.coeffRef(i, 1) - in2_points.coeffRef(j, 1);
660 const float diff_z = in1_points.coeffRef(i, 2) - in2_points.coeffRef(j, 2);
661 float dist = diff_x * diff_x + diff_y * diff_y + diff_z * diff_z;
662 if (dist < min_dist) {
663 min_dist = dist;
664 min_index1 = i;
665 min_index2 = j;
666 }
667 }
668 }
669 *dist_point = min_dist;
670 *dist_feature = 0.f;
671 for (int i = 0; i < dim; ++i) {
672 float diff = in1_features.coeffRef(min_index1, i) - in2_features.coeffRef(min_index2, i);
673 *dist_feature += diff * diff;
674 }
675 return true;
676}
677
678bool NCut::IsMovableObstacle(const std::vector<int> &cluster_ids, std::string *label) {
679 NcutBoundingBox box;
680 GetComponentBoundingBox(cluster_ids, &box);
681 float dummy_length = GetBboxLength(box);
682 float dummy_width = GetBboxWidth(box);
683 float length = std::max(dummy_length, dummy_width);
684 float width = std::min(dummy_length, dummy_width);
685 if (IsPotentialPedestrianSize(length, width) || IsPotentialBicyclistSize(length, width)
686 || IsPotentialCarSize(length, width)) {
687 *label = GetClustersLabel(cluster_ids);
688 if (*label != "unknown" || *label != "background") {
689 return true;
690 }
691 }
692 return false;
693}
694
695std::string NCut::GetClustersLabel(const std::vector<int> &cluster_ids) {
696 std::vector<int> point_ids;
697 GetClustersPids(cluster_ids, &point_ids);
698 base::PointFCloudPtr cloud = base::PointFCloudPtr(new base::PointFCloud(*_cloud_obstacles, point_ids));
699 return GetPcLabel(cloud);
700}
701
702void NCut::GetClustersPids(const std::vector<int> &cids, std::vector<int> *pids_in) {
703 std::vector<int> &pids = *pids_in;
704 int num_points = 0;
705 for (size_t i = 0; i < cids.size(); ++i) {
706 num_points += static_cast<int>(_cluster_points[cids[i]].size());
707 }
708 pids.resize(num_points, -1);
709 int offset = 0;
710 for (size_t i = 0; i < cids.size(); ++i) {
711 const std::vector<int> &curr_pids = _cluster_points[cids[i]];
712 memcpy(pids.data() + offset, curr_pids.data(), sizeof(int) * curr_pids.size());
713 offset += static_cast<int>(curr_pids.size());
714 }
715}
716
717int NCut::GetComponentBoundingBox(const std::vector<int> &cluster_ids, NcutBoundingBox *box_in) {
718 NcutBoundingBox &box = *box_in;
719 if (cluster_ids.empty()) {
720 return 0;
721 }
722 int cid = cluster_ids[0];
723 float x_min = std::get<0>(_cluster_bounding_box[cid]);
724 float x_max = std::get<1>(_cluster_bounding_box[cid]);
725 float y_min = std::get<2>(_cluster_bounding_box[cid]);
726 float y_max = std::get<3>(_cluster_bounding_box[cid]);
727 float z_min = std::get<4>(_cluster_bounding_box[cid]);
728 float z_max = std::get<5>(_cluster_bounding_box[cid]);
729 int num_points = static_cast<int>(_cluster_points[cid].size());
730 for (size_t i = 1; i < cluster_ids.size(); ++i) {
731 cid = cluster_ids[i];
732 x_min = std::min(x_min, std::get<0>(_cluster_bounding_box[cid]));
733 x_max = std::max(x_max, std::get<1>(_cluster_bounding_box[cid]));
734 y_min = std::min(y_min, std::get<2>(_cluster_bounding_box[cid]));
735 y_max = std::max(y_max, std::get<3>(_cluster_bounding_box[cid]));
736 z_min = std::min(y_min, std::get<4>(_cluster_bounding_box[cid]));
737 z_max = std::max(y_max, std::get<5>(_cluster_bounding_box[cid]));
738 num_points += static_cast<int>(_cluster_points[cid].size());
739 }
740 std::get<0>(box) = x_min;
741 std::get<1>(box) = x_max;
742 std::get<2>(box) = y_min;
743 std::get<3>(box) = y_max;
744 std::get<4>(box) = z_min;
745 std::get<5>(box) = z_max;
746 return num_points;
747}
748
749std::string NCut::GetPcRoughLabel(const base::PointFCloudPtr &cloud, bool only_check_pedestrian) {
750 if (cloud->size() < OBSTACLE_MINIMUM_NUM_POINTS) {
751 return "unknown";
752 }
753 float x_max = -std::numeric_limits<float>::max();
754 float y_max = -std::numeric_limits<float>::max();
755 float x_min = std::numeric_limits<float>::max();
756 float y_min = std::numeric_limits<float>::max();
757 for (size_t i = 0; i < cloud->size(); ++i) {
758 base::PointF pt = (*cloud)[i];
759 x_min = std::min(x_min, pt.x);
760 x_max = std::max(x_max, pt.x);
761 y_min = std::min(y_min, pt.y);
762 y_max = std::max(y_max, pt.y);
763 }
764 float dummy_length = x_max - x_min;
765 float dummy_width = y_max - y_min;
766 float length = std::max(dummy_length, dummy_width);
767 float width = std::min(dummy_length, dummy_width);
768 std::string label = "unknown";
769 bool is_candidate = false;
770 if (only_check_pedestrian) {
771 if (IsPotentialPedestrianSize(length, width) || IsPotentialBicyclistSize(length, width)) {
772 is_candidate = true;
773 }
774 } else {
775 if (IsPotentialPedestrianSize(length, width) || IsPotentialBicyclistSize(length, width)
776 || IsPotentialCarSize(length, width)) {
777 is_candidate = true;
778 }
779 }
780 if (is_candidate) {
781 label = GetPcLabel(cloud);
782 }
783 return label;
784}
785
786void NCut::GetSegmentRoughSize(const base::PointFCloudPtr &cloud, float *length, float *width, float *height) {
787 float x_max = -std::numeric_limits<float>::max();
788 float y_max = -std::numeric_limits<float>::max();
789 float z_max = -std::numeric_limits<float>::max();
790 float x_min = std::numeric_limits<float>::max();
791 float y_min = std::numeric_limits<float>::max();
792 float z_min = std::numeric_limits<float>::max();
793 for (size_t i = 0; i < cloud->size(); ++i) {
794 base::PointF pt = (*cloud)[i];
795 x_min = std::min(x_min, pt.x);
796 x_max = std::max(x_max, pt.x);
797 y_min = std::min(y_min, pt.y);
798 y_max = std::max(y_max, pt.y);
799 z_min = std::min(z_min, pt.z);
800 z_max = std::max(z_max, pt.z);
801 }
802 *length = (x_max - x_min);
803 *width = (y_max - y_min);
804 *height = (z_max - z_min);
805}
806
807} // namespace lidar
808} // namespace perception
809} // namespace apollo
void SetGridRadius(float grid_radius)
Set the grid radius
Definition flood_fill.h:73
bool Pos2d(float x, float y, int *irow, int *jcol) const
Get row and col position
Definition flood_fill.cc:43
void SetCellSize(float cell_size)
Set the cell size
Definition flood_fill.h:81
void BuildGrid(base::PointFCloudConstPtr cloud)
Build grid based on point cloud
Definition flood_fill.cc:55
std::string GetPcRoughLabel(const base::PointFCloudPtr &cloud, bool only_check_pedestrian)
Get the label of point cloud
Definition ncut.cc:749
void GetSegmentRoughSize(const base::PointFCloudPtr &cloud, float *length, float *width, float *height)
Get the segment size
Definition ncut.cc:786
bool Init(const NCutParam &param)
Init Ncut according to parameters
Definition ncut.cc:53
void Segment(base::PointFCloudConstPtr cloud)
Segmen main entrance
Definition ncut.cc:87
#define ADEBUG
Definition log.h:41
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
bool GetProtoFromFile(const std::string &file_name, google::protobuf::Message *message)
Parses the content of the file specified by the file_name as a representation of protobufs,...
Definition file.cc:132
std::string GetAbsolutePath(const std::string &prefix, const std::string &relative_path)
Get absolute path by concatenating prefix and relative_path.
Definition file.cc:179
uint32_t width
Width of point cloud
Point< float > PointF
Definition point.h:56
AttributePointCloud< PointF > PointFCloud
std::shared_ptr< PointFCloud > PointFCloudPtr
std::shared_ptr< const PointFCloud > PointFCloudConstPtr
double OrientCloud(const PointFCloud &pc, PointFCloud *pc_out, bool demean)
float diff(Image< float > *I, int x1, int y1, int x2, int y2)
Dissimilarity measure between pixels
Image< float > * laplacian(Image< float > *src)
Compute laplacian
Definition filter.h:115
class register implement
Definition arena_queue.h:37