Ubuntu Weekly Recipe

第619回HDMIキャプチャーボードでZoomへの配信映像を加工する

リモートワークの流行に伴い、日本でもオンラインミーティングが活用されるようになりました。ただし日本の住宅事情を考慮すると、映像を伴うオンラインミーティングには重大なリスクが存在します。そう、背景に映り込む生活環境です。今回はHDMIキャプチャーボードでその問題の解消を試みてみましょう。

ウェブカメラの画像を加工する様々な方法

要するに、今回はこういうことを実現してみたいというお話です。

図1 ワイプ付きの配信
画像

この世に存在する「PC上で動くビデオカンファレンスシステム」のほとんどは、いわゆる「ウェブカメラ」を映像のソースとして利用します。昨今のウェブカメラの多くはPCに組み込まれているものであれ、外付けであれ、USB接続を利用した「USB Video device Class」に準拠したデバイスです。よって加工するタイミングは次の2種類が考えられます。

  1. PCに入力されたUVCデバイスの映像を加工した上で配信する
  2. カメラ側で加工してからUVCデバイスの映像としてPCに入力する

1のよくある例が、Zoomでいうところの「バーチャル背景」でしょう。これはウェブカメラから映像のうち「背景」をリアルタイムに自動認識し、登録済みの画像に置き換える仕組みです。前景と背景をキレイに区別し、切り取るためにも無地の背景、特に「グリーンバック」と呼ばれる緑色のスクリーンが推奨されています[1]⁠。

2については市販のものだけで実現するのはおそらく難しいでしょう。ただし、Raspberry Piなどを組み合わせてウェブカメラそのものを「自作」してしまうという方法は使えます。もし今後もこのリモートワークの活用が続くようなら、そのうち美肌補正とか「盛れるウェブカメラ」も出てくるかもしれませんね。

そして今回紹介するのは、1と2の合わせ技ともいえる方法です。つまり、一度ウェブカメラからUVCデバイスの映像を受け取り、それを加工したものを別のUVCデバイスの映像として出力し、それをPC側で再度受け取って配信に利用します。

Linuxマシン上の映像をUVCデバイスとして出力するにはさらに2種類の方法に分かれます。

  1. v4l2loopbackなどを利用してソフトウェアで実現する
  2. UVC対応のHDMIキャプチャーボードを利用してハードウェアで実現する

1についてはウェブカメラとそれなりのスペックのPCさえあれば実現できます。ただ、設定等いくつかの「環境によってはなんかうまく動かない」ポイントが多いため、期待通りの処理になるまでにはそれなりの試行錯誤が必要です。

それに対して2は、機材への投資は必要なものの、配信デバイスからはただのUVCデバイスとして見えるので、動かないということはあまり考えられません。映像をどのように加工するか、に専念できます。

現在ではYouTuberに代表されるゲーム配信が広く普及しているため、HDMI入力からHDMI出力とUSB出力(UVC対応)に分岐するタイプのHDMIキャプチャーデバイスが数多く販売されるようになりました。今回はこれを使って、映像を加工してみることにしましょう。

本記事ではTreasLin HSV323を利用します。ただし「UVC対応」とうたわれているUSB端子に出力するタイプのキャプチャーデバイスであれば、どれであっても同じ手順で実現可能です。

HDMIキャプチャーデバイスはUbuntuでどのように見えるのか

HDMIキャプチャーデバイスには少なくとも、HDMI入力・USBのふたつの端子が備わっているはずです。さらにパススルーに対応したタイプのものであれば、追加でHDMI出力端子もあります。また、電源は外部からとるものと映像出力を担うUSB端子からとるものののどちらかになるでしょう。TreasLin HSV323は後者でした。

まずはPCのHDMI出力とキャプチャーデバイスのHDMI入力をHDMIケーブルで接続した上で、USBも接続し、キャプチャーデバイスの電源を入れます。するとUbuntuから見ると「外部ディスプレイ」が追加されたように見えるはずです。

図2 システム設定のディスプレイから見ると「QCW 32"」が追加されていることがわかる
画像

配信映像は外部ディスプレイ側の映像をまるっとそのまま配信できると取り回しが楽なので、⁠画面を拡張」のままにしておきます。配信方法次第ではどちらのディスプレイにも同じものを表示する「ミラー」にしてしまうのもありでしょう[2]⁠。

ちなみにHDMI出力を繋がなくてもUbuntu側から見れば、まるでディスプレイが存在するかのように見えます。起動したアプリケーションをキャプチャー側のディスプレイに移動することも可能ですが、HDMI出力を繋がない限りはそれを表示するすべがないことに注意してください。移動してしまったものを戻したい場合は、システム設定のディスプレイから一旦「ミラー」にしてひとつのプライマリディスプレイにまとめてしまうか、画面左ににあるDockからアプリケーションのアイコンを右クリックし、該当するアプリケーションを選択した上で、⁠Super + Shift + ←」もしくは「Super + Shift + →」でディスプレイ間を移動するという方法があります。

普通のディスプレイデバイスなので、xrandrコマンドでもディスプレイのプロパティを確認できます。

$ xrandr --props
(中略)
HDMI-A-1 connected 1920x1080+1920+0 (normal left inverted right x axis y axis) 708mm x 398mm
        EDID:
                00ffffffffffff004477300001000000
                0a160103807341780acf74a3574cb023
                09484c21080081804540614095000101
                010101010101023a801871382d40582c
                4500c48e2100001e662150b051001b30
                40703600c48e2100001e000000fc004d
                537461722044656d6f0a2020000000fd
                00324b1e5017000a20202020202001e0
                020330f24d010304050790121314169f
                2022260907071117508301000072030c
                001000b84420c0840102030401410000
                8c0ad08a20e02d10103e9600c48e2100
                00188c0ad090204031200c405500c48e
                21000018011d00bc52d01e20b8285540
                c48e2100001e011d80d0721c1620102c
                2580c48e2100009e000000000000006f
        _MUTTER_PRESENTATION_OUTPUT: 0
        GAMMA_LUT_SIZE: 4096
                range: (0, -1)
        DEGAMMA_LUT_SIZE: 4096
                range: (0, -1)
        GAMMA_LUT: 0
                range: (0, 65535)
        CTM: 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0
                0 1
        DEGAMMA_LUT: 0
                range: (0, 65535)
        TearFree: auto
                supported: off, on, auto
        vrr_capable: 0
                range: (0, 1)
        max bpc: 8
                range: (8, 16)
        underscan vborder: 0
                range: (0, 128)
        underscan hborder: 0
                range: (0, 128)
        underscan: off
                supported: off, on, auto
        scaling mode: None
                supported: None, Full, Center, Full aspect
        link-status: Good
                supported: Good, Bad
        CONNECTOR_ID: 80
                supported: 80
        non-desktop: 0
                range: (0, 1)
   1920x1080     60.00*+  50.00    59.94    30.00    24.00    29.97    23.98
   4096x2160     24.00    23.98
   3840x2160     30.00    25.00    24.00    29.97    23.98
   1680x1050     60.00
   1280x1024     60.02
   1440x900      59.90
   1360x768      60.02
   1280x800      60.00
   1280x720      60.00    50.00    59.94
   1024x768      60.00
   800x600       60.32
   720x576       50.00
   720x480       60.00    59.94
   640x480       60.00    59.94

キャプチャーデバイスとしてはUVCなので、lsusbコマンドを実行するとそのデバイスが認識されていることもわかります。

$ lsusb
Bus 002 Device 004: ID 1e4e:7103 Cubeternet MiraBox Video Capture
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 8087:0a2b Intel Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 002: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

今回は一番上の「Cubeternet MiraBox Video Capture」がそれです。

$ sudo lsusb -vs 002:004

Bus 002 Device 004: ID 1e4e:7103 Cubeternet MiraBox Video Capture
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               3.00
  bDeviceClass          239 Miscellaneous Device
  bDeviceSubClass         2
  bDeviceProtocol         1 Interface Association
  bMaxPacketSize0         9
  idVendor           0x1e4e Cubeternet
  idProduct          0x7103
  bcdDevice            1.00
  iManufacturer           6 MiraBox Video Capture
  iProduct                7 MiraBox Video Capture
(後略)

さらにCheeseでウェブカメラとしての動作を確認したいところですが、なぜかCheeseではウェブカメラとして認識してくれませんでした。そこでv4l-utilsコマンドを使って、ウェブカメラの詳細を確認してみます。

$ sudo apt install v4l-utils
$ v4l2-ctl --list-devices
MiraBox Video Capture : MiraBox (usb-0000:00:14.0-3):
        /dev/video0
        /dev/video1

ビデオデバイスとしてはふたつ存在するようですが、ウェブカメラとして使えるのは上の/dev/video0だけです。

$ v4l2-ctl -d /dev/video0 --all
Driver Info:
        Driver name      : uvcvideo
        Card type        : MiraBox Video Capture : MiraBox
        Bus info         : usb-0000:00:14.0-3
        Driver version   : 5.4.30
        Capabilities     : 0x84a00001
                Video Capture
                Metadata Capture
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps      : 0x04200001
                Video Capture
                Streaming
                Extended Pix Format
Priority: 2
Video input : 0 (Camera 1: ok)
Format Video Capture:
        Width/Height      : 1920/1080
        Pixel Format      : 'YUYV' (YUYV 4:2:2)
        Field             : None
        Bytes per Line    : 3840
        Size Image        : 4147200
        Colorspace        : sRGB
        Transfer Function : Default (maps to sRGB)
        YCbCr/HSV Encoding: Default (maps to ITU-R 601)
        Quantization      : Default (maps to Limited Range)
        Flags             :
Crop Capability Video Capture:
        Bounds      : Left 0, Top 0, Width 1920, Height 1080
        Default     : Left 0, Top 0, Width 1920, Height 1080
        Pixel Aspect: 1/1
Selection Video Capture: crop_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
Selection Video Capture: crop_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
Streaming Parameters Video Capture:
        Capabilities     : timeperframe
        Frames per second: 60.000 (60/1)
        Read buffers     : 0

ちなみにCheeseでは認識しませんが、第303回のguvcviewで簡単ビデオ撮影でも紹介されているguvcviewならウェブカメラとして認識可能です。

$ sudo apt install guvcview
図3 guvcviewでサブディスプレイとして認識されているキャプチャーデバイスをウェブカメラとして表示する
画像

上記の例だとメインディスプレイとサブディスプレイの間にLibreOffice Impressのウィンドウをまたがせている状態で、サブディスプレイの画像をウェブカメラとして表示しています。

OBSを使って映像を加工する

さて、本題の映像を加工する部分です。これにもいろいろな方法がありますが、今回は定番のOBS Studioを使います。

OBS(Open Broadcaster Software)はビデオの録画や配信でよく使われているソフトウェアで、さまざまな配信サービスへの対応や複数の映像ソースの合成、簡易的な映像編集などをサポートしている便利なツールです。

Ubuntu 20.04 LTSならリポジトリからobs-studioパッケージとして、ほぼ最新の25.0.3をインストール可能です。ただsnapパッケージなら、最新の25.0.8がUbuntuのどのリリースでも使えます。今回はsnap版をインストールしましょう。

$ sudo snap install obs-studio

あとはSuper+Aで表示されるアプリケーション一覧から「obs」と入力すればOBSを起動できます。初期起動時はセットアップウィザードを起動するか確認されますが、今回は特定の配信サービスを利用するわけではないので「Cancel」を押しておきましょう。さらに日本語のインターフェースに変更したい場合は、FileメニューからSettingsを選び、Languageを「日本語」に変更して「OK」ボタンを押してください。OBSが自動的に再起動し、日本語インターフェースになります。

図4 OBSのメイン画面
画像

OBS自体は非常に多機能です。Ubuntuの他のアプリケーションとは異なるUIなので、慣れるまでに時間がかかるかもしれません。ただ一度使い方を覚えたら便利になるので、最初は根気強くいろいろ調べながら触ってください。

とりあえず今回は「ウェブカメラの映像と任意の画像を合成して、UVCデバイスとして出力する」ことを目標とします。

ウェブカメラと任意の画像を合成する

OBSでは複数の「ソース」を合成して「シーン」として出力します。ウェブカメラと任意の画像を合成するということはつまり、ウェブカメラと任意の画像のふたつのソースを用意するということです。

まずは任意の画像を選択しましょう。

図5 ソースから+ボタンを押して「画像」を選ぶ
画像

ここで「ウインドウキャプチャ」を選択すれば任意のアプリケーションの表示を合成できますし、⁠テキスト」を選択すれば文字列を合成できます。また映像だけでなく音声も「ソース」として合成可能です。

図6 ⁠ソースを作成/選択」で名前を指定する
画像

今回は8.04の壁紙を使うことにしましょう。snap版のOBSはルートファイルシステムにはアクセスできないので、あらかじめホームディレクトリ以下にコピーしておきます。

$ cp /usr/share/backgrounds/hardy_wallpaper_uhd.png ~/snap/obs-studio/common/
図7 参照ボタンからファイルパスを選択しOKボタンを押す
画像

ただしこのままだと一部しか表示されないので、ソースの「hardy」を右クリックし、⁠出力サイズ変更(ソースサイズ⁠⁠」を選択してください。ただしもともと画像が3840x2400とサイズが大きいので、あとで調整することにしましょう。

図8 出力する画像が設定された
画像

次はウェブカメラを合成します。まずはあらかじめウェブカメラを接続しておいてください。画像のときと同様にソースから+ボタンを押して、今度は「映像キャプチャデバイス(V4L2⁠⁠」を選択します。

図9 ウェブカメラのプロパティ
画像

この時点でフレームレートや解像度を変更できますが、今回はこのままにしておきます。

図10 ウェブカメラと画像を合成できた
画像

思ったよりもカメラの映像が小さくなっています。これは画像側の解像度が大きく、それに合わせてキャンパスサイズを変更してしまったためです。キャンパスサイズはファイルの設定にある「映像」から変更できます。

図11 キャンパスの解像度を画面解像度に戻した
画像

このままだと先ほどと同じように画像の解像度が大きすぎるので、調整します。ソースの「hardy」を右クリックして、⁠変換」を選択、⁠画面に合わせる」を選びましょう。

図12 画像が1920x1080のサイズにスケーリングされた
画像

今度はカメラの画像が大きいですね。こちらはカメラの画像をクリックしたら表示される赤枠を使って手作業で調整します。

図13 カメラの映像は画面右上にワイプっぽく表示
画像

さて、本題の「カメラの背景」をなんとかする処理です。これには「エフェクトフィルタ」を使用します。

図14 カメラ映像を右クリックして、⁠フィルタ」を選択すると表示されるフィルタ設定ダイアログ
画像

今回は「イメージマスク/ブレンド」を利用します。他のフィルターに関してはOBSのWikiを参照してください。

イメージマスクではマスク画像を用意して、それを合成します。今回は単純に「周囲の余計な背景を切り取りたい」だけななので、モノクロの円形画像を用意しましょう。黒い部分がマスクされ、白い部分が残ります。

もちろん円形以外の複雑な形でもかまいません。GIMPや任意の画像ツールを作って好みの画像を作成してください。ここではImageMagickを使ってコマンドで画像を生成してしまいます。

$ sudo apt install imagemagick
$ convert -size 1920x1080 xc:black -fill white \
  -draw "circle 960,540 1260,940" \
  ~/snap/obs-studio/common/circle.png

マスク画像のアスペクト比はウェブカメラのそれと合わせておいたほうが楽です。ここでは16:9にしています。ちなみに使っているカメラのスペックの都合上、1920x1080だと遅延が大きいのでカメラそのものの解像度も1280x768に変更しました。

図15 設定ダイアログからマスク画像を選択・適用した状態
画像
図16 カメラや被写体の位置を調整して完成!
画像

マスク部分は固定になってしまうので、細かい位置の調整はカメラや被写体側で行ってください。これでウェブカメラから加工した画像が作成できました。

エフェクトフィルタはさまざまな効果を適用できますので、いろいろと試してみると良いでしょう。

合成した画像をUVCデバイスとして出力する

合成した画像をUVCデバイスとして出力します。これはOBSの文脈で言うとシーンの出力になります。そしてOBSにはシーンの出力方法として任意のディスプレイへ全画面出力する「全画面プロジェクター」が存在するのです。

シーンを右クリックして、⁠全画面プロジェクター」からHDMIキャプチャーデバイスであるディスプレイを選択します。

図17 今回は「ディスプレイ2」がキャプチャーデバイス
画像

これでUVCデバイスとして現在のシーンが出力されているはずです。ためしにguvcviewで確認してみましょう。

図18 画像だとわかりにくいかもしれないがウェブカメラとして合成画像がリアルタイムで表示されている
画像
図19 Zoomのビデオ設定でもHDMIキャプチャーボードが選択できていることがわかる
画像

これでうまく使えばZoomなどでも「人物をできるだけアップにせずに配信する」とか「背景をできるだけ切り取って配信する」ことが可能になります。ちらかっている部屋を写したくないし、バーチャル背景が使えるほど片付いていもいないという時に使えるのではないでしょうか。

ちなみにリモートミーティングにおける個人的にベストな解は「カメラを切る」です。

おすすめ記事

記事・ニュース一覧