Apollo 11.0
自动驾驶开放平台
ncut_segmentation.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 <limits>
21#include <map>
22
23#include <omp.h>
24
25#include "cyber/common/file.h"
26#include "cyber/common/log.h"
29
30namespace apollo {
31namespace perception {
32namespace lidar {
33
35using base::ObjectPtr;
36
38 std::string param_file = GetConfigFile(options.config_path, options.config_file);
39 AINFO << "-- param_file: " << param_file;
40
41 if (!Configure(param_file)) {
42 AERROR << "failed to load ncut config.";
43 return false;
44 }
45
46 _outliers.reset(new std::vector<ObjectPtr>);
47 if (!_outliers) {
48 AERROR << "Failed to reset outliers.";
49 return false;
50 }
51#pragma omp parallel
52 { num_threads_ = omp_get_num_threads(); }
53
54 AINFO << "number threads " << num_threads_;
55 _segmentors.resize(num_threads_);
56 for (int i = 0; i < num_threads_; ++i) {
57 _segmentors[i].reset(new NCut);
58 if (!(_segmentors[i]->Init(ncut_param_))) {
59 AERROR << "failed to init NormalizedCut " << i << ".";
60 return false;
61 }
62 }
63
64 roi_cloud_ = base::PointFCloudPool::Instance().Get();
65 roi_world_cloud_ = base::PointDCloudPool::Instance().Get();
66
67#ifdef DEBUG_NCUT
68 _viewer = pcl::visualization::PCLVisualizer::Ptr(new pcl::visualization::PCLVisualizer("3D Viewer"));
69 _viewer->setBackgroundColor(0, 0, 0);
70 _viewer->addCoordinateSystem(1.0);
71 _viewer->initCameraParameters();
72 _viewer_count = 0;
73 _rgb_cloud = CPointCloudPtr(new CPointCloud);
74#endif
75
76 AINFO << "NCutSegmentation init success, num_threads: " << num_threads_;
77 return true;
78}
79
80bool NCutSegmentation::Configure(std::string param_file) {
81 NCutSegmentationParam seg_param_;
82 // get cnnseg params
83 ACHECK(GetProtoFromFile(param_file, &seg_param_)) << "Failed to parse CNNSegParam config file." << param_file;
84 grid_radius_ = seg_param_.grid_radius();
85 height_threshold_ = seg_param_.height_threshold();
86 partition_cell_size_ = seg_param_.partition_cell_size();
87 vehicle_filter_cell_size_ = seg_param_.vehicle_filter_cell_size();
88 pedestrian_filter_cell_size_ = seg_param_.pedestrian_filter_cell_size();
89 outlier_length_ = seg_param_.outlier_length();
90 outlier_width_ = seg_param_.outlier_width();
91 outlier_height_ = seg_param_.outlier_height();
92 outlier_min_num_points_ = seg_param_.outlier_min_num_points();
93 remove_ground_ = seg_param_.remove_ground_points();
94 remove_roi_ = seg_param_.remove_roi();
95 ncut_param_ = seg_param_.ncut_param();
96 do_classification_ = seg_param_.do_classification();
97 AINFO << "NCut Segmentation " << seg_param_.DebugString();
98 return true;
99}
100
102 // check input
103 if (frame == nullptr) {
104 AERROR << "Input null frame ptr.";
105 return false;
106 }
107 if (frame->cloud == nullptr) {
108 AERROR << "Input null frame cloud.";
109 return false;
110 }
111 if (frame->world_cloud == nullptr) {
112 AERROR << "Input null frame world cloud.";
113 return false;
114 }
115 if (frame->cloud->size() == 0) {
116 AERROR << "Input none points.";
117 return false;
118 }
119 if (frame->cloud->size() != frame->world_cloud->size()) {
120 AERROR << "Cloud size and world cloud size not consistent.";
121 return false;
122 }
123
124 // record input cloud and lidar frame
125 original_cloud_ = frame->cloud;
126 original_world_cloud_ = frame->world_cloud;
127 lidar_frame_ref_ = frame;
128
129 std::vector<base::ObjectPtr>* segments = &(frame->segmented_objects);
130 double start_t = omp_get_wtime();
131
132 AINFO << "input point cloud: " << original_cloud_->size() << " points";
133#ifdef DEBUG_NCUT
134 VisualizePointCloud(original_cloud_);
135#endif
136
137 base::PointFCloudPtr cloud_above_ground(new base::PointFCloud);
138 if (remove_ground_) {
139 AINFO << "remove ground";
140 CloudMask mask;
141 mask.Set(original_cloud_->size(), 0);
142 mask.AddIndicesOfIndices(lidar_frame_ref_->roi_indices, lidar_frame_ref_->non_ground_indices, 1);
143 mask.GetValidIndices(&lidar_frame_ref_->secondary_indices);
144 }
145
146 // if used as secondary segmentor, got from cloud directly
147 cloud_above_ground->CopyPointCloud(*lidar_frame_ref_->cloud, lidar_frame_ref_->secondary_indices);
148
149#ifdef DEBUG_NCUT
150 AINFO << "filter ground, elapsed time: " << omp_get_wtime() - start_t << cloud_above_ground->size()
151 << " points left";
152 start_t = omp_get_wtime();
153 VisualizePointCloud(cloud_above_ground);
154#endif
155
156 // .3 filter vehicle
157 base::PointFCloudPtr cloud_after_car_filter = cloud_above_ground;
158 if (filter_vehicle_) {
159 ObstacleFilter(cloud_above_ground, vehicle_filter_cell_size_, false, &cloud_after_car_filter, segments);
160 }
161#ifdef DEBUG_NCUT
162 AINFO << "filter vehicle, elapsed time: " << omp_get_wtime() - start_t
163 << "filter vehicle: " << cloud_after_car_filter->size() << " points left";
164 start_t = omp_get_wtime();
165 VisualizePointCloud(cloud_after_car_filter);
166#endif
167
168 // .4 filter pedestrian
169 base::PointFCloudPtr cloud_after_people_filter = cloud_after_car_filter;
170 if (filter_pedestrian_) {
171 ObstacleFilter(
172 cloud_after_car_filter, pedestrian_filter_cell_size_, true, &cloud_after_people_filter, segments);
173 }
174
175#ifdef DEBUG_NCUT
176 AINFO << "filter pedestrian, elapsed time: " << omp_get_wtime() - start_t;
177 start_t = omp_get_wtime();
178 AINFO << "filter pedestrian: " << cloud_after_people_filter->size() << " points left";
179 VisualizePointCloud(cloud_after_people_filter);
180 AINFO << "after filter car/pedestrian #segments " << segments->size();
181// VisualizeSegments(*segments);
182#endif
183
184 // .5 partition into small regions
185 std::vector<base::PointFCloudPtr> cloud_components;
186 PartitionConnectedComponents(cloud_after_people_filter, partition_cell_size_, &cloud_components);
187
188#ifdef DEBUG_NCUT
189 AINFO << "partition small regions, elapsed time: " << omp_get_wtime() - start_t;
190 start_t = omp_get_wtime();
191 AINFO << "partition " << cloud_components.size() << " components";
192#endif
193
194 std::vector<bool> cloud_outlier_flag(cloud_components.size());
195
196#pragma omp parallel
197 {
198#pragma omp for
199 for (size_t i = 0; i < cloud_components.size(); ++i) {
200 cloud_outlier_flag[i] = IsOutlier(cloud_components[i]);
201 }
202 }
203
204 std::vector<int> cloud_outlier;
205 std::vector<int> cloud_tbd;
206 for (int i = 0; i < static_cast<int>(cloud_components.size()); ++i) {
207 if (cloud_outlier_flag[i]) {
208 cloud_outlier.push_back(i);
209 } else {
210 cloud_tbd.push_back(i);
211 }
212 }
213
214 // .5.1 outlier
215 for (size_t i = 0; i < cloud_outlier.size(); ++i) {
216 base::PointFCloudPtr pc = cloud_components[cloud_outlier[i]];
218 obj->lidar_supplement.cloud = *pc;
219 _outliers->push_back(obj);
220 }
221#ifdef DEBUG_NCUT
222 AINFO << "filter outlier, elapsed time: " << omp_get_wtime() - start_t;
223 start_t = omp_get_wtime();
224#endif
225 // .6 graph cut each
226 std::vector<std::vector<base::PointFCloudPtr>> threads_segment_pcs(num_threads_);
227 std::vector<std::vector<std::string>> threads_segment_labels(num_threads_);
228 std::vector<std::vector<base::PointFCloudPtr>> threads_outlier_pcs(num_threads_);
229 // .6.1 process each component in parallel
230 omp_set_num_threads(num_threads_);
231#pragma omp parallel
232 {
233 int tid = omp_get_thread_num();
234 std::shared_ptr<NCut> my_ncut = _segmentors[tid];
235 std::vector<base::PointFCloudPtr>& my_segment_pcs = threads_segment_pcs[tid];
236 std::vector<std::string>& my_segment_labels = threads_segment_labels[tid];
237 std::vector<base::PointFCloudPtr>& my_outlier_pcs = threads_outlier_pcs[tid];
238
239#pragma omp for schedule(guided)
240 for (size_t i = 0; i < cloud_tbd.size(); ++i) {
241 my_ncut->Segment(cloud_components[cloud_tbd[i]]);
242 ADEBUG << "after segment with num segments" << my_ncut->NumSegments();
243 for (int j = 0; j < my_ncut->NumSegments(); ++j) {
244 base::PointFCloudPtr pc = my_ncut->GetSegmentPointCloud(j);
245 std::string label = my_ncut->GetSegmentLabel(j);
246 if (IsOutlier(pc)) {
247 my_outlier_pcs.push_back(pc);
248 } else {
249 my_segment_pcs.push_back(pc);
250 my_segment_labels.push_back(label);
251 }
252 }
253 }
254 }
255#ifdef DEBUG_NCUT
256 ADEBUG << "parallel normalized cut, elapsed time: " << omp_get_wtime() - start_t;
257 start_t = omp_get_wtime();
258#endif
259 // .6.2 aggregate results
260 std::vector<int> segment_offset(num_threads_, static_cast<int>(segments->size()));
261 for (int i = 1; i < num_threads_; ++i) {
262 segment_offset[i] = segment_offset[i - 1] + static_cast<int>(threads_segment_pcs[i - 1].size());
263 }
264 int new_num_segments
265 = static_cast<int>(threads_segment_pcs[num_threads_ - 1].size()) + segment_offset[num_threads_ - 1];
266 segments->resize(new_num_segments);
267#pragma omp parallel for
268 for (int i = 0; i < num_threads_; ++i) {
269 int offset = segment_offset[i];
270 for (size_t j = 0; j < threads_segment_pcs[i].size(); ++j) {
271 base::ObjectPtr& obj_ptr = (*segments)[offset + j];
272 obj_ptr.reset(new base::Object());
273 obj_ptr->lidar_supplement.cloud = *threads_segment_pcs[i][j];
274
275 if (do_classification_) {
276 obj_ptr->type = Label2Type(threads_segment_labels[i][j]);
277 }
278 }
279 }
280 std::vector<int> outlier_offset(num_threads_, static_cast<int>(_outliers->size()));
281 for (int i = 1; i < num_threads_; ++i) {
282 outlier_offset[i] = outlier_offset[i - 1] + static_cast<int>(threads_outlier_pcs[i - 1].size());
283 }
284 int new_num_outliers
285 = static_cast<int>(threads_outlier_pcs[num_threads_ - 1].size()) + outlier_offset[num_threads_ - 1];
286 _outliers->resize(new_num_outliers);
287#pragma omp parallel for
288 for (int i = 0; i < num_threads_; ++i) {
289 int offset = outlier_offset[i];
290 for (size_t j = 0; j < threads_outlier_pcs[i].size(); ++j) {
291 base::ObjectPtr& obj_ptr = (*_outliers)[offset + j];
292 obj_ptr.reset(new base::Object);
293 obj_ptr->lidar_supplement.cloud = *threads_outlier_pcs[i][j];
294 }
295 }
296 AINFO << "aggregate results, elapsed time: " << omp_get_wtime() - start_t;
297 AINFO << "final #segments " << segments->size();
298 AINFO << "final #outliers " << _outliers->size();
299
300#ifdef DEBUG_NCUT
301 VisualizeSegments(*segments);
302 VisualizeSegments(*_outliers);
303#endif
304
305 return true;
306}
307
308base::ObjectType NCutSegmentation::Label2Type(const std::string& label) {
309 if (label == "unknown") {
311 }
312 if (label == "nonMot") {
314 }
315 if (label == "pedestrian") {
317 }
318 if (label == "smallMot") {
320 }
322}
323
324void NCutSegmentation::PartitionConnectedComponents(
325 const base::PointFCloudPtr& in_cloud,
326 float cell_size,
327 std::vector<base::PointFCloudPtr>* out_clouds) {
328 std::vector<base::PointFCloudPtr>& temp_clouds = *out_clouds;
329 FloodFill FFfilter(grid_radius_, cell_size);
330 std::vector<std::vector<int>> component_points;
331 std::vector<int> num_cells_per_components;
332 FFfilter.GetSegments(in_cloud, &component_points, &num_cells_per_components);
333 temp_clouds.resize(component_points.size());
334 for (size_t i = 0; i < component_points.size(); ++i) {
335 temp_clouds[i] = base::PointFCloudPtr(new base::PointFCloud(*in_cloud, component_points[i]));
336 }
337}
338
339void NCutSegmentation::ObstacleFilter(
340 const base::PointFCloudPtr& in_cloud,
341 float cell_size,
342 bool filter_pedestrian_only,
343 base::PointFCloudPtr* out_cloud,
344 std::vector<base::ObjectPtr>* segments) {
345 FloodFill FFfilter(grid_radius_, cell_size);
346 std::vector<std::vector<int>> component_points;
347 std::vector<int> num_cells_per_components;
348 FFfilter.GetSegments(in_cloud, &component_points, &num_cells_per_components);
349
350#ifdef DEBUG_NCUT
351 AINFO << "flood fill: " << component_points.size() << " components";
352// VisualizeComponents(in_cloud, component_points);
353#endif
354
355 const unsigned int min_num_points = 50;
356 const int num_components = static_cast<int>(component_points.size());
357 std::vector<std::string> component_labels(num_components, "unknown");
358 int tid = 0;
359 for (int i = 0; i < num_components; ++i) {
360 if (component_points[i].size() > min_num_points) {
361 base::PointFCloudPtr pc = base::PointFCloudPtr(new base::PointFCloud(*in_cloud, component_points[i]));
362 std::string label = _segmentors[tid]->GetPcRoughLabel(pc, filter_pedestrian_only);
363 AINFO << "before: component id: " << i << ", label: " << label;
364 if (filter_pedestrian_only) {
365 label = (label == "pedestrian" || label == "nonMot") ? label : "unknown";
366 }
367 AINFO << "after: component id: " << i << ", label: " << label;
368 component_labels[i] = label;
369 }
370 } // end of for
371
372 AINFO << "classification done";
373 std::vector<int> remaining_pids;
374 std::vector<int> obstacle_components;
375 for (int i = 0; i < num_components; ++i) {
376 if (component_labels[i] != "unknown") {
377 obstacle_components.push_back(i);
378 } else {
379 remaining_pids.insert(remaining_pids.begin(), component_points[i].begin(), component_points[i].end());
380 }
381 }
382 AINFO << "obstacle_filter: filter unknown out, obstacle_components.size = " << obstacle_components.size()
383 << ", remaining_pids.size = " << remaining_pids.size();
384 int offset = static_cast<int>(segments->size());
385 segments->resize(offset + obstacle_components.size());
386 for (size_t i = 0; i < obstacle_components.size(); ++i) {
387 int cid = obstacle_components[i];
388 ObjectPtr& object_ptr = (*segments)[offset + i];
389 object_ptr.reset(new base::Object);
390 object_ptr->lidar_supplement.cloud.CopyPointCloud(*in_cloud, component_points[cid]);
391 }
392 *out_cloud = base::PointFCloudPtr(new base::PointFCloud(*in_cloud, remaining_pids));
393}
394
395bool NCutSegmentation::IsOutlier(const base::PointFCloudPtr& in_cloud) {
396 size_t min_num_points = std::max(outlier_min_num_points_, 1);
397 if (in_cloud->size() < min_num_points) {
398 return true;
399 }
400 float x_max = -std::numeric_limits<float>::max();
401 float y_max = -std::numeric_limits<float>::max();
402 float z_max = -std::numeric_limits<float>::max();
403 float x_min = std::numeric_limits<float>::max();
404 float y_min = std::numeric_limits<float>::max();
405 float z_min = std::numeric_limits<float>::max();
406 base::PointF pt_max = (*in_cloud)[0];
407 for (size_t i = 0; i < in_cloud->size(); ++i) {
408 const base::PointF& pt = (*in_cloud)[i];
409 x_min = std::min(x_min, pt.x);
410 x_max = std::max(x_max, pt.x);
411 y_min = std::min(y_min, pt.y);
412 y_max = std::max(y_max, pt.y);
413 z_min = std::min(z_min, pt.z);
414 if (pt.z > z_max) {
415 z_max = pt.z;
416 pt_max = pt;
417 }
418 }
419 float length = x_max - x_min;
420 float width = y_max - y_min;
421 float height = z_max - z_min;
422 if (length < outlier_length_ && width < outlier_width_) {
423 return true;
424 }
425 if (height < outlier_height_) {
426 return true;
427 }
428 // std::pair<float, bool> dist = _ground_detector.distance_to_ground(pt_max);
429 // if (dist.second && dist.first < _outlier_height) {
430 // return true;
431 //}
432 return false;
433}
434
435#ifdef DEBUG_NCUT
436void NCutSegmentation::VisualizePointCloud(const base::PointFCloudPtr& cloud) {
437 // _viewer->removePointCloud(_viewer_id, 0);
438 _viewer->removeAllPointClouds(0);
439 _viewer->removeAllShapes(0);
440 _rgb_cloud->clear();
441 for (size_t i = 0; i < cloud->size(); ++i) {
442 CPoint pt;
443 pt.x = (*cloud)[i].x;
444 pt.y = (*cloud)[i].y;
445 pt.z = (*cloud)[i].z;
446 pt.r = 255;
447 pt.g = 255;
448 pt.b = 255;
449 _rgb_cloud->push_back(pt);
450 }
451 snprintf(_viewer_id, sizeof(_viewer_id), "vis%06d", _viewer_count++);
452 _viewer->addPointCloud(_rgb_cloud, _viewer_id, 0);
453 _viewer->spin();
454}
455
456void NCutSegmentation::VisualizeSegments(const std::vector<base::ObjectPtr>& segments) {
457 // _viewer->removePointCloud(_viewer_id, 0);
458 unsigned int seed = 0;
459 _viewer->removeAllPointClouds(0);
460 _viewer->removeAllShapes(0);
461 _rgb_cloud->clear();
462 for (size_t i = 0; i < segments.size(); ++i) {
463 int red = 50 + rand_r(&seed) % 206;
464 int green = 50 + rand_r(&seed) % 206;
465 int blue = 50 + rand_r(&seed) % 206;
466 const base::PointFCloud& pc = segments[i]->lidar_supplement.cloud;
467 for (size_t j = 0; j < pc.size(); ++j) {
468 CPoint pt;
469 pt.x = pc[j].x;
470 pt.y = pc[j].y;
471 pt.z = pc[j].z;
472 pt.r = static_cast<uint8_t>(red);
473 pt.g = static_cast<uint8_t>(green);
474 pt.b = static_cast<uint8_t>(blue);
475 _rgb_cloud->push_back(pt);
476 }
477 }
478 snprintf(_viewer_id, sizeof(_viewer_id), "vis%06d", _viewer_count++);
479 _viewer->addPointCloud(_rgb_cloud, _viewer_id, 0);
480 _viewer->spin();
481}
482
483void NCutSegmentation::VisualizeComponents(
484 const base::PointFCloudPtr& cloud,
485 const std::vector<std::vector<int>>& component_points) {
486 // _viewer->removePointCloud(_viewer_id, 0);
487 unsigned int seed = 0;
488 _viewer->removeAllPointClouds(0);
489 _viewer->removeAllShapes(0);
490 _rgb_cloud->clear();
491 std::vector<CPoint> centers(component_points.size());
492 for (size_t i = 0; i < component_points.size(); ++i) {
493 int red = 50 + rand_r(&seed) % 206;
494 int green = 50 + rand_r(&seed) % 206;
495 int blue = 50 + rand_r(&seed) % 206;
496 const int num_points = static_cast<int>(component_points[i].size());
497 CPoint center;
498 center.x = 0.f;
499 center.y = 0.f;
500 center.z = 0.f;
501 for (size_t j = 0; j < component_points[i].size(); ++j) {
502 CPoint pt;
503 int pid = component_points[i][j];
504 pt.x = (*cloud)[pid].x;
505 pt.y = (*cloud)[pid].y;
506 pt.z = (*cloud)[pid].z;
507 pt.r = static_cast<uint8_t>(red);
508 pt.g = static_cast<uint8_t>(green);
509 pt.b = static_cast<uint8_t>(blue);
510 _rgb_cloud->push_back(pt);
511 center.x += pt.x;
512 center.y += pt.y;
513 center.z += pt.z;
514 }
515 center.x /= static_cast<float>(num_points);
516 center.y /= static_cast<float>(num_points);
517 center.z /= static_cast<float>(num_points);
518 centers[i] = center;
519 }
520 snprintf(_viewer_id, sizeof(_viewer_id), "vis%06d", _viewer_count++);
521 _viewer->addPointCloud(_rgb_cloud, _viewer_id, 0);
522 for (size_t i = 0; i < component_points.size(); ++i) {
523 char text[256];
524 char text_id[256];
525 snprintf(text, sizeof(text), "%zu", i);
526 snprintf(text_id, sizeof(text_id), "c%zu", i);
527 _viewer->addText3D(text, centers[i], 0.3, 1.0, 1.0, 1.0, text_id, 0);
528 }
529 _viewer->spin();
530}
531#endif
532
534
535} // namespace lidar
536} // namespace perception
537} // namespace apollo
#define PERCEPTION_REGISTER_LIDARDETECTOR(name)
void GetValidIndices(base::PointIndices *indices)
Definition cloud_mask.cc:50
void AddIndicesOfIndices(const base::PointIndices &indices, const base::PointIndices &indices_of_indices, int value=1)
Definition cloud_mask.cc:70
void Set(size_t size, int init_value)
Definition cloud_mask.h:34
bool Init(const LidarDetectorInitOptions &options=LidarDetectorInitOptions()) override
Init ncut segmentation
bool Detect(const LidarDetectorOptions &options, LidarFrame *frame) override
Detection function of ncut
#define ACHECK(cond)
Definition log.h:80
#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
uint32_t height
Height of point cloud
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< Object > ObjectPtr
Definition object.h:127
pcl::PointXYZRGB CPoint
Definition pcl_util.h:32
pcl::PointCloud< CPoint > CPointCloud
Definition pcl_util.h:33
std::shared_ptr< apollo::perception::base::Object > ObjectPtr
pcl::PointCloud< CPoint >::Ptr CPointCloudPtr
Definition pcl_util.h:34
std::string GetConfigFile(const std::string &config_path, const std::string &config_file)
Definition util.cc:80
class register implement
Definition arena_queue.h:37
std::shared_ptr< base::AttributePointCloud< base::PointD > > world_cloud
Definition lidar_frame.h:37
std::vector< std::shared_ptr< base::Object > > segmented_objects
Definition lidar_frame.h:53
std::shared_ptr< base::AttributePointCloud< base::PointF > > cloud
Definition lidar_frame.h:35