Apollo 10.0
自动驾驶开放平台
visualization_engine.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2017 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 <limits>
20
21#include <boost/filesystem.hpp>
22
23#include "cyber/common/file.h"
24#include "cyber/common/log.h"
25
26namespace apollo {
27namespace localization {
28namespace msf {
29
32
33#define PI 3.1415926535897932346
34
35unsigned char color_table[3][3] = {{0, 0, 255}, {0, 255, 0}, {255, 0, 0}};
36
37const char car_img_path[3][1024] = {
38 "modules/localization/msf/local_tool/local_visualization/img/red_car.png",
39 "modules/localization/msf/local_tool/local_visualization/img/green_car.png",
40 "modules/localization/msf/local_tool/local_visualization/img/blue_car.png"};
41
42// =================VisualizationEngine=================
43bool MapImageKey::operator<(const MapImageKey &key) const {
44 // Compare elements by priority.
45 return std::forward_as_tuple(level, zone_id, node_north_id, node_east_id) <
46 std::forward_as_tuple(key.level, key.zone_id, key.node_north_id,
47 key.node_east_id);
48}
49
50// =================MapImageCache=================
51bool MapImageCache::Get(const MapImageKey &key, cv::Mat *image) {
52 auto found_iter = _map.find(key);
53 if (found_iter == _map.end()) {
54 return false;
55 }
56 // move the corresponding key to list front
57 _list.splice(_list.begin(), _list, found_iter->second);
58 *image = found_iter->second->second;
59 return true;
60}
61
62void MapImageCache::Set(const MapImageKey &key, const cv::Mat &image) {
63 auto found_iter = _map.find(key);
64 if (found_iter != _map.end()) {
65 // move the corresponding key to list front
66 _list.splice(_list.begin(), _list, found_iter->second);
67 found_iter->second->second = image;
68 return;
69 }
70 // reached capacity, remove node in list, remove key in map
71 if (_map.size() == _capacity) {
72 MapImageKey key_to_del = _list.back().first;
73 _list.back().second.release();
74 _list.pop_back();
75 _map.erase(key_to_del);
76 }
77 _list.emplace_front(key, image);
78 _map[key] = _list.begin();
79}
80
81// =================VisualizationEngine=================
83 : map_image_cache_(20),
84 image_window_(1024, 1024, CV_8UC3, cv::Scalar(0, 0, 0)),
85 big_window_(3072, 3072, CV_8UC3),
86 tips_window_(48, 1024, CV_8UC3, cv::Scalar(0, 0, 0)) {}
87
88bool VisualizationEngine::Init(const std::string &map_folder,
89 const std::string &map_visual_folder,
90 const VisualMapParam &map_param,
91 const unsigned int resolution_id,
92 const int zone_id,
93 const Eigen::Affine3d &extrinsic,
94 const unsigned int loc_info_num) {
95 map_folder_ = map_folder;
96 map_visual_folder_ = map_visual_folder;
97 map_param_ = map_param;
98 velodyne_extrinsic_ = extrinsic;
99 loc_info_num_ = loc_info_num;
100 expected_car_loc_id_ = loc_info_num;
101
102 trajectory_groups_.resize(loc_info_num_);
103
104 for (unsigned int i = 0; i < loc_info_num_; i++) {
105 cv::Mat tem_mat = cv::imread(car_img_path[i % 3]);
106 if (tem_mat.empty()) {
107 is_draw_car_ = false;
108 break;
109 }
110 car_img_mats_.push_back(tem_mat);
111 }
112
113 if (resolution_id_ >= map_param_.map_resolutions.size()) {
114 AERROR << "Invalid resolution id.";
115 return false;
116 }
117
118 zone_id_ = zone_id;
119 resolution_id_ = resolution_id;
120
121 cloud_img_ =
122 cv::Mat(cv::Size(map_param_.map_node_size_x, map_param_.map_node_size_y),
123 CV_8UC3);
124 cloud_img_mask_ =
125 cv::Mat(cv::Size(map_param_.map_node_size_x, map_param_.map_node_size_y),
126 CV_8UC1);
127
128 Preprocess(map_folder, map_visual_folder);
129
130 std::string params_file = image_visual_resolution_path_ + "/param.txt";
131 bool success = InitOtherParams(params_file);
132 if (!success) {
133 AERROR << "Init other params failed.";
134 }
135
136 cv::namedWindow(window_name_, cv::WINDOW_NORMAL);
137 cv::resizeWindow(window_name_, 1024, 1024);
138
139 is_init_ = true;
140
141 return true;
142}
143
146 const ::apollo::common::EigenVector3dVec &cloud) {
147 if (!is_init_) {
148 AERROR << "Visualziation should be init first.";
149 return;
150 }
151
152 if (loc_infos.size() != loc_info_num_) {
153 AERROR << "Please check the localization info num.";
154 return;
155 }
156
157 cur_loc_infos_ = std::move(loc_infos);
158
159 if (!UpdateCarLocId(expected_car_loc_id_)) {
160 if (!UpdateCarLocId(car_loc_id_)) {
161 if (!UpdateCarLocId()) {
162 return;
163 }
164 } else {
165 if (expected_car_loc_id_ == loc_info_num_) {
166 expected_car_loc_id_ = car_loc_id_;
167 }
168 }
169 }
170
171 UpdateTrajectoryGroups();
172 cloud_ = cloud;
173 Draw();
174}
175
177 auto_play_ = auto_play;
178}
179
180void VisualizationEngine::Preprocess(const std::string &map_folder,
181 const std::string &map_visual_folder) {
182 std::string image_path = map_folder_ + "/image";
183 std::string image_visual_path = map_visual_folder;
184 char buf[256];
185 snprintf(buf, sizeof(buf), "/%03u", resolution_id_);
186 image_visual_resolution_path_ = image_visual_path + buf;
187 AINFO << "image_visual_resolution_path: " << image_visual_resolution_path_;
188 std::string image_resolution_path = image_path + buf;
189 AINFO << "image_resolution_path: " << image_resolution_path;
190
191 if (!EnsureDirectory(image_visual_path)) {
192 AERROR << "image_visual_path: " << image_visual_path
193 << " cannot be created.";
194 return;
195 }
196
197 if (DirectoryExists(image_visual_resolution_path_)) {
198 AINFO << "image_visual_resolution_path: " << image_visual_resolution_path_
199 << "already exists.";
200 return;
201 }
202 if (!EnsureDirectory(image_visual_resolution_path_)) {
203 AERROR << "image_visual_resolution_path: " << image_visual_resolution_path_
204 << " cannot be created.";
205 return;
206 }
207
208 boost::filesystem::path image_resolution_path_boost(image_resolution_path);
209 // push path of map's images to vector
210 std::vector<std::string> map_bin_path;
211 boost::filesystem::recursive_directory_iterator end_iter;
212 boost::filesystem::recursive_directory_iterator iter(
213 image_resolution_path_boost);
214 for (; iter != end_iter; ++iter) {
215 if (!boost::filesystem::is_directory(*iter)) {
216 if (iter->path().extension() == ".png") {
217 map_bin_path.push_back(iter->path().string());
218 }
219 } else {
220 std::string tmp = iter->path().string();
221 tmp = tmp.substr(image_resolution_path.length(), tmp.length());
222 tmp = image_visual_resolution_path_ + tmp;
223 EnsureDirectory(tmp);
224 }
225 }
226
227 if (map_bin_path.empty()) {
228 return;
229 }
230
231 GenerateMutiResolutionImages(map_bin_path,
232 static_cast<int>(image_resolution_path.length()),
233 image_visual_resolution_path_);
234}
235
236void VisualizationEngine::Draw() {
237 UpdateLevel();
238
239 if (follow_car_) {
240 SetViewCenter(car_pose_.translation()[0], car_pose_.translation()[1]);
241 }
242
243 MapImageKey iamge_key;
244 CoordToImageKey(_view_center, &iamge_key);
245
246 // get 3*3 images on that level
247 for (int i = -1; i <= 1; ++i) {
248 for (int j = -1; j <= 1; ++j) {
249 MapImageKey key = iamge_key;
250 key.node_north_id += i * cur_stride_;
251 key.node_east_id += j * cur_stride_;
252 if (LoadImageToCache(key)) {
253 map_image_cache_.Get(key, &subMat_[i + 1][j + 1]);
254 } else {
255 subMat_[i + 1][j + 1] =
256 cv::Mat(1024, 1024, CV_8UC3, cv::Scalar(0, 0, 0));
257 }
258 }
259 }
260
261 for (int i = 0; i < 3; ++i) {
262 for (int j = 0; j < 3; ++j) {
263 subMat_[i][j].copyTo(
264 big_window_(cv::Rect(j * 1024, i * 1024, 1024, 1024)));
265 }
266 }
267
268 cv::Point map_grid_index =
269 CoordToMapGridIndex(_view_center, resolution_id_, cur_stride_);
270 cv::Point node_grid_index = MapGridIndexToNodeGridIndex(map_grid_index);
271 cv::Point bias = node_grid_index - map_grid_index;
272
273 DrawTrajectory(bias);
274 DrawCloud(bias);
275 DrawLoc(bias);
276 DrawStd(bias);
277
278 int width = static_cast<int>(1024 * cur_scale_) / cur_stride_;
279 int dis = width / 2;
280 int left_top_x = node_grid_index.x + 1024 - dis;
281 int left_top_y = node_grid_index.y + 1024 - dis;
282
283 cv::resize(big_window_(cv::Rect(left_top_x, left_top_y, width, width)),
284 image_window_, cv::Size(1024, 1024), 0, 0, cv::INTER_LINEAR);
285 cv::flip(image_window_, image_window_, 0);
286
287 DrawLegend();
288 DrawInfo();
289 DrawTips();
290
291 cv::namedWindow(window_name_, cv::WINDOW_NORMAL);
292 // cv::setMouseCallback(window_name_, processMouse, 0);
293 cv::imshow(window_name_, image_window_);
294
295 int waitTime = 0;
296 if (auto_play_) {
297 waitTime = 10;
298 }
299 ProcessKey(cv::waitKey(waitTime));
300}
301
302void VisualizationEngine::DrawTrajectory(const cv::Point &bias) {
303 AINFO << "Draw trajectory.";
304 if (cur_level_ == 0 && is_draw_trajectory_) {
305 unsigned int i = (car_loc_id_ + 1) % loc_info_num_;
306 for (unsigned int k = 0; k < loc_info_num_; k++) {
307 std::map<double, Eigen::Vector2d> &trj = trajectory_groups_[i];
308 if (trj.empty()) {
309 i = (i + 1) % loc_info_num_;
310 continue;
311 }
312
313 cv::Point lt;
314 cv::Point pre_lt;
315 std::map<double, Eigen::Vector2d>::iterator iter = trj.end();
316 --iter;
317 const Eigen::Vector2d &loc_2d = iter->second;
318 lt = CoordToMapGridIndex(loc_2d, resolution_id_, cur_stride_);
319 lt = lt + bias + cv::Point(1024, 1024);
320
321 if (lt.x >= 0 && lt.y >= 0 && lt.x < 1024 * 3 && lt.y < 1024 * 3) {
322 unsigned char b = color_table[i % 3][0];
323 unsigned char g = color_table[i % 3][1];
324 unsigned char r = color_table[i % 3][2];
325 cv::circle(big_window_, lt, 4, cv::Scalar(b, g, r), 1);
326 pre_lt = lt;
327
328 int count = 0;
329 while (iter != trj.begin() && count < 500) {
330 --iter;
331 const Eigen::Vector2d &loc_2d = iter->second;
332 lt = CoordToMapGridIndex(loc_2d, resolution_id_, cur_stride_);
333 lt = lt + bias + cv::Point(1024, 1024);
334
335 if (lt.x >= 0 && lt.y >= 0 && lt.x < 1024 * 3 && lt.y < 1024 * 3) {
336 unsigned char b = color_table[i % 3][0];
337 unsigned char g = color_table[i % 3][1];
338 unsigned char r = color_table[i % 3][2];
339
340 cv::circle(big_window_, lt, 3, cv::Scalar(b, g, r), 1);
341 cv::line(big_window_, pre_lt, lt, cv::Scalar(b, g, r), 1);
342 pre_lt = lt;
343
344 ++count;
345 }
346 }
347 }
348
349 i = (i + 1) % loc_info_num_;
350 }
351 }
352}
353
354void VisualizationEngine::DrawLoc(const cv::Point &bias) {
355 AINFO << "Draw loc.";
356 if (cur_level_ == 0) {
357 unsigned int i = (car_loc_id_ + 1) % loc_info_num_;
358 for (unsigned int k = 0; k < loc_info_num_; k++) {
359 LocalizatonInfo &loc_info = cur_loc_infos_[i];
360 cv::Point lt;
361 const Eigen::Translation3d &loc = loc_info.location;
362
363 Eigen::Vector2d loc_2d;
364 loc_2d[0] = loc.x();
365 loc_2d[1] = loc.y();
366
367 lt = CoordToMapGridIndex(loc_2d, resolution_id_, cur_stride_);
368 lt = lt + bias + cv::Point(1024, 1024);
369
370 if (lt.x >= 0 && lt.y >= 0 && lt.x < 1024 * 3 && lt.y < 1024 * 3) {
371 if (is_draw_car_ && loc_info.is_has_attitude) {
372 const Eigen::Quaterniond &quatd = loc_info.attitude;
373 double quaternion[] = {quatd.w(), quatd.x(), quatd.y(), quatd.z()};
374 double euler_angle[3] = {0};
375 QuaternionToEuler(quaternion, euler_angle);
376 euler_angle[0] = euler_angle[0] * 180 / PI;
377 euler_angle[1] = euler_angle[1] * 180 / PI;
378 euler_angle[2] = euler_angle[2] * 180 / PI;
379 // Eigen::Matrix3d matrix = quatd.toRotationMatrix();
380 // Eigen::Vector3d euler_angle = matrix.eulerAngles(1, 0, 2);
381 // euler_angle = euler_angle * 180 / PI;
382 double yaw = euler_angle[2];
383
384 cv::Mat mat_tem;
385 cv::resize(car_img_mats_[i], mat_tem, cv::Size(48, 24), 0, 0,
386 cv::INTER_LINEAR);
387 cv::Mat rotated_mat;
388 // AINFO << "yaw: " << yaw;
389 // RotateImg(mat_tem, rotated_mat, 90 - yaw);
390 // RotateImg(mat_tem, rotated_mat, - yaw - 90);
391 // RotateImg(mat_tem, rotated_mat, yaw + 90);
392 RotateImg(mat_tem, &rotated_mat, yaw - 90);
393 cv::Point car_lt =
394 lt - cv::Point(rotated_mat.cols / 2, rotated_mat.rows / 2);
395 cv::Point car_rb =
396 car_lt + cv::Point(rotated_mat.cols, rotated_mat.rows);
397 if (car_lt.x >= 0 && car_lt.y >= 0 && car_rb.x <= 1024 * 3 &&
398 car_rb.y <= 1024 * 3) {
399 cv::Mat mat_mask;
400 cv::cvtColor(rotated_mat, mat_mask, cv::COLOR_BGR2GRAY);
401 rotated_mat.copyTo(
402 big_window_(cv::Rect(car_lt.x, car_lt.y, rotated_mat.cols,
403 rotated_mat.rows)),
404 mat_mask);
405 }
406 } else {
407 unsigned char b = color_table[i % 3][0];
408 unsigned char g = color_table[i % 3][1];
409 unsigned char r = color_table[i % 3][2];
410 cv::circle(big_window_, lt, 4, cv::Scalar(b, g, r), 1);
411 }
412 }
413
414 i = (i + 1) % loc_info_num_;
415 }
416 }
417}
418
419void VisualizationEngine::DrawStd(const cv::Point &bias) {
420 AINFO << "Draw std.";
421 if (cur_level_ == 0 && is_draw_std_) {
422 unsigned int i = (car_loc_id_ + 1) % loc_info_num_;
423 for (unsigned int k = 0; k < loc_info_num_; k++) {
424 LocalizatonInfo &loc_info = cur_loc_infos_[i];
425 if (loc_info.is_has_std) {
426 cv::Point lt;
427 const Eigen::Translation3d &loc = loc_info.location;
428 const Eigen::Vector3d &std = loc_info.std_var;
429 Eigen::Vector2d loc_2d;
430 loc_2d[0] = loc.x();
431 loc_2d[1] = loc.y();
432
433 lt = CoordToMapGridIndex(loc_2d, resolution_id_, cur_stride_);
434 lt = lt + bias + cv::Point(1024, 1024);
435
436 if (lt.x >= 0 && lt.y >= 0 && lt.x < 1024 * 3 && lt.y < 1024 * 3) {
437 unsigned char b = color_table[i % 3][0];
438 unsigned char g = color_table[i % 3][1];
439 unsigned char r = color_table[i % 3][2];
440
441 cv::Size size(static_cast<int>(std::sqrt(std[0]) * 200.0 + 1.0),
442 static_cast<int>(std::sqrt(std[1]) * 200.0 + 1.0));
443 cv::ellipse(big_window_, lt, size, 0, 0, 360, cv::Scalar(b, g, r), 2,
444 8);
445 }
446 }
447
448 i = (i + 1) % loc_info_num_;
449 }
450 }
451}
452
453void VisualizationEngine::DrawCloud(const cv::Point &bias) {
454 if (!cur_loc_infos_[car_loc_id_].is_has_attitude) {
455 return;
456 }
457
458 AINFO << "Draw cloud.";
459 if (cur_level_ == 0) {
460 CloudToMat(car_pose_, velodyne_extrinsic_, cloud_, &cloud_img_,
461 &cloud_img_mask_);
462 cv::Point lt;
463 lt = CoordToMapGridIndex(cloud_img_lt_coord_, resolution_id_, cur_stride_);
464 lt = lt + bias + cv::Point(1024, 1024);
465
466 cv::Point rb = lt + cv::Point(1024, 1024);
467 if (lt.x >= 0 && lt.y >= 0 && rb.x <= 1024 * 3 && rb.y <= 1024 * 3) {
468 cloud_img_.copyTo(big_window_(cv::Rect(lt.x, lt.y, 1024, 1024)),
469 cloud_img_mask_);
470 // cloud_img_.copyTo(big_window_(cv::Rect(lt.x, lt.y, 1024, 1024)));
471 }
472 }
473}
474
475void VisualizationEngine::DrawLegend() {
476 AINFO << "Draw legend.";
477 int fontFace = cv::FONT_HERSHEY_SIMPLEX;
478 double fontScale = 0.6;
479 int thickness = 2.0;
480 int baseline = 0;
481 cv::Size textSize;
482
483 for (unsigned int i = 0; i < loc_info_num_; i++) {
484 LocalizatonInfo &loc_info = cur_loc_infos_[i];
485 if (!loc_info.is_valid) {
486 continue;
487 }
488
489 std::string text = loc_info.description;
490 textSize = cv::getTextSize(text, fontFace, fontScale, thickness, &baseline);
491 cv::Point textOrg(790, (15 + textSize.height) * (i + 1));
492 cv::putText(image_window_, text, textOrg, fontFace, fontScale,
493 cv::Scalar(255, 0, 0), thickness, 8);
494
495 unsigned char b = color_table[i % 3][0];
496 unsigned char g = color_table[i % 3][1];
497 unsigned char r = color_table[i % 3][2];
498 cv::circle(
499 image_window_,
500 cv::Point(755, (15 + textSize.height) * (i + 1) - textSize.height / 2),
501 8, cv::Scalar(b, g, r), 3);
502 }
503}
504
505void VisualizationEngine::DrawInfo() {
506 AINFO << "Draw info.";
507 LocalizatonInfo &loc_info = cur_loc_infos_[car_loc_id_];
508
509 int fontFace = cv::FONT_HERSHEY_SIMPLEX;
510 double fontScale = 0.8;
511 int thickness = 2.0;
512 int baseline = 0;
513 cv::Size textSize;
514
515 std::string text;
516
517 // draw frame id.
518 char info[256];
519 snprintf(info, sizeof(info), "Frame: %d.", loc_info.frame_id);
520 text = info;
521 textSize = cv::getTextSize(text, fontFace, fontScale, thickness, &baseline);
522 cv::Point textOrg(10, 10 + textSize.height);
523 cv::putText(image_window_, text, textOrg, fontFace, fontScale,
524 cv::Scalar(255, 0, 0), thickness, 8);
525
526 // draw timestamp.
527 snprintf(info, sizeof(info), "Timestamp: %lf.", loc_info.timestamp);
528 text = info;
529 textSize = cv::getTextSize(text, fontFace, fontScale, thickness, &baseline);
530 textOrg = cv::Point(10, 2 * (10 + textSize.height));
531 cv::putText(image_window_, text, textOrg, fontFace, fontScale,
532 cv::Scalar(255, 0, 0), thickness, 8);
533}
534
535void VisualizationEngine::DrawTips() {
536 AINFO << "Draw tips.";
537
538 tips_window_.setTo(cv::Scalar(0, 0, 0));
539
540 int fontFace = cv::FONT_HERSHEY_SIMPLEX;
541 double fontScale = 0.5;
542 int thickness = 1.0;
543 int baseline = 0;
544 cv::Size textSize;
545
546 std::string text;
547
548 // draw tips.
549 char info[256];
550 snprintf(info, sizeof(info),
551 "e: zoom out; q: zoom in; m: max scale; n: origin scale; w: up; s: "
552 "down; a: move left; d: move right; r: move center");
553 text = info;
554 textSize = cv::getTextSize(text, fontFace, fontScale, thickness, &baseline);
555 cv::Point textOrg(5, 5 + textSize.height);
556 cv::putText(tips_window_, text, textOrg, fontFace, fontScale,
557 cv::Scalar(255, 255, 255), thickness, 8);
558
559 snprintf(info, sizeof(info),
560 "f: follow car/free view; p: play/pause; c: change current loc; 1: "
561 "draw car/not; 2: draw trajectory/not; others: next");
562 text = info;
563 textSize = cv::getTextSize(text, fontFace, fontScale, thickness, &baseline);
564 textOrg = cv::Point(5, 2 * (5 + textSize.height));
565 cv::putText(tips_window_, text, textOrg, fontFace, fontScale,
566 cv::Scalar(255, 255, 255), thickness, 8);
567
568 tips_window_.copyTo(image_window_(cv::Rect(0, 976, 1024, 48)));
569}
570
571void VisualizationEngine::UpdateLevel() {
572 if (cur_scale_ > max_stride_ * 1.5) {
573 SetScale(max_stride_ * 1.5);
574 }
575 if (cur_scale_ < 0.5) {
576 SetScale(0.5);
577 }
578 // calculate which image level to use
579 cur_level_ = 0;
580 cur_stride_ = 1;
581 double radius = cur_scale_ / 2;
582 while (radius >= 1.0) {
583 radius /= 2;
584 ++cur_level_;
585 cur_stride_ *= 2;
586 }
587}
588
589void VisualizationEngine::GenerateMutiResolutionImages(
590 const std::vector<std::string> &src_files, const int base_path_length,
591 const std::string &dst_folder) {
592 int x_min = std::numeric_limits<int>::max();
593 int x_max = -1;
594 int y_min = std::numeric_limits<int>::max();
595 int y_max = -1;
596 for (size_t i = 0; i < src_files.size(); ++i) {
597 int len = static_cast<int>(src_files[i].length());
598 // src_file example: image/000/north/50/00034661/00003386.png
599 int y = std::stoi(src_files[i].substr(len - 21, 8));
600 int x = std::stoi(src_files[i].substr(len - 12, 8));
601
602 x_min = x < x_min ? x : x_min;
603 x_max = x > x_max ? x : x_max;
604 y_min = y < y_min ? y : y_min;
605 y_max = y > y_max ? y : y_max;
606 }
607 y_max += 1;
608 x_max += 1;
609
610 // calculate how many level need to create
611 int level = 1;
612 int range = 1;
613 while (range < x_max - x_min || range < y_max - y_min) {
614 range *= 2;
615 ++level;
616 }
617
618 // for image level 0, just copy from raw image
619 for (unsigned int i = 0; i < src_files.size(); i++) {
620 std::string p = src_files[i].substr(
621 base_path_length, src_files[i].length() - 4 - base_path_length);
622 p = dst_folder + p;
623 cv::Mat tmp = cv::imread(src_files[i]);
624 cv::imwrite(p + "_0.png", tmp);
625 }
626
627 // generate higher image level;
628 std::string image_visual_path_dst = src_files[0].substr(
629 base_path_length, src_files[0].length() - 22 - base_path_length);
630 image_visual_path_dst = dst_folder + image_visual_path_dst;
631
632 int pt_x = x_min;
633 int pt_y = y_min;
634 int step = 1;
635 for (int lvl = 1; lvl < level; lvl++) {
636 int nstep = 2 * step;
637 for (pt_x = x_min; pt_x < x_max; pt_x += nstep) {
638 for (pt_y = y_min; pt_y < y_max; pt_y += nstep) {
639 bool flag = false;
640 char ss[200];
641
642 cv::Mat large(2048, 2048, CV_8UC3, cv::Scalar(0, 0, 0));
643 cv::Mat small;
644
645 for (int i = 0; i < 2; i++) {
646 for (int j = 0; j < 2; j++) {
647 snprintf(ss, sizeof(ss), "%s/%08d/%08d_%d.png",
648 image_visual_path_dst.c_str(), pt_y + i * step,
649 pt_x + j * step, lvl - 1);
651 flag = true;
652 cv::Mat img = cv::imread(ss);
653 img.copyTo(large(cv::Rect(j * 1024, i * 1024, 1024, 1024)));
654 }
655 }
656 }
657 if (flag) {
658 snprintf(ss, sizeof(ss), "%s/%08d/%08d_%d.png",
659 image_visual_path_dst.c_str(), pt_y, pt_x, lvl);
660 cv::resize(large, small, cv::Size(1024, 1024), 0, 0,
661 cv::INTER_LINEAR);
662 cv::imwrite(ss, small);
663 }
664 }
665 }
666 step = nstep;
667 }
668
669 // write param
670 std::fstream outf(dst_folder + "/param.txt", std::ios::out);
671 outf << x_min << " " << y_min << std::endl;
672 outf << x_max << " " << y_max << std::endl;
673 outf << level << std::endl;
674 outf << image_visual_path_dst.substr(
675 dst_folder.length(),
676 image_visual_path_dst.length() - dst_folder.length() - 3)
677 << std::endl;
678 outf.close();
679}
680
681bool VisualizationEngine::InitOtherParams(const std::string &params_file) {
682 int x_min = 0;
683 int y_min = 0;
684 int x_max = 0;
685 int y_max = 0;
686 int level = 0;
687 std::string path = "";
688
689 std::ifstream inf(params_file);
690 if (!inf.is_open()) {
691 AERROR << "Open params file failed.";
692 return false;
693 }
694
695 inf >> x_min >> y_min >> x_max >> y_max >> level >> path;
696 inf.close();
697
698 if (level > 5) {
699 level = 5;
700 }
701
702 InitOtherParams(x_min, y_min, x_max, y_max, level, path);
703
704 return true;
705}
706
707void VisualizationEngine::InitOtherParams(const int x_min, const int y_min,
708 const int x_max, const int y_max,
709 const int level,
710 const std::string &path) {
711 lt_node_index_.x = x_min;
712 lt_node_index_.y = y_min;
713 lt_node_grid_index_.x = lt_node_index_.x * map_param_.map_node_size_x;
714 lt_node_grid_index_.y = lt_node_index_.y * map_param_.map_node_size_y;
715
716 double init_center_x = (static_cast<double>(x_max + x_min) / 2 *
717 map_param_.map_resolutions[resolution_id_] *
718 map_param_.map_node_size_x) +
719 map_param_.map_min_x;
720 double init_center_y = (static_cast<double>(y_max + y_min) / 2 *
721 map_param_.map_resolutions[resolution_id_] *
722 map_param_.map_node_size_y) +
723 map_param_.map_min_y;
724 SetViewCenter(init_center_x, init_center_y);
725 max_level_ = level;
726 max_stride_ = 1;
727 for (int i = 1; i < level; ++i) {
728 max_stride_ *= 2;
729 }
730 // SetScale((double)max_stride_);
731 image_visual_leaf_path_ = image_visual_resolution_path_ + path;
732 AINFO << "image_visual_leaf_path: " << image_visual_leaf_path_;
733}
734
735void VisualizationEngine::CloudToMat(
736 const Eigen::Affine3d &cur_pose, const Eigen::Affine3d &velodyne_extrinsic,
737 const ::apollo::common::EigenVector3dVec &cloud, cv::Mat *cloud_img,
738 cv::Mat *cloud_img_mask) {
739 unsigned int img_width = map_param_.map_node_size_x;
740 unsigned int img_height = map_param_.map_node_size_y;
741 Eigen::Vector3d cen = car_pose_.translation();
742 cloud_img_lt_coord_[0] = cen[0] - map_param_.map_resolutions[resolution_id_] *
743 (static_cast<float>(img_width) / 2.0f);
744 cloud_img_lt_coord_[1] = cen[1] - map_param_.map_resolutions[resolution_id_] *
745 (static_cast<float>(img_height) / 2.0f);
746
747 cloud_img_.setTo(cv::Scalar(0, 0, 0));
748 cloud_img_mask_.setTo(cv::Scalar(0));
749
750 for (unsigned int i = 0; i < cloud.size(); i++) {
751 const Eigen::Vector3d &pt = cloud[i];
752 Eigen::Vector3d pt_global = cur_pose * velodyne_extrinsic * pt;
753
754 int col = static_cast<int>((pt_global[0] - cloud_img_lt_coord_[0]) /
755 map_param_.map_resolutions[resolution_id_]);
756 int row = static_cast<int>((pt_global[1] - cloud_img_lt_coord_[1]) /
757 map_param_.map_resolutions[resolution_id_]);
758 if (col < 0 || row < 0 ||
759 col >= static_cast<int>(map_param_.map_node_size_x) ||
760 row >= static_cast<int>(map_param_.map_node_size_y)) {
761 continue;
762 }
763
764 unsigned char b = color_table[car_loc_id_ % 3][0];
765 unsigned char g = color_table[car_loc_id_ % 3][1];
766 unsigned char r = color_table[car_loc_id_ % 3][2];
767 cloud_img->at<cv::Vec3b>(row, col) = cv::Vec3b(b, g, r);
768 cloud_img_mask->at<unsigned char>(row, col) = 1;
769 }
770}
771
772void VisualizationEngine::CoordToImageKey(const Eigen::Vector2d &coord,
773 MapImageKey *key) {
774 key->level = cur_level_;
775
776 DCHECK_LT(resolution_id_, map_param_.map_resolutions.size());
777 key->zone_id = zone_id_;
778 int n = static_cast<int>((coord[0] - map_param_.map_min_x) /
779 (static_cast<float>(map_param_.map_node_size_x) *
780 map_param_.map_resolutions[resolution_id_]));
781 int m = static_cast<int>((coord[1] - map_param_.map_min_y) /
782 (static_cast<float>(map_param_.map_node_size_y) *
783 map_param_.map_resolutions[resolution_id_]));
784 int max_n = static_cast<int>((map_param_.map_max_x - map_param_.map_min_x) /
785 (static_cast<float>(map_param_.map_node_size_x) *
786 map_param_.map_resolutions[resolution_id_]));
787 int max_m = static_cast<unsigned int>(
788 (map_param_.map_max_y - map_param_.map_min_y) /
789 (static_cast<float>(map_param_.map_node_size_y) *
790 map_param_.map_resolutions[resolution_id_]));
791
792 if (n >= 0 && m >= 0 && n < max_n && m < max_m) {
793 key->node_north_id = m;
794 key->node_east_id = n;
795 } else {
796 DCHECK(false); // should never reach here
797 }
798
799 m = static_cast<int>(key->node_north_id) - lt_node_index_.y;
800 if (m < 0) {
801 m = m - (cur_stride_ - 1);
802 }
803 key->node_north_id = m / cur_stride_ * cur_stride_ + lt_node_index_.y;
804
805 n = static_cast<int>(key->node_east_id) - lt_node_index_.x;
806 if (n < 0) {
807 n = n - (cur_stride_ - 1);
808 }
809 key->node_east_id = n / cur_stride_ * cur_stride_ + lt_node_index_.x;
810}
811
812cv::Point VisualizationEngine::CoordToMapGridIndex(
813 const Eigen::Vector2d &coord, const unsigned int resolution_id,
814 const int stride) {
815 cv::Point p;
816 p.x = static_cast<int>((coord[0] - map_param_.map_min_x) /
817 map_param_.map_resolutions[resolution_id]);
818 p.y = static_cast<int>((coord[1] - map_param_.map_min_y) /
819 map_param_.map_resolutions[resolution_id]);
820
821 cv::Point pr;
822 pr.x = p.x - lt_node_grid_index_.x;
823 pr.y = p.y - lt_node_grid_index_.y;
824 pr.x = pr.x / stride;
825 pr.y = pr.y / stride;
826
827 return pr;
828}
829
830cv::Point VisualizationEngine::MapGridIndexToNodeGridIndex(const cv::Point &p) {
831 cv::Point pi;
832 pi.x = p.x % map_param_.map_node_size_x;
833 pi.x = pi.x < 0 ? pi.x + map_param_.map_node_size_x : pi.x;
834 pi.y = p.y % map_param_.map_node_size_y;
835 pi.y = pi.y < 0 ? pi.y + map_param_.map_node_size_y : pi.y;
836
837 return pi;
838}
839
840bool VisualizationEngine::LoadImageToCache(const MapImageKey &key) {
841 cv::Mat img;
842
843 if (!map_image_cache_.Get(key, &img)) {
844 char path[1024];
845 snprintf(path, sizeof(path), "%s/%02d/%08d/%08d_%d.png",
846 image_visual_leaf_path_.c_str(), key.zone_id, key.node_north_id,
847 key.node_east_id, key.level);
848 if (cyber::common::PathExists(path)) {
849 img = cv::imread(path);
850 AINFO << "visualizer load: " << path;
851 map_image_cache_.Set(key, img);
852 return true;
853 } else {
854 return false;
855 }
856 }
857 return true;
858}
859
860void VisualizationEngine::RotateImg(const cv::Mat &in_img, cv::Mat *out_img,
861 double angle) {
862 int width = (in_img.cols > in_img.rows) ? in_img.cols : in_img.rows;
863 width += 4;
864 cv::Mat mat_tem(width, width, CV_8UC3);
865 mat_tem.setTo(cv::Scalar(0, 0, 0));
866 in_img.copyTo(
867 mat_tem(cv::Rect(width / 2 - in_img.cols / 2, width / 2 - in_img.rows / 2,
868 in_img.cols, in_img.rows)));
869 cv::Point2f pt(static_cast<float>(width) / 2.0f,
870 static_cast<float>(width) / 2.0f);
871 cv::Mat rotation_mat = cv::getRotationMatrix2D(pt, angle, 1.0);
872 cv::warpAffine(mat_tem, *out_img, rotation_mat, cv::Size(width, width));
873}
874
875void VisualizationEngine::SetViewCenter(const double center_x,
876 const double center_y) {
877 _view_center[0] = center_x;
878 _view_center[1] = center_y;
879}
880
881void VisualizationEngine::UpdateViewCenter(const double move_x,
882 const double move_y) {
883 _view_center[0] += move_x;
884 _view_center[1] += move_y;
885}
886
887void VisualizationEngine::SetScale(const double scale) { cur_scale_ = scale; }
888
889void VisualizationEngine::UpdateScale(const double factor) {
890 cur_scale_ *= factor;
891}
892
893bool VisualizationEngine::UpdateCarLocId() {
894 for (unsigned int i = 0; i < loc_info_num_ - 1; i++) {
895 unsigned int tem_car_loc_id = (car_loc_id_ + i + 1) % loc_info_num_;
896 if (cur_loc_infos_[tem_car_loc_id].is_valid) {
897 car_loc_id_ = tem_car_loc_id;
898 car_pose_ = cur_loc_infos_[car_loc_id_].pose;
899 return true;
900 }
901 }
902 return false;
903}
904
905bool VisualizationEngine::UpdateCarLocId(const unsigned int car_loc_id) {
906 if (car_loc_id >= loc_info_num_) {
907 return false;
908 }
909
910 if (cur_loc_infos_[car_loc_id].is_valid) {
911 car_loc_id_ = car_loc_id;
912 car_pose_ = cur_loc_infos_[car_loc_id_].pose;
913 return true;
914 }
915
916 return false;
917}
918
919bool VisualizationEngine::UpdateTrajectoryGroups() {
920 for (unsigned int i = 0; i < loc_info_num_; i++) {
921 if (cur_loc_infos_[i].is_valid) {
922 Eigen::Vector2d loc;
923 LocalizatonInfo &loc_info = cur_loc_infos_[i];
924 loc[0] = loc_info.location.x();
925 loc[1] = loc_info.location.y();
926 std::map<double, Eigen::Vector2d> &trajectory = trajectory_groups_[i];
927 trajectory[loc_info.timestamp] = loc;
928 }
929 }
930
931 return true;
932}
933
934void VisualizationEngine::ProcessKey(int key) {
935 const int move = 20;
936 char c_key = static_cast<char>(key);
937 switch (c_key) {
938 case 'e': {
939 UpdateScale(1.1);
940 Draw();
941 break;
942 }
943 case 'q': {
944 UpdateScale(0.9);
945 Draw();
946 break;
947 }
948 case 'w': {
949 UpdateViewCenter(0, move);
950 Draw();
951 break;
952 }
953 case 'a': {
954 UpdateViewCenter(-move, 0);
955 Draw();
956 break;
957 }
958 case 's': {
959 UpdateViewCenter(0, -move);
960 Draw();
961 break;
962 }
963 case 'd': {
964 UpdateViewCenter(move, 0);
965 Draw();
966 break;
967 }
968 case 'n': {
969 SetScale(1.0);
970 Draw();
971 break;
972 }
973 case 'm': {
974 SetScale(max_stride_);
975 Draw();
976 break;
977 }
978 case 'r': {
979 const Eigen::Vector3d &loc = car_pose_.translation();
980 SetViewCenter(loc[0], loc[1]);
981 Draw();
982 break;
983 }
984 case 'f': {
985 follow_car_ = !follow_car_;
986 Draw();
987 break;
988 }
989 case 'p': {
990 auto_play_ = !auto_play_;
991 break;
992 }
993 case 'c': {
994 UpdateCarLocId();
995 expected_car_loc_id_ = car_loc_id_;
996 Draw();
997 break;
998 }
999 case '1': {
1000 is_draw_car_ = !is_draw_car_;
1001 break;
1002 }
1003 case '2': {
1004 is_draw_trajectory_ = !is_draw_trajectory_;
1005 break;
1006 }
1007 default:
1008 break;
1009 }
1010}
1011
1012} // namespace msf
1013} // namespace localization
1014} // namespace apollo
bool Get(const MapImageKey &key, cv::Mat *image)
void Set(const MapImageKey &key, const cv::Mat &image)
bool Init(const std::string &map_folder, const std::string &map_visual_folder, const VisualMapParam &map_param, const unsigned int resolution_id, const int zone_id, const Eigen::Affine3d &extrinsic, const unsigned int loc_info_num=1)
void Visualize(::apollo::common::EigenVector< LocalizatonInfo > &&loc_infos, const ::apollo::common::EigenVector3dVec &cloud)
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
std::vector< EigenType, Eigen::aligned_allocator< EigenType > > EigenVector
Definition eigen_defs.h:33
bool PathExists(const std::string &path)
Check if the path exists.
Definition file.cc:195
bool DirectoryExists(const std::string &directory_path)
Check if the directory specified by directory_path exists and is indeed a directory.
Definition file.cc:207
bool EnsureDirectory(const std::string &directory_path)
Check if a specified directory specified by directory_path exists.
Definition file.cc:299
uint32_t width
Width of point cloud
class register implement
Definition arena_queue.h:37
Definition future.h:29
The key structure of a map image .
bool operator<(const MapImageKey &key) const
The data structure to store parameters of a map
unsigned int map_node_size_y
The map node size in pixels.
std::vector< float > map_resolutions
The pixel resolutions in the map in meters.
double map_min_x
The minimum and maximum UTM range in the map.
unsigned int map_node_size_x
The map node size in pixels.
#define PI
The engine for localization visualization.