Point Cloud Rendering

05 1月 2011 Under: opencv2.x-samples

ステレオマッチングによる視差画像の計算を行い,視差画像を3次元空間への再投影したあと,別の視点の2次元画像へ画素情報を投影するサンプルプログラムです.動作の様子は下記動画のようになっています.また,Kinect+OpenNIで撮影されたデータも扱えるようにコードは書かれています.Kinectで使う場合の詳細とサンプルはこちら[link]

プログラムで使用するデータセットはここからダウンロード可能です.データセットダウンロード

また,入力となっているステレオ画像対のその他の視点は,ここからダウンロードできます.(必要なデータは上記リンクにすでに含まれています)[link]

なお,このプログラムはOpenCV2.3以上で動作します(2011/12/30確認).また,OpenMPによる並列化に対応しています.

C++


(A-1) ステレオ画像の読み込みとsemi-global block matchingによる視差計算

ステレオ画像を読み込み,semi-global block matching (SGBM) により視差画像を計算します.
SGBM関数では,信頼の出来ない計算結果の視差には最小視差-16の値が割り振ら,イレギュラーとして扱われます.
このサンプルでは,そのイレギュラー値をそれらしい値で割り当て直す,「fillOcclusion」関数が実装されています.
この関数では,周囲にある,イレギュラーとなっていない値を操作し,最小の値を持つ値でイレギュラー値を埋め直します.

(A-2) Qマトリクスの構築と視差画像の3次元空間への投影

視差画像を3次元空間に投影するためには,OpenCVではQマトリクスが要求されます.この行列は,cv::stereoRectify関数により入力となるステレオカメラを補正すれば得ることが可能ですが,このサンプルでは,カメラキャリブレーション結果からQマトリクスを計算する関数「makeQMatrix」を実装してあります.

この行列を計算したのち,cv::reprojectImageTo3D関数にて視差を3次元空間に投影します.投影後は3チャネル(x,y,z座標相当)のfloat配列に格納されます.その後3次元点の投影関数であるcv::projectPoints関数に形式を合わせるためにリシェイプを行っています.

(A-3) カメラの設定

仮想カメラの設定を行うため,各カメラ行列を設定します.このサンプルでは,視点(viewpoint),と注視点(lookatpoint)により仮想カメラの位置方向をコントロールます.

(A-4) 3次元座標から新たな視点の画像への点群を投影

投影された3次元点をcv::projectPointsを用いて仮想視点に投影します.その後,投影した点に,対応する各画素の値で埋めていきます.画像は離散的なため,視点を変更すると量子化誤差が発生します.
そのため,このサンプルでは1画素以内の量子化誤差が発生するピクセルにはあらかじめ周辺のピクセルも投影することや,有効な画素で周囲から埋めるフィルタで量子化誤差を抑制する機能を実装してあります.

(A-5) キーマップ

視点移動のキーマップはviライクな配置になっています.

h:x座標をデクリメント
j:y座標をデクリメント
k:y座標をインクリメント
l:x座標をインクリメント
J:z座標をデクリメント(Shift+j)
K:z座標をインクリメント(Shift+k)
f:量子化抑制フィルタのオンオフ切り替え(デフォルトはオン)

視点情報はループ毎に更新され,仮想カメラの方向(rotation)はlookat関数により更新されます.

(B-1) 画像と対応するデプスマップの読込

Kinectのデータ呼び出し用のサンプルです.ステレオ画像とは違ってデプスのデータと画像のデータ1枚(先ほどは画像2枚)を読み込みます.添付のデータの.RAWファイルにはunsigned shortの画素配列がそのまま保存されています.
Kinectの操作ライブラリであるOpenNIを用いてデータを取得すると,先ほどとは違い視差ではなく撮影したカメラから物体までの距離であるデプスマップが記録されます.このデータは視差に反比例するデータのため値が小さいほど近くを表しています.またイレギュラー値は0で表されています.デプスマップのイレギュラー値を再割り当てをするために,fillOcclusion関数では,先ほどは最小の値を周囲から探してきましたが,今回は最大の値を周囲から探します.

(B-1) デプスマップの変換

(A)のサンプルと同様の動作をさせるためにデプスの形式を変換します.なお後の処理は(A)の場合と同様ですが,Kinectで撮影されたデータは奥行の分解能が高く,また精度も高いため,量子化が起こりにくくなっています.

結果

レンダリング結果

レンダリングした視差画像

“Point Cloud Rendering” への17件のコメント

  1. Kevin より:

    Professor, could I ask whether the space and time of kinect color camera and depth camera have been synchronized before point cloud view? or the pixel coordinate of color camera has been transformed to the depth point of depth camera?Thanks!

  2. adachi より:

    超初心者です.よろしくお願いします.
    サンプルプログラムがコンパイルできなくて困っています.356行目で
    default argument for ‘cv::Mat& mask’ has type ‘cv::Mat’
    というエラーメッセージが出ます.2012/12/30のコメントにあるMat()関係あるのでしょうか.使用環境は,ubuntu12.04.1, OpenCV-2.4.6.1です.

  3. Sarah より:

    Hi, or shall I say moshi moshi :)

    I found your article through the Internet, & I have two questions of you don’t mind;

    Using OPENCV, does it require a NEVADA GPU ?!
    + Which kind of platforms do you use to display the result?!

    sorry but I can’t speak Japaneses very well.

    many thanks to you. :)

    • fukushima より:

      The code does not require GPU processing. The code is tested on windows XP with Visal studio2010, and use OpenCV2.3 or newer.

      • Sarah より:

        well, I’m using VS 2012 on w7, but neither Opencv nor Emgu have worked successfully, do I have to use VS 2010 ?!

        & really thank you so much indeed for your answer, appreciate that.

  4. Beginer より:

    初心者です。
    内容とは関係ないエラー症状なのかもしれませんが、答えていただけると助かります。

    [エラーメッセージ]
    3D.exe の 0x7645fc56 でハンドルされていない例外が発生しました: Microsoft C++ の例外: cv::Exception (メモリの場所 0x001adbb0)。

    [呼び出し履歴]
    →kernel32.dll!7645fc56()
     [下のフレームは間違っているか、または見つかりません。kernel32.dll に対して読み込まれた シンボルはありません。]
     kernel32.dll!7645fc56()
     opencv_core231d.dll!50eeb1dc()

    プログラムを入力し、[デバック] > [デバック開始]をすると上記のような状態になり、
    進められません。
    どのような原因が考えられるのでしょうか?
    またどういった解決法がありますか?
    何卒、よろしくお願いします。

    • fukushima より:

      OpenCVを使ったもっとシンプルなコードでも同じ症状がでませんか?ライブラリのリンク方法が間違っている可能性があります.

      • Beginer より:

        fukushimaさん、返信ありがとうございます!
        こちらの返信が遅れてしまって申し訳ありません…

        ご返答に関してなのですが、
        pragma comment(lib,”C://~”)などを用いて、kernel32.dllをプログラム文面内で
        読み込ませる必要がある、ということなのでしょうか。
        (違っていたらお手数なのですが…再度ご指摘いただけると助かります)

        返信、本当にありがとうございました! やってみます!

        • fukushima より:

          リリースコンパイルに設定して,opencv_core231.lib (opencv_core231″d”.libではなく)をリンクしてみても駄目です?インストーラで入れたライブラリにデバッグコンパイルの情報が入っていたかどうか覚えていないので...

          • kk より:

            私も同様のエラーが出ます。
            Opencvは2.1でエラーメッセージは下記のようになっています。

            OpenCV Error: Bad argument (Distortion coefficients must be 1×4, 4×1, 1×5 or 5×1
            floating-point vector) in unknown function, file ..\..\..\..\ocv\opencv\src\cv\
            cvcalibration.cpp, line 864

            コード上では
            static void projectImagefromXYZ_内の
            cv::projectPoints(xyz,R,t,K,Mat(),pt);でエラーになります。

          • fukushima より:

            OpenCV2.2~2.3にかけて,calib3dの仕様(とMat全般の入力形式)が大幅にアップデートされています.
            特に,projectPointでは,歪みの係数の扱いが変わっており,Mat()という空欄の引数を受け付けなくなりました.
            (さらに歪みの係数が最大8つまでの形に代わっています.)

            ひとまず,上記のコードはOpenCV2.3ではコンパイルと実行が出来るように訂正しました.(2.1でももしかしたら動くかもしれません)

  5. fukushima より:

    baselineはステレオカメラ間の距離となっており,この環境では1.4cmを表しています.
    あと,focal_lengthも適切な値を与える必要があります.

    baselineがあちこちで使われていますが,カメラ間の距離を基準の距離にしているだけ(1cmのように仮想空間内で1カメラ間距離のような単位として使用)でQマトリクスを計算している部分以外正確なbaselineの値は必要ありません.

    この2つの値が実際と異なっていても実空間とまったく同じようにはレンダリング出来ないだけで画像を作ることは可能です.(xyzのz空間の刻み幅が現実に比べて不均一にかつ歪む)

    • hara より:

      fukushima様、返信有難うございました。
      focal_length=598.5であり、この値はOpenCVのカメラキャリブレーションを行った際に取得できる、内部パラメータfx,fyの値(単位ピクセル)と推察しました。
      一方ご説明いただいたようにbaseline=14は14mmを表すとのことです。
      makeQMatrix()の引数にはbaseline*16を渡しているので、16がmm->ピクセル変換を行う係数と考えてよろしいでしょうか?
      また、そうだった場合、16という係数は画像毎に異なる値のように思われますが、何を表す数値なのでしょう?
      お手数をおかけして申し訳ございませんが、ご回答お待ちしております。

      • fukushima より:

        StereoSGBMクラスのステレオマッチングアルゴリズムが実際の視差の16倍で返ってくるため16倍しています.(サブピクセル精度の値がその隙間に入っています)
        ですので,本来は単純にbaselineの値を入れればOKです.

        • hara より:

          fukushima様、早速の返信有難うございました。
          教えて頂いた内容で試してみたいと思います。

  6. hara より:

    お世話になっております。
    上記サンプルプログラムのstereoTest()において、変数baselineは何を表しているのでしょうか?
    上記プログラムにおいて別画像を指定する際、適切な値を設定しなければならないと思われますが、何の値を設定すれば良いかわかりませんでした。

hara への返信