Apollo 11.0
自动驾驶开放平台
traffic_light_tracking_component.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 <limits>
19#include <map>
20#include <utility>
21
22#include <opencv2/highgui/highgui.hpp>
23#include <opencv2/imgproc/imgproc.hpp>
24#include <opencv2/opencv.hpp>
25
26#include "modules/perception/traffic_light_tracking/proto/traffic_light_tracking_component.pb.h"
27
28#include "cyber/common/file.h"
30#include "cyber/time/clock.h"
34
35namespace apollo {
36namespace perception {
37namespace trafficlight {
38
39using apollo::perception::onboard::FLAGS_start_visualizer;
42
43class TLInfo {
44 public:
45 cv::Scalar tl_color_;
46 std::string tl_string_;
47 std::string tl_string_ex_;
48};
49
50std::map<base::TLColor, TLInfo> s_tl_infos = {
52 {cv::Scalar(255, 255, 255), "UNKNOWN", "UNKNOWN traffic light"}},
54 {cv::Scalar(0, 0, 255), "RED", "RED traffic light"}},
56 {cv::Scalar(0, 255, 0), "GREEN", "GREEN traffic light"}},
58 {cv::Scalar(0, 255, 255), "YELLOW", "YELLOW traffic light"}}};
59
61 // init component config
62 if (InitConfig() != cyber::SUCC) {
63 AERROR << "TrafficLightTrackComponent InitConfig failed.";
64 return false;
65 }
66 // init algorithm plugin
67 if (InitAlgorithmPlugin() != cyber::SUCC) {
68 AERROR << "TrafficLightTrackComponent InitAlgorithmPlugin failed.";
69 return false;
70 }
71
72 if (InitV2XListener() != cyber::SUCC) {
73 AERROR << "TrafficLightTrackComponent InitV2XListener failed.";
74 return false;
75 }
76
77 return true;
78}
79
81 const std::shared_ptr<TrafficDetectMessage>& message) {
83 auto time_imags = std::to_string(message->timestamp_);
84 AINFO << "Enter tracking component, message timestamp: " << time_imags;
85
86 bool status = InternalProc(message);
87
88 return status;
89}
90
91int TrafficLightTrackComponent::InitConfig() {
93 if (!GetProtoConfig(&traffic_light_param)) {
94 AINFO << "load trafficlights tracking component proto param failed";
95 return cyber::FAIL;
96 }
97
98 v2x_trafficlights_input_channel_name_ =
99 traffic_light_param.v2x_trafficlights_input_channel_name();
100
101 auto plugin_param = traffic_light_param.plugin_param();
102 tl_tracker_name_ = plugin_param.name();
103 config_path_ = plugin_param.config_path();
104 config_file_ = plugin_param.config_file();
105 AINFO << "tl_tracker_name: " << tl_tracker_name_
106 << " config_path: " << config_path_ << " config_file: " << config_file_;
107
108 writer_ = node_->CreateWriter<apollo::perception::TrafficLightDetection>(
109 traffic_light_param.traffic_light_output_channel_name());
110 v2x_msg_buffer_.set_capacity(max_v2x_msg_buff_size_);
111
112 return cyber::SUCC;
113}
114
115bool TrafficLightTrackComponent::InitAlgorithmPlugin() {
116 trafficlight::BaseTrafficLightTracker* tracker =
117 trafficlight::BaseTrafficLightTrackerRegisterer::GetInstanceByName(
118 tl_tracker_name_);
119 CHECK_NOTNULL(tracker);
120 tracker_.reset(tracker);
121 ACHECK(tracker_ != nullptr);
122
123 tracker_init_options_.config_path = config_path_;
124 tracker_init_options_.config_file = config_file_;
125
126 if (!tracker_->Init(tracker_init_options_)) {
127 AERROR << "Trafficlight tracking init failed";
128 return false;
129 }
130
131 return cyber::SUCC;
132}
133
134int TrafficLightTrackComponent::InitV2XListener() {
135 typedef const std::shared_ptr<apollo::v2x::IntersectionTrafficLightData>
136 V2XTrafficLightsMsgType;
137 std::function<void(const V2XTrafficLightsMsgType&)> sub_v2x_tl_callback =
138 std::bind(&TrafficLightTrackComponent::OnReceiveV2XMsg, this,
139 std::placeholders::_1);
140 auto sub_v2x_reader = node_->CreateReader(
141 v2x_trafficlights_input_channel_name_, sub_v2x_tl_callback);
142 return cyber::SUCC;
143}
144
145bool TrafficLightTrackComponent::InternalProc(
146 const std::shared_ptr<TrafficDetectMessage const>& message) {
147 PERF_BLOCK("traffic_light_tracking")
148 bool status = tracker_->Track(message->traffic_light_frame_.get());
150
151 auto frame = message->traffic_light_frame_;
152 AINFO << "Enter SyncV2XTrafficLights founction.";
153 SyncV2XTrafficLights(frame.get());
154 stoplines_ = message->stoplines_;
155
156 std::shared_ptr<TrafficLightDetection> out_msg(new TrafficLightDetection);
157 auto& camera_name = frame->data_provider->sensor_name();
158 AINFO << "Enter TransformOutputMessage founction.";
159 if (!TransformOutputMessage(frame.get(), camera_name, &out_msg, message)) {
160 AERROR << "transform_output_message failed, msg_time: "
161 << message->timestamp_;
162 return false;
163 }
164
165 // send msg
166 writer_->Write(out_msg);
167 AINFO << "Send trafficlight tracking output message.";
168
169 return true;
170}
171
172void TrafficLightTrackComponent::OnReceiveV2XMsg(
173 const std::shared_ptr<apollo::v2x::IntersectionTrafficLightData> v2x_msg) {
174 std::lock_guard<std::mutex> lck(mutex_);
175 v2x_msg_buffer_.push_back(*v2x_msg);
176}
177
178void TrafficLightTrackComponent::SyncV2XTrafficLights(
179 camera::TrafficLightFrame* frame) {
180 const double camera_frame_timestamp = frame->timestamp;
181 auto sync_single_light = [&](base::TrafficLightPtr light) {
182 for (auto itr = v2x_msg_buffer_.rbegin(); itr != v2x_msg_buffer_.rend();
183 ++itr) {
184 double v2x_timestamp = itr->header().timestamp_sec();
185 // find close enough v2x msg
186 if (std::fabs(camera_frame_timestamp - v2x_timestamp) <
187 v2x_sync_interval_seconds_) {
188 const int v2x_lights_num =
189 itr->road_traffic_light(0).single_traffic_light_size();
190 const auto& v2x_lights = itr->road_traffic_light(0);
191 for (int i = 0; i < v2x_lights_num; ++i) {
192 const auto& v2x_light = v2x_lights.single_traffic_light(i);
193 // check signal id
194 if (light->id != v2x_light.id()) {
195 continue;
196 }
198 bool blink = false;
199 switch (v2x_light.color()) {
200 default:
203 break;
205 v2x_color = base::TLColor::TL_RED;
206 break;
208 v2x_color = base::TLColor::TL_YELLOW;
209 break;
211 v2x_color = base::TLColor::TL_GREEN;
212 break;
214 v2x_color = base::TLColor::TL_BLACK;
215 break;
217 v2x_color = base::TLColor::TL_GREEN;
218 blink = true;
219 break;
220 }
221 // use v2x result directly
222 AINFO << "Sync V2X success. update color from "
223 << static_cast<int>(light->status.color) << " to "
224 << static_cast<int>(v2x_color) << "; signal id: " << light->id;
225 light->status.color = v2x_color;
226 light->status.blink = blink;
227 }
228 break;
229 }
230 }
231 };
232 for (auto& light : frame->traffic_lights) {
233 sync_single_light(light);
234 }
235}
236
237double TrafficLightTrackComponent::stopline_distance(
238 const Eigen::Matrix4d& cam_pose) {
239 if (stoplines_.empty()) {
240 AWARN << "compute car to stopline's distance failed(no stopline). "
241 << "cam_pose:" << cam_pose;
242 return -1;
243 }
244 const apollo::hdmap::Curve& stopline = stoplines_.Get(0);
245 if (stopline.segment().empty()) {
246 AWARN << "compute car to stopline's distance"
247 << " failed(stopline has no segment line). "
248 << "cam_pose:" << cam_pose
249 << " stopline:" << stopline.ShortDebugString();
250 return -1;
251 }
252 if (!stopline.segment(0).has_line_segment()) {
253 AWARN << "compute car to stopline's distance "
254 << "failed(stopline has no segment). "
255 << "cam_pose:" << cam_pose
256 << " stopline:" << stopline.ShortDebugString();
257 return -1;
258 }
259
260 if (stopline.segment(0).line_segment().point().empty()) {
261 AWARN << "compute car to stopline's distance "
262 << "failed(stopline has no point). "
263 << "cam_pose:" << cam_pose
264 << " stopline:" << stopline.ShortDebugString();
265 return -1;
266 }
267
268 Eigen::Vector3d stopline_pt(stopline.segment(0).line_segment().point(0).x(),
269 stopline.segment(0).line_segment().point(0).y(),
270 stopline.segment(0).line_segment().point(0).z());
271 Eigen::Vector3d stopline_pt_cam =
272 (cam_pose.inverse() *
273 Eigen::Vector4d(stopline_pt(0), stopline_pt(1), stopline_pt(2), 1.0))
274 .head(3);
275
276 return stopline_pt_cam(2);
277}
278
279bool TrafficLightTrackComponent::TransformOutputMessage(
280 camera::TrafficLightFrame* frame, const std::string& camera_name,
281 std::shared_ptr<TrafficLightDetection>* out_msg,
282 const std::shared_ptr<TrafficDetectMessage const>& message) {
283 auto& lights = frame->traffic_lights;
284 auto* header = (*out_msg)->mutable_header();
285 double publish_time = Clock::NowInSeconds();
286 header->set_timestamp_sec(publish_time); // message publishing time
287
288 // Set traffic light color to unknown before the process
289 detected_trafficlight_color_ = base::TLColor::TL_UNKNOWN_COLOR;
290
291 // sec -> nano-sec
292 uint64_t ts_int64 = static_cast<uint64_t>(frame->timestamp * 1e9);
293 header->set_camera_timestamp(ts_int64);
294
295 (*out_msg)->set_camera_name(camera_name);
296
297 // Do voting from multiple traffic light detections
298 cnt_r_ = 0;
299 int max_r_id = -1;
300 double max_r_conf = 0;
301
302 cnt_g_ = 0;
303 int max_g_id = -1;
304 double max_g_conf = 0;
305
306 cnt_y_ = 0;
307 int max_y_id = -1;
308 double max_y_conf = 0;
309
310 cnt_u_ = 0;
311
312 int max_n_id = -1;
313
314 for (int i = 0; i < static_cast<int>(lights.size()); i++) {
315 switch (lights.at(i)->status.color) {
317 // quick fix for 0 confidence color decision
318 if (std::abs(lights.at(i)->status.confidence) <
319 std::numeric_limits<double>::min()) {
320 lights.at(i)->status.color = base::TLColor::TL_UNKNOWN_COLOR;
321 max_n_id = i;
322 break;
323 }
324 cnt_r_ += lights.at(i)->status.confidence;
325 if (lights.at(i)->status.confidence >= max_r_conf) {
326 max_r_id = i;
327 max_r_conf = lights.at(i)->status.confidence;
328 }
329 break;
331 // quick fix for 0 confidence color decision
332 if (std::abs(lights.at(i)->status.confidence) <
333 std::numeric_limits<double>::min()) {
334 lights.at(i)->status.color = base::TLColor::TL_UNKNOWN_COLOR;
335 max_n_id = i;
336 break;
337 }
338 cnt_g_ += lights.at(i)->status.confidence;
339 if (lights.at(i)->status.confidence >= max_g_conf) {
340 max_g_id = i;
341 max_g_conf = lights.at(i)->status.confidence;
342 }
343 break;
345 // quick fix for 0 confidence color decision
346 if (std::abs(lights.at(i)->status.confidence) <
347 std::numeric_limits<double>::min()) {
348 lights.at(i)->status.color = base::TLColor::TL_UNKNOWN_COLOR;
349 max_n_id = i;
350 break;
351 }
352 cnt_y_ += lights.at(i)->status.confidence;
353 if (lights.at(i)->status.confidence >= max_y_conf) {
354 max_y_id = i;
355 max_y_conf = lights.at(i)->status.confidence;
356 }
357 break;
359 cnt_u_ += lights.at(i)->status.confidence;
360 max_n_id = i;
361 break;
362 default:
363 max_n_id = i;
364 break;
365 }
366 }
367
368 int max_light_id = -1;
369 if (cnt_r_ >= cnt_g_ && cnt_r_ >= cnt_y_ && cnt_r_ > 0) {
370 max_light_id = max_r_id;
371 } else if (cnt_y_ > cnt_r_ && cnt_y_ >= cnt_g_) {
372 max_light_id = max_y_id;
373 } else if (cnt_g_ > cnt_r_ && cnt_g_ > cnt_y_) {
374 max_light_id = max_g_id;
375 } else if (cnt_r_ == 0 && cnt_g_ == 0 && cnt_y_ == 0) {
376 max_light_id = max_n_id;
377 }
378
379 // swap the final output light to the first place
380 if (max_light_id > 0) {
381 std::swap(lights[0], lights[max_light_id]);
382 }
383
384 if (max_light_id >= 0) {
385 for (size_t i = 0; i < lights.size(); i++) {
387 (*out_msg)->add_traffic_light();
388 light_result->set_id(lights.at(i)->id);
389 light_result->set_confidence(lights.at(0)->status.confidence);
390 light_result->set_color(
391 static_cast<apollo::perception::TrafficLight_Color>(
392 lights.at(0)->status.color));
393 light_result->set_blink(lights.at(0)->status.blink);
394 }
395 // set contain_lights
396 (*out_msg)->set_contain_lights(lights.size() > 0);
397 detected_trafficlight_color_ = lights.at(0)->status.color;
398 }
399
400 AINFO << "Enter TransformDebugMessage founction.";
401 // add traffic light debug info
402 if (!TransformDebugMessage(frame, out_msg, message)) {
403 AERROR << "ProcComponent::Proc failed to transform debug msg.";
404 return false;
405 }
406
407 return true;
408}
409
410bool TrafficLightTrackComponent::TransformDebugMessage(
411 const camera::TrafficLightFrame* frame,
412 std::shared_ptr<apollo::perception::TrafficLightDetection>* out_msg,
413 const std::shared_ptr<TrafficDetectMessage const>& in_message) {
414 const auto& lights = frame->traffic_lights;
415 // add traffic light debug info
416 TrafficLightDebug* light_debug = (*out_msg)->mutable_traffic_light_debug();
417
418 // signal number
419 light_debug->set_signal_num(static_cast<int>(lights.size()));
420
421 if (!lights.empty() && !lights[0]->region.debug_roi.empty()) {
422 const auto& debug_roi = lights[0]->region.debug_roi;
423 // Crop ROI
424 TransRect2Box(debug_roi[0], light_debug->mutable_cropbox());
425
426 // debug ROI (candidate detection boxes)
427 for (auto iter = debug_roi.begin() + 1; iter != debug_roi.end(); ++iter) {
428 TransRect2Box(*iter, light_debug->add_box());
429 TransRect2Box(*iter, light_debug->add_debug_roi());
430 }
431 }
432
433 for (const auto& light : lights) {
434 // Detection ROI
435 auto* box = light_debug->add_box();
436 TransRect2Box(light->region.detection_roi, box);
437 box->set_color(static_cast<TrafficLight_Color>(light->status.color));
438 box->set_selected(true);
439
440 // Projection ROI
441 TransRect2Box(light->region.projection_roi, light_debug->add_box());
442 TransRect2Box(light->region.projection_roi,
443 light_debug->add_projected_roi());
444
445 // Crop ROI
446 TransRect2Box(light->region.debug_roi[0], light_debug->add_crop_roi());
447
448 // Rectified ROI
449 auto* rectified_roi = light_debug->add_rectified_roi();
450 TransRect2Box(light->region.detection_roi, rectified_roi);
451 rectified_roi->set_color(
452 static_cast<TrafficLight_Color>(light->status.color));
453 rectified_roi->set_selected(true);
454 }
455
456 if (lights.size() > 0) {
457 auto& pose_ptr = in_message->carpose_;
458 Eigen::Matrix4d cam_pose;
459 auto& camera_name = frame->data_provider->sensor_name();
460 cam_pose = pose_ptr->c2w_poses_.at(camera_name);
461 light_debug->set_distance_to_stop_line(stopline_distance(cam_pose));
462 }
463
464 if (FLAGS_start_visualizer) {
465 Visualize(*frame, lights);
466 }
467
468 return true;
469}
470
471void TrafficLightTrackComponent::TransRect2Box(
473 box->set_x(rect.x);
474 box->set_y(rect.y);
475 box->set_width(rect.width);
476 box->set_height(rect.height);
477}
478
479void TrafficLightTrackComponent::Visualize(
480 const camera::TrafficLightFrame& frame,
481 const std::vector<base::TrafficLightPtr>& lights) const {
482 char str[100];
483 std::string tl_string;
484 cv::Scalar tl_color;
485
486 if (lights.empty()) {
487 return;
488 }
489 cv::Mat output_image(image_height_, image_width_, CV_8UC3,
490 cv::Scalar(0, 0, 0));
491 base::Image8U out_image(image_height_, image_width_, base::Color::RGB);
492 camera::DataProvider::ImageOptions image_options;
493 image_options.target_color = base::Color::BGR;
494 frame.data_provider->GetImage(image_options, &out_image);
495 memcpy(output_image.data, out_image.cpu_data(),
496 out_image.total() * sizeof(uint8_t));
497
498 for (const auto& light : lights) {
499 // Crop ROI
500 const auto& crop_roi = light->region.debug_roi[0];
501 const cv::Rect rect_crop(crop_roi.x, crop_roi.y, crop_roi.width,
502 crop_roi.height);
503 if (light == lights[0])
504 cv::rectangle(output_image, rect_crop, cv::Scalar(255, 255, 255), 2);
505 else
506 cv::rectangle(output_image, rect_crop, cv::Scalar(255, 255, 255));
507
508 // Project lights
509 const auto& projection_roi = light->region.projection_roi;
510 const cv::Rect projection_rect(projection_roi.x, projection_roi.y,
511 projection_roi.width, projection_roi.height);
512 cv::rectangle(output_image, projection_rect, cv::Scalar(255, 0, 0), 3);
513
514 // Detect lights
515 const auto& rectified_roi = light->region.detection_roi;
516 const cv::Rect rectified_rect(rectified_roi.x, rectified_roi.y,
517 rectified_roi.width, rectified_roi.height);
518 cv::Scalar tl_color;
519 std::map<base::TLColor, TLInfo>::iterator itor =
520 s_tl_infos.find(light->status.color);
521 if (itor != s_tl_infos.end()) {
522 tl_color = itor->second.tl_color_;
523 tl_string = itor->second.tl_string_;
524 } else {
525 tl_color = cv::Scalar(255, 255, 255);
526 tl_string = "UNKNOWN";
527 }
528 snprintf(str, sizeof(str), "ID:%s C:%.3lf", light->id.c_str(),
529 light->status.confidence);
530 cv::rectangle(output_image, rectified_rect, tl_color, 2);
531 cv::putText(output_image, str,
532 cv::Point(rectified_roi.x + 30,
533 rectified_roi.y + rectified_roi.height + 30),
534 cv::FONT_HERSHEY_DUPLEX, 1.0, tl_color, 2);
535 }
536
537 // Show text of voting results
538 std::map<base::TLColor, TLInfo>::iterator itor =
539 s_tl_infos.find(detected_trafficlight_color_);
540 if (itor != s_tl_infos.end()) {
541 tl_color = itor->second.tl_color_;
542 tl_string = itor->second.tl_string_ex_;
543 } else {
544 tl_color = cv::Scalar(255, 255, 255);
545 tl_string = "UNKNOWN traffic light";
546 }
547 double all = cnt_r_ + cnt_g_ + cnt_y_ + cnt_u_;
548 if (all < 0.0001) {
549 all = 1.0;
550 }
551 cv::putText(output_image, tl_string, cv::Point(10, 90),
552 cv::FONT_HERSHEY_DUPLEX, 2.0, tl_color, 3);
553
554 snprintf(str, sizeof(str), "Red lights:%.2f", cnt_r_ / all);
555 cv::putText(output_image, str, cv::Point(10, 150), cv::FONT_HERSHEY_DUPLEX,
556 1.5, cv::Scalar(0, 0, 255), 3);
557 snprintf(str, sizeof(str), "Green lights:%.2f", cnt_g_ / all);
558 cv::putText(output_image, str, cv::Point(10, 200), cv::FONT_HERSHEY_DUPLEX,
559 1.5, cv::Scalar(0, 255, 0), 3);
560 snprintf(str, sizeof(str), "Yellow lights:%.2f", cnt_y_ / all);
561 cv::putText(output_image, str, cv::Point(10, 250), cv::FONT_HERSHEY_DUPLEX,
562 1.5, cv::Scalar(0, 255, 255), 3);
563 snprintf(str, sizeof(str), "Unknown lights:%.2f", cnt_u_ / all);
564 cv::putText(output_image, str, cv::Point(10, 300), cv::FONT_HERSHEY_DUPLEX,
565 1.5, cv::Scalar(255, 255, 255), 3);
566
567 std::string folder = "/apollo/data/debug_vis/";
569 AINFO << "EnsureDirectory folder " << folder << " error.";
570 }
571 cv::resize(output_image, output_image, cv::Size(), 0.5, 0.5);
572 cv::imwrite(absl::StrCat(folder, std::to_string(frame.timestamp), ".jpg"),
573 output_image);
574 // todo(huqilin): need to create debug_vis folder.
575 // cv::imshow("Apollo traffic light detection", output_image);
576 cv::waitKey(1);
577}
578
579} // namespace trafficlight
580} // namespace perception
581} // namespace apollo
a singleton clock that can be used to get the current timestamp.
Definition clock.h:39
static double NowInSeconds()
gets the current time in second.
Definition clock.cc:56
bool GetProtoConfig(T *config) const
std::shared_ptr< Node > node_
bool Proc(const std::shared_ptr< TrafficDetectMessage > &message) override
#define ACHECK(cond)
Definition log.h:80
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
#define AWARN
Definition log.h:43
bool EnsureDirectory(const std::string &directory_path)
Check if a specified directory specified by directory_path exists.
Definition file.cc:285
Rect< int > RectI
Definition box.h:159
std::shared_ptr< TrafficLight > TrafficLightPtr
std::map< base::TLColor, TLInfo > s_tl_infos
class register implement
Definition arena_queue.h:37
Definition future.h:29
#define PERF_FUNCTION(...)
Definition profiler.h:54
#define PERF_BLOCK_END
Definition profiler.h:53
#define PERF_BLOCK(...)
Definition profiler.h:52
repeated CurveSegment segment