リモートワークの流行に伴い、日本でもオンラインミーティングが活用されるようになりました。ただし日本の住宅事情を考慮すると、映像を伴うオンラインミーティングには重大なリスクが存在します。そう、背景に映り込む生活環境です。今回はHDMIキャプチャーボードでその問題の解消を試みてみましょう。
ウェブカメラの画像を加工する様々な方法
要するに、今回はこういうことを実現してみたいというお話です。
図1 ワイプ付きの配信
この世に存在する「PC上で動くビデオカンファレンスシステム」のほとんどは、いわゆる「ウェブカメラ」を映像のソースとして利用します。昨今のウェブカメラの多くはPCに組み込まれているものであれ、外付けであれ、USB接続を利用した「USB Video device Class」に準拠したデバイスです。よって加工するタイミングは次の2種類が考えられます。
PCに入力されたUVCデバイスの映像を加工した上で配信する
カメラ側で加工してからUVCデバイスの映像としてPCに入力する
1のよくある例が、Zoomでいうところの「バーチャル背景」でしょう。これはウェブカメラから映像のうち「背景」をリアルタイムに自動認識し、登録済みの画像に置き換える仕組みです。前景と背景をキレイに区別し、切り取るためにも無地の背景、特に「グリーンバック」と呼ばれる緑色のスクリーンが推奨されています[1] 。
2については市販のものだけで実現するのはおそらく難しいでしょう。ただし、Raspberry Piなどを組み合わせてウェブカメラそのものを「自作」してしまうという方法は使えます。もし今後もこのリモートワークの活用が続くようなら、そのうち美肌補正とか「盛れるウェブカメラ」も出てくるかもしれませんね。
そして今回紹介するのは、1と2の合わせ技ともいえる方法です。つまり、一度ウェブカメラからUVCデバイスの映像を受け取り、それを加工したものを別のUVCデバイスの映像として出力し、それをPC側で再度受け取って配信に利用します。
Linuxマシン上の映像をUVCデバイスとして出力するにはさらに2種類の方法に分かれます。
v4l2loopback などを利用してソフトウェアで実現する
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] 。
[2] いわゆる「ゲーム配信」だとHDMI入力はゲーム機本体につなぎ、USB端子をPCに、HDMI出力をディスプレイにつなぐことになります。今回はPCの映像をPCに出したいだけなので、HDMI出力をつなぐかどうかは自由です。また、映像ソース自体がウェブカメラから受信したUbuntu上のソフトウェアなので、加工前と加工後を比較したいのであれば、「 画面を拡張」にしておいたほうが扱いやすいです。
ちなみに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などでも「人物をできるだけアップにせずに配信する」とか「背景をできるだけ切り取って配信する」ことが可能になります。ちらかっている部屋を写したくないし、バーチャル背景が使えるほど片付いていもいないという時に使えるのではないでしょうか。
ちなみにリモートミーティングにおける個人的にベストな解は「カメラを切る」です。