コンテンツにスキップ

Top

Windows 10 の WSL2 上 の Ubuntu 20.04 で Webカメラ を操作する

WSL上のUbuntuでUSBカメラをいじろうと思ったら思いのほか大変だったのでメモを記す。
なお、ここまでやっても最終的には大きな解像度だとタイムアウトが発生し、画像の取得ができなかった。
(160x120とかの小さな解像度はOKだったが)

しかも、WSLじゃない、ふつうのWindows上でUSBカメラが認識されなくなった。
WSL上でUSBカメラを使おうとするのは当面やめたほうが良いかもしれん。

手順

①Windows UpdateをしてWindows 10を最新の状態にする

②Windows PowerShellを管理者権限で起動し、wsl --installを実行する

③謎の呪文を唱える
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

④もう一回謎の呪文を唱える
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all

すると再起動が促されるので再起動する。

⑤再度、管理者権限で Windows PowerShell を起動し、以下を実行する
wsl --set-default-version 2

⑥wslをアップデートして最新にしておく(不要かもしれない)
wsl --update
なお、更新されたかは wsl --status で確認できる

⑦Microsoft Storeを起動し、検索窓からUbuntu 20.04を探し、インストールする
インストール後、起動すると新ユーザーの作成を促されるので、ユーザー名とパスワードを入力する。
とりあえず、

$ sudo apt update
$ sudo apt upgrade -y
はしとく。

⑧Windows PCにXmingをインストールする
https://sourceforge.net/projects/xming/ から Xmingをインストール。
XmingはXサーバーで、これがないとUbuntu側からWindows側にGUIを表示させることができない。
注意点としてはXmingの起動引数に -ac を入れないといけないという点。
Xmingのショートカットのプロパティを開き、以下のようにリンク先に -ac を追加する。

"C:\Program Files (x86)\Xming\Xming.exe" :0 -clipboard -multiwindow
→
"C:\Program Files (x86)\Xming\Xming.exe" :0 -clipboard -multiwindow -ac
これがないとGUIが表示されない。

⑨Ubuntuにubuntu-desktopをインストールする
(もしかしたら不要かもしれない)

$ sudo apt install -y ubuntu-desktop

⑩UbuntuにDISPLAY変数を設定する
以下を実行する。毎回実行するのは面倒なので、.bashrcにでも書いておく。

$ export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0
ちなみに、
$ export DISPLAY=localhost:0.0
はダメだった。

⑪windowsにusbipを入れる
https://github.com/dorssel/usbipd-win/releases/tag/v2.2.0
から、usbipd-win_2.2.0.msiをダウンロードしてインストールする。

⑫Windows PowerShellを管理者権限で起動しusbipdが動くことを確認

usbipd list
usbデバイスの一覧が出ればOK。

⑬Linuxでusbipを使えるようにする

以下のコマンドを実行

$ sudo apt install -y linux-tools-5.4.0-77-generic hwdata
次に以下のコマンドで書き換え
(secure_pathの先頭に/usr/lib/linux-tools/5.4.0-77-generic:を追加)
$ sudo visudo

Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
→
Defaults        secure_path="/usr/lib/linux-tools/5.4.0-77-generic:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"

usbipが2.x以降になってからPowerShellで下記エラーがでるようになったので、

usbipd: error: WSL 'usbip' client not correctly installed. See https://github.com/dorssel/usbipd-win/wiki/WSL-support for the latest instructions.
Ubuntu上で以下のコマンドを実行することにより修正する。
$ sudo update-alternatives --install /usr/local/bin/usbip usbip /usr/lib/linux-tools/5.4.0-77-generic/usbip 20

⑭管理者権限でPowerShellを起動し、usbデバイスをアタッチする

PowerShellで以下を実行し、USBカメラのデバイス番号を取得する。

C:\Windows\system32> usbipd list
Connected:
BUSID  DEVICE                                                        STATE
1-3    ASIX AX88179A USB 3.2 Gen1 to Gigabit Ethernet Adapter        Not shared
2-6    HD Camera, IR Camera                                          Not shared
2-8    Synaptics WBDI Fingerprint Reader - USB 086                   Not shared
2-10   インテル(R) ワイヤレス Bluetooth(R)                           Not shared
3-1    FULL HD webcam, USB Microphone                                Not shared
3-3    USB 入力デバイス                                              Not shared

Persisted:
GUID                                  DEVICE
497875c2-a7d8-4228-8b4c-11ecdaccdbea  FULL HD webcam, USB Microphone

上記だと、3-1がUSBカメラ。

で、これをUbuntuにアタッチする

PS C:\Windows\system32> usbipd wsl attach --busid=3-1
PS C:\Windows\system32>

これでアタッチされた。何もメッセージがでないけど。
listを見たら変わっていることがわかる。

PS C:\Windows\system32> usbipd list
Connected:
BUSID  DEVICE                                                        STATE
1-3    ASIX AX88179A USB 3.2 Gen1 to Gigabit Ethernet Adapter        Not shared
2-6    HD Camera, IR Camera                                          Not shared
2-8    Synaptics WBDI Fingerprint Reader - USB 086                   Not shared
2-10   インテル(R) ワイヤレス Bluetooth(R)                           Not shared
3-1    FULL HD webcam, USB Microphone                                Attached
3-3    USB 入力デバイス                                              Not shared

Persisted:
GUID                                  DEVICE
Attachedになっている。

Ubuntu上でも見えるようになっている。lsusbで確認すると、

$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 007: ID 1bcf:2283 Sunplus Innovation Technology Inc. FULL HD webcam
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
とwebcamがでてきている。

が、これで本来なら使えるはずなのだが、/dev/video0と/dev/video1がでてこない。

対応するために、カーネルをコンパイルしなければならなかった。

⑪WSLのカーネルをコンパイルする

Ubuntu上で以下を実行する。

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install build-essential flex bison libssl-dev libelf-dev libncurses-dev autoconf libudev-dev libtool zip unzip v4l-utils libssl-dev python3-pip cmake git iputils-ping net-tools
$ cd /usr/src
$ TAGVERNUM=5.10.74.3
$ TAGVER=linux-msft-wsl-${TAGVERNUM}
$ sudo git clone -b ${TAGVER} https://github.com/microsoft/WSL2-Linux-Kernel.git ${TAGVERNUM}-microsoft-standard
$ cd ${TAGVERNUM}-microsoft-standard
$ sudo cp /proc/config.gz config.gz
$ sudo gunzip config.gz
$ sudo mv config .config

menu configで項目を変更する

sudo make menuconfig

Device Drivers > Multimedia support > Video4Linux optionsの中をすべて選択(*印にする)

Device Drivers > Multimedia support > Media Driver > Media USB Adapter > USB Video Class と UVC input events device support を選択(*印にする)

Device Drivers > USB support > USB/IPサポートを選択(*印にする)

vi .config
CONFIG_DEBUG_INFO_BTF=y
→
CONFIG_DEBUG_INFO_BTF=n
に変更する。
すでに、 3818 # CONFIG_DEBUG_INFO_BTF is not set になっていたら何もしない。

カーネルをコンパイルする。

$ sudo make -j$(nproc)
$ sudo make modules_install -j$(nproc)
$ sudo make install -j$(nproc)

出来上がったカーネルをWindows上から見える場所にコピーする。

$ sudo cp /usr/src/${TAGVERNUM}-microsoft-standard/vmlinux /mnt/c/Users/<windows username>/

WSL2の設定ファイル(C:\Users\\.wslconfig)を新規作成し、カーネルの場所を指定する。

[wsl2]
kernel=C:\\Users\\<windows username>\\vmlinux

⑫管理者権限でPowerShellを起動すると読み直されているので、もうOK!

以下のプログラムで動作確認をする。

import cv2

cap = cv2.VideoCapture(0)

cap.set(cv2.CAP_PROP_FRAME_WIDTH, 160)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 120)
cap.set(cv2.CAP_PROP_FPS, 10)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('Y','U','Y','V'))
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)

while True:
    _, img = cap.read()
    cv2.imshow("", img)
    key = cv2.waitKey(1)
    if key == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

なお、注意が必要なのは、/dev/video0と/dev/video1はアタッチのたびに権限を変更しないといけないこと。

$ sudo chmod 777 /dev/video*
しないと、
[ WARN:0@0.067] global /io/opencv/modules/videoio/src/cap_v4l.cpp (889) open VIDEOIO(V4L2:/dev/video0): can't open camera by index
な感じでエラーになる。

高解像度だとでない

ここまで頑張ってやっとこさWSLでUSBカメラが使えるようになったのに、640x480とかにしたら

[ WARN:0@10.526] global /io/opencv/modules/videoio/src/cap_v4l.cpp (1000) tryIoctl VIDEOIO(V4L2:/dev/video0): select() timeout.

とタイムアウトエラーがでた。
解像度が大きいと駄目なようだ。
YUYVではなくモーションJPEGにしたら多少大きな解像度でもいけたが、フルHDとかは無理だった。

もういやなのであきらめた。

以上!

USBカメラがつながらなくなった!

WSL2でUSBカメラが使えないのはあきらめたが、ふと気づいたらWindows10でふつうにWebカメラが使えなくなった。

なんで?といろいろ調べたがわからず、usbipd-winをアンインストールしたら直った。

なんかもう、ってかんじ。