Apollo 10.0
自动驾驶开放平台
postprocess.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 *****************************************************************************/
16
18
19#include <map>
20#include <unordered_map>
21#include <utility>
22
23#include "cyber/common/log.h"
24
25namespace apollo {
26namespace perception {
27namespace camera {
28
29// Car,Truck,Van,Bus,Pedestrian,Cyclist,TrafficCone,Uknown
30constexpr int kClassOfObstacles = 8;
31// 2dcenter_x、2dcenter_y、2d_box_w、2d_box_h、score、object_label
32constexpr int kScoreIndex = 4;
33constexpr int kLabelIndex = 5;
34
41
42struct Object {
43 cv::Rect_<float> rect;
44 int label;
45 float prob;
46};
47
49 int grid0;
50 int grid1;
51 int stride;
52};
53
62static void generate_grids_and_stride(
63 std::vector<int> &strides, std::vector<GridAndStride> &grid_strides) {
64 for (auto stride : strides) {
65 int num_grid_y = 640 / stride;
66 int num_grid_x = 640 / stride;
67 for (int g1 = 0; g1 < num_grid_y; g1++) {
68 for (int g0 = 0; g0 < num_grid_x; g0++) {
69 grid_strides.push_back((GridAndStride){g0, g1, stride});
70 }
71 }
72 }
73}
74
86static void generate_yolox3d_proposals(std::vector<GridAndStride> grid_strides,
87 const float *feat_blob,
88 float prob_threshold,
89 std::vector<Object> &objects) {
90 const int num_anchors = grid_strides.size(); // 8400
91
92 for (int anchor_idx = 0; anchor_idx < num_anchors; anchor_idx++) {
93 const int grid0 = grid_strides[anchor_idx].grid0;
94 const int grid1 = grid_strides[anchor_idx].grid1;
95 const int stride = grid_strides[anchor_idx].stride;
96
97 const int basic_pos = anchor_idx * (kClassOfObstacles + 5);
98
99 // yolox3d/models/yolo_head.py decode logic
100 float x_center = (feat_blob[basic_pos + 0] + grid0) * stride;
101 float y_center = (feat_blob[basic_pos + 1] + grid1) * stride;
102 float w = exp(feat_blob[basic_pos + 2]) * stride;
103 float h = exp(feat_blob[basic_pos + 3]) * stride;
104 float x0 = x_center - w * 0.5f;
105 float y0 = y_center - h * 0.5f;
106
107 if (w - 10 < 0 || h - 10 < 0) {
108 continue;
109 }
110
111 std::vector<float> scores;
112 scores.clear();
113 float box_objectness = feat_blob[basic_pos + kScoreIndex];
114 for (int class_idx = 0; class_idx < kClassOfObstacles; class_idx++) {
115 float box_cls_score = feat_blob[basic_pos + kLabelIndex + class_idx];
116 float box_prob = box_objectness * box_cls_score;
117 scores.push_back(box_prob);
118 }
119 auto max_iter = std::max_element(scores.begin(), scores.end());
120 int max_idx = std::distance(scores.begin(), max_iter);
121
122 if (*max_iter > prob_threshold) {
123 Object obj;
124 obj.rect.x = x0;
125 obj.rect.y = y0;
126 obj.rect.width = w;
127 obj.rect.height = h;
128 obj.label = max_idx;
129 obj.prob = *max_iter;
130 objects.push_back(obj);
131 }
132 } // point anchor loop
133}
134
146static inline float intersection_area(const Object &a, const Object &b) {
147 cv::Rect_<float> inter = a.rect & b.rect;
148 return inter.area();
149}
150
158static void nms_sorted_bboxes(const std::vector<Object> &faceobjects,
159 std::vector<int> &picked, float nms_threshold) {
160 picked.clear();
161
162 const int n = faceobjects.size();
163
164 std::vector<float> areas(n);
165 for (int i = 0; i < n; i++) {
166 areas[i] = faceobjects[i].rect.area();
167 }
168
169 for (int i = 0; i < n; i++) {
170 const Object &a = faceobjects[i];
171
172 int keep = 1;
173 for (int j = 0; j < static_cast<int>(picked.size()); j++) {
174 const Object &b = faceobjects[picked[j]];
175
176 // intersection over union
177 float inter_area = intersection_area(a, b);
178 float union_area = areas[i] + areas[picked[j]] - inter_area;
179 // float IoU = inter_area / union_area
180 if (inter_area / union_area > nms_threshold) keep = 0;
181 }
182
183 if (keep) picked.push_back(i);
184 }
185}
186
199static void qsort_descent_inplace(std::vector<Object> &faceobjects, int left,
200 int right) {
201 int i = left;
202 int j = right;
203 float p = faceobjects[(left + right) / 2].prob;
204
205 while (i <= j) {
206 while (faceobjects[i].prob > p) i++;
207
208 while (faceobjects[j].prob < p) j--;
209
210 if (i <= j) {
211 // swap
212 std::swap(faceobjects[i], faceobjects[j]);
213
214 i++;
215 j--;
216 }
217 }
218
219#pragma omp parallel sections
220 {
221#pragma omp section
222 {
223 if (left < j) qsort_descent_inplace(faceobjects, left, j);
224 }
225#pragma omp section
226 {
227 if (i < right) qsort_descent_inplace(faceobjects, i, right);
228 }
229 }
230}
231
232static void qsort_descent_inplace(std::vector<Object> &objects) {
233 if (objects.empty()) return;
234
235 qsort_descent_inplace(objects, 0, objects.size() - 1);
236}
237
238void YoloxGetAllObjects(const float *data,
239 const yolox3d::ModelParam &model_param,
240 const float scale,
241 std::vector<std::vector<float>> *objects_out) {
242 objects_out->clear();
243
244 std::vector<Object> objects;
245 std::vector<Object> proposals;
246 std::vector<int> strides = {8, 16, 32};
247 std::vector<GridAndStride> grid_strides;
248 generate_grids_and_stride(strides, grid_strides);
249 generate_yolox3d_proposals(grid_strides, data,
250 model_param.confidence_threshold(), proposals);
251 AINFO << "Yolox proposals size is " << proposals.size();
252
253 qsort_descent_inplace(proposals);
254 AINFO << "Yolox proposals after sort size is " << proposals.size();
255
256 std::vector<int> picked;
257 nms_sorted_bboxes(proposals, picked, model_param.nms_param().threshold());
258 AINFO << "Yolox objects after nms is " << picked.size();
259
260 int count = picked.size();
261 objects.resize(count);
262 for (int i = 0; i < count; i++) {
263 objects[i] = proposals[picked[i]];
264
265 // adjust offset to original unpadded
266 float x0 = (objects[i].rect.x) / scale;
267 float y0 = (objects[i].rect.y) / scale;
268 float x1 = (objects[i].rect.x + objects[i].rect.width) / scale;
269 float y1 = (objects[i].rect.y + objects[i].rect.height) / scale;
270
271 // clip
272 x0 = std::max(std::min(x0, static_cast<float>(1920 - 1)), 0.f);
273 y0 = std::max(std::min(y0, static_cast<float>(1920 - 1)), 0.f);
274 x1 = std::max(std::min(x1, static_cast<float>(1920 - 1)), 0.f);
275 y1 = std::max(std::min(y1, static_cast<float>(1920 - 1)), 0.f);
276
277 objects[i].rect.x = x0;
278 objects[i].rect.y = y0;
279 objects[i].rect.width = x1 - x0;
280 objects[i].rect.height = y1 - y0;
281
282 std::vector<float> object_temp;
283 object_temp.push_back(x0); // left x
284 object_temp.push_back(y0); // left y
285 object_temp.push_back(x1 - x0); // w
286 object_temp.push_back(y1 - y0); // h
287
288 object_temp.push_back(objects[i].prob); // score
289 object_temp.push_back(objects[i].label); // class label
290
291 objects_out->push_back(object_temp);
292 }
293}
294
296 const base::Rect<float> &b) {
297 base::Rect<float> intersection = a & b;
298 base::Rect<float> unionsection = a | b;
299 if (unionsection.Area() == 0) return 0.0f;
300 return intersection.Area() / unionsection.Area();
301}
302
303void YoloxFillBase(const std::vector<float> &detect, const int width,
304 const int height, const int image_width,
305 const int image_height, base::ObjectPtr obj) {
306 // yolo image size is (416, 416), raw image is (1080, 1920)
307 obj->camera_supplement.box.xmin = detect[0];
308 obj->camera_supplement.box.ymin = detect[1];
309 obj->camera_supplement.box.xmax = detect[0] + detect[2];
310 obj->camera_supplement.box.ymax = detect[1] + detect[3];
311}
312
313void YoloxFillBbox3d(const yolox3d::ModelParam &model_param,
314 const std::vector<float> &detect, base::ObjectPtr obj) {
315 auto obj_l = 0.0;
316 auto obj_w = 0.0;
317 auto obj_h = 0.0;
318
319 if (model_param.with_box3d()) {
320 if (base ::ObjectType ::VEHICLE ==
321 base::kSubType2TypeMap.at(obj->sub_type)) {
322 obj_l = model_param.car_template().l();
323 obj_w = model_param.car_template().w();
324 obj_h = model_param.car_template().h();
325 if ("TRUCK" == base::kSubType2NameMap.at(obj->sub_type)) {
326 obj_l = model_param.car_template().l() * 1.5;
327 obj_w = model_param.car_template().w() * 1.5;
328 obj_h = model_param.car_template().h() * 1.5;
329 } else if ("BUS" == base::kSubType2NameMap.at(obj->sub_type)) {
330 obj_l = model_param.car_template().l() * 3;
331 obj_w = model_param.car_template().w() * 1.5;
332 obj_h = model_param.car_template().h() * 2.0;
333 }
334 } else if (base ::ObjectType ::PEDESTRIAN ==
335 base::kSubType2TypeMap.at(obj->sub_type)) {
336 obj_l = model_param.ped_template().l();
337 obj_w = model_param.ped_template().w();
338 obj_h = model_param.ped_template().h();
339 } else if (base ::ObjectType ::BICYCLE ==
340 base::kSubType2TypeMap.at(obj->sub_type)) {
341 obj_l = model_param.cyclist_template().l();
342 obj_w = model_param.cyclist_template().w();
343 obj_h = model_param.cyclist_template().h();
344 } else if (base ::ObjectType ::UNKNOWN_UNMOVABLE ==
345 base::kSubType2TypeMap.at(obj->sub_type)) {
346 obj_l = model_param.trafficcone_template().l();
347 obj_w = model_param.trafficcone_template().w();
348 obj_h = model_param.trafficcone_template().h();
349 }
350
351 // length, width, height
352 obj->size[0] = obj_l;
353 obj->size[1] = obj_w;
354 obj->size[2] = obj_h;
355 obj->camera_supplement.alpha = detect[8];
356 }
357}
358
359float YoloxBboxIOU(const std::vector<float> &box1,
360 const std::vector<float> &box2) {
361 const int center_x_index = 0;
362 const int center_y_index = 1;
363 const int width_index = 2;
364 const int height_index = 3;
365
366 int b1_x1 = static_cast<int>(box1[center_x_index]) -
367 std::floor(box1[width_index] / 2.0);
368 int b1_y1 = static_cast<int>(box1[center_y_index]) -
369 std::floor(box1[height_index] / 2.0);
370 int b1_x2 = static_cast<int>(box1[center_x_index]) +
371 std::floor(box1[width_index] / 2.0);
372 int b1_y2 = static_cast<int>(box1[center_y_index]) +
373 std::floor(box1[height_index] / 2.0);
374
375 int b2_x1 = static_cast<int>(box2[center_x_index]) -
376 std::floor(box2[width_index] / 2.0);
377 int b2_y1 = static_cast<int>(box2[center_y_index]) -
378 std::floor(box2[height_index] / 2.0);
379 int b2_x2 = static_cast<int>(box2[center_x_index]) +
380 std::floor(box2[width_index] / 2.0);
381 int b2_y2 = static_cast<int>(box2[center_y_index]) +
382 std::floor(box2[height_index] / 2.0);
383
384 // get the corrdinates of the intersection rectangle
385 int inter_rect_x1 = std::max(b1_x1, b2_x1);
386 int inter_rect_y1 = std::max(b1_y1, b2_y1);
387 int inter_rect_x2 = std::min(b1_x2, b2_x2);
388 int inter_rect_y2 = std::min(b1_y2, b2_y2);
389
390 // Intersection area
391 float inter_area = static_cast<float>(
392 Yoloxclamp(inter_rect_x2 - inter_rect_x1 + 1, 0, INT_MAX) *
393 Yoloxclamp(inter_rect_y2 - inter_rect_y1 + 1, 0, INT_MAX));
394 // Union Area
395 float b1_area = (b1_x2 - b1_x1 + 1) * (b1_y2 - b1_y1 + 1);
396 float b2_area = (b2_x2 - b2_x1 + 1) * (b2_y2 - b2_y1 + 1);
397
398 float iou = inter_area / (b1_area + b2_area - inter_area + 1e-3);
399 return iou;
400}
401
402void YoloxTruncated(base::ObjectPtr obj, const int image_width,
403 const int image_height) {
404 const float boundary_len = 20.0; // TODO(lordon): 20 piexl move it to conf
405 float min_x = static_cast<float>(boundary_len);
406 float min_y = static_cast<float>(boundary_len);
407 float max_x = static_cast<float>(image_width - boundary_len);
408 float max_y = static_cast<float>(image_height - boundary_len);
409 double eps = 1e-2;
410
411 if (obj->camera_supplement.box.xmin <= min_x ||
412 obj->camera_supplement.box.xmax >= max_x ||
413 obj->camera_supplement.box.ymin <= min_y ||
414 obj->camera_supplement.box.ymax >= max_y) {
415 // Truncation assignment based on bbox positions
416 if ((obj->camera_supplement.box.ymin < eps) ||
417 (obj->camera_supplement.box.ymax >= 1.0 - eps)) {
418 obj->camera_supplement.truncated_vertical = 0.5;
419 } else {
420 obj->camera_supplement.truncated_vertical = 0.0;
421 }
422 if ((obj->camera_supplement.box.xmin < eps) ||
423 (obj->camera_supplement.box.xmax >= 1.0 - eps)) {
424 obj->camera_supplement.truncated_horizontal = 0.5;
425 } else {
426 obj->camera_supplement.truncated_horizontal = 0.0;
427 }
428 }
429}
430
431void YoloxGetObjectsCpu(const std::shared_ptr<base::Blob<float>> &objects_blob,
432 const yolox3d::ModelParam &model_param,
433 const yolox3d::NMSParam &nms_param, const int width,
434 const int height, const int image_width,
435 const int image_height,
436 std::vector<base::ObjectPtr> *objects) {
437 ACHECK(objects_blob != nullptr);
438 ACHECK(objects != nullptr);
439
440 const float *detect_data = objects_blob->cpu_data();
441 ACHECK(detect_data != nullptr);
442
443 float scale = std::min(model_param.resize().width() / (image_width * 1.0),
444 model_param.resize().height() / (image_height * 1.0));
445
446 std::vector<std::vector<float>> detect_objects;
447 YoloxGetAllObjects(detect_data, model_param, scale, &detect_objects);
448
449 // center_x, center_y, width, height, class_score, class_label
450 objects->clear();
451 for (const std::vector<float> &detect : detect_objects) {
452 const int label = detect[kLabelIndex];
453 // ignore unknown
454 if (7 <= label) {
455 continue;
456 }
457
458 base::ObjectPtr obj = nullptr;
459 obj.reset(new base::Object);
460
461 obj->sub_type = kYoloSubTypeYolox[label];
462 obj->type = base::kSubType2TypeMap.at(obj->sub_type);
463 obj->confidence = detect[kScoreIndex];
464
465 obj->type_probs.assign(static_cast<int>(base::ObjectType::MAX_OBJECT_TYPE),
466 0);
467 obj->sub_type_probs.assign(
468 static_cast<int>(base::ObjectSubType::MAX_OBJECT_TYPE), 0);
469 obj->type_probs[static_cast<int>(obj->type)] = detect[kLabelIndex];
470 obj->sub_type_probs[static_cast<int>(obj->sub_type)] = detect[kLabelIndex];
471
472 YoloxFillBase(detect, width, height, image_width, image_height, obj);
473
474 YoloxFillBbox3d(model_param, detect, obj);
475
476 // TODO(lordon): add ignore truncated bool param
477 YoloxTruncated(obj, image_width, image_height);
478
479 objects->push_back(obj);
480 }
481}
482
483} // namespace camera
484} // namespace perception
485} // namespace apollo
A wrapper around SyncedMemory holders serving as the basic computational unit for images,...
Definition blob.h:88
#define ACHECK(cond)
Definition log.h:80
#define AINFO
Definition log.h:42
const std::map< ObjectSubType, std::string > kSubType2NameMap
const std::map< ObjectSubType, ObjectType > kSubType2TypeMap
ObjectSubType mapping
std::shared_ptr< Object > ObjectPtr
Definition object.h:127
void YoloxGetAllObjects(const float *data, const yolox3d::ModelParam &model_param, const float scale, std::vector< std::vector< float > > *objects_out)
Get all objects accoring to confidence
float YoloxBboxIOU(const std::vector< float > &box1, const std::vector< float > &box2)
Computes IoU between bboxes.
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 YoloxFillBase(const std::vector< float > &detect, const int width, const int height, const int image_width, const int image_height, base::ObjectPtr obj)
Get 2dbbox for objects
float ComputeYoloxOverlap(const base::Rect< float > &a, const base::Rect< float > &b)
constexpr int kClassOfObstacles
void YoloxFillBbox3d(const yolox3d::ModelParam &model_param, const std::vector< float > &detect, base::ObjectPtr obj)
Add 3d bbox values for objects
void YoloxTruncated(base::ObjectPtr obj, const int image_width, const int image_height)
object is truncated or not
const base::ObjectSubType kYoloSubTypeYolox[]
class register implement
Definition arena_queue.h:37