Driver camera view when in reverse

Added toggle to show the driver camera when in the reverse gear.
This commit is contained in:
FrogAi
2024-03-11 18:27:44 -07:00
parent 0a675d40aa
commit 73734fee8c
7 changed files with 112 additions and 87 deletions

View File

@@ -251,6 +251,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DisableOpenpilotLongitudinal", PERSISTENT}, {"DisableOpenpilotLongitudinal", PERSISTENT},
{"DisengageVolume", PERSISTENT}, {"DisengageVolume", PERSISTENT},
{"DragonPilotTune", PERSISTENT}, {"DragonPilotTune", PERSISTENT},
{"DriverCamera", PERSISTENT},
{"DriveStats", PERSISTENT}, {"DriveStats", PERSISTENT},
{"DynamicPathWidth", PERSISTENT}, {"DynamicPathWidth", PERSISTENT},
{"EngageVolume", PERSISTENT}, {"EngageVolume", PERSISTENT},

View File

@@ -27,6 +27,8 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(SettingsWindow *parent) : FrogPilot
{"BlindSpotPath", "Blind Spot Path", "Visualize your blind spots with a red path when another vehicle is detected nearby.", ""}, {"BlindSpotPath", "Blind Spot Path", "Visualize your blind spots with a red path when another vehicle is detected nearby.", ""},
{"LeadInfo", "Lead Info and Logics", "Get detailed information about the vehicle ahead, including speed and distance, and the logic behind your following distance.", ""}, {"LeadInfo", "Lead Info and Logics", "Get detailed information about the vehicle ahead, including speed and distance, and the logic behind your following distance.", ""},
{"DriverCamera", "Driver Camera On Reverse", "Show the driver's camera feed when you shift to reverse.", "../assets/img_driver_face_static.png"},
{"ModelUI", "Model UI", "Personalize how the model's visualizations appear on your screen.", "../assets/offroad/icon_calibration.png"}, {"ModelUI", "Model UI", "Personalize how the model's visualizations appear on your screen.", "../assets/offroad/icon_calibration.png"},
{"DynamicPathWidth", "Dynamic Path Width", "Have the path width dynamically adjust based on the current engagement state of openpilot.", ""}, {"DynamicPathWidth", "Dynamic Path Width", "Have the path width dynamically adjust based on the current engagement state of openpilot.", ""},
{"LaneLinesWidth", "Lane Lines", "Adjust the visual thickness of lane lines on your display.\n\nDefault matches the MUTCD average of 4 inches.", ""}, {"LaneLinesWidth", "Lane Lines", "Adjust the visual thickness of lane lines on your display.\n\nDefault matches the MUTCD average of 4 inches.", ""},

View File

@@ -163,7 +163,7 @@ void OnroadAlerts::updateAlert(const Alert &a) {
} }
void OnroadAlerts::paintEvent(QPaintEvent *event) { void OnroadAlerts::paintEvent(QPaintEvent *event) {
if (alert.size == cereal::ControlsState::AlertSize::NONE) { if (alert.size == cereal::ControlsState::AlertSize::NONE || scene.show_driver_camera && alert.status != cereal::ControlsState::AlertStatus::CRITICAL) {
return; return;
} }
static std::map<cereal::ControlsState::AlertSize, const int> alert_heights = { static std::map<cereal::ControlsState::AlertSize, const int> alert_heights = {
@@ -256,7 +256,10 @@ void ExperimentalButton::updateState(const UIState &s, bool leadInfo) {
void ExperimentalButton::paintEvent(QPaintEvent *event) { void ExperimentalButton::paintEvent(QPaintEvent *event) {
QPainter p(this); QPainter p(this);
QPixmap img = experimental_mode ? experimental_img : engage_img; QPixmap img = experimental_mode ? experimental_img : engage_img;
drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, QColor(0, 0, 0, 166), (isDown() || !(engageable || scene.always_on_lateral_active)) ? 0.6 : 1.0);
if (!scene.show_driver_camera) {
drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, QColor(0, 0, 0, 166), (isDown() || !(engageable || scene.always_on_lateral_active)) ? 0.6 : 1.0);
}
} }
@@ -328,7 +331,7 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA); has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA);
is_metric = s.scene.is_metric; is_metric = s.scene.is_metric;
speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph"); speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph");
hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals && (turnSignalLeft || turnSignalRight)); hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals && (turnSignalLeft || turnSignalRight)) || showDriverCamera;
status = s.status; status = s.status;
// update engageability/experimental mode button // update engageability/experimental mode button
@@ -361,87 +364,91 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
QString speedStr = QString::number(std::nearbyint(speed)); QString speedStr = QString::number(std::nearbyint(speed));
QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(setSpeed)) : ""; QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(setSpeed)) : "";
// Draw outer box + border to contain set speed and speed limit if (!showDriverCamera) {
const int sign_margin = 12; // Draw outer box + border to contain set speed and speed limit
const int us_sign_height = 186; const int sign_margin = 12;
const int eu_sign_size = 176; const int us_sign_height = 186;
const int eu_sign_size = 176;
const QSize default_size = {172, 204}; const QSize default_size = {172, 204};
QSize set_speed_size = default_size; QSize set_speed_size = default_size;
if (is_metric || has_eu_speed_limit) set_speed_size.rwidth() = 200; if (is_metric || has_eu_speed_limit) set_speed_size.rwidth() = 200;
if (has_us_speed_limit && speedLimitStr.size() >= 3) set_speed_size.rwidth() = 223; if (has_us_speed_limit && speedLimitStr.size() >= 3) set_speed_size.rwidth() = 223;
if (has_us_speed_limit) set_speed_size.rheight() += us_sign_height + sign_margin; if (has_us_speed_limit) set_speed_size.rheight() += us_sign_height + sign_margin;
else if (has_eu_speed_limit) set_speed_size.rheight() += eu_sign_size + sign_margin; else if (has_eu_speed_limit) set_speed_size.rheight() += eu_sign_size + sign_margin;
int top_radius = 32; int top_radius = 32;
int bottom_radius = has_eu_speed_limit ? 100 : 32; int bottom_radius = has_eu_speed_limit ? 100 : 32;
QRect set_speed_rect(QPoint(60 + (default_size.width() - set_speed_size.width()) / 2, 45), set_speed_size); QRect set_speed_rect(QPoint(60 + (default_size.width() - set_speed_size.width()) / 2, 45), set_speed_size);
p.setPen(QPen(whiteColor(75), 6)); p.setPen(QPen(whiteColor(75), 6));
p.setBrush(blackColor(166)); p.setBrush(blackColor(166));
drawRoundedRect(p, set_speed_rect, top_radius, top_radius, bottom_radius, bottom_radius); drawRoundedRect(p, set_speed_rect, top_radius, top_radius, bottom_radius, bottom_radius);
// Draw MAX // Draw MAX
QColor max_color = QColor(0x80, 0xd8, 0xa6, 0xff); QColor max_color = QColor(0x80, 0xd8, 0xa6, 0xff);
QColor set_speed_color = whiteColor(); QColor set_speed_color = whiteColor();
if (is_cruise_set) { if (is_cruise_set) {
if (status == STATUS_DISENGAGED) { if (status == STATUS_DISENGAGED) {
max_color = whiteColor(); max_color = whiteColor();
} else if (status == STATUS_OVERRIDE) { } else if (status == STATUS_OVERRIDE) {
max_color = QColor(0x91, 0x9b, 0x95, 0xff); max_color = QColor(0x91, 0x9b, 0x95, 0xff);
} else if (speedLimit > 0) { } else if (speedLimit > 0) {
auto interp_color = [=](QColor c1, QColor c2, QColor c3) { auto interp_color = [=](QColor c1, QColor c2, QColor c3) {
return speedLimit > 0 ? interpColor(setSpeed, {speedLimit + 5, speedLimit + 15, speedLimit + 25}, {c1, c2, c3}) : c1; return speedLimit > 0 ? interpColor(setSpeed, {speedLimit + 5, speedLimit + 15, speedLimit + 25}, {c1, c2, c3}) : c1;
}; };
max_color = interp_color(max_color, QColor(0xff, 0xe4, 0xbf), QColor(0xff, 0xbf, 0xbf)); max_color = interp_color(max_color, QColor(0xff, 0xe4, 0xbf), QColor(0xff, 0xbf, 0xbf));
set_speed_color = interp_color(set_speed_color, QColor(0xff, 0x95, 0x00), QColor(0xff, 0x00, 0x00)); set_speed_color = interp_color(set_speed_color, QColor(0xff, 0x95, 0x00), QColor(0xff, 0x00, 0x00));
}
} else {
max_color = QColor(0xa6, 0xa6, 0xa6, 0xff);
set_speed_color = QColor(0x72, 0x72, 0x72, 0xff);
} }
} else { p.setFont(InterFont(40, QFont::DemiBold));
max_color = QColor(0xa6, 0xa6, 0xa6, 0xff); p.setPen(max_color);
set_speed_color = QColor(0x72, 0x72, 0x72, 0xff); p.drawText(set_speed_rect.adjusted(0, 27, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("MAX"));
} p.setFont(InterFont(90, QFont::Bold));
p.setFont(InterFont(40, QFont::DemiBold)); p.setPen(set_speed_color);
p.setPen(max_color); p.drawText(set_speed_rect.adjusted(0, 77, 0, 0), Qt::AlignTop | Qt::AlignHCenter, setSpeedStr);
p.drawText(set_speed_rect.adjusted(0, 27, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("MAX"));
p.setFont(InterFont(90, QFont::Bold));
p.setPen(set_speed_color);
p.drawText(set_speed_rect.adjusted(0, 77, 0, 0), Qt::AlignTop | Qt::AlignHCenter, setSpeedStr);
const QRect sign_rect = set_speed_rect.adjusted(sign_margin, default_size.height(), -sign_margin, -sign_margin); const QRect sign_rect = set_speed_rect.adjusted(sign_margin, default_size.height(), -sign_margin, -sign_margin);
// US/Canada (MUTCD style) sign // US/Canada (MUTCD style) sign
if (has_us_speed_limit) { if (has_us_speed_limit) {
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.setBrush(whiteColor()); p.setBrush(whiteColor());
p.drawRoundedRect(sign_rect, 24, 24); p.drawRoundedRect(sign_rect, 24, 24);
p.setPen(QPen(blackColor(), 6)); p.setPen(QPen(blackColor(), 6));
p.drawRoundedRect(sign_rect.adjusted(9, 9, -9, -9), 16, 16); p.drawRoundedRect(sign_rect.adjusted(9, 9, -9, -9), 16, 16);
p.setFont(InterFont(28, QFont::DemiBold)); p.setFont(InterFont(28, QFont::DemiBold));
p.drawText(sign_rect.adjusted(0, 22, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("SPEED")); p.drawText(sign_rect.adjusted(0, 22, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("SPEED"));
p.drawText(sign_rect.adjusted(0, 51, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("LIMIT")); p.drawText(sign_rect.adjusted(0, 51, 0, 0), Qt::AlignTop | Qt::AlignHCenter, tr("LIMIT"));
p.setFont(InterFont(70, QFont::Bold)); p.setFont(InterFont(70, QFont::Bold));
p.drawText(sign_rect.adjusted(0, 85, 0, 0), Qt::AlignTop | Qt::AlignHCenter, speedLimitStr); p.drawText(sign_rect.adjusted(0, 85, 0, 0), Qt::AlignTop | Qt::AlignHCenter, speedLimitStr);
} }
// EU (Vienna style) sign // EU (Vienna style) sign
if (has_eu_speed_limit) { if (has_eu_speed_limit) {
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.setBrush(whiteColor()); p.setBrush(whiteColor());
p.drawEllipse(sign_rect); p.drawEllipse(sign_rect);
p.setPen(QPen(Qt::red, 20)); p.setPen(QPen(Qt::red, 20));
p.drawEllipse(sign_rect.adjusted(16, 16, -16, -16)); p.drawEllipse(sign_rect.adjusted(16, 16, -16, -16));
p.setFont(InterFont((speedLimitStr.size() >= 3) ? 60 : 70, QFont::Bold)); p.setFont(InterFont((speedLimitStr.size() >= 3) ? 60 : 70, QFont::Bold));
p.setPen(blackColor()); p.setPen(blackColor());
p.drawText(sign_rect, Qt::AlignCenter, speedLimitStr); p.drawText(sign_rect, Qt::AlignCenter, speedLimitStr);
}
} }
// current speed // current speed
p.setFont(InterFont(176, QFont::Bold)); if (!showDriverCamera) {
drawText(p, rect().center().x(), 210, speedStr); p.setFont(InterFont(176, QFont::Bold));
p.setFont(InterFont(66)); drawText(p, rect().center().x(), 210, speedStr);
drawText(p, rect().center().x(), 290, speedUnit, 200); p.setFont(InterFont(66));
drawText(p, rect().center().x(), 290, speedUnit, 200);
}
p.restore(); p.restore();
} }
@@ -807,7 +814,7 @@ void AnnotatedCameraWidget::paintGL() {
// for replay of old routes, never go to widecam // for replay of old routes, never go to widecam
wide_cam_requested = wide_cam_requested && s->scene.calibration_wide_valid; wide_cam_requested = wide_cam_requested && s->scene.calibration_wide_valid;
} }
CameraWidget::setStreamType(cameraView == 3 ? VISION_STREAM_DRIVER : CameraWidget::setStreamType(cameraView == 3 || showDriverCamera ? VISION_STREAM_DRIVER :
cameraView == 2 || wide_cam_requested ? VISION_STREAM_WIDE_ROAD : cameraView == 2 || wide_cam_requested ? VISION_STREAM_WIDE_ROAD :
VISION_STREAM_ROAD); VISION_STREAM_ROAD);
@@ -826,7 +833,7 @@ void AnnotatedCameraWidget::paintGL() {
painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen); painter.setPen(Qt::NoPen);
if (s->scene.world_objects_visible) { if (s->scene.world_objects_visible && !showDriverCamera) {
update_model(s, model, sm["uiPlan"].getUiPlan()); update_model(s, model, sm["uiPlan"].getUiPlan());
drawLaneLines(painter, s); drawLaneLines(painter, s);
@@ -936,24 +943,28 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets(QPainter &p) {
mapOpen = scene.map_open; mapOpen = scene.map_open;
showDriverCamera = scene.show_driver_camera;
turnSignalLeft = scene.turn_signal_left; turnSignalLeft = scene.turn_signal_left;
turnSignalRight = scene.turn_signal_right; turnSignalRight = scene.turn_signal_right;
if (leadInfo) { if (!showDriverCamera) {
drawLeadInfo(p); if (leadInfo) {
} drawLeadInfo(p);
}
if (alwaysOnLateral || conditionalExperimental) {
drawStatusBar(p); if (alwaysOnLateral || conditionalExperimental) {
} drawStatusBar(p);
}
if (customSignals && (turnSignalLeft || turnSignalRight)) {
if (!animationTimer->isActive()) { if (customSignals && (turnSignalLeft || turnSignalRight)) {
animationTimer->start(totalFrames * 11); // 440 milliseconds per loop; syncs up perfectly with my 2019 Lexus ES 350 turn signal clicks if (!animationTimer->isActive()) {
animationTimer->start(totalFrames * 11); // 440 milliseconds per loop; syncs up perfectly with my 2019 Lexus ES 350 turn signal clicks
}
drawTurnSignals(p);
} else if (animationTimer->isActive()) {
animationTimer->stop();
} }
drawTurnSignals(p);
} else if (animationTimer->isActive()) {
animationTimer->stop();
} }
map_settings_btn_bottom->setEnabled(map_settings_btn->isEnabled()); map_settings_btn_bottom->setEnabled(map_settings_btn->isEnabled());

View File

@@ -130,6 +130,7 @@ private:
bool experimentalMode; bool experimentalMode;
bool leadInfo; bool leadInfo;
bool mapOpen; bool mapOpen;
bool showDriverCamera;
bool turnSignalLeft; bool turnSignalLeft;
bool turnSignalRight; bool turnSignalRight;

View File

@@ -217,6 +217,9 @@ void CameraWidget::updateFrameMat() {
if (active_stream_type == VISION_STREAM_DRIVER) { if (active_stream_type == VISION_STREAM_DRIVER) {
if (stream_width > 0 && stream_height > 0) { if (stream_width > 0 && stream_height > 0) {
frame_mat = get_driver_view_transform(w, h, stream_width, stream_height); frame_mat = get_driver_view_transform(w, h, stream_width, stream_height);
if (uiState()->scene.show_driver_camera) {
frame_mat.v[0] *= -1.0;
}
} }
} else { } else {
// Project point at "infinity" to compute x and y offsets // Project point at "infinity" to compute x and y offsets

View File

@@ -228,6 +228,9 @@ static void update_state(UIState *s) {
scene.turn_signal_left = carState.getLeftBlinker(); scene.turn_signal_left = carState.getLeftBlinker();
scene.turn_signal_right = carState.getRightBlinker(); scene.turn_signal_right = carState.getRightBlinker();
} }
if (scene.driver_camera) {
scene.show_driver_camera = carState.getGearShifter() == cereal::CarState::GearShifter::REVERSE;
}
} }
if (sm.updated("controlsState")) { if (sm.updated("controlsState")) {
auto controlsState = sm["controlsState"].getControlsState(); auto controlsState = sm["controlsState"].getControlsState();
@@ -302,6 +305,8 @@ void ui_update_frogpilot_params(UIState *s) {
scene.custom_icons = custom_theme ? params.getInt("CustomIcons") : 0; scene.custom_icons = custom_theme ? params.getInt("CustomIcons") : 0;
scene.custom_signals = custom_theme ? params.getInt("CustomSignals") : 0; scene.custom_signals = custom_theme ? params.getInt("CustomSignals") : 0;
scene.driver_camera = params.getBool("DriverCamera");
scene.model_ui = params.getBool("ModelUI"); scene.model_ui = params.getBool("ModelUI");
scene.dynamic_path_width = scene.model_ui && params.getBool("DynamicPathWidth"); scene.dynamic_path_width = scene.model_ui && params.getBool("DynamicPathWidth");
scene.lane_line_width = params.getInt("LaneLinesWidth") * (scene.is_metric ? 1.0f : INCH_TO_CM) / 200.0f; scene.lane_line_width = params.getInt("LaneLinesWidth") * (scene.is_metric ? 1.0f : INCH_TO_CM) / 200.0f;

View File

@@ -178,12 +178,14 @@ typedef struct UIScene {
bool blind_spot_path; bool blind_spot_path;
bool blind_spot_right; bool blind_spot_right;
bool conditional_experimental; bool conditional_experimental;
bool driver_camera;
bool dynamic_path_width; bool dynamic_path_width;
bool enabled; bool enabled;
bool experimental_mode; bool experimental_mode;
bool lead_info; bool lead_info;
bool map_open; bool map_open;
bool model_ui; bool model_ui;
bool show_driver_camera;
bool turn_signal_left; bool turn_signal_left;
bool turn_signal_right; bool turn_signal_right;
bool unlimited_road_ui_length; bool unlimited_road_ui_length;