Raspberry Pi4とRustで高速に描画する話。

STJ

こんにちは、センスタイムジャパンの中嶋です。研究開発センター、ソフトウェア開発チームでお仕事をさせていただいています。13日登場のhirakenさんの部下になります。

普段はエンジニアとしてサブチームをまとめ、ソフトウェアの設計、開発、テスト等を行っています。複数の認識モジュールをまとめ、センサから画像などのデータ取得、認識へ投入、必要に応じてお客さんのモジュール等へ結果を送信する、というのが基本的な流れです。

Raspberry Pi 4、意外と強力な演算力を持っていますが、映像のキャプチャや画面描画をおこなうと結構重くなりがちですよね。今回はgstreamerやOpenGL、(そしてRust)を使ってFull HD(1920x1080解像度)の映像を30fpsで更新する構成についてざっくり記載します。特に理由はないのですが、今回はRustで実装しました。

ハードウェア構成

ハードウェア構成はこんな感じ。今回は映像のソースとしてHDMIのキャプチャデバイスを使いました。特殊なことはしていないのでUSBカメラなどでも同じ構成が使えるはずです。

ソフトウェア構成

ソフトウェアは以下の構成。映像のキャプチャと描画を別のスレッドに分割しているのは認識など周期の異なる処理を別スレッドとして追加することを想定しているため。

`

実験プロジェクトなので単一プロセス、外部ハードウェアやソフトウェアとの通信もなく、非常にシンプルです。

映像のキャプチャ

映像のキャプチャはGStreamerを使います。パイプラインはこんな感じ。appsinkにcallbackを設定することで画像にアクセスすることができます。

v4l2src device=/dev/video0
  ! video/x-raw, width=1920, height=1080, framerate=(fraction)30/1, format=YUY2
  ! appsink name=input_sink

フォーマットがYUY2となっているのは使っているデバイスの制約です。安価な製品の場合、仕様と実態が異なっていることが多々あるため、以下のコマンドなどで確認するのがおすすめ。

$ sudo apt install v4l-utils
$ v4l2-ctl -d /dev/video0 --list-formats-ext 

ioctl: VIDIOC_ENUM_FMT
        Type: Video Capture
        [0]: 'MJPG' (Motion-JPEG, compressed)
                Size: Discrete 1920x1080
                        Interval: Discrete 0.017s (60.000 fps)
                        Interval: Discrete 0.033s (30.000 fps)
<中略>
        [1]: 'YUYV' (YUYV 4:2:2)
                Size: Discrete 1920x1080
                        Interval: Discrete 0.017s (60.000 fps)
                        Interval: Discrete 0.033s (30.000 fps)
<後略>

受け取った画像gst::Samplestd::sync::mpsc::sync_channelを使って描画スレッドに送信します。

描画

OpenGL環境はXWindow等を経由せずに直接EGL(+ gbm & drm)で構築します。実装は"こちらのレポジトリ"が参考になります。

初期化の流れは

  1. drmを初期化
  2. gbmを初期化
  3. EGLを初期化
  4. OpenGLを初期化

描画の流れは

  1. 画像をキャプチャスレッドから受信、テクスチャへ転送。
  2. GLSLを使ってYUV->RGBへ色変換しつつテクスチャを描画。
  3. その他追加したい内容を描画

まとめと課題

このままだと何を描画するにしてもOpenGLで実装する必要があって大変辛いです。CairoのOpenGLサポートに少し期待していますが、自力ビルドが必要になる等、難易度が高め。

Rustどこいった...。

投稿者プロフィール

nakajima
nakajima
研究開発センター ソフトウェア開発チームのシニアエンジニア。
どちらかといえば猫派。コーヒーは豆から挽く。