【Python】opencvで複数のWebカメラを識別して使用したい

## はじめに
OpenCVからVideoデバイスにアクセスする場合、デバイスIDを指定する方法しかありません。

```py
DEVICE_ID = 1
cap = cv2.VideoCapture (DEVICE_ID)
```

しかしVideoデバイスが複数接続された場合、このデバイスIDはいつも同じとは限らないため、不都合が起きたりします。例えばある物体を1つのカメラは上から、もう1つのカメラで横から撮影し、各々のカメラで得られたフレーム画像に別々の画像処理を施したいときに困ります。 今回は、複数接続時に、安定して同じデバイスにアクセスすることを目指します。


## 対象者
この記事は下記のような人を対象にしています。

- 複数のWebカメラを使用する機会のある方
- マジックナンバー嫌いな方

## 環境
- Windows10
- Python3.7

## 結論
CV-camera-finderをお借りすることで、linuxのlsusbコマンドのようなことを実現できます。linuxの方はこちらをご参照ください。助けになると思います。

https://dev.classmethod.jp/articles/opencv-device-id-by-port-number/

## CV-camera-finder の使用

https://github.com/pvys/CV-camera-finder

異なる種類のWebカメラを識別するだけであればこの方のコードをお借りするだけで解決します。まず、適当にgit cloneしてきて、64/ の中にあるpymf.pydを作業ディレクトリに移します。

```
# 1.git clone
git clone https://github.com/pvys/CV-camera-finder.git

# 2.CV-camera-finder/64/pymf.pyd を作業ディレクトリに移動
```
続いて、作業ディレクトリに確認用のコードを準備します。
```cam_test.py
# 複数種類のカメラの中から1つのカメラを選択して起動するコード例

from pymf import get_MF_devices
import os
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
import cv2

device_list = get_MF_devices()
print("device list")
print(device_list)
print("Select camera ('y' or 'n')")

cv_index = None
for i, device_name in enumerate(device_list):
    # find index of camera you want
    q = input(f"Wanna use {device_name}?\n")
    if q.strip() == "y":
        cv_index = i
        break

if cv_index is None:
    print("Not found")
else:
    # make sure you use Media Foundation
    cap = cv2.VideoCapture(cv_index + cv2.CAP_MSMF)
    while (cap.isOpened):
        ret, frame = cap.read()
        if ret:
            cv2.imshow("frame", frame)
            k = cv2.waitKey(1)
            if k > 0:
                break
        else:
            break
    cap.release()
    cv2.destroyAllWindows()
```
実行結果例↓
```terminal
(venv) C:\Users\youichi_io\work>python cam_test.py
device list
['C270 HD WEBCAM', 'c922 Pro Stream Webcam', 'USB2.0 HD UVC WebCam']
Select camera ('y' or 'n')
Wanna use C270 HD WEBCAM?
n
Wanna use c922 Pro Stream Webcam?
y
# 選択したカメラが起動する
```
cam_test.pyを修正し、複数のWebカメラを識別して使用します。

```cam_test2.py
# 複数のWebカメラを識別して使用するコード例

from pymf import get_MF_devices
import os
os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
import cv2
import time

device_list = get_MF_devices()
print("device list")
print(device_list)

use_cameras = [
    'C270 HD WEBCAM',
    'c922 Pro Stream Webcam'
]

cam1 = None
for i, device_name in enumerate(device_list):
    if device_name == use_cameras[0]:
        cam1 = cv2.VideoCapture(i)

# カメラ設定に時間差がないとエラーになりやすい
time.sleep(1)

# 分かりやすくするためにあえて冗長に書いています
cam2 = None
for i, device_name in enumerate(device_list):
    if device_name == use_cameras[1]:
        cam2 = cv2.VideoCapture(i)

while True:
    ret1, frame1 = cam1.read()
    ret2, frame2 = cam2.read()
    if ret1:
        # C270 HD WEBCAM の処理
        cv2.imshow("C270 frame", frame1)
    if ret2:
        # c922 Pro Stream Webcam の処理
        cv2.imshow("c922 frame", frame2)
        
    k = cv2.waitKey(1)
    if k > 0:
        break
cap.release()
cv2.destroyAllWindows()
```
異なる種類のWebカメラを識別することはできますが、同じ種類のWebカメラを識別することはできません。やり方を知っている方はコメント欄などで教えてくださると幸いです。

## おわりに
複数のWebカメラを識別して使用する方法についてまとめました。
もしかしてffmpegとか使えば同種類のカメラの識別が可能なんですかね？あとでやってみます(次回)。