Apollo交通灯感知源码位置:Apollo/modules/perception/camera/lib/traffic_light
采用的交通灯感知方案总共分为三步:
第一步选取ROI不是必需的,也可以在全图上直接进行检测分类。但是,全图检测分类方案中为了保证推理的实时性,往往将原图resize成较小图后放入CNN网络,这会造成大目标变为小目标,降低检测查准率和召回率,以及分类准确率。选取ROI的优点是:节省算力、保证实时性。
Apollo示例中采用两个网络分别进行检测和分类,也可以使用一个网络同时给出检测和分类结果。
这里重点讲解决策后处理。该类名为SemanticReviser,交通灯的Semantic是指交通灯的“直行、左转、右转、掉头等”;Reviser是指针对每个Semantic的交通灯,对其颜色进行修正。
该模块的作用:
- 修正深度学习给出的个别灯颜色的误分类(a. 通过单帧内相同语义交通灯的颜色进行投票 b. 根据历史帧和当前帧中交通灯颜色信息)
- 据帧间信息给出绿灯是否在闪烁状态
具体操作方法解析:
semantic_decision.cpp中入口函数为:
bool SemanticReviser::Track(const TrafficLightTrackerOptions &options,CameraFrame *frame)
该函数根据语义对一帧内所有交通灯进行归类。场景示例:一帧内可能存在2~4个直行灯,这些灯的颜色及闪烁状态是完全一致的。
将相同语义的所有灯归入一个SemanticTable. 随后,对每一个SemanticTable调用函数
void SemanticReviser::ReviseByTimeSeries(double time_stamp, SemanticTable semantic_table, std::vector *lights)
场景示例:一帧内4个直行交通灯中3个被检测为红色,一个被误检为绿灯,则投票得到该SemanticTable颜色为红色。
在ReviseByTimeSeries函数中,调用函数
base::TLColor SemanticReviser::ReviseBySemantic(SemanticTable semantic_table, std::vector *lights)
在利用帧间信息修改交通灯颜色时用到了一些先验知识:
其中,修改交通灯颜色调用函数
void SemanticReviser::ReviseLights(std::vector *lights,
const std::vector &light_ids,
base::TLColor dst_color)
将当前帧的时间戳更新进history_semantic_的时间戳。时间戳是一定更新的;如果根据history_semantic_颜色修正了当前SemanticTable的颜色,则history_semantic_的颜色不变;否则,将history_semantic_颜色Update为当前SemanticTable对应的颜色。
更新history_semantic_中的颜色调用函数
void SemanticReviser::UpdateHistoryAndLights(
const SemanticTable &cur, std::vector *lights,
std::vector::iterator *history)
在闪烁状态时,将当前帧的黑色设为历史帧灯的颜色(如黑色出现之前为绿色,则将当前帧颜色设为绿色),在SemanticTable结构体中单独设置blink状态位,并通过存储的last_dark_time_stamp和last_bright_time_stamp两个属性判断是否是闪烁状态。如果出现 亮-》黑-》亮的模式,则在第二亮出现时将blink状态为置为true。需要当前时间戳(刚出现第二次亮的状态)与last_bright_time_stamp时间间隔大于阈值(把个别帧漏检导致的亮-》黑-》亮 模式过滤掉),则为闪烁。对应逻辑为:
case base::TLColor::TL_GREEN: //确定绿色是否在闪烁
UpdateHistoryAndLights(semantic_table, lights, &iter);
if (time_stamp - iter->last_bright_time_stamp > blink_threshold_s_ && //两次亮的时间间隔大于阈值
iter->last_dark_time_stamp > iter->last_bright_time_stamp) //之前对应 亮-黑 的模式
{
iter->blink = true;
}
iter->last_bright_time_stamp = time_stamp;
ADEBUG << "High confidence color " << s_color_strs[cur_color];
break;
退出闪烁状态的逻辑:
if (pre_color != iter->color || //brigth颜色发生变化
fabs(iter->last_dark_time_stamp - iter->last_bright_time_stamp) >
non_blink_threshold_s_) //长时间保持bright或dark状态超过阈值
{
iter->blink = false;
}