Qt driverview (#21063)
* CameraViewWidget
* continue
* cleanup
* mv DriverViewWindow to ui/qt/offroad
* write IsDriverViewEnabled in showEvent/hideEvnet
* sm.update(0) in onTimeout()
* CameraViewWidget
* use unique_ptr for vipc_client
* virtual draw
* fix viewport
* connected()->frameReceived()
* bg_colors use QColor
* fix draw
* rebase master
* whitespace
* apply reviews
* indent
* like onroad
continue
* white space
* continue
* show == false
* remove border
* use widget's size
* fix shadowed rect
* cleanup driverview
* fix transform
* remove video_rect
Co-authored-by: deanlee <deanlee3@gmail.com>
Co-authored-by: Comma Device <device@comma.ai>
old-commit-hash: 9876723169
commatwo_master
parent
dbc269d1a3
commit
56e9dbcf99
14 changed files with 503 additions and 176 deletions
@ -0,0 +1,109 @@ |
||||
#include "selfdrive/ui/qt/offroad/driverview.h" |
||||
|
||||
#include <QPainter> |
||||
|
||||
#include "selfdrive/ui/qt/qt_window.h" |
||||
#include "selfdrive/ui/qt/util.h" |
||||
|
||||
const int FACE_IMG_SIZE = 130; |
||||
|
||||
DriverViewWindow::DriverViewWindow(QWidget* parent) : QWidget(parent) { |
||||
setAttribute(Qt::WA_OpaquePaintEvent); |
||||
layout = new QStackedLayout(this); |
||||
layout->setStackingMode(QStackedLayout::StackAll); |
||||
|
||||
cameraView = new CameraViewWidget(VISION_STREAM_RGB_FRONT, this); |
||||
layout->addWidget(cameraView); |
||||
|
||||
scene = new DriverViewScene(this); |
||||
connect(cameraView, &CameraViewWidget::frameUpdated, scene, &DriverViewScene::frameUpdated); |
||||
layout->addWidget(scene); |
||||
layout->setCurrentWidget(scene); |
||||
} |
||||
|
||||
void DriverViewWindow::mousePressEvent(QMouseEvent* e) { |
||||
emit done(); |
||||
} |
||||
|
||||
DriverViewScene::DriverViewScene(QWidget* parent) : sm({"driverState"}), QWidget(parent) { |
||||
face = QImage("../assets/img_driver_face.png").scaled(FACE_IMG_SIZE, FACE_IMG_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation); |
||||
} |
||||
|
||||
void DriverViewScene::showEvent(QShowEvent* event) { |
||||
frame_updated = false; |
||||
is_rhd = params.getBool("IsRHD"); |
||||
params.putBool("IsDriverViewEnabled", true); |
||||
} |
||||
|
||||
void DriverViewScene::hideEvent(QHideEvent* event) { |
||||
params.putBool("IsDriverViewEnabled", false); |
||||
} |
||||
|
||||
void DriverViewScene::frameUpdated() { |
||||
frame_updated = true; |
||||
sm.update(0); |
||||
update(); |
||||
} |
||||
|
||||
void DriverViewScene::paintEvent(QPaintEvent* event) { |
||||
QPainter p(this); |
||||
|
||||
// startup msg
|
||||
if (!frame_updated) { |
||||
p.setPen(QColor(0xff, 0xff, 0xff)); |
||||
p.setRenderHint(QPainter::TextAntialiasing); |
||||
configFont(p, "Inter", 100, "Bold"); |
||||
p.drawText(geometry(), Qt::AlignCenter, "camera starting"); |
||||
return; |
||||
} |
||||
|
||||
const int width = 4 * height() / 3; |
||||
const QRect rect2 = {rect().center().x() - width / 2, rect().top(), width, rect().height()}; |
||||
const QRect valid_rect = {is_rhd ? rect2.right() - rect2.height() / 2 : rect2.left(), rect2.top(), rect2.height() / 2, rect2.height()}; |
||||
|
||||
// blackout
|
||||
const int blackout_x_r = valid_rect.right(); |
||||
const QRect& blackout_rect = Hardware::TICI() ? rect() : rect2; |
||||
const int blackout_w_r = blackout_rect.right() - valid_rect.right(); |
||||
const int blackout_x_l = blackout_rect.left(); |
||||
const int blackout_w_l = valid_rect.left() - blackout_x_l; |
||||
|
||||
QColor bg(0, 0, 0, 140); |
||||
p.setPen(QPen(bg)); |
||||
p.setBrush(QBrush(bg)); |
||||
p.drawRect(blackout_x_l, rect2.top(), blackout_w_l, rect2.height()); |
||||
p.drawRect(blackout_x_r, rect2.top(), blackout_w_r, rect2.height()); |
||||
|
||||
// face bounding box
|
||||
cereal::DriverState::Reader driver_state = sm["driverState"].getDriverState(); |
||||
bool face_detected = driver_state.getFaceProb() > 0.4; |
||||
if (face_detected) { |
||||
auto fxy_list = driver_state.getFacePosition(); |
||||
float face_x = fxy_list[0]; |
||||
float face_y = fxy_list[1]; |
||||
int fbox_x = valid_rect.center().x() + (is_rhd ? face_x : -face_x) * valid_rect.width(); |
||||
int fbox_y = valid_rect.center().y() + face_y * valid_rect.height(); |
||||
|
||||
float alpha = 0.2; |
||||
face_x = std::abs(face_x); |
||||
face_y = std::abs(face_y); |
||||
if (face_x <= 0.35 && face_y <= 0.4) { |
||||
alpha = 0.8 - (face_x > face_y ? face_x : face_y) * 0.6 / 0.375; |
||||
} |
||||
|
||||
const int box_size = 0.6 * rect2.height() / 2; |
||||
QPen pen(QColor(255, 255, 255, alpha * 255)); |
||||
pen.setWidth(10); |
||||
p.setPen(pen); |
||||
p.setBrush(Qt::NoBrush); |
||||
p.drawRoundedRect(fbox_x - box_size / 2, fbox_y - box_size / 2, box_size, box_size, 35.0, 35.0); |
||||
} |
||||
|
||||
// icon
|
||||
const int img_offset = 30; |
||||
const int img_x = is_rhd ? rect2.right() - FACE_IMG_SIZE - img_offset : rect2.left() + img_offset; |
||||
const int img_y = rect2.bottom() - FACE_IMG_SIZE - img_offset; |
||||
p.setPen(Qt::NoPen); |
||||
p.setOpacity(face_detected ? 1.0 : 0.3); |
||||
p.drawImage(img_x, img_y, face); |
||||
} |
@ -0,0 +1,48 @@ |
||||
#pragma once |
||||
|
||||
#include <memory> |
||||
|
||||
#include <QStackedLayout> |
||||
|
||||
#include "selfdrive/common/util.h" |
||||
#include "selfdrive/ui/qt/widgets/cameraview.h" |
||||
|
||||
class DriverViewScene : public QWidget { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
explicit DriverViewScene(QWidget *parent); |
||||
|
||||
public slots: |
||||
void frameUpdated(); |
||||
|
||||
protected: |
||||
void showEvent(QShowEvent *event) override; |
||||
void hideEvent(QHideEvent *event) override; |
||||
void paintEvent(QPaintEvent *event) override; |
||||
|
||||
private: |
||||
Params params; |
||||
SubMaster sm; |
||||
QImage face; |
||||
bool is_rhd = false; |
||||
bool frame_updated = false; |
||||
}; |
||||
|
||||
class DriverViewWindow : public QWidget { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
explicit DriverViewWindow(QWidget *parent); |
||||
|
||||
signals: |
||||
void done(); |
||||
|
||||
protected: |
||||
void mousePressEvent(QMouseEvent* e) override; |
||||
|
||||
private: |
||||
CameraViewWidget *cameraView; |
||||
DriverViewScene *scene; |
||||
QStackedLayout *layout; |
||||
}; |
@ -0,0 +1,217 @@ |
||||
#include "selfdrive/ui/qt/widgets/cameraview.h" |
||||
|
||||
#include "selfdrive/ui/qt/qt_window.h" |
||||
|
||||
namespace { |
||||
const char frame_vertex_shader[] = |
||||
#ifdef NANOVG_GL3_IMPLEMENTATION |
||||
"#version 150 core\n" |
||||
#else |
||||
"#version 300 es\n" |
||||
#endif |
||||
"in vec4 aPosition;\n" |
||||
"in vec4 aTexCoord;\n" |
||||
"uniform mat4 uTransform;\n" |
||||
"out vec4 vTexCoord;\n" |
||||
"void main() {\n" |
||||
" gl_Position = uTransform * aPosition;\n" |
||||
" vTexCoord = aTexCoord;\n" |
||||
"}\n"; |
||||
|
||||
const char frame_fragment_shader[] = |
||||
#ifdef NANOVG_GL3_IMPLEMENTATION |
||||
"#version 150 core\n" |
||||
#else |
||||
"#version 300 es\n" |
||||
#endif |
||||
"precision mediump float;\n" |
||||
"uniform sampler2D uTexture;\n" |
||||
"in vec4 vTexCoord;\n" |
||||
"out vec4 colorOut;\n" |
||||
"void main() {\n" |
||||
" colorOut = texture(uTexture, vTexCoord.xy);\n" |
||||
#ifdef QCOM |
||||
" vec3 dz = vec3(0.0627f, 0.0627f, 0.0627f);\n" |
||||
" colorOut.rgb = ((vec3(1.0f, 1.0f, 1.0f) - dz) * colorOut.rgb / vec3(1.0f, 1.0f, 1.0f)) + dz;\n" |
||||
#endif |
||||
"}\n"; |
||||
|
||||
const mat4 device_transform = {{ |
||||
1.0, 0.0, 0.0, 0.0, |
||||
0.0, 1.0, 0.0, 0.0, |
||||
0.0, 0.0, 1.0, 0.0, |
||||
0.0, 0.0, 0.0, 1.0, |
||||
}}; |
||||
|
||||
mat4 get_driver_view_transform() { |
||||
const float driver_view_ratio = 1.333; |
||||
mat4 transform; |
||||
if (Hardware::TICI()) { |
||||
// from dmonitoring.cc
|
||||
const int full_width_tici = 1928; |
||||
const int full_height_tici = 1208; |
||||
const int adapt_width_tici = 668; |
||||
const int crop_x_offset = 32; |
||||
const int crop_y_offset = -196; |
||||
const float yscale = full_height_tici * driver_view_ratio / adapt_width_tici; |
||||
const float xscale = yscale*(1080)/(2160)*full_width_tici/full_height_tici; |
||||
transform = (mat4){{ |
||||
xscale, 0.0, 0.0, xscale*crop_x_offset/full_width_tici*2, |
||||
0.0, yscale, 0.0, yscale*crop_y_offset/full_height_tici*2, |
||||
0.0, 0.0, 1.0, 0.0, |
||||
0.0, 0.0, 0.0, 1.0, |
||||
}}; |
||||
} else { |
||||
// frame from 4/3 to 16/9 display
|
||||
transform = (mat4){{ |
||||
driver_view_ratio*(1080)/(1920), 0.0, 0.0, 0.0, |
||||
0.0, 1.0, 0.0, 0.0, |
||||
0.0, 0.0, 1.0, 0.0, |
||||
0.0, 0.0, 0.0, 1.0, |
||||
}}; |
||||
} |
||||
return transform; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
CameraViewWidget::CameraViewWidget(VisionStreamType stream_type, QWidget* parent) : stream_type(stream_type), QOpenGLWidget(parent) { |
||||
setAttribute(Qt::WA_OpaquePaintEvent); |
||||
|
||||
timer = new QTimer(this); |
||||
connect(timer, &QTimer::timeout, this, &CameraViewWidget::updateFrame); |
||||
} |
||||
|
||||
CameraViewWidget::~CameraViewWidget() { |
||||
makeCurrent(); |
||||
doneCurrent(); |
||||
glDeleteVertexArrays(1, &frame_vao); |
||||
glDeleteBuffers(1, &frame_vbo); |
||||
glDeleteBuffers(1, &frame_ibo); |
||||
} |
||||
|
||||
void CameraViewWidget::initializeGL() { |
||||
initializeOpenGLFunctions(); |
||||
|
||||
gl_shader = std::make_unique<GLShader>(frame_vertex_shader, frame_fragment_shader); |
||||
GLint frame_pos_loc = glGetAttribLocation(gl_shader->prog, "aPosition"); |
||||
GLint frame_texcoord_loc = glGetAttribLocation(gl_shader->prog, "aTexCoord"); |
||||
|
||||
auto [x1, x2, y1, y2] = stream_type == VISION_STREAM_RGB_FRONT ? std::tuple(0.f, 1.f, 1.f, 0.f) : std::tuple(1.f, 0.f, 1.f, 0.f); |
||||
const uint8_t frame_indicies[] = {0, 1, 2, 0, 2, 3}; |
||||
const float frame_coords[4][4] = { |
||||
{-1.0, -1.0, x2, y1}, //bl
|
||||
{-1.0, 1.0, x2, y2}, //tl
|
||||
{ 1.0, 1.0, x1, y2}, //tr
|
||||
{ 1.0, -1.0, x1, y1}, //br
|
||||
}; |
||||
|
||||
glGenVertexArrays(1, &frame_vao); |
||||
glBindVertexArray(frame_vao); |
||||
glGenBuffers(1, &frame_vbo); |
||||
glBindBuffer(GL_ARRAY_BUFFER, frame_vbo); |
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(frame_coords), frame_coords, GL_STATIC_DRAW); |
||||
glEnableVertexAttribArray(frame_pos_loc); |
||||
glVertexAttribPointer(frame_pos_loc, 2, GL_FLOAT, GL_FALSE, |
||||
sizeof(frame_coords[0]), (const void *)0); |
||||
glEnableVertexAttribArray(frame_texcoord_loc); |
||||
glVertexAttribPointer(frame_texcoord_loc, 2, GL_FLOAT, GL_FALSE, |
||||
sizeof(frame_coords[0]), (const void *)(sizeof(float) * 2)); |
||||
glGenBuffers(1, &frame_ibo); |
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, frame_ibo); |
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(frame_indicies), frame_indicies, GL_STATIC_DRAW); |
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); |
||||
glBindVertexArray(0); |
||||
|
||||
if (stream_type == VISION_STREAM_RGB_FRONT) { |
||||
frame_mat = matmul(device_transform, get_driver_view_transform()); |
||||
} else { |
||||
auto intrinsic_matrix = stream_type == VISION_STREAM_RGB_WIDE ? ecam_intrinsic_matrix : fcam_intrinsic_matrix; |
||||
float zoom_ = zoom / intrinsic_matrix.v[0]; |
||||
if (stream_type == VISION_STREAM_RGB_WIDE) { |
||||
zoom_ *= 0.5; |
||||
} |
||||
float zx = zoom_ * 2 * intrinsic_matrix.v[2] / width(); |
||||
float zy = zoom_ * 2 * intrinsic_matrix.v[5] / height(); |
||||
|
||||
const mat4 frame_transform = {{ |
||||
zx, 0.0, 0.0, 0.0, |
||||
0.0, zy, 0.0, -y_offset / height() * 2, |
||||
0.0, 0.0, 1.0, 0.0, |
||||
0.0, 0.0, 0.0, 1.0, |
||||
}}; |
||||
frame_mat = matmul(device_transform, frame_transform); |
||||
} |
||||
vipc_client = std::make_unique<VisionIpcClient>("camerad", stream_type, true); |
||||
} |
||||
|
||||
void CameraViewWidget::showEvent(QShowEvent *event) { |
||||
timer->start(0); |
||||
} |
||||
|
||||
void CameraViewWidget::hideEvent(QHideEvent *event) { |
||||
timer->stop(); |
||||
vipc_client->connected = false; |
||||
latest_frame = nullptr; |
||||
} |
||||
|
||||
void CameraViewWidget::paintGL() { |
||||
if (!latest_frame) { |
||||
glClearColor(0, 0, 0, 1.0); |
||||
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); |
||||
return; |
||||
} |
||||
|
||||
glViewport(0, 0, width(), height()); |
||||
|
||||
glBindVertexArray(frame_vao); |
||||
glActiveTexture(GL_TEXTURE0); |
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture[latest_frame->idx]->frame_tex); |
||||
if (!Hardware::EON()) { |
||||
// this is handled in ion on QCOM
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, latest_frame->width, latest_frame->height, |
||||
0, GL_RGB, GL_UNSIGNED_BYTE, latest_frame->addr); |
||||
} |
||||
|
||||
glUseProgram(gl_shader->prog); |
||||
glUniform1i(gl_shader->getUniformLocation("uTexture"), 0); |
||||
glUniformMatrix4fv(gl_shader->getUniformLocation("uTransform"), 1, GL_TRUE, frame_mat.v); |
||||
|
||||
assert(glGetError() == GL_NO_ERROR); |
||||
glEnableVertexAttribArray(0); |
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void *)0); |
||||
glDisableVertexAttribArray(0); |
||||
glBindVertexArray(0); |
||||
} |
||||
|
||||
void CameraViewWidget::updateFrame() { |
||||
if (!vipc_client->connected && vipc_client->connect(false)) { |
||||
// init vision
|
||||
for (int i = 0; i < vipc_client->num_buffers; i++) { |
||||
texture[i].reset(new EGLImageTexture(&vipc_client->buffers[i])); |
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture[i]->frame_tex); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
||||
|
||||
// BGR
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); |
||||
assert(glGetError() == GL_NO_ERROR); |
||||
} |
||||
latest_frame = nullptr; |
||||
} |
||||
|
||||
if (vipc_client->connected) { |
||||
VisionBuf *buf = vipc_client->recv(); |
||||
if (buf != nullptr) { |
||||
latest_frame = buf; |
||||
update(); |
||||
emit frameUpdated(); |
||||
} else { |
||||
LOGE("visionIPC receive timeout"); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,44 @@ |
||||
#pragma once |
||||
|
||||
#include <memory> |
||||
|
||||
#include <QOpenGLFunctions> |
||||
#include <QOpenGLWidget> |
||||
|
||||
#include "cereal/visionipc/visionipc_client.h" |
||||
#include "selfdrive/common/glutil.h" |
||||
#include "selfdrive/common/mat.h" |
||||
#include "selfdrive/common/visionimg.h" |
||||
#include "selfdrive/ui/ui.h" |
||||
|
||||
class CameraViewWidget : public QOpenGLWidget, protected QOpenGLFunctions { |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
using QOpenGLWidget::QOpenGLWidget; |
||||
explicit CameraViewWidget(VisionStreamType stream_type, QWidget* parent = nullptr); |
||||
~CameraViewWidget(); |
||||
|
||||
signals: |
||||
void frameUpdated(); |
||||
|
||||
protected: |
||||
void paintGL() override; |
||||
void initializeGL() override; |
||||
void showEvent(QShowEvent *event) override; |
||||
void hideEvent(QHideEvent *event) override; |
||||
|
||||
protected slots: |
||||
void updateFrame(); |
||||
|
||||
private: |
||||
VisionBuf *latest_frame = nullptr; |
||||
GLuint frame_vao, frame_vbo, frame_ibo; |
||||
mat4 frame_mat; |
||||
std::unique_ptr<VisionIpcClient> vipc_client; |
||||
std::unique_ptr<EGLImageTexture> texture[UI_BUF_COUNT]; |
||||
std::unique_ptr<GLShader> gl_shader; |
||||
|
||||
VisionStreamType stream_type; |
||||
QTimer* timer; |
||||
}; |
Loading…
Reference in new issue