べれすく!

素人プログラマによるモノづくりやブログ生活を話すブログ

【スポンサーリンク】

【OpenCV4.3+Python3.6】ウェブカメラ2台でステレオマッチングして距離を見る話。

お久しぶりです。べれすくです。
 
皆さんはステレオカメラってご存知でしょうか?
最近は自動運転などで話題ですからご存知の方もおおいとおもいます。
2台のカメラの並列に並べて同時に撮影し、その見え方の違いから、カメラから映っているものまでの距離がわかるカメラのことです。 f:id:Veresk:20200727004101j:plain
今回はネットオークションで安売りしていたウェブカメラをたくさん購入したので、ステレオカメラの製作にチャレンジしたいと思います。

もくじ

 
 

オークションでウェブカメラをたくさん買いました。

テレワーク需要でウェブカメラの値段がクソ高いこの時期に買ってしまいました。
 
f:id:Veresk:20200727004326j:plain 
 
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の導入については過去の記事を見てください。

veresk.hatenablog.com

早速ステレオマッチングのプログラムを組む

自分でステレオマッチングのプログラムを組むこともできるのですが、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()

サンプルコードパクってきて... 実行!! f:id:Veresk:20200727010624p:plain
ありゃありゃ。
コンパイルエラーではないけど何かしらの問題で終わっちゃったぽい。
何も表示されず。
Process finished with exit code -1073741819 (0xC0000005)かぁ
調べたところPythonのバージョン違いとかライブラリのバージョン違いとかそれっぽい。

CV3で仕様変更していた模様

どうやらOpenCV2からOpen CV3の仕様変更で cv2.StereoSGBM は cv2.StereoSGBM_create に変更になったみたい。

docs.opencv.org

ちょちょっとかえて再実行

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()

f:id:Veresk:20200727011132p:plain
f:id:Veresk:20200727011147p:plain
おおっ。動いた!
ギターの輪郭やテレビの端が見て取れます。
FPSも15ほど出てるので快適快適!
ここからカメラの位置とか角度、パラメータを微調整していきます。(コードは調整後の物です。)

結果

f:id:Veresk:20200727011331p:plain
f:id:Veresk:20200727011341p:plain
パラメータの調整が結構面倒くさい。
手前が白く、奥に行くにつれて黒くなります。
少しでもわかりやすくしようと思って手元の水筒を距離差が出るように映してみました。
うっすらだけど手に向かうにつれて色が黒っぽくなっていくのがわかると思います。
うーん。まぁ成功といえば成功なのかな?
 

まとめ

今回は何とか2台のカメラから大体の距離をつかむことができました。
パラメータの調整を本格的に行えばもっときれいに見えると思います。
さらにここから点群化をすると3Dに見えて面白いかもしれませんね。
 
今回はこの辺にしようと思います。
それでは!