23#include "modules/common_msgs/perception_msgs/traffic_light_detection.pb.h"
33static const char ROADMAP_IMG_PATH[] =
34 "/apollo/modules/planning/planning_base/learning_based/data/semantic_map/"
35 "sunnyvale_with_two_offices.png";
36static const char SPEEDLIMITMAP_IMG_PATH[] =
37 "/apollo/modules/planning/planning_base/learning_based/data/semantic_map/"
38 "sunnyvale_with_two_offices_speedlimit.png";
40BirdviewImgFeatureRenderer::BirdviewImgFeatureRenderer() {}
50 const std::string map_name =
51 FLAGS_map_dir.substr(FLAGS_map_dir.find_last_of(
"/") + 1);
52 if (map_name !=
"sunnyvale_with_two_offices" && map_name !=
"sunnyvale") {
53 AERROR <<
"Map other than sunnyvale_with_two_offices are not supported";
56 if (map_name ==
"sunnyvale") {
57 AWARN <<
"use sunnyvale_with_two_offices for sunnyvale for now";
61 map_bottom_left_point_x_ = 585875.3316302994;
62 map_bottom_left_point_y_ = 4139916.6342316796;
63 bool roadmap_img_status = LoadRoadMap(ROADMAP_IMG_PATH);
64 bool speedlimit_img_status = LoadSpeedlimitMap(SPEEDLIMITMAP_IMG_PATH);
66 if (!roadmap_img_status || !speedlimit_img_status) {
67 AERROR <<
"Base map image read failed";
72 cv::Mat(config_.
height(), config_.
width(), CV_8UC1, cv::Scalar(0));
74 cv::Mat(config_.
height(), config_.
width(), CV_8UC1, cv::Scalar(0));
76 bool render_ego_point_status = RenderEgoCurrentPoint(&ego_cur_point_img_);
77 bool render_ego_box_status = RenderEgoCurrentBox(&ego_cur_box_img_);
79 if (!render_ego_point_status || !render_ego_box_status) {
80 AERROR <<
"Ego point or box img rendering failed";
84 if (base_roadmap_img_.size[0] != base_speedlimit_img_.size[0] ||
85 base_roadmap_img_.size[1] != base_speedlimit_img_.size[1]) {
86 AERROR <<
"base map sizes doesn't match";
90 std::vector<cv::Mat> merge_imgs = {ego_cur_point_img_, ego_cur_box_img_};
91 cv::merge(merge_imgs, stacked_ego_cur_status_img_);
98 int ego_trajectory_point_history_size =
99 learning_data_frame.adc_trajectory_point_size();
100 if (ego_trajectory_point_history_size < 1) {
101 AERROR <<
"Ego past history is empty";
106 cv::Mat(config_.
height(), config_.
width(), CV_8UC1, cv::Scalar(0));
108 cv::Mat(config_.
height(), config_.
width(), CV_8UC1, cv::Scalar(0));
110 cv::Mat(config_.
height(), config_.
width(), CV_8UC1, cv::Scalar(0));
112 cv::Mat(config_.
height(), config_.
width(), CV_8UC3, cv::Scalar(0, 0, 0));
114 cv::Mat(config_.
height(), config_.
width(), CV_8UC1, cv::Scalar(0));
115 cv::Mat speed_limit =
116 cv::Mat(config_.
height(), config_.
width(), CV_8UC3, cv::Scalar(0, 0, 0));
117 cv::Mat traffic_light =
118 cv::Mat(config_.
height(), config_.
width(), CV_8UC1, cv::Scalar(0));
121 ego_trajectory_point_history_size - 1);
122 const double current_time_sec = current_traj_point.
timestamp_sec();
123 const auto& current_path_point =
124 current_traj_point.trajectory_point().path_point();
125 const double current_x = current_path_point.x();
126 const double current_y = current_path_point.y();
127 const double current_heading = current_path_point.theta();
129 if (!RenderEgoPastPoint(learning_data_frame, current_time_sec, current_x,
130 current_y, current_heading, &ego_past)) {
131 AERROR <<
"RenderEgoPastPoint failed";
134 if (!RenderObsPastBox(learning_data_frame, current_time_sec, &obs_past)) {
135 AERROR <<
"RenderObsPastBox failed";
138 if (!RenderObsFutureBox(learning_data_frame, current_time_sec, &obs_future)) {
139 AERROR <<
"RenderObsFutureBox failed";
142 if (!RenderLocalRoadMap(current_x, current_y, current_heading, &road_map)) {
143 AERROR <<
"RenderLocalRoadMap failed";
146 if (!RenderRouting(learning_data_frame, current_x, current_y, current_heading,
148 AERROR <<
"RenderRouting failed";
151 if (!RenderLocalSpeedlimitMap(current_x, current_y, current_heading,
153 AERROR <<
"RenderLocalSpeedlimitMap failed";
156 if (!RenderTrafficLight(learning_data_frame, current_x, current_y,
157 current_heading, &traffic_light)) {
158 AERROR <<
"RenderTrafficLight failed";
162 std::vector<cv::Mat> merge_imgs = {ego_cur_box_img_, ego_past, obs_past,
163 obs_future, road_map, routing,
164 speed_limit, traffic_light};
165 cv::merge(merge_imgs, *img_feature);
171 int ego_trajectory_point_history_size =
172 learning_data_frame.adc_trajectory_point_size();
173 if (ego_trajectory_point_history_size < 1) {
174 AERROR <<
"Ego past history is empty";
179 cv::Mat(config_.
height(), config_.
width(), CV_8UC3, cv::Scalar(0, 0, 0));
182 ego_trajectory_point_history_size - 1);
183 const double current_time_sec = current_traj_point.
timestamp_sec();
184 const auto& current_path_point =
185 current_traj_point.trajectory_point().path_point();
186 const double current_x = current_path_point.x();
187 const double current_y = current_path_point.y();
188 const double current_heading = current_path_point.theta();
190 if (!RenderLocalRoadMap(current_x, current_y, current_heading, &bgr_canvas)) {
191 AERROR <<
"RenderLocalRoadMap failed";
194 if (!RenderRouting(learning_data_frame, current_x, current_y, current_heading,
196 AERROR <<
"RenderRouting failed";
199 if (!RenderTrafficLight(learning_data_frame, current_x, current_y,
200 current_heading, &bgr_canvas)) {
201 AERROR <<
"RenderTrafficLight failed";
204 if (!RenderObsPastBox(learning_data_frame, current_time_sec, &bgr_canvas)) {
205 AERROR <<
"RenderObsPastBox failed";
208 if (!RenderObsFutureBox(learning_data_frame, current_time_sec, &bgr_canvas)) {
209 AERROR <<
"RenderObsFutureBox failed";
212 if (!RenderEgoCurrentBox(&bgr_canvas)) {
213 AERROR <<
"RenderEgoCurrentBox failed";
216 if (!RenderEgoPastPoint(learning_data_frame, current_time_sec, current_x,
217 current_y, current_heading, &bgr_canvas)) {
218 AERROR <<
"RenderEgoPastPoint failed";
222 bgr_canvas.copyTo(*img_feature);
229 stacked_ego_cur_status_img_.copyTo(*img_feature);
235 ego_cur_point_img_.copyTo(*img_feature);
241 ego_cur_box_img_.copyTo(*img_feature);
245bool BirdviewImgFeatureRenderer::LoadRoadMap(
const std::string& map_file) {
246 base_roadmap_img_ = cv::imread(map_file);
247 return !base_roadmap_img_.empty();
250bool BirdviewImgFeatureRenderer::LoadSpeedlimitMap(
251 const std::string& map_file) {
252 base_speedlimit_img_ = cv::imread(map_file);
253 return !base_speedlimit_img_.empty();
256bool BirdviewImgFeatureRenderer::RenderLocalRoadMap(
257 const double ego_current_x,
const double ego_current_y,
258 const double ego_current_heading, cv::Mat* img_feature) {
259 return CropByPose(ego_current_x, ego_current_y, ego_current_heading,
260 base_roadmap_img_, img_feature);
263bool BirdviewImgFeatureRenderer::RenderLocalSpeedlimitMap(
264 const double ego_current_x,
const double ego_current_y,
265 const double ego_current_heading, cv::Mat* img_feature) {
266 return CropByPose(ego_current_x, ego_current_y, ego_current_heading,
267 base_speedlimit_img_, img_feature);
270bool BirdviewImgFeatureRenderer::RenderEgoCurrentPoint(
271 cv::Mat* img_feature,
const cv::Scalar& gray_scale,
272 const cv::Scalar& bgr_color) {
274 if ((*img_feature).channels() == 3) {
283bool BirdviewImgFeatureRenderer::RenderEgoCurrentBox(
284 cv::Mat* img_feature,
const cv::Scalar& gray_scale,
285 const cv::Scalar& bgr_color) {
287 if ((*img_feature).channels() == 3) {
293 std::vector<cv::Point2i> box_corner_points =
294 GetAffinedBoxImgIdx(0.0, 0.0, M_PI_2,
296 std::make_pair(param.front_edge_to_center(),
297 param.left_edge_to_center()),
298 std::make_pair(param.front_edge_to_center(),
299 -param.right_edge_to_center()),
300 std::make_pair(-param.back_edge_to_center(),
301 -param.right_edge_to_center()),
302 std::make_pair(-param.back_edge_to_center(),
303 param.left_edge_to_center()),
308 std::vector<std::vector<cv::Point>>({std::move(box_corner_points)}),
313bool BirdviewImgFeatureRenderer::RenderEgoPastPoint(
314 const LearningDataFrame& learning_data_frame,
const double current_time_sec,
315 const double ego_current_x,
const double ego_current_y,
316 const double ego_current_heading, cv::Mat* img_feature,
317 const cv::Scalar& gray_scale,
const cv::Scalar& bgr_color) {
319 if ((*img_feature).channels() == 3) {
324 const auto& ego_past = learning_data_frame.adc_trajectory_point();
325 cv::Scalar gradual_change_color;
326 for (
const auto& ego_past_point : ego_past) {
327 const double relative_time =
328 current_time_sec - ego_past_point.timestamp_sec();
332 gradual_change_color =
334 cv::circle(*img_feature,
335 GetAffinedPointImgIdx(
336 ego_past_point.trajectory_point().path_point().x(),
337 ego_past_point.trajectory_point().path_point().y(),
338 ego_current_x, ego_current_y, M_PI_2 - ego_current_heading),
339 2, gradual_change_color, -1);
344bool BirdviewImgFeatureRenderer::RenderObsPastBox(
345 const LearningDataFrame& learning_data_frame,
const double current_time_sec,
346 cv::Mat* img_feature,
const cv::Scalar& gray_scale,
347 const cv::Scalar& bgr_color) {
349 if ((*img_feature).channels() == 3) {
355 for (
const auto& obstacle : learning_data_frame.obstacle()) {
356 if (obstacle.obstacle_trajectory().evaluated_trajectory_point_size() == 0) {
359 const double obstacle_box_length = obstacle.length();
360 const double obstacle_box_width = obstacle.width();
361 const auto& past_traj =
362 obstacle.obstacle_trajectory().evaluated_trajectory_point();
363 cv::Scalar gradual_change_color;
364 for (
const auto& traj_point : past_traj) {
365 const double relative_time =
366 current_time_sec - traj_point.timestamp_sec();
370 gradual_change_color =
372 const auto& path_point = traj_point.trajectory_point().path_point();
373 std::vector<cv::Point2i> box_corner_points = GetAffinedBoxImgIdx(
374 path_point.x(), path_point.y(), M_PI_2 + path_point.theta(),
376 std::make_pair(obstacle_box_length / 2, obstacle_box_width / 2),
377 std::make_pair(obstacle_box_length / 2, -obstacle_box_width / 2),
378 std::make_pair(-obstacle_box_length / 2, -obstacle_box_width / 2),
379 std::make_pair(-obstacle_box_length / 2, obstacle_box_width / 2),
384 std::vector<std::vector<cv::Point>>({std::move(box_corner_points)}),
392bool BirdviewImgFeatureRenderer::RenderObsFutureBox(
393 const LearningDataFrame& learning_data_frame,
const double current_time_sec,
394 cv::Mat* img_feature,
const cv::Scalar& gray_scale,
395 const cv::Scalar& bgr_color) {
397 if ((*img_feature).channels() == 3) {
403 for (
const auto& obstacle : learning_data_frame.obstacle()) {
404 const double obstacle_box_length = obstacle.length();
405 const double obstacle_box_width = obstacle.width();
407 if (obstacle.obstacle_prediction().is_static()) {
408 const auto& past_traj_points_size =
409 obstacle.obstacle_trajectory().evaluated_trajectory_point_size();
410 if (past_traj_points_size == 0) {
411 AERROR <<
"obstacle[" << obstacle.id()
412 <<
"] is static without tracking history points";
415 const auto& last_past_traj_point =
416 obstacle.obstacle_trajectory().evaluated_trajectory_point(
417 past_traj_points_size - 1);
418 const auto& path_point =
419 last_past_traj_point.trajectory_point().path_point();
420 std::vector<cv::Point2i> box_corner_points = GetAffinedBoxImgIdx(
421 path_point.x(), path_point.y(), M_PI_2 + path_point.theta(),
423 std::make_pair(obstacle_box_length / 2, obstacle_box_width / 2),
424 std::make_pair(obstacle_box_length / 2, -obstacle_box_width / 2),
425 std::make_pair(-obstacle_box_length / 2, -obstacle_box_width / 2),
426 std::make_pair(-obstacle_box_length / 2, obstacle_box_width / 2),
431 std::vector<std::vector<cv::Point>>({std::move(box_corner_points)}),
436 const auto& predicted_traj_size =
437 obstacle.obstacle_prediction().trajectory_size();
438 if (predicted_traj_size == 0) {
439 AWARN <<
"obstacle[" << obstacle.id() <<
"] has no prediction trajectory";
444 int max_prob_idx = 0;
445 for (
int i = 0; i < predicted_traj_size; ++i) {
446 const auto& trajectory = obstacle.obstacle_prediction().trajectory(i);
447 if (trajectory.probability() > max_prob) {
448 max_prob = trajectory.probability();
453 const auto& predicted_traj = obstacle.obstacle_prediction()
454 .trajectory(max_prob_idx)
456 cv::Scalar gradual_change_color;
457 for (
const auto& traj_point : predicted_traj) {
458 const double relative_time =
459 traj_point.timestamp_sec() - current_time_sec;
463 gradual_change_color =
465 const auto& path_point = traj_point.trajectory_point().path_point();
466 std::vector<cv::Point2i> box_corner_points = GetAffinedBoxImgIdx(
467 path_point.x(), path_point.y(), M_PI_2 + path_point.theta(),
469 std::make_pair(obstacle_box_length / 2, obstacle_box_width / 2),
470 std::make_pair(obstacle_box_length / 2, -obstacle_box_width / 2),
471 std::make_pair(-obstacle_box_length / 2, -obstacle_box_width / 2),
472 std::make_pair(-obstacle_box_length / 2, obstacle_box_width / 2),
477 std::vector<std::vector<cv::Point>>({std::move(box_corner_points)}),
484bool BirdviewImgFeatureRenderer::RenderTrafficLight(
485 const LearningDataFrame& learning_data_frame,
const double ego_current_x,
486 const double ego_current_y,
const double ego_current_heading,
487 cv::Mat* img_feature,
const cv::Scalar& gray_scale,
488 const cv::Scalar& bgr_color) {
490 if ((*img_feature).channels() == 3) {
495 for (
const auto& traffic_light_status :
496 learning_data_frame.traffic_light_detection().traffic_light()) {
497 const auto& traffic_light =
500 if (traffic_light ==
nullptr) {
501 AERROR <<
"traffic_light [" << traffic_light_status.id() <<
"] not found";
505 color = cv::Scalar(255);
506 }
else if (traffic_light_status.color() ==
508 color = cv::Scalar(192);
510 color = cv::Scalar(128);
512 for (
const auto& overlap_id : traffic_light->signal().overlap_id()) {
513 const auto& overlap =
515 if (overlap ==
nullptr) {
516 AERROR <<
"overlap [" << overlap_id.id() <<
"] not found";
519 for (
const auto& overlap_object : overlap->overlap().object()) {
520 if (!overlap_object.has_lane_overlap_info()) {
524 overlap_object.id());
525 if (lane ==
nullptr) {
526 AERROR <<
"lane [" << overlap_object.id().id() <<
"] not found";
529 for (
const auto& segment : lane->lane().central_curve().segment()) {
530 const int segment_point_size = segment.line_segment().point_size();
531 for (
int i = 0; i < segment_point_size - 1; ++i) {
532 const auto& p0 = GetAffinedPointImgIdx(
533 segment.line_segment().point(i).x(),
534 segment.line_segment().point(i).y(), ego_current_x,
535 ego_current_y, M_PI_2 - ego_current_heading);
536 const auto& p1 = GetAffinedPointImgIdx(
537 segment.line_segment().point(i + 1).x(),
538 segment.line_segment().point(i + 1).y(), ego_current_x,
539 ego_current_y, M_PI_2 - ego_current_heading);
540 cv::line(*img_feature, p0, p1, color, 4);
550bool BirdviewImgFeatureRenderer::RenderRouting(
551 const LearningDataFrame& learning_data_frame,
const double ego_current_x,
552 const double ego_current_y,
const double ego_current_heading,
553 cv::Mat* img_feature,
const cv::Scalar& gray_scale,
554 const cv::Scalar& bgr_color) {
556 if ((*img_feature).channels() == 3) {
562 const int routing_lanes_size =
563 learning_data_frame.routing().local_routing_lane_id_size();
564 if (routing_lanes_size == 0) {
565 AERROR <<
"routing is empty";
569 cv::Scalar color_gradient = color / routing_lanes_size;
570 for (
int i = 0; i < routing_lanes_size; ++i) {
571 const cv::Scalar routing_color = color - color_gradient * i;
574 learning_data_frame.routing().local_routing_lane_id(i)));
575 if (lane ==
nullptr) {
577 << learning_data_frame.routing().local_routing_lane_id(i)
581 for (
const auto& segment : lane->lane().central_curve().segment()) {
582 const int segment_point_size = segment.line_segment().point_size();
583 for (
int i = 0; i < segment_point_size - 1; ++i) {
584 const auto& p0 = GetAffinedPointImgIdx(
585 segment.line_segment().point(i).x(),
586 segment.line_segment().point(i).y(), ego_current_x, ego_current_y,
587 M_PI_2 - ego_current_heading);
588 const auto& p1 = GetAffinedPointImgIdx(
589 segment.line_segment().point(i + 1).x(),
590 segment.line_segment().point(i + 1).y(), ego_current_x,
591 ego_current_y, M_PI_2 - ego_current_heading);
592 cv::line(*img_feature, p0, p1, routing_color, 12);
599bool BirdviewImgFeatureRenderer::CropByPose(
const double ego_x,
601 const double ego_heading,
602 const cv::Mat& base_map,
603 cv::Mat* img_feature) {
606 cv::Point2i ego_img_idx = GetPointImgIdx(ego_x - map_bottom_left_point_x_,
607 ego_y - map_bottom_left_point_y_, 0,
608 base_roadmap_img_.size[0]);
609 if (ego_img_idx.x < 0 || ego_img_idx.x + 1 > base_roadmap_img_.size[1] ||
610 ego_img_idx.y < 0 || ego_img_idx.y + 1 > base_roadmap_img_.size[0]) {
611 AERROR <<
"ego vehicle position out of bound of base map";
615 const int rough_radius =
static_cast<int>(sqrt(
617 if (ego_img_idx.x - rough_radius < 0 || ego_img_idx.y - rough_radius < 0) {
618 AERROR <<
"cropping out of bound of base map";
622 cv::Rect rough_rect(ego_img_idx.x - rough_radius,
623 ego_img_idx.y - rough_radius, 2 * rough_radius,
625 cv::Mat rotation_matrix =
626 cv::getRotationMatrix2D(cv::Point2i(rough_radius, rough_radius),
627 90.0 - ego_heading * 180.0 / M_PI, 1.0);
629 cv::warpAffine(base_map(rough_rect), rotated_mat, rotation_matrix,
630 base_map(rough_rect).size());
632 cv::Rect fine_rect(rough_radius - config_.
ego_idx_x(),
636 rotated_mat(fine_rect).copyTo(*img_feature);
640cv::Point2i BirdviewImgFeatureRenderer::GetPointImgIdx(
641 const double local_point_x,
const double local_point_y,
642 const int center_point_idx_x,
const int center_point_idx_y) {
645 static_cast<int>(local_point_x / config_.
resolution()),
647 static_cast<int>(local_point_y / config_.
resolution()));
650cv::Point2i BirdviewImgFeatureRenderer::GetAffinedPointImgIdx(
651 const double point_x,
const double point_y,
const double center_x,
652 const double center_y,
const double theta) {
653 const double local_x = point_x - center_x;
654 const double local_y = point_y - center_y;
656 const double rotated_local_x = local_x *
cos(theta) - local_y *
sin(theta);
657 const double rotated_local_y = local_x *
sin(theta) + local_y *
cos(theta);
659 return GetPointImgIdx(rotated_local_x, rotated_local_y, config_.
ego_idx_x(),
663std::vector<cv::Point2i> BirdviewImgFeatureRenderer::GetAffinedBoxImgIdx(
664 const double box_center_x,
const double box_center_y,
665 const double box_theta,
666 const std::vector<std::pair<double, double>>& box_corner_points,
667 const double center_x,
const double center_y,
const double theta) {
668 const double local_box_center_x = box_center_x - center_x;
669 const double local_box_center_y = box_center_y - center_y;
671 const double affined_local_box_center_x =
672 local_box_center_x *
cos(theta) - local_box_center_y *
sin(theta);
673 const double affined_local_box_center_y =
674 local_box_center_x *
sin(theta) + local_box_center_y *
cos(theta);
676 std::vector<cv::Point2i> affined_box;
678 for (
const auto& point : box_corner_points) {
679 const double affined_corner_x = point.first *
cos(box_theta) -
680 point.second *
sin(box_theta) +
681 affined_local_box_center_x;
682 const double affined_corner_y = point.first *
sin(box_theta) +
683 point.second *
cos(box_theta) +
684 affined_local_box_center_y;
685 affined_box.push_back(GetPointImgIdx(affined_corner_x, affined_corner_y,
Define the birdview img feature renderer class
static const VehicleConfig & GetConfig()
Get the current vehicle configuration.
static const HDMap & BaseMap()
LaneInfoConstPtr GetLaneById(const Id &id) const
OverlapInfoConstPtr GetOverlapById(const Id &id) const
SignalInfoConstPtr GetSignalById(const Id &id) const
bool RenderBGREnv(const LearningDataFrame &learning_data_frame, cv::Mat *img_feature)
generate bgr img as input feature for certain model
bool RenderCurrentEgoPoint(const LearningDataFrame &learning_data_frame, cv::Mat *img_feature)
generate a single channel img
bool Init(const PlanningSemanticMapConfig &config)
initialize renderer
bool RenderCurrentEgoBox(const LearningDataFrame &learning_data_frame, cv::Mat *img_feature)
generate a single channel img
bool RenderMultiChannelEnv(const LearningDataFrame &learning_data_frame, cv::Mat *img_feature)
generate multi-channel img as input feature for certain model
bool RenderCurrentEgoStatus(const LearningDataFrame &learning_data_frame, cv::Mat *img_feature)
generate a two channel img, one for cur box, one for cur point
Planning module main class.
apollo::hdmap::Id MakeMapId(const std::string &id)
create a Map ID given a string.
optional VehicleParam vehicle_param
optional double timestamp_sec
repeated ADCTrajectoryPoint adc_trajectory_point
optional double max_obs_past_horizon
optional double max_obs_future_horizon
optional double max_ego_past_horizon
optional double resolution