【OpenCV4.3+Python3.6】ウェブカメラ2台でステレオマッチングして距離を見る話。
お久しぶりです。べれすくです。
皆さんはステレオカメラってご存知でしょうか?
最近は自動運転などで話題ですからご存知の方もおおいとおもいます。
2台のカメラの並列に並べて同時に撮影し、その見え方の違いから、カメラから映っているものまでの距離がわかるカメラのことです。
今回はネットオークションで安売りしていたウェブカメラをたくさん購入したので、ステレオカメラの製作にチャレンジしたいと思います。
もくじ
オークションでウェブカメラをたくさん買いました。
テレワーク需要でウェブカメラの値段がクソ高いこの時期に買ってしまいました。
Webカメラ9個セット!!
値段にして2000円ぐらいですかね。
画像処理をやる人ならウェブカメラはいくらあってもこまりません!
開発環境
CPU : Core i7-8550U
Memory : 8GB
GPU : Intel UHD 620
Camera : MegaPixcel 0.3Mpixcel x10 Zoom Webcam
PyCharmでPython3.6、OpenCV4.3で実行しています。
↓PyCharmやOpenCVの導入については過去の記事を見てください。
早速ステレオマッチングのプログラムを組む
自分でステレオマッチングのプログラムを組むこともできるのですが、50倍ぐらいスピードが違うので、OpenCVのライブラリを使いましょう。
import cv2 import numpy as np capture_right = cv2.VideoCapture(2) capture_left = cv2.VideoCapture(0) window_size = 5 minDisp = 0 numDisp = 200 - minDisp stereo = cv2.StereoSGBM_c( minDisparity=minDisp, # 視差の下限 numDisparities=numDisp, # 最大の上限 SADWindowSize =15, # SADの窓サイズ uniquenessRatio=10, # パーセント単位で表現されるマージン speckleWindowSize=100, # 視差領域の最大サイズ speckleRange=10, # それぞれの連結成分における最大視差値 disp12MaxDiff=1, # left-right 視差チェックにおけて許容される最大の差 P1=8 * 3 * window_size ** 2, # 視差のなめらかさを制御するパラメータ1 P2=32 * 3 * window_size ** 2, # 視差のなめらかさを制御するパラメータ2 fullDP = False #時間かかるけどハイクオリティにするやつ ) cv2.namedWindow("img", cv2.WINDOW_NORMAL) while(True): ret, frame_right = capture_right.read() ret2, frame_left = capture_left.read() frame_right = cv2.cvtColor(frame_right, cv2.COLOR_RGB2GRAY) frame_left = cv2.cvtColor(frame_left, cv2.COLOR_RGB2GRAY) frame_left = cv2.GaussianBlur(frame_left, (9, 9), 0) frame_right = cv2.GaussianBlur(frame_right, (9, 9), 0) cv2.imshow('frame_left',frame_left) cv2.imshow('frame_right',frame_right) disparity = stereo.compute(frame_left, frame_right) map = (disparity - np.min(disparity)) / (np.max(disparity) - np.min(disparity)) cv2.imshow("img", map) if cv2.waitKey(1) & 0xFF == ord('q'): break capture_left.release() capture_right.release() cv2.destroyAllWindows()
サンプルコードパクってきて...
実行!!
ありゃありゃ。
コンパイルエラーではないけど何かしらの問題で終わっちゃったぽい。
何も表示されず。
Process finished with exit code -1073741819 (0xC0000005)かぁ
調べたところPythonのバージョン違いとかライブラリのバージョン違いとかそれっぽい。
CV3で仕様変更していた模様
どうやらOpenCV2からOpen CV3の仕様変更で cv2.StereoSGBM は cv2.StereoSGBM_create に変更になったみたい。
ちょちょっとかえて再実行
import cv2 import numpy as np capture_right = cv2.VideoCapture(2) capture_left = cv2.VideoCapture(0) window_size = 5 minDisp = 0 numDisp = 200 - minDisp stereo = cv2.StereoSGBM_create( minDisparity=minDisp, # 視差の下限 numDisparities=numDisp, # 最大の上限 blockSize=15, # SADの窓サイズ uniquenessRatio=10, # パーセント単位で表現されるマージン speckleWindowSize=100, # 視差領域の最大サイズ speckleRange=10, # それぞれの連結成分における最大視差値 disp12MaxDiff=1, # left-right 視差チェックにおけて許容される最大の差 P1=8 * 3 * window_size ** 2, # 視差のなめらかさを制御するパラメータ1 P2=32 * 3 * window_size ** 2, # 視差のなめらかさを制御するパラメータ2 ) cv2.namedWindow("img", cv2.WINDOW_NORMAL) while(True): ret, frame_right = capture_right.read() ret2, frame_left = capture_left.read() frame_right = cv2.cvtColor(frame_right, cv2.COLOR_RGB2GRAY) frame_left = cv2.cvtColor(frame_left, cv2.COLOR_RGB2GRAY) frame_left = cv2.GaussianBlur(frame_left, (9, 9), 0) frame_right = cv2.GaussianBlur(frame_right, (9, 9), 0) cv2.imshow('frame_left',frame_left) cv2.imshow('frame_right',frame_right) disparity = stereo.compute(frame_left, frame_right) map = (disparity - np.min(disparity)) / (np.max(disparity) - np.min(disparity)) cv2.imshow("img", map) if cv2.waitKey(1) & 0xFF == ord('q'): break capture_left.release() capture_right.release() cv2.destroyAllWindows()
おおっ。動いた!
ギターの輪郭やテレビの端が見て取れます。
FPSも15ほど出てるので快適快適!
ここからカメラの位置とか角度、パラメータを微調整していきます。(コードは調整後の物です。)
結果
パラメータの調整が結構面倒くさい。
手前が白く、奥に行くにつれて黒くなります。
少しでもわかりやすくしようと思って手元の水筒を距離差が出るように映してみました。
うっすらだけど手に向かうにつれて色が黒っぽくなっていくのがわかると思います。
うーん。まぁ成功といえば成功なのかな?
まとめ
今回は何とか2台のカメラから大体の距離をつかむことができました。
パラメータの調整を本格的に行えばもっときれいに見えると思います。
さらにここから点群化をすると3Dに見えて面白いかもしれませんね。
今回はこの辺にしようと思います。
それでは!