2019年3月6日水曜日

OpenCVにて、USB CAM複数接続が動かないビョーキ(未解決)

OpenCV、画像処理には欠かせない便利なライブラリ.たまにお世話になっている.

今回はOpenCVのトラブルで死んだという話だ.詰んでしまった.

トラブルは、1台のPCにUSB CAMを3台接続したいが、ダメ~と怒られる.
(IP CAMではなくUSB接続のカメラである)

諸条件により、以前は無問題で使えていたが、最新のOpenCVでは複数台接続が出来なくなった.この諸条件というのがクセ者なので以下に諸々を記す.

【原因のあらまし】    (環境はLinuxにて)
1)USB CAMを3台接続してもちゃんと動く場合.
PCIバスにUSBホストコントローラが3つぶら下がっている.各USBホストコントローラ当りUSBCAMを1台接続する.これなら正常.しかしこういう接続はしたくない.
2)ダメな場合.
USBホストコントローラに複数のUSBCAMを接続した場合.「デバイスに空きがありません」系のメッセージで止まる.
3)いうまでもなくダメな場合.
USBHUBを介して複数台のUSBCAMを接続した場合でももちろんダメと云われる.
【トラブル詳細】
エラーメッセージの全容はこうだ.
libv4l2: error turning on stream: No space left on device
VIDIOC_STREAMON: No space left on device
OpenCV Error: Assertion failed (scn == 3 || scn == 4) in cvtColor, file /build/opencv-00QkEr/opencv-2.4.9.1+dfsg1/modules/imgproc/src/color.cpp, line 3737
terminate called after throwing an instance of 'cv::Exception'
  what():  /build/opencv-00QkEr/opencv-2.4.9.1+dfsg1/modules/imgproc/src/color.cpp:3737: error: (-215) scn == 3 || scn == 4 in function cvtColor

V4L2という、OpenCVではよく見かけるライブラリが、「そんな空きはありません」と怒っている.

このエラーが生じる場面は、
  cam0 = cv2.VideoCapture(0)
  cam1 = cv2.VideoCapture(1)     ←ここ
のように2台目のUSBCAMをopenしようとした時点だと思われる.

ここで、システム面の条件を.
OS: Kona-linux4.0
Linux 4.9.0-8-amd64 #1 SMP Debian 4.9.144-3.1 (2019-02-19) x86_64 GNU/Linux

OpenCV: 2.4.9.1
OpenCV: 3.4.5       ←upgradeしてみたけど症状は同じ

USBCAM駆動ソフト: C++、Python、 Python3      ←同じ症状

USBCAM: 中華製名無し製品
以前この投稿で書いたもの「画角の狭いUSB CAMが多いとお嘆きの貴兄に、広角のUSB CAMを贈ります

Python3+OpenCV3.4.5コード: C++でも状況は同じなので、簡単なPython3のコードを示す.USBCAMを2つOPENして新しいウインドウ2つにカメラ画像を表示するものだ.OpenCV3になって小さな地雷がたくさんあって困るわ、プンスカ!
import cv2
cam0 = cv2.VideoCapture(0)
cam1 = cv2.VideoCapture(1)    ←ここで死ぬ
if cam0.isOpened() is False:
    raise("CAM0_NG")
if cam1.isOpened() is False:
    raise("CAM0_NG")
#fourcc = cv2.CV_FOURCC('M','J','P','G')     <---NG
fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
print(format(fourcc,'0x'))
cam0.set(cv2.CAP_PROP_FOURCC,fourcc)
cam0.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cam0.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
cam1.set(cv2.CAP_PROP_FOURCC,fourcc)

print( cam0.get(cv2.CAP_PROP_FRAME_WIDTH))
print( cam0.get(cv2.CAP_PROP_FRAME_HEIGHT))
print( cam1.get(cv2.CAP_PROP_FRAME_WIDTH))
print( cam1.get(cv2.CAP_PROP_FRAME_HEIGHT))

while True:
    ret, img0 = cam0.read()
    ret, img1 = cam1.read()
    cv2.imshow('0 PUSH SPAVE KEY to CLOSE', img0)
    cv2.imshow('1 PUSH SPAVE KEY to CLOSE', img1)
    if cv2.waitKey(1) == 32: break
cam0.release()
cam1.release()
cv2.destroyAllWindows()


【トラブル詳細2】
PCだけでなく、Rapsberry-piでも試してみたら、症状は同じだった.
OpenCV: 2.4.9.1


【トラブル回避策1】
上で述べたとおり、こういう接続にすれば問題が生じない.
しかしPCなら可能だがRapsberry-piはUSBホストコントローラが1つだけのように見える(lsusb)ので、この回避策は使えないし、たとえPCであってもこれは嫌だよぅ.


【トラブル回避策2】
USBCAMを別の製品に変えると治ってしまうのだ.LogiCoolの古い製品だ.
2つのUSBCAMの素性をlsusb -vで調べてみる.

名無し中華製品:
解像度:640x480、160x120、320x240、352x288、800x600、1280x720、1280x1024、1600x1200
IF:上の各々に、非圧縮/MJPEG が選択可

Logitech, Inc. Webcam B500:
最大解像度が1280x1024までという他は名無し中華と同じ.非圧縮/MJPEGも同じ.

この状況、いかがだろうか?
V4L2が「そんなリソースないわ」と怒る原因は、1600x1200の高解像度ゆえのwork area確保のためか、1600x1200の高解像度ゆえの帯域不足か、そんな匂いがしないだろうか?

しかし、上のPython3コードはそれらに対処したつもりなのである.この部分だ.
  fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
  cam0.set(cv2.CAP_PROP_FOURCC,fourcc)
  cam0.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
  cam0.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
MJPEGで帯域を下げ、解像度下げで帯域を下げたつもりだ.しかしそれでもダメなのである.

ちなみに、ネットの質問コーナーに核心的なことが書かれている.曰く、
・原因は、コントローラに十分なUSB帯域幅がないことです
・カメラにMJEPGを使用させることができれば、問題は解決します
・OpenCVは取得を行うためにFFMPEGを使用しているかもしれませんし(linuxビルドは通常FFMPEGをサポートしています)、そしてOpenCVのドキュメントはFFMPEGが使用されているなら取得フォーマットを変更できないと認めています
・FFMPEGの設定を変更する必要があります
・カメラを別のUSBコントローラに移動してみてください

この優秀な回答者は、わたしの疑いのほとんど全てを語りつくしている....


【トラブル回避策3】
上で、ラズパイでも同じ症状だったと書いたが、実はそうでもない.
3年ぐらい前のラズパイにinstallしたOS等の環境なら無問題なのである、名無し中華UABCAMであっても

試しに同じラズパイでapt-get updateしたら複数USBCAMがダメになった.(logicoolでOKかどうかは未確認)

ちなみにOpenCVのrevisionはapt-get update前後で同じ 2.4.9.1 だった.
V4L2がどうかは未確認だが、OpenCVに連携している何らかのライブラリ群が原因と推測する.


【MJPEGの使用】
質問コーナーの回答をこう読む.
 ・FFMPEGが非圧縮伝送/MJPEGのどちらを使っているか不明
 ・FFMPEGは選択を拒む

ゆえに、FFMPEGを殺してOpenCV3.4.5をビルドしたら治らんだろうか?
OpenCVのビルドはこちらのページを参考にさせてもらった.
CMAKEのoptionの中にある、FFMPEGをOFFにしてビルドした.
   -D WITH_FFMPEG=ON
   -D WITH_FFMPEG=OFF    ←これ

これもやったけど、上のPython3コードで結果はダメなままだったのだ.


【撤退宣言】
これ以上の探索は無理だなぁ.V4L2のソースをたぐるとかはちょっと能力オーバーだ.

ライブラリ群を数年前のversionに先祖返りさせるっていう後ろ向き作業も虚しい.

中華製名無しUSBCAMから1600x1200なんつう使わないプロファイルを消してしまいたいけど、それはメーカーじゃないと無理無理.

OpenCV3系では3.4.5が最終版のようだ.
そして最新版は4.0.1が2018.12にリリースされたらしい.何が変わったんだろうね.

皆さんのお役に立てず申し訳ありません、切腹.

0 件のコメント:

コメントを投稿