39 AINFO <<
"-- param_file: " << param_file;
41 if (!Configure(param_file)) {
42 AERROR <<
"failed to load ncut config.";
46 _outliers.reset(
new std::vector<ObjectPtr>);
48 AERROR <<
"Failed to reset outliers.";
52 { num_threads_ = omp_get_num_threads(); }
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 <<
".";
64 roi_cloud_ = base::PointFCloudPool::Instance().Get();
65 roi_world_cloud_ = base::PointDCloudPool::Instance().Get();
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();
76 AINFO <<
"NCutSegmentation init success, num_threads: " << num_threads_;
80bool NCutSegmentation::Configure(std::string param_file) {
83 ACHECK(GetProtoFromFile(param_file, &seg_param_)) <<
"Failed to parse CNNSegParam config file." << param_file;
97 AINFO <<
"NCut Segmentation " << seg_param_.DebugString();
103 if (frame ==
nullptr) {
104 AERROR <<
"Input null frame ptr.";
107 if (frame->
cloud ==
nullptr) {
108 AERROR <<
"Input null frame cloud.";
112 AERROR <<
"Input null frame world cloud.";
115 if (frame->
cloud->size() == 0) {
116 AERROR <<
"Input none points.";
120 AERROR <<
"Cloud size and world cloud size not consistent.";
125 original_cloud_ = frame->
cloud;
127 lidar_frame_ref_ = frame;
130 double start_t = omp_get_wtime();
132 AINFO <<
"input point cloud: " << original_cloud_->size() <<
" points";
134 VisualizePointCloud(original_cloud_);
138 if (remove_ground_) {
139 AINFO <<
"remove ground";
141 mask.
Set(original_cloud_->size(), 0);
150 AINFO <<
"filter ground, elapsed time: " << omp_get_wtime() - start_t << cloud_above_ground->size()
152 start_t = omp_get_wtime();
153 VisualizePointCloud(cloud_above_ground);
158 if (filter_vehicle_) {
159 ObstacleFilter(cloud_above_ground, vehicle_filter_cell_size_,
false, &cloud_after_car_filter, segments);
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);
170 if (filter_pedestrian_) {
172 cloud_after_car_filter, pedestrian_filter_cell_size_,
true, &cloud_after_people_filter, segments);
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();
185 std::vector<base::PointFCloudPtr> cloud_components;
186 PartitionConnectedComponents(cloud_after_people_filter, partition_cell_size_, &cloud_components);
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";
194 std::vector<bool> cloud_outlier_flag(cloud_components.size());
199 for (
size_t i = 0; i < cloud_components.size(); ++i) {
200 cloud_outlier_flag[i] = IsOutlier(cloud_components[i]);
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);
210 cloud_tbd.push_back(i);
215 for (
size_t i = 0; i < cloud_outlier.size(); ++i) {
218 obj->lidar_supplement.cloud = *pc;
219 _outliers->push_back(obj);
222 AINFO <<
"filter outlier, elapsed time: " << omp_get_wtime() - start_t;
223 start_t = omp_get_wtime();
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_);
230 omp_set_num_threads(num_threads_);
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];
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) {
245 std::string label = my_ncut->GetSegmentLabel(j);
247 my_outlier_pcs.push_back(pc);
249 my_segment_pcs.push_back(pc);
250 my_segment_labels.push_back(label);
256 ADEBUG <<
"parallel normalized cut, elapsed time: " << omp_get_wtime() - start_t;
257 start_t = omp_get_wtime();
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());
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) {
273 obj_ptr->lidar_supplement.cloud = *threads_segment_pcs[i][j];
275 if (do_classification_) {
276 obj_ptr->type = Label2Type(threads_segment_labels[i][j]);
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());
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) {
293 obj_ptr->lidar_supplement.cloud = *threads_outlier_pcs[i][j];
296 AINFO <<
"aggregate results, elapsed time: " << omp_get_wtime() - start_t;
297 AINFO <<
"final #segments " << segments->size();
298 AINFO <<
"final #outliers " << _outliers->size();
301 VisualizeSegments(*segments);
302 VisualizeSegments(*_outliers);
309 if (label ==
"unknown") {
312 if (label ==
"nonMot") {
315 if (label ==
"pedestrian") {
318 if (label ==
"smallMot") {
324void NCutSegmentation::PartitionConnectedComponents(
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) {
339void NCutSegmentation::ObstacleFilter(
342 bool filter_pedestrian_only,
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);
351 AINFO <<
"flood fill: " << component_points.size() <<
" components";
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");
359 for (
int i = 0; i < num_components; ++i) {
360 if (component_points[i].size() > min_num_points) {
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";
367 AINFO <<
"after: component id: " << i <<
", label: " << label;
368 component_labels[i] = label;
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);
379 remaining_pids.insert(remaining_pids.begin(), component_points[i].begin(), component_points[i].end());
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]);
396 size_t min_num_points = std::max(outlier_min_num_points_, 1);
397 if (in_cloud->size() < min_num_points) {
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();
407 for (
size_t i = 0; i < in_cloud->size(); ++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);
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_) {
425 if (height < outlier_height_) {
438 _viewer->removeAllPointClouds(0);
439 _viewer->removeAllShapes(0);
441 for (
size_t i = 0; i < cloud->size(); ++i) {
443 pt.x = (*cloud)[i].x;
444 pt.y = (*cloud)[i].y;
445 pt.z = (*cloud)[i].z;
449 _rgb_cloud->push_back(pt);
451 snprintf(_viewer_id,
sizeof(_viewer_id),
"vis%06d", _viewer_count++);
452 _viewer->addPointCloud(_rgb_cloud, _viewer_id, 0);
456void NCutSegmentation::VisualizeSegments(
const std::vector<base::ObjectPtr>& segments) {
458 unsigned int seed = 0;
459 _viewer->removeAllPointClouds(0);
460 _viewer->removeAllShapes(0);
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;
467 for (
size_t j = 0; j < pc.size(); ++j) {
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);
478 snprintf(_viewer_id,
sizeof(_viewer_id),
"vis%06d", _viewer_count++);
479 _viewer->addPointCloud(_rgb_cloud, _viewer_id, 0);
483void NCutSegmentation::VisualizeComponents(
485 const std::vector<std::vector<int>>& component_points) {
487 unsigned int seed = 0;
488 _viewer->removeAllPointClouds(0);
489 _viewer->removeAllShapes(0);
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());
501 for (
size_t j = 0; j < component_points[i].size(); ++j) {
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);
515 center.x /=
static_cast<float>(num_points);
516 center.y /=
static_cast<float>(num_points);
517 center.z /=
static_cast<float>(num_points);
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) {
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);
#define PERCEPTION_REGISTER_LIDARDETECTOR(name)
void GetValidIndices(base::PointIndices *indices)
void AddIndicesOfIndices(const base::PointIndices &indices, const base::PointIndices &indices_of_indices, int value=1)
void Set(size_t size, int init_value)
bool Init(const LidarDetectorInitOptions &options=LidarDetectorInitOptions()) override
Init ncut segmentation
bool Detect(const LidarDetectorOptions &options, LidarFrame *frame) override
Detection function of ncut
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,...
uint32_t height
Height of point cloud
uint32_t width
Width of point cloud
AttributePointCloud< PointF > PointFCloud
std::shared_ptr< PointFCloud > PointFCloudPtr
std::shared_ptr< Object > ObjectPtr
pcl::PointCloud< CPoint > CPointCloud
std::shared_ptr< apollo::perception::base::Object > ObjectPtr
pcl::PointCloud< CPoint >::Ptr CPointCloudPtr
std::string GetConfigFile(const std::string &config_path, const std::string &config_file)
base::PointIndices roi_indices
std::shared_ptr< base::AttributePointCloud< base::PointD > > world_cloud
base::PointIndices non_ground_indices
std::vector< std::shared_ptr< base::Object > > segmented_objects
base::PointIndices secondary_indices
std::shared_ptr< base::AttributePointCloud< base::PointF > > cloud
optional bool remove_ground_points
optional float grid_radius
optional NCutParam ncut_param
optional float outlier_width
optional float vehicle_filter_cell_size
optional uint32 outlier_min_num_points
optional float outlier_height
optional float pedestrian_filter_cell_size
optional float height_threshold
optional float outlier_length
optional float partition_cell_size
optional bool do_classification