Apollo 11.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
45
46struct Object {
47 cv::Rect_<float> rect;
48 int label;
49 float prob;
50};
51
53 int grid0;
54 int grid1;
55 int stride;
56};
57
58static void generate_grids_and_stride(std::vector<int> &strides, std::vector<GridAndStride> &grid_strides) {
59 for (auto stride : strides) {
60 int num_grid_y = 640 / stride;
61 int num_grid_x = 640 / stride;
62 for (int g1 = 0; g1 < num_grid_y; g1++) {
63 for (int g0 = 0; g0 < num_grid_x; g0++) {
64 grid_strides.push_back((GridAndStride){g0, g1, stride});
65 }
66 }
67 }
68}
69
70static void generate_yolox3d_proposals(
71 std::vector<GridAndStride> grid_strides,
72 const float *feat_blob,
73 float prob_threshold,
74 std::vector<Object> &objects) {
75 const int num_anchors = grid_strides.size(); // 8400
76
77 for (int anchor_idx = 0; anchor_idx < num_anchors; anchor_idx++) {
78 const int grid0 = grid_strides[anchor_idx].grid0;
79 const int grid1 = grid_strides[anchor_idx].grid1;
80 const int stride = grid_strides[anchor_idx].stride;
81
82 const int basic_pos = anchor_idx * (kClassOfObstacles + 5);
83
84 // yolox3d/models/yolo_head.py decode logic
85 float x_center = (feat_blob[basic_pos + 0] + grid0) * stride;
86 float y_center = (feat_blob[basic_pos + 1] + grid1) * stride;
87 float w = exp(feat_blob[basic_pos + 2]) * stride;
88 float h = exp(feat_blob[basic_pos + 3]) * stride;
89 float x0 = x_center - w * 0.5f;
90 float y0 = y_center - h * 0.5f;
91
92 if (w - 10 < 0 || h - 10 < 0) {
93 continue;
94 }
95
96 std::vector<float> scores;
97 scores.clear();
98 float box_objectness = feat_blob[basic_pos + kScoreIndex];
99 for (int class_idx = 0; class_idx < kClassOfObstacles; class_idx++) {
100 float box_cls_score = feat_blob[basic_pos + kLabelIndex + class_idx];
101 float box_prob = box_objectness * box_cls_score;
102 scores.push_back(box_prob);
103 }
104 auto max_iter = std::max_element(scores.begin(), scores.end());
105 int max_idx = std::distance(scores.begin(), max_iter);
106
107 if (*max_iter > prob_threshold) {
108 Object obj;
109 obj.rect.x = x0;
110 obj.rect.y = y0;
111 obj.rect.width = w;
112 obj.rect.height = h;
113 obj.label = max_idx;
114 obj.prob = *max_iter;
115 objects.push_back(obj);
116 }
117 } // point anchor loop
118}
119
120static inline float intersection_area(const Object &a, const Object &b) {
121 cv::Rect_<float> inter = a.rect & b.rect;
122 return inter.area();
123}
124
125static void nms_sorted_bboxes(const std::vector<Object> &faceobjects, std::vector<int> &picked, float nms_threshold) {
126 picked.clear();
127
128 const int n = faceobjects.size();
129
130 std::vector<float> areas(n);
131 for (int i = 0; i < n; i++) {
132 areas[i] = faceobjects[i].rect.area();
133 }
134
135 for (int i = 0; i < n; i++) {
136 const Object &a = faceobjects[i];
137
138 int keep = 1;
139 for (int j = 0; j < static_cast<int>(picked.size()); j++) {
140 const Object &b = faceobjects[picked[j]];
141
142 // intersection over union
143 float inter_area = intersection_area(a, b);
144 float union_area = areas[i] + areas[picked[j]] - inter_area;
145 // float IoU = inter_area / union_area
146 if (inter_area / union_area > nms_threshold)
147 keep = 0;
148 }
149
150 if (keep)
151 picked.push_back(i);
152 }
153}
154
155static void qsort_descent_inplace(std::vector<Object> &faceobjects, int left, int right) {
156 int i = left;
157 int j = right;
158 float p = faceobjects[(left + right) / 2].prob;
159
160 while (i <= j) {
161 while (faceobjects[i].prob > p) {
162 i++;
163 }
164
165 while (faceobjects[j].prob < p) {
166 j--;
167 }
168
169 if (i <= j) {
170 // swap
171 std::swap(faceobjects[i], faceobjects[j]);
172
173 i++;
174 j--;
175 }
176 }
177
178#pragma omp parallel sections
179 {
180#pragma omp section
181 {
182 if (left < j)
183 qsort_descent_inplace(faceobjects, left, j);
184 }
185#pragma omp section
186 {
187 if (i < right)
188 qsort_descent_inplace(faceobjects, i, right);
189 }
190 }
191}
192
193static void qsort_descent_inplace(std::vector<Object> &objects) {
194 if (objects.empty())
195 return;
196
197 qsort_descent_inplace(objects, 0, objects.size() - 1);
198}
199
201 const float *data,
202 const yolox3d::ModelParam &model_param,
203 const float scale,
204 std::vector<std::vector<float>> *objects_out) {
205 objects_out->clear();
206 std::vector<Object> objects;
207
208 std::vector<Object> proposals;
209 std::vector<int> strides = {8, 16, 32};
210 std::vector<GridAndStride> grid_strides;
211 generate_grids_and_stride(strides, grid_strides);
212 generate_yolox3d_proposals(grid_strides, data, model_param.confidence_threshold(), proposals);
213 qsort_descent_inplace(proposals);
214
215 std::vector<int> picked;
216 nms_sorted_bboxes(proposals, picked, model_param.nms_param().threshold());
217 int count = picked.size();
218
219 objects.resize(count);
220 for (int i = 0; i < count; i++) {
221 objects[i] = proposals[picked[i]];
222
223 // adjust offset to original unpadded
224 float x0 = (objects[i].rect.x) / scale;
225 float y0 = (objects[i].rect.y) / scale;
226 float x1 = (objects[i].rect.x + objects[i].rect.width) / scale;
227 float y1 = (objects[i].rect.y + objects[i].rect.height) / scale;
228
229 // clip
230 x0 = std::max(std::min(x0, static_cast<float>(1920 - 1)), 0.f);
231 y0 = std::max(std::min(y0, static_cast<float>(1920 - 1)), 0.f);
232 x1 = std::max(std::min(x1, static_cast<float>(1920 - 1)), 0.f);
233 y1 = std::max(std::min(y1, static_cast<float>(1920 - 1)), 0.f);
234
235 objects[i].rect.x = x0;
236 objects[i].rect.y = y0;
237 objects[i].rect.width = x1 - x0;
238 objects[i].rect.height = y1 - y0;
239
240 std::vector<float> object_temp;
241 object_temp.push_back(x0); // left x
242 object_temp.push_back(y0); // left y
243 object_temp.push_back(x1 - x0); // w
244 object_temp.push_back(y1 - y0); // h
245
246 object_temp.push_back(objects[i].prob); // score
247 object_temp.push_back(objects[i].label); // class label
248
249 objects_out->push_back(object_temp);
250 }
251}
252
254 base::Rect<float> intersection = a & b;
255 base::Rect<float> unionsection = a | b;
256 if (unionsection.Area() == 0)
257 return 0.0f;
258 return intersection.Area() / unionsection.Area();
259}
260
262 const std::vector<float> &detect,
263 const int width,
264 const int height,
265 const int image_width,
266 const int image_height,
267 base::ObjectPtr obj) {
268 // yolo image size is (416, 416), raw image is (1080, 1920)
269 obj->camera_supplement.box.xmin = detect[0];
270 obj->camera_supplement.box.ymin = detect[1];
271 obj->camera_supplement.box.xmax = detect[0] + detect[2];
272 obj->camera_supplement.box.ymax = detect[1] + detect[3];
273}
274
275void YoloxFillBbox3d(const yolox3d::ModelParam &model_param, const std::vector<float> &detect, base::ObjectPtr obj) {
276 auto obj_l = 0.0;
277 auto obj_w = 0.0;
278 auto obj_h = 0.0;
279
280 if (model_param.with_box3d()) {
281 if (base ::ObjectType ::VEHICLE == base::kSubType2TypeMap.at(obj->sub_type)) {
282 obj_l = model_param.car_template().l();
283 obj_w = model_param.car_template().w();
284 obj_h = model_param.car_template().h();
285 if ("TRUCK" == base::kSubType2NameMap.at(obj->sub_type)) {
286 obj_l = model_param.car_template().l() * 1.5;
287 obj_w = model_param.car_template().w() * 1.5;
288 obj_h = model_param.car_template().h() * 1.5;
289 } else if ("BUS" == base::kSubType2NameMap.at(obj->sub_type)) {
290 obj_l = model_param.car_template().l() * 3;
291 obj_w = model_param.car_template().w() * 1.5;
292 obj_h = model_param.car_template().h() * 2.0;
293 }
294 } else if (base ::ObjectType ::PEDESTRIAN == base::kSubType2TypeMap.at(obj->sub_type)) {
295 obj_l = model_param.ped_template().l();
296 obj_w = model_param.ped_template().w();
297 obj_h = model_param.ped_template().h();
298 } else if (base ::ObjectType ::BICYCLE == base::kSubType2TypeMap.at(obj->sub_type)) {
299 obj_l = model_param.cyclist_template().l();
300 obj_w = model_param.cyclist_template().w();
301 obj_h = model_param.cyclist_template().h();
302 } else if (base ::ObjectType ::UNKNOWN_UNMOVABLE == base::kSubType2TypeMap.at(obj->sub_type)) {
303 obj_l = model_param.trafficcone_template().l();
304 obj_w = model_param.trafficcone_template().w();
305 obj_h = model_param.trafficcone_template().h();
306 }
307
308 // length, width, height
309 obj->size[0] = obj_l;
310 obj->size[1] = obj_w;
311 obj->size[2] = obj_h;
312 obj->camera_supplement.alpha = detect[8];
313 }
314}
315
316float YoloxBboxIOU(const std::vector<float> &box1, const std::vector<float> &box2) {
317 const int center_x_index = 0;
318 const int center_y_index = 1;
319 const int width_index = 2;
320 const int height_index = 3;
321
322 int b1_x1 = static_cast<int>(box1[center_x_index]) - std::floor(box1[width_index] / 2.0);
323 int b1_y1 = static_cast<int>(box1[center_y_index]) - std::floor(box1[height_index] / 2.0);
324 int b1_x2 = static_cast<int>(box1[center_x_index]) + std::floor(box1[width_index] / 2.0);
325 int b1_y2 = static_cast<int>(box1[center_y_index]) + std::floor(box1[height_index] / 2.0);
326
327 int b2_x1 = static_cast<int>(box2[center_x_index]) - std::floor(box2[width_index] / 2.0);
328 int b2_y1 = static_cast<int>(box2[center_y_index]) - std::floor(box2[height_index] / 2.0);
329 int b2_x2 = static_cast<int>(box2[center_x_index]) + std::floor(box2[width_index] / 2.0);
330 int b2_y2 = static_cast<int>(box2[center_y_index]) + std::floor(box2[height_index] / 2.0);
331
332 // get the corrdinates of the intersection rectangle
333 int inter_rect_x1 = std::max(b1_x1, b2_x1);
334 int inter_rect_y1 = std::max(b1_y1, b2_y1);
335 int inter_rect_x2 = std::min(b1_x2, b2_x2);
336 int inter_rect_y2 = std::min(b1_y2, b2_y2);
337
338 // Intersection area
339 float inter_area = static_cast<float>(
340 Yoloxclamp(inter_rect_x2 - inter_rect_x1 + 1, 0, INT_MAX)
341 * Yoloxclamp(inter_rect_y2 - inter_rect_y1 + 1, 0, INT_MAX));
342 // Union Area
343 float b1_area = (b1_x2 - b1_x1 + 1) * (b1_y2 - b1_y1 + 1);
344 float b2_area = (b2_x2 - b2_x1 + 1) * (b2_y2 - b2_y1 + 1);
345
346 float iou = inter_area / (b1_area + b2_area - inter_area + 1e-3);
347 return iou;
348}
349
350void YoloxTruncated(base::ObjectPtr obj, const int image_width, const int image_height) {
351 const float boundary_len = 20.0; // TODO(lordon): 20 piexl move it to conf
352 float min_x = static_cast<float>(boundary_len);
353 float min_y = static_cast<float>(boundary_len);
354 float max_x = static_cast<float>(image_width - boundary_len);
355 float max_y = static_cast<float>(image_height - boundary_len);
356 double eps = 1e-2;
357
358 if (obj->camera_supplement.box.xmin <= min_x || obj->camera_supplement.box.xmax >= max_x
359 || obj->camera_supplement.box.ymin <= min_y || obj->camera_supplement.box.ymax >= max_y) {
360 // Truncation assignment based on bbox positions
361 if ((obj->camera_supplement.box.ymin < eps) || (obj->camera_supplement.box.ymax >= 1.0 - eps)) {
362 obj->camera_supplement.truncated_vertical = 0.5;
363 } else {
364 obj->camera_supplement.truncated_vertical = 0.0;
365 }
366 if ((obj->camera_supplement.box.xmin < eps) || (obj->camera_supplement.box.xmax >= 1.0 - eps)) {
367 obj->camera_supplement.truncated_horizontal = 0.5;
368 } else {
369 obj->camera_supplement.truncated_horizontal = 0.0;
370 }
371 }
372}
373
375 const std::shared_ptr<base::Blob<float>> &objects_blob,
376 const yolox3d::ModelParam &model_param,
377 const yolox3d::NMSParam &nms_param,
378 const int width,
379 const int height,
380 const int image_width,
381 const int image_height,
382 std::vector<base::ObjectPtr> *objects) {
383 ACHECK(objects_blob != nullptr);
384 ACHECK(objects != nullptr);
385
386 const float *detect_data = objects_blob->cpu_data();
387 ACHECK(detect_data != nullptr);
388
389 float scale = std::min(
390 model_param.resize().width() / (image_width * 1.0), model_param.resize().height() / (image_height * 1.0));
391
392 std::vector<std::vector<float>> detect_objects;
393 YoloxGetAllObjects(detect_data, model_param, scale, &detect_objects);
394 AINFO << "Yolox objects after nms is " << detect_objects.size();
395
396 // center_x, center_y, width, height, class_score, class_label
397 objects->clear();
398 for (const std::vector<float> &detect : detect_objects) {
399 const int label = detect[kLabelIndex];
400 // ignore unknown
401 if (7 <= label) {
402 continue;
403 }
404
405 base::ObjectPtr obj = nullptr;
406 obj.reset(new base::Object);
407
408 obj->sub_type = kYoloSubTypeYolox[label];
409 obj->type = base::kSubType2TypeMap.at(obj->sub_type);
410 obj->confidence = detect[kScoreIndex];
411
412 obj->type_probs.assign(static_cast<int>(base::ObjectType::MAX_OBJECT_TYPE), 0);
413 obj->sub_type_probs.assign(static_cast<int>(base::ObjectSubType::MAX_OBJECT_TYPE), 0);
414 obj->type_probs[static_cast<int>(obj->type)] = detect[kLabelIndex];
415 obj->sub_type_probs[static_cast<int>(obj->sub_type)] = detect[kLabelIndex];
416
417 YoloxFillBase(detect, width, height, image_width, image_height, obj);
418
419 YoloxFillBbox3d(model_param, detect, obj);
420
421 // TODO(lordon): add ignore truncated bool param
422 YoloxTruncated(obj, image_width, image_height);
423
424 objects->push_back(obj);
425 }
426}
427
428} // namespace camera
429} // namespace perception
430} // 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 2d bbox 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