以前EdisonでOV7670モジュールを動かしましたが、折角なのでステレオカメラを作ってみました。
『Intel® EdisonにOV7670カメラモジュールを繋げる』
そして、ステレオカメラから取得できる視差画像から、OpenCVを利用して奥行き情報を取得したいと思います。
回路作り
基本的には、OV7670モジュールをそれぞれEdisonに接続しただけですが、端子節約のため下記3端子は共有するようにしました。
・OE
・RRST
・RCLK
ピンアサインや回路の引き回しなどは、表計算ソフトを使って描いていきます。
(なんか便利な設計ツールないかな…)
そして、ケースなんかを3Dプリンターで作ったりして完成したのがこちらです。
カメラ間の距離は44mm(適当に決めた)で、取得できる画像は下記のようになります。
(後の処理がしやすいようにグレースケールに変換しています。)
奥行き情報の取得
2枚の画像からの奥行き情報取得は、OpenCVのStereoSGBMを使用しました。
/** * 奥行き情報を取得する * @param dist 奥行きマップの出力先 * @param left 左画像 * @param right 右画像 */ void createDepthMap(Mat dist, Mat left, Mat right) { Mat result = Mat(info.rows, info.cols, CV_16S); StereoSGBM sgbm = StereoSGBM(0, 16*3, 21); sgbm.speckleWindowSize = 200; sgbm.speckleRange = 32; sgbm.operator()(left, right, result); double minVal; double maxVal; minMaxLoc(result, &minVal, &maxVal); result.convertTo(dist, CV_8UC1, 255/(maxVal-minVal)); equalizeHist(dist, dist); }
9行目:
StereoSGBMのパラメータは、
minDisparity : 最小視差値。特別理由がなければ0。
numDisparities : 最大視差値と最小視差値の差。16の倍数でないといけない。
blockSize : 比較するブロックサイズ。一般的には3〜11が推奨。
10行目:
speckleWindowSize : ノイズを除去して視差値をスムーズにするフィルタ。0にすると適用しない。50〜500の範囲で使用する。
11行目:
speckleRange : フィルタをかける際の隣同士の最大視差値?通常は1か2がいい。
結果
まぁ…なんとなくは取れているかなといった具合です。
やはりキネクトみたいには行きませんね。(あっちは赤外線プロジェクション方式ですが)
普通はキャリブレーションを行うみたいで、今回それを端折ったのが精度がでない原因の1つかもしれません。
(キャリブレーションはチェス盤用意したり若干めんどくさいようなので…)
パターンマッチングでもやってみる
StereoSGBMを使用せずにパターンマッチングでも試してみました。
24*24サイズでパターンマッチした座標のズレを検出しています。
結果
こちらもサイズやしきい値などを変えて何パターンか試してみましたが…いまいちかな。
今回試したのはここまで。
あとがき
カメラモジュールを2つ使ってとりあえず試してみた程度の内容ですが、一応成功かなと思ってます。
精度が出ないのは残念でしたが、まったくステレオカメラの知識がなくても試せてしまうのはOpenCVのおかげですね。
今度はもっと精度がだせるよう、ちゃんと勉強してからリベンジしたいと思います。
ではでは〜。