Apollo 11.0
自动驾驶开放平台
cnn_segmentation.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2018 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 *****************************************************************************/
17
18#include <map>
19#include <utility>
20
21#include "cyber/common/file.h"
22#include "cyber/common/log.h"
31
32namespace apollo {
33namespace perception {
34namespace lidar {
35
37using base::AttributePointCloud;
38using base::Object;
39using base::PointF;
40
42 sensor_name_ = options.sensor_name;
43 std::string config_file =
44 GetConfigFile(options.config_path, options.config_file);
45 ACHECK(apollo::cyber::common::GetProtoFromFile(config_file, &model_param_));
46
47 // init feature parameters
48 const cnnseg::FeatureParam& feature_param = model_param_.feature_param();
49 range_ = feature_param.point_cloud_range();
50 width_ = feature_param.width();
51 height_ = feature_param.height();
52 min_height_ = feature_param.min_height();
53 max_height_ = feature_param.max_height();
54
55 const auto& model_info = model_param_.info();
56 std::string model_path = GetModelPath(model_info.name());
57 std::string proto_file =
58 GetModelFile(model_path, model_info.proto_file().file());
59 std::string weight_file =
60 GetModelFile(model_path, model_info.weight_file().file());
61
62 // init inference model
63 std::vector<std::string> input_names =
64 inference::GetBlobNames(model_info.inputs());
65 std::vector<std::string> output_names =
66 inference::GetBlobNames(model_info.outputs());
67
68 inference_.reset(inference::CreateInferenceByName(
69 model_param_.info().framework(), proto_file, weight_file, output_names,
70 input_names));
71 CHECK_NOTNULL(inference_.get());
72
73 gpu_id_ = model_param_.preprocess().has_gpu_id()
74 ? model_param_.preprocess().gpu_id()
75 : -1;
76 BASE_GPU_CHECK(cudaSetDevice(gpu_id_));
77 inference_->set_gpu_id(gpu_id_); // inference sets CPU mode when -1
78
79 std::map<std::string, std::vector<int>> input_shapes;
80 inference::AddShape(&input_shapes, model_info.inputs());
81 auto& input_shape = input_shapes[input_names.at(0)];
82 if (!feature_param.use_intensity_feature()) {
83 input_shape[1] -= 2;
84 }
85 if (!feature_param.use_constant_feature()) {
86 input_shape[1] -= 2;
87 }
88 ACHECK(inference_->Init(input_shapes)) << "Failed to init inference.";
89
90 // init blobs
91 instance_pt_blob_ = inference_->get_blob(output_names.at(0));
92 CHECK_NOTNULL(instance_pt_blob_.get());
93 category_pt_blob_ = inference_->get_blob(output_names.at(1));
94 CHECK_NOTNULL(category_pt_blob_.get());
95 confidence_pt_blob_ = inference_->get_blob(output_names.at(2));
96 CHECK_NOTNULL(confidence_pt_blob_.get());
97 height_pt_blob_ = inference_->get_blob(output_names.at(3));
98 CHECK_NOTNULL(height_pt_blob_.get());
99 feature_blob_ = inference_->get_blob(input_names.at(0));
100 CHECK_NOTNULL(feature_blob_.get());
101 if (model_param_.do_classification()) {
102 classify_pt_blob_ = inference_->get_blob(output_names.at(5));
103 CHECK_NOTNULL(classify_pt_blob_.get());
104 }
105 if (model_param_.do_heading()) {
106 heading_pt_blob_ = inference_->get_blob(output_names.at(4));
107 CHECK_NOTNULL(heading_pt_blob_.get());
108 }
109
110 // init feature generator
111 feature_generator_.reset(new FeatureGenerator);
112 ACHECK(feature_generator_->Init(feature_param, feature_blob_.get()))
113 << "Failed to init feature generator.";
114
115 point2grid_.reserve(kDefaultPointCloudSize);
116
117 // init cluster and background segmentation methods
118 ACHECK(InitClusterAndBackgroundSegmentation());
119
120 return true;
121}
122
123bool CNNSegmentation::InitClusterAndBackgroundSegmentation() {
124 // init spp engine
125 SppParams params;
126 params.height_gap = model_param_.engine_config().height_gap();
127 params.confidence_range = model_param_.confidence_range();
128
129 // init spp data
130 auto& spp_data = spp_engine_.GetSppData();
131 spp_data.instance_pt_blob = instance_pt_blob_.get();
132 spp_data.category_pt_blob = category_pt_blob_.get();
133 spp_data.confidence_pt_blob = confidence_pt_blob_.get();
134
135 spp_data.objectness_threshold = model_param_.objectness_thresh();
136 spp_data.confidence_threshold = model_param_.confidence_thresh();
137 spp_data.top_z_threshold = model_param_.height_thresh();
138 spp_data.class_num = static_cast<size_t>(MetaType::MAX_META_TYPE);
139 if (height_pt_blob_ != nullptr) {
140 spp_data.height_pt_blob = height_pt_blob_.get();
141 }
142 if (model_param_.do_classification()) {
143 spp_data.classify_pt_blob = classify_pt_blob_.get();
144 }
145 if (model_param_.do_heading()) {
146 spp_data.heading_pt_blob = heading_pt_blob_.get();
147 }
148 spp_data.MakeReference(width_, height_, range_);
149
150 // init spp engine
151 spp_engine_.Init(width_, height_, range_, params, sensor_name_);
152
153 roi_cloud_ = base::PointFCloudPool::Instance().Get();
154 roi_world_cloud_ = base::PointDCloudPool::Instance().Get();
155
156 return true;
157}
158
159void CNNSegmentation::MapPointToGrid(
160 const std::shared_ptr<AttributePointCloud<PointF>>& pc_ptr) {
161 float inv_res_x = 0.5f * static_cast<float>(width_) / range_;
162 // float inv_res_y = 0.5 * static_cast<float>(height_) / range_;
163 point2grid_.assign(pc_ptr->size(), -1);
164 int pos_x = -1;
165 int pos_y = -1;
166 for (size_t i = 0; i < pc_ptr->size(); ++i) {
167 const auto& pt = pc_ptr->at(i);
168 if (pt.z <= min_height_ || pt.z >= max_height_) {
169 continue;
170 }
171 // the coordinates of x and y are exchanged here
172 // (row <-> x, column <-> y)
173 // int pos_x = F2I(pt.y, range_, inv_res_x); // col
174 // int pos_y = F2I(pt.x, range_, inv_res_y); // row
175 // 2018.6.21, switch to axis rotated projection
176 GroupPc2Pixel(pt.x, pt.y, inv_res_x, range_, &pos_x, &pos_y);
177 if (pos_y < 0 || pos_y >= height_ || pos_x < 0 || pos_x >= width_) {
178 continue;
179 }
180 point2grid_[i] = pos_y * width_ + pos_x;
181 }
182}
183
185 LidarFrame* frame) {
186 // check input
187 if (frame == nullptr) {
188 AERROR << "Input null frame ptr.";
189 return false;
190 }
191 if (frame->cloud == nullptr) {
192 AERROR << "Input null frame cloud.";
193 return false;
194 }
195 if (frame->world_cloud == nullptr) {
196 AERROR << "Input null frame world cloud.";
197 return false;
198 }
199 if (frame->cloud->size() == 0) {
200 AERROR << "Input none points.";
201 return false;
202 }
203 if (frame->cloud->size() != frame->world_cloud->size()) {
204 AERROR << "Cloud size and world cloud size not consistent.";
205 return false;
206 }
207 // record input cloud and lidar frame
208 original_cloud_ = frame->cloud;
209 original_world_cloud_ = frame->world_cloud;
210 lidar_frame_ref_ = frame;
211
212 // roi cloud
213 roi_cloud_->CopyPointCloud(*lidar_frame_ref_->cloud,
214 lidar_frame_ref_->roi_indices);
215 roi_world_cloud_->CopyPointCloud(*lidar_frame_ref_->world_cloud,
216 lidar_frame_ref_->roi_indices);
217
218 // check output
219 frame->segmented_objects.clear();
220
221 // note we should use origninal cloud here, frame->cloud may be exchanged
222 Timer timer;
223 // map 3d points to 2d image grids
224 MapPointToGrid(original_cloud_);
225 mapping_time_ = timer.toc(true);
226
227 if (cudaSetDevice(gpu_id_) != cudaSuccess) {
228 AERROR << "Failed to set device to " << gpu_id_;
229 return false;
230 }
231
232 // generate features
233 feature_generator_->Generate(original_cloud_, point2grid_);
234 feature_time_ = timer.toc(true);
235
236 // model inference
237 inference_->Infer();
238 infer_time_ = timer.toc(true);
239
240 // processing clustering
241 GetObjectsFromSppEngine(&frame->segmented_objects);
242
243 AINFO << "CNNSEG: mapping: " << mapping_time_ << "\t"
244 << " feature: " << feature_time_ << "\t"
245 << " infer: " << infer_time_ << "\t"
246 << " fg-seg: " << fg_seg_time_ << "\t"
247 << " collect: " << collect_time_;
248 return true;
249}
250
251void CNNSegmentation::GetObjectsFromSppEngine(
252 std::vector<std::shared_ptr<Object>>* objects) {
253 Timer timer;
254 spp_engine_.GetSppData().grid_indices = point2grid_.data();
255
256 size_t num_foreground =
257 spp_engine_.ProcessForegroundSegmentation(original_cloud_);
258 fg_seg_time_ = timer.toc(true);
259
260 // copy height from roi cloud to origin cloud,
261 // note ground points include other noise points
262 // filtered by ground detection post process
263 AINFO << "Use origin cloud and copy height";
264 for (std::size_t i = 0; i < lidar_frame_ref_->roi_indices.indices.size();
265 ++i) {
266 const int roi_id = lidar_frame_ref_->roi_indices.indices[i];
267 original_cloud_->mutable_points_height()->at(roi_id) =
268 roi_cloud_->points_height(i);
269 if (roi_cloud_->mutable_points_label()->at(i) ==
270 static_cast<uint8_t>(LidarPointLabel::GROUND)) {
271 original_cloud_->mutable_points_label()->at(roi_id) =
272 roi_cloud_->points_label().at(i);
273 }
274 }
275
276 memcpy(&original_world_cloud_->mutable_points_height()->at(0),
277 &original_cloud_->points_height().at(0),
278 sizeof(float) * original_cloud_->size());
279 memcpy(&original_world_cloud_->mutable_points_label()->at(0),
280 &original_cloud_->points_label().at(0),
281 sizeof(uint8_t) * original_cloud_->size());
282 if (model_param_.remove_ground_points()) {
283 num_foreground = spp_engine_.RemoveGroundPointsInForegroundCluster(
284 original_cloud_, lidar_frame_ref_->roi_indices,
285 lidar_frame_ref_->non_ground_indices);
286 if (num_foreground == 0) {
287 ADEBUG << "No foreground segmentation output";
288 }
289 }
290
291 const auto& clusters = spp_engine_.clusters();
292 objects->clear();
293 base::ObjectPool::Instance().BatchGet(clusters.size(), objects);
294 size_t valid = 0;
295
296 // prepare for valid point cloud for seconary segmentor
297 // after removing pts from primary segmentor, ground and non roi pts
298 CloudMask mask;
299 if (model_param_.fill_recall_with_segmentor()) {
300 mask.Set(original_cloud_->size(), 0);
301 mask.AddIndicesOfIndices(lidar_frame_ref_->roi_indices,
302 lidar_frame_ref_->non_ground_indices, 1);
303 }
304
305 for (int i = 0; i < static_cast<int>(clusters.size()); ++i) {
306 if (clusters[i]->points.size() <= model_param_.min_pts_num() &&
307 clusters[i]->pixels.size() < model_param_.min_pts_num()) {
308 continue;
309 }
310 auto& cluster = clusters[i];
311 auto& object = objects->at(valid);
312 object->lidar_supplement.num_points_in_roi = cluster->points_in_roi;
313 object->lidar_supplement.on_use = true;
314 object->lidar_supplement.is_background = false;
315 // ACHECK(cluster->points.size() == cluster->point_ids.size())
316 // << "cluster points size: " << cluster->points.size()
317 // << "cluster point ids size: " << cluster->point_ids.size();
318 object->lidar_supplement.cloud.CopyPointCloud(*original_cloud_,
319 cluster->point_ids);
320 object->lidar_supplement.cloud_world.CopyPointCloud(*original_world_cloud_,
321 cluster->point_ids);
322
323 // for miss detection, try to fill recall with segmentor
324 if (model_param_.fill_recall_with_segmentor()) {
325 base::PointIndices ind;
326 std::vector<int> indices(cluster->point_ids.begin(),
327 cluster->point_ids.end());
328 ind.indices = std::move(indices);
329 mask.RemoveIndices(ind);
330 }
331
332 // for (auto& id : cluster->point_ids) {
333 // original_cloud_->points_label(id)
334 // = static_cast<uint8_t>(LidarPointLabel::OBJECT);
335 //}
336 object->confidence = cluster->confidence;
337 object->id = static_cast<int>(valid);
338 if (model_param_.do_classification()) {
339 object->lidar_supplement.raw_probs.push_back(std::vector<float>(
340 static_cast<int>(base::ObjectType::MAX_OBJECT_TYPE), 0.f));
341 object->lidar_supplement.raw_classification_methods.push_back(Name());
342 object->lidar_supplement.raw_probs
343 .back()[static_cast<int>(base::ObjectType::UNKNOWN)] =
344 cluster->class_prob[static_cast<int>(MetaType::META_UNKNOWN)];
345 object->lidar_supplement.raw_probs
346 .back()[static_cast<int>(base::ObjectType::PEDESTRIAN)] =
347 cluster->class_prob[static_cast<int>(MetaType::META_PEDESTRIAN)];
348 object->lidar_supplement.raw_probs
349 .back()[static_cast<int>(base::ObjectType::BICYCLE)] =
350 cluster->class_prob[static_cast<int>(MetaType::META_NONMOT)];
351 object->lidar_supplement.raw_probs
352 .back()[static_cast<int>(base::ObjectType::VEHICLE)] =
353 cluster->class_prob[static_cast<int>(MetaType::META_SMALLMOT)] +
354 cluster->class_prob[static_cast<int>(MetaType::META_BIGMOT)];
355 // copy to type
356 object->type_probs.assign(
357 object->lidar_supplement.raw_probs.back().begin(),
358 object->lidar_supplement.raw_probs.back().end());
359 object->type = static_cast<base::ObjectType>(
360 std::distance(object->type_probs.begin(),
361 std::max_element(object->type_probs.begin(),
362 object->type_probs.end())));
363 }
364
365 if (model_param_.do_heading()) {
366 // object->theta = cluster->yaw;
367 // object->direction[0] = cos(cluster->yaw);
368 // object->direction[1] = sin(cluster->yaw);
369 // 2018.6.21, switch to axis rotated projection
370 // should be reverted after retrain model.
371 static const float quater_pi = static_cast<float>(M_PI) * 0.25f;
372 object->theta = cluster->yaw - quater_pi;
373 object->direction[0] = cosf(cluster->yaw - quater_pi);
374 object->direction[1] = sinf(cluster->yaw - quater_pi);
375 object->direction[2] = 0;
376 object->lidar_supplement.is_orientation_ready = true;
377 }
378 ++valid;
379 }
380 objects->resize(valid);
381
382 // add additional object seg logic with segmentor if cnnseg miss detects
383 if (model_param_.fill_recall_with_segmentor()) {
384 mask.GetValidIndices(&lidar_frame_ref_->secondary_indices);
385 }
386
387 collect_time_ = timer.toc(true);
388}
389
391
392} // namespace lidar
393} // namespace perception
394} // namespace apollo
#define PERCEPTION_REGISTER_LIDARDETECTOR(name)
std::string Name() const override
Name of CNNSegmentation module
bool Detect(const LidarDetectorOptions &options, LidarFrame *frame) override
Detect foreground objects using CNNSegmentation
bool Init(const LidarDetectorInitOptions &options=LidarDetectorInitOptions()) override
Init of CNNSegmentation object
void Init(size_t width, size_t height, float range, const SppParams &param=SppParams(), const std::string &sensor_name="velodyne64")
Definition spp_engine.cc:26
size_t RemoveGroundPointsInForegroundCluster(const base::PointFCloudConstPtr full_point_cloud, const base::PointIndices &roi_indices, const base::PointIndices &roi_non_ground_indices)
const SppClusterList & clusters() const
Definition spp_engine.h:60
size_t ProcessForegroundSegmentation(const base::PointFCloudConstPtr point_cloud)
double toc(bool reset=false)
Definition lidar_timer.h:28
#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
Point< float > PointF
Definition point.h:56
std::vector< std::string > GetBlobNames(const google::protobuf::RepeatedPtrField< common::ModelBlob > &model_blobs)
Definition model_util.cc:23
void AddShape(std::map< std::string, std::vector< int > > *shape_map, const google::protobuf::RepeatedPtrField< common::ModelBlob > &model_blobs)
Definition model_util.cc:32
Inference * CreateInferenceByName(const std::string &frame_work, const std::string &proto_file, const std::string &weight_file, const std::vector< std::string > &outputs, const std::vector< std::string > &inputs, const std::string &model_root)
void GroupPc2Pixel(float pc_x, float pc_y, float scale, float range, int *x, int *y)
Definition util.h:38
std::string GetModelFile(const std::string &model_name, const std::string &file_name)
Get the model file path by model path and file name
Definition util.cc:55
std::string GetConfigFile(const std::string &config_path, const std::string &config_file)
Definition util.cc:80
std::string GetModelPath(const std::string &model_name)
Get the model path by model name, search from APOLLO_MODEL_PATH
Definition util.cc:44
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
base::Blob< float > * instance_pt_blob
Definition spp_struct.h:32
optional common::PointCloudPreProcess preprocess