Raspberry Pi Advent Calendar 2019の11日目の記事です。前日はtera1707さんのRaspberryPi3にI2Cサーマルカメラ(サーモグラフィ)をつなげて温度を画像化するでした。興味深いのでいつかやってみたいと思います。
さて。あるプロジェクトで、Raspberry Pi(以下ラズパイ)から複数台のUSBカメラで静止画を撮影する必要がありました。そのときにプログラムからカメラを指定する方法でつまづきましたので解決方法を記しておきます。
環境
Raspberry Pi 3 モデルB
ロジクール C270 HD WEBCAM(×2台)
2台のUSBカメラはラズパイに4ヵ所あるUSBのいずれかに刺さっている状態です。まずカメラがOSに認識されているかを調べてみましょう。
$ ls /dev/video*
普通なら次のようにデバイスファイルの一覧が出るはずです。ただし、video0とvideo1はそれぞれ特定のカメラとひも付いているわけではありません。
/dev/video0 /dev/video1
Pythonで撮影する
ラズパイでカメラ撮影をする方法はいくつもありますが、OpenCVライブラリをここでは利用します。なければインストールを。
$ pip install opencv-python
ではさっそくプログラムから撮影してみましょう。最小のコードを書くとこうなります。
import cv2
cam = cv2.VideoCapture(0)
ret, im = cam.read()
VideoCaptureインスタンスのreadメソッドで撮影を行いますが、コンストラクタに渡す数はカメラのインデックスであり、上記で一覧出力した/dev/video0 /dev/video1
に含まれる整数がそれに該当します。ここでやっかいなのが、0と1は様々なタイミングですり変わります。つまり、0を指定すればカメラAだ、のようにできればシンプルなのですがそれは難しいようです。そこで、USBのどこに接続されているカメラかを調べることでこれを解決します。ここではLinuxでカメラに関する多機能ユーティリティツールであるVideo4Linuxを利用します。
Video4Linuxのインストール
$ sudo apt-get -y install v4l-utils
いまほしい情報は、/dev/video0 /dev/video1
と2台のカメラの紐づけであり、これはいくつかの方法があります。
シンボリックリンクを利用する
ls -l /dev/v4l/by-path
シンボリックリンクにUSBのバス情報が含まれているので、矢印右辺と紐づけできます。
lrwxrwxrwx 1 root root 12 Nov 14 15:17 platform-3f980000.usb-usb-0:1.2:1.0-video-index0 -> ../../video0
lrwxrwxrwx 1 root root 12 Nov 14 15:17 platform-3f980000.usb-usb-0:1.4:1.0-video-index0 -> ../../video1
v4lを利用する
次の方法は、以下のコマンドを利用します
v4l2-ctl --list-devices
すると、以下のようにデバイス情報、バス情報、インデックスをペアとして、一覧を出力してくれます。
UVC Camera (046d:0825) (usb-3f980000.usb-1.2):
/dev/video0
UVC Camera (046d:0825) (usb-3f980000.usb-1.4):
/dev/video1
Pythonからsubprocessで呼んでみよう
subprocessを使えばPythonから上記の情報を得ることができます。
import subprocess
cmd = "v4l2-ctl --list-devices"
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
ret = p.communicate()[0]
ret = ret.split("\n")
ret = filter(None, ret)
print(ret)
出力は次の通り
['UVC Camera (046d:0825) (usb-3f980000.usb-1.2):', '\t/dev/video0', 'UVC Camera (046d:0825) (usb-3f980000.usb-1.4):', '\t/dev/video1']
おまけ:カメラのシリアル番号を取得する
上記出力のデバイス情報である046d:0825
を使って、カメラのシリアル番号を取得することができます。
$ sudo lsusb -v -d 046d:0825 | grep -i serial
以下のような8桁のシリアル番号が取得できるため、使いようによっては便利かと思います。
iSerial 2 10763230
iSerial 2 B9D5A4E0