Blanktar

  1. top
  2. blog
  3. 2020
  4. 08

Python/OpenCVでGStreamerを使って仮想のWebカメラを作る

画面キャプチャをやってみた先ほどの記事に引き続き、Python/OpenCV + GStreamerで遊んでみた記事です。 今度はv4l2sinkというプラグインを使って仮想Webカメラを作ってみました。

組み合わせるとOBSやFaceRigのように画面キャプチャやWebカメラの入力を混ぜて、結果をWebカメラとして出力するソフトを作れるはず。

v4l2loopbackの準備をする

まずは、v4l2loopbackをインストールします。 Gentooなら以下のような感じで。

$ sudo emerge v4l2loopback

これは特別なコンパイルオプションとか必要無いので、Ubuntuでも以下のように普通にaptで入るっぽい。

$ sudo apt install v4l2loopback-dkms

インストール出来たら、カーネルモジュールの読み込みを行ないます。

$ sudo modprobe v4l2loopback exclusive_caps=1

exclusive_capsはChromeとかで認識させるために必要っぽい。

一時的なら上記のコマンドだけで大丈夫ですが、再起動後も使いたい場合は永続化の設定をしてください。 永続化の方法は環境に合わせてよしなに。

v4l2loopbackの読み込みが出来たら、/dev/video0とかそんな名前で仮想Webカメラが追加されているはずです。 番号を変えたいときはmodprobeするときに以下のようにオプションを渡します。

$ sudo modprobe v4l2loopback video_nr=42,123 exclusive_caps=1

この例だと、/dev/video42/dev/video123の二つを生やします。 以下の例では/dev/video42にしたということにして進めます。

GStreamerだけでv4l2sinkの動作確認

v4l2loopbackの準備が出来たので、一旦gst-launchコマンドで動作確認をしてみます。

$ gst-launch-1.0 videotestsrc ! videoconvert ! videorotate ! video/x-raw,format=I420 ! v4l2sink device=/dev/video42

上手くいけば、これで/dev/video42にテスト用の映像が流れているはずです。

mplayerがインストールされていれば、以下のコマンドで流れている映像を確認出来ます。 もちろんそれ以外のWebカメラの映像を見れるソフトでも可。

$ mplayer tv:// device=/dev/video42

テレビの試験放送みたいなカラフル画像

Pythonからv4l2sinkに出力する

GStreamerからキャプチャしたときと同じで、gst-launchコマンドに渡したのと同じようなものを渡せばOKです。

さきほどテスト用に使ったvideotestsrcの代わりに、今度はappsrcを映像ソースとして使います。 これがOpenCVからの出力になるイメージ。

import cv2
import numpy


out = cv2.VideoWriter(
    'appsrc ! videoconvert ! videoscale ! video/x-raw,format=I420 ! v4l2sink device=/dev/video42',
    0,           # 出力形式。今回は0で。
    30,          # FPS
    (320, 240),  # 出力画像サイズ
    True,        # カラー画像フラグ
)

while cv2.waitKey(1) != 27:
    img = numpy.random.randint(0, 255, (240, 320, 3), numpy.uint8)  # いわゆる砂嵐画像を生成

    cv2.imshow('preview', img)
    out.write(img)

複雑な映像を用意するのが面倒だったので、乱数で砂嵐映像を生成して流し込んでみました。

実行すると、プレビュー用にウィンドウが開いてGStreamerに長しているものと同じ砂嵐が表示されるはずです。

OpenCVの出力ウィンドウに砂嵐画像が表示されている

この状態で先ほどと同じ方法で/dev/video0を確認すると、同じ映像が見れるはずです。 これでいつでもビデオ通話の相手に砂嵐を送ることが出来ますね(?)


参考: