Apollo 10.0
自动驾驶开放平台
yolox3d_obstacle_detector.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2023 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 "cyber/common/file.h"
19#include "cyber/common/log.h"
25
26namespace apollo {
27namespace perception {
28namespace camera {
29
31
33 const yolox3d::ModelParam &model_param) {
34 // loading config params
35 width_ = model_param.resize().width();
36 height_ = model_param.resize().height();
37
38 image_width_ = options_.image_width;
39 image_height_ = options_.image_height;
40
41 AINFO << " image_height =" << image_height_
42 << ", image_width=" << image_width_ << ", resize height=" << height_
43 << ", width=" << width_;
44}
45
47 const yolox3d::ModelParam &model_param) {
48 confidence_threshold_ = model_param.confidence_threshold();
49 border_ratio_ = model_param.border_ratio();
50
51 // Init NMS proto param by config file
52 const auto &nms_param = model_param.nms_param();
53 nms_.set_sigma(nms_param.sigma());
54 nms_.set_type(nms_param.type());
55 nms_.set_threshold(nms_param.threshold());
56}
57
59 std::map<std::string, std::vector<int>> *shape_map,
60 const google::protobuf::RepeatedPtrField<common::ModelBlob> &model_blobs) {
61 for (const auto &blob : model_blobs) {
62 std::vector<int> shape(blob.shape().begin(), blob.shape().end());
63 shape_map->insert(std::make_pair(blob.name(), shape));
64 }
65}
66
67std::vector<std::string> GetBlobNames3DYolox(
68 const google::protobuf::RepeatedPtrField<common::ModelBlob> &model_blobs) {
69 std::vector<std::string> blob_names;
70 for (const auto &blob : model_blobs) {
71 blob_names.push_back(blob.name());
72 }
73 return blob_names;
74}
75
76bool Yolox3DObstacleDetector::Init3DNetwork(const common::ModelInfo &model_info,
77 const std::string &model_path) {
78 // Network files
79 std::string proto_file =
80 GetModelFile(model_path, model_info.proto_file().file());
81 std::string weight_file =
82 GetModelFile(model_path, model_info.weight_file().file());
83
84 // Network input and output names
85 std::vector<std::string> input_names =
86 GetBlobNames3DYolox(model_info.inputs());
87 std::vector<std::string> output_names =
88 GetBlobNames3DYolox(model_info.outputs());
89
90 // Network type
91 const auto &framework = model_info.framework();
92 std::string plugin_name = model_info.infer_plugin();
93 static const std::string class_namespace = "apollo::perception::inference::";
94 if (model_info.has_infer_plugin() && !plugin_name.empty()) {
95 plugin_name = class_namespace + plugin_name;
98 net_3D_->set_model_info(proto_file, input_names, output_names);
99 AINFO << "net_3D load plugin success: " << plugin_name;
100 } else {
101 net_3D_.reset(inference::CreateInferenceByName(framework, proto_file,
102 weight_file, output_names,
103 input_names, model_path));
104 }
105
106 ACHECK(net_3D_ != nullptr);
107 net_3D_->set_gpu_id(gpu_id_);
108
109 std::map<std::string, std::vector<int>> shape_map;
110 AddShape3DYolox(&shape_map, model_info.inputs());
111 AddShape3DYolox(&shape_map, model_info.outputs());
112
113 if (!net_3D_->Init(shape_map)) {
114 AERROR << model_info.name() << "init failed!";
115 return false;
116 }
117 return true;
118}
119
120cv::Mat resizeKeepAspectRatioYolox(const cv::Mat &input,
121 const cv::Size &dstSize,
122 const cv::Scalar &bgcolor) {
123 cv::Mat output;
124
125 double h1 = dstSize.width * (input.rows / static_cast<double>(input.cols));
126 double w2 = dstSize.height * (input.cols / static_cast<double>(input.rows));
127 if (h1 <= dstSize.height) {
128 cv::resize(input, output, cv::Size(dstSize.width, h1));
129 } else {
130 cv::resize(input, output, cv::Size(w2, dstSize.height));
131 }
132
133 int top = (dstSize.height - output.rows) / 2;
134 int down = (dstSize.height - output.rows + 1) / 2;
135 int left = (dstSize.width - output.cols) / 2;
136 int right = (dstSize.width - output.cols + 1) / 2;
137
138 cv::copyMakeBorder(output, output, top, down, left, right,
139 cv::BORDER_CONSTANT, bgcolor);
140
141 return output;
142}
143
145 base::ObjectPtr obj) {
146 ACHECK(image != nullptr);
147
148 // get left top and right bottom point
149 float left_x = Yoloxclamp(obj->camera_supplement.box.xmin, 0.f,
150 static_cast<float>(image->cols()));
151 float left_y = Yoloxclamp(obj->camera_supplement.box.ymin, 0.f,
152 static_cast<float>(image->rows()));
153 float right_x = Yoloxclamp(obj->camera_supplement.box.xmax, 0.f,
154 static_cast<float>(image->cols()));
155 float right_y = Yoloxclamp(obj->camera_supplement.box.ymax, 0.f,
156 static_cast<float>(image->rows()));
157
158 // get width and height of object
159 float width = right_x - left_x;
160 float height = right_y - left_y;
161
162 // crop object
163 cv::Rect object_roi(left_x, left_y, width, height);
164 cv::Mat cropped_obj = img_(object_roi);
165
166 // resize croped image to 224*224, no padding
167 // cropped_obj =
168 // resizeKeepAspectRatioYolox(cropped_obj, cv::Size(224, 224), {0, 0, 0});
169 cv::resize(cropped_obj, cropped_obj, cv::Size(224, 224));
170
171 // normallize channel value from 0~255 to 0~1 and change it to float type
172 cropped_obj.convertTo(cropped_obj, CV_32F, 1.0f / 255.0);
173
174 // BGR
175 std::vector<float> mean_values{0.406, 0.456, 0.485};
176 std::vector<float> std_values{0.225, 0.224, 0.229};
177
178 std::vector<cv::Mat> bgrChannels(3);
179 cv::split(cropped_obj, bgrChannels);
180 for (int i = 0; i < 3; ++i) {
181 bgrChannels[i].convertTo(bgrChannels[i], CV_32FC1, 1 / std_values[i],
182 (0.0 - mean_values[i]) / std_values[i]);
183 }
184 cv::Mat dst;
185 cv::merge(bgrChannels, dst);
186 cropped_obj = dst;
187
188 auto model_inputs = model_param_.info_3d().info().inputs();
189
190 auto input_blob_3d = net_3D_->get_blob(model_inputs[0].name());
191
192 ACHECK(input_blob_3d != nullptr);
193
194 int model_input_chs = cropped_obj.channels();
195 int model_input_rows = cropped_obj.rows;
196 int model_input_cols = cropped_obj.cols;
197 float *input_data = input_blob_3d->mutable_cpu_data();
198
199 // copy to blob
200 input_blob_3d->Reshape(
201 {1, model_input_chs, model_input_rows, model_input_cols});
202 for (int i = 0; i < model_input_chs; ++i) {
203 cv::extractChannel(
204 cropped_obj,
205 cv::Mat(model_input_rows, model_input_cols, CV_32FC1,
206 input_data + i * model_input_rows * model_input_cols),
207 i);
208 }
209
210 // 3D model infer
211 net_3D_->Infer();
212
213 // get results
214 auto model_outputs = model_param_.info_3d().info().outputs();
215 auto blob_orient = net_3D_->get_blob(model_outputs[0].name());
216 auto blob_conf = net_3D_->get_blob(model_outputs[1].name());
217 auto blob_dim = net_3D_->get_blob(model_outputs[2].name());
218
219 ACHECK(blob_orient != nullptr);
220 ACHECK(blob_conf != nullptr);
221 ACHECK(blob_dim != nullptr);
222
223 const float *orient_data = blob_orient->cpu_data();
224 const float *conf_data = blob_conf->cpu_data();
225 const float *dim_data = blob_dim->cpu_data();
226
227 // recover to alpha according to bin number
228 const int bin_number = model_param_.info_3d().bin_num();
229 int max_index = 0;
230 int max_conf = conf_data[max_index];
231 for (int i = 1; i < bin_number; ++i) {
232 if (conf_data[i] > max_conf) {
233 max_conf = conf_data[i];
234 max_index = i;
235 }
236 }
237
238 int orient_index = 2 * max_index;
239 float alpha = 0.f;
240 float cos_result = orient_data[orient_index];
241 float sin_result = orient_data[orient_index + 1];
242 float angle_offset = std::atan2(sin_result, cos_result);
243
244 float wedge = 2 * M_PI / bin_number;
245 alpha = angle_offset + max_index * wedge;
246 alpha = alpha + wedge / 2 - M_PI;
247 if (alpha > M_PI) {
248 alpha -= 2 * M_PI;
249 }
250 obj->camera_supplement.alpha = alpha;
251
252 // add dim to object template
253 obj->size[0] += dim_data[2];
254 obj->size[1] += dim_data[1];
255 obj->size[2] += dim_data[0];
256 return;
257}
258
260 options_ = options;
261
262 gpu_id_ = options.gpu_id;
263 BASE_GPU_CHECK(cudaSetDevice(gpu_id_));
264 BASE_GPU_CHECK(cudaStreamCreate(&stream_));
265
266 std::string config_file =
267 GetConfigFile(options.config_path, options.config_file);
268 if (!cyber::common::GetProtoFromFile(config_file, &model_param_)) {
269 AERROR << "Read proto_config failed! " << config_file;
270 return false;
271 }
272
273 LoadInputShape(model_param_);
274 LoadParam(model_param_);
275
276 const auto &model_info = model_param_.info();
277 std::string model_path = GetModelPath(model_info.name());
278 if (!InitNetwork(model_info, model_path)) {
279 AERROR << "Init network failed!";
280 return false;
281 }
282 net_->Infer();
283 AINFO << "yolox3d 2D model init success";
284 std::cout << "yolox3d 2D model init success " << std::endl;
285
286 const auto &model_info_3d = model_param_.info_3d().info();
287 std::string model_path_3d = GetModelPath(model_info_3d.name());
288 if (!Init3DNetwork(model_info_3d, model_path_3d)) {
289 AERROR << "Init network failed!";
290 return false;
291 }
292 net_3D_->Infer();
293 AINFO << "yolox3d 3D model init success";
294 std::cout << "yolox3d 3D model init success " << std::endl;
295
296 return true;
297}
298
300 base::BlobPtr<float> input_blob) {
301 ACHECK(image != nullptr);
302 ACHECK(input_blob != nullptr);
303
304 // init cv img containter, same to image row and col
305 img_ = cv::Mat(image->rows(), image->cols(), CV_8UC3);
306 memcpy(img_.data, image->cpu_data(),
307 image->rows() * image->cols() * image->channels() * sizeof(uint8_t));
308
309 // generate new pure black bg as same size as ratio
310 float ratio = std::max(image_width_, image_height_);
311 cv::Mat out(ratio, ratio, CV_8UC3, {114, 114, 114}); // 114 sames to yolox3d
312
313 img_.copyTo(out(cv::Rect(0, 0, img_.cols,
314 img_.rows))); // pad to bottom, sames to yolox3d
315
316 cv::resize(out, out, cv::Size(width_, height_),
317 cv::INTER_LINEAR); // use INTER_LINEAR, sames to yolox3d
318
319 out.convertTo(out, CV_32F, 1.0); // without normalize, sames to yolox3d
320 // cv::imwrite("pics/yolox3d_pad_image.png", out);
321
322 int model_input_chs = out.channels();
323 int model_input_rows = out.rows;
324 int model_input_cols = out.cols;
325
326 // fill input_blob -> tensor_image will be used by model
327 input_blob->Reshape({1, model_input_chs, model_input_rows, model_input_cols});
328 float *input_data = input_blob->mutable_cpu_data();
329
330 for (int i = 0; i < model_input_chs; ++i) {
331 cv::extractChannel(
332 out,
333 cv::Mat(model_input_rows, model_input_cols, CV_32FC1,
334 input_data + i * model_input_rows * model_input_cols),
335 i);
336 }
337 return true;
338}
339
341 if (frame == nullptr) {
342 return false;
343 }
344
345 if (cudaSetDevice(gpu_id_) != cudaSuccess) {
346 AERROR << "Failed to set device to " << gpu_id_;
347 return false;
348 }
349
350 auto model_inputs = model_param_.info().inputs();
351 auto input_blob_2d = net_->get_blob(model_inputs[0].name());
352
353 // use bgr to infer
354 DataProvider::ImageOptions image_options;
355 image_options.target_color = base::Color::BGR;
356
357 base::Image8U image;
358 frame->data_provider->GetImage(image_options, &image);
359
360 // cpu preprocess
361 PERF_BLOCK("2d image preprocess time: ")
362 Preprocess(&image, input_blob_2d);
364
365 // model infer, save to output blob
366 PERF_BLOCK("2d infer time: ")
367 net_->Infer();
369
370 // get objects from network inference result
371 auto model_outputs = model_param_.info().outputs();
372
373 // Attention(lordon): onnx predict blob is 1, pth is 0
374 auto predict_blob = net_->get_blob(model_outputs[1].name());
375 frame->feature_blob = net_->get_blob(model_outputs[0].name());
376
377 PERF_BLOCK("2d postprocess stage: ")
378 YoloxGetObjectsCpu(predict_blob, model_param_, nms_, width_, height_,
379 image_width_, image_height_, &frame->detected_objects);
381
382 // post processing
383 float border_ratio = 0.01;
384 int left_boundary =
385 static_cast<int>(border_ratio * static_cast<float>(image.cols()));
386 int right_boundary = static_cast<int>((1.0f - border_ratio) *
387 static_cast<float>(image.cols()));
388 for (auto &obj : frame->detected_objects) {
389 obj->camera_supplement.area_id = 1;
390
391 PERF_BLOCK("3d process of per object: ")
392 Yolo3DInference(&image, obj);
394
395 // clear cut off ratios
396 auto &box = obj->camera_supplement.box;
397 if (box.xmin >= left_boundary) {
398 obj->camera_supplement.cut_off_ratios[2] = 0;
399 }
400 if (box.xmax <= right_boundary) {
401 obj->camera_supplement.cut_off_ratios[3] = 0;
402 }
403 }
404
405 return true;
406}
408
409} // namespace camera
410} // namespace perception
411} // namespace apollo
#define REGISTER_OBSTACLE_DETECTOR(name)
static PluginManager * Instance()
get singleton instance of PluginManager
std::shared_ptr< Base > CreateInstance(const std::string &derived_class)
create plugin instance of derived class based on Base
A wrapper around Blob holders serving as the basic computational unit for images.
Definition image_8u.h:44
const uint8_t * cpu_data() const
Definition image_8u.h:99
virtual bool InitNetwork(const common::ModelInfo &model_info, const std::string &model_root)
Interface for network initialization
std::shared_ptr< inference::Inference > net_
bool Init(const ObstacleDetectorInitOptions &options=ObstacleDetectorInitOptions()) override
Necessary, Init yolox3d model params normal, but now use pipline instead
bool Detect(onboard::CameraFrame *frame) override
Detect stage
bool Init3DNetwork(const common::ModelInfo &model_info, const std::string &model_path)
Init model inference
void LoadInputShape(const yolox3d::ModelParam &model_param)
Resize model input picture size according to the config file
void LoadParam(const yolox3d::ModelParam &model_param)
Load yolo libtorch model params from model file
bool Preprocess(const base::Image8U *image, base::BlobPtr< float > input_blob)
Preprocess of image before inference, resize input data blob and fill image data to blob
void Yolo3DInference(const base::Image8U *image, base::ObjectPtr obj)
Load yolo3D libtorch model params from model file
#define ACHECK(cond)
Definition log.h:80
#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
std::shared_ptr< Object > ObjectPtr
Definition object.h:127
std::shared_ptr< Blob< Dtype > > BlobPtr
Definition blob.h:313
cv::Mat resizeKeepAspectRatioYolox(const cv::Mat &input, const cv::Size &dstSize, const cv::Scalar &bgcolor)
constexpr T Yoloxclamp(const T &val, const T &low, const T &high)
Clamp target value between low and high tools for iou
void YoloxGetObjectsCpu(const std::shared_ptr< base::Blob< float > > &objects_blob, const yolox3d::ModelParam &model_param, const yolox3d::NMSParam &nms_param, const int width, const int height, const int image_width, const int image_height, std::vector< base::ObjectPtr > *objects)
Return Yolox Objects
void AddShape3DYolox(std::map< std::string, std::vector< int > > *shape_map, const google::protobuf::RepeatedPtrField< common::ModelBlob > &model_blobs)
std::vector< std::string > GetBlobNames3DYolox(const google::protobuf::RepeatedPtrField< common::ModelBlob > &model_blobs)
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)
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
#define PERF_BLOCK_END
Definition profiler.h:53
#define PERF_BLOCK(...)
Definition profiler.h:52
std::shared_ptr< camera::DataProvider > data_provider