5

現在、私は外部追跡システムを使用してWebカメラベースのARに代わる方法を実装しようとしています。私は、私の環境内で、外因性較正のために保存されたすべてを保存しました。私はcv::solvePnP()を使用することにしましたが、それはかなり正確に私がしたいと思っていますが、2週間後に私はそれを動作させるように努力しています。下の図は自分の設定を示しています。 c1は私のカメラ、c2は私が使用している光学式のトラッカー、Mはカメラに取り付けられた追跡されたマーカー、そしてchはチェッカーボードです。cv :: SolvePnPによる外部較正

Diagram of my configuration

それは私がcv::findChessboardCorners()で取得私のイメージのピクセル座標を渡し立っています。世界点は、カメラc1に付された追跡されたマーカMを参照して取得される(したがって、外部的なものは、このマーカのフレームからカメラの原点への変換である)。私は局所的な最小値の可能性を緩和するために50ポイントまでのデータセットでこれをテストしましたが、今は4つの2D/3Dポイントペアでテストしています。その結果得られた外因性は、cv::solvePnP()から返されたrvecとtvecから得られます。これは、手作業で作成された基本的な視覚的分析と基本的な視覚的分析の両方に対して、平行移動と回転の両方に関して大きく異なります(翻訳は、最大で10mm離れている)。

当初、私のボードの姿勢がどのように決定されたかにあいまいさがあったことが問題だったと思っていましたが、今はそうではないと確信しています。数学はかなり単純なようで、システムをセットアップするすべての作業の後、本質的に1ライナーであることに追いつくことは大きな欲求不満です。私は正直なところ選択肢がなくなっているので、もし誰かが助けてくれれば、私はあなたの借金に大いに役立つでしょう。私のテストコードは、以下に掲載されています。私の実装では、いくつかのレンダリング呼び出しを除いて同じです。私のプログラムで動作し、私が持っているグランドトゥルース外因は次のとおりです(基本的に1つの軸の周りの純粋な回転と小さな翻訳):

1  0  0  29 
0 .77 -.64 32.06 
0 .64 .77 -39.86 
0  0  0  1 

ありがとう!

#include <opencv2\opencv.hpp> 
#include <opencv2\highgui\highgui.hpp> 

int main() 
{ 
    int imageSize  = 4; 
    int markupsSize = 4; 
    std::vector<cv::Point2d> imagePoints; 
    std::vector<cv::Point3d> markupsPoints; 
    double tempImage[3], tempMarkups[3]; // Temp variables or iterative data construction 

    cv::Mat CamMat = (cv::Mat_<double>(3,3) << (566.07469648019332), 0, 318.20416967732666, 
     0, (565.68051204299513), -254.95231997403764, 0, 0, 1); 

    cv::Mat DistMat = (cv::Mat_<double>(5,1) << -1.1310542849120900e-001, 4.5797249991542077e-001, 
    7.8356355644908070e-003, 3.7617039978623504e-003, -1.2734302146228518e+000); 

    cv::Mat rvec = cv::Mat::zeros(3,1, cv::DataType<double>::type); 
    cv::Mat tvec = cv::Mat::zeros(3,1,cv::DataType<double>::type); 
    cv::Mat R; 
    cv::Mat extrinsic = cv::Mat::eye(4, 4, CV_64F); 

    // Escape if markup lists aren't equally sized 
    if(imageSize != markupsSize) 
    { 
    //TODO: Replace with try, throw error code, and catch in qSlicerLocalizationModuleWidget 
    return 0; 
    } 

    // Four principal chessboard corners only 
    imagePoints.push_back(cv::Point2d(368.906, 248.123)); 
    imagePoints.push_back(cv::Point2d(156.583, 252.414)); 
    imagePoints.push_back(cv::Point2d(364.808, 132.384)); 
    imagePoints.push_back(cv::Point2d(156.692, 128.289)); 

    markupsPoints.push_back(cv::Point3d(495.115, 39.106, 93.79)); 
    markupsPoints.push_back(cv::Point3d(463.143, -86.286, -51.178)); 
    markupsPoints.push_back(cv::Point3d(500.236, 121.988, 24.056)); 
    markupsPoints.push_back(cv::Point3d(471.276, -3.23, -127.809));  

    // Larger data set 
    /*imagePoints.push_back(cv::Point2d(482.066, 233.778)); 
    imagePoints.push_back(cv::Point2d(448.024, 232.038)); 
    imagePoints.push_back(cv::Point2d(413.895, 230.785)); 
    imagePoints.push_back(cv::Point2d(380.653, 229.242)); 
    imagePoints.push_back(cv::Point2d(347.983, 227.785)); 
    imagePoints.push_back(cv::Point2d(316.103, 225.977)); 
    imagePoints.push_back(cv::Point2d(284.02, 224.905)); 
    imagePoints.push_back(cv::Point2d(252.929, 223.611)); 
    imagePoints.push_back(cv::Point2d(483.41, 200.527)); 
    imagePoints.push_back(cv::Point2d(449.456, 199.406)); 
    imagePoints.push_back(cv::Point2d(415.843, 197.849)); 
    imagePoints.push_back(cv::Point2d(382.59, 196.763)); 
    imagePoints.push_back(cv::Point2d(350.094, 195.616)); 
    imagePoints.push_back(cv::Point2d(317.922, 194.027)); 
    imagePoints.push_back(cv::Point2d(286.922, 192.814)); 
    imagePoints.push_back(cv::Point2d(256.006, 192.022)); 
    imagePoints.push_back(cv::Point2d(484.292, 167.816)); 
    imagePoints.push_back(cv::Point2d(450.678, 166.982)); 
    imagePoints.push_back(cv::Point2d(417.377, 165.961)); 

    markupsPoints.push_back(cv::Point3d(457.132, 59.822, 89.247)); 
    markupsPoints.push_back(cv::Point3d(451.634, 42.015, 69.719)); 
    markupsPoints.push_back(cv::Point3d(447.06, 22.927, 48.635)); 
    markupsPoints.push_back(cv::Point3d(442.424, 4.454, 28.659)); 
    markupsPoints.push_back(cv::Point3d(437.621, -14.395, 7.495)); 
    markupsPoints.push_back(cv::Point3d(433.386, -33.034, -12.009)); 
    markupsPoints.push_back(cv::Point3d(429.227, -51.001, -32.269)); 
    markupsPoints.push_back(cv::Point3d(424.291, -70.266, -52.667)); 
    markupsPoints.push_back(cv::Point3d(460.300, 79.769, 69.948)); 
    markupsPoints.push_back(cv::Point3d(455.020, 61.379, 49.306)); 
    markupsPoints.push_back(cv::Point3d(450.501, 43.288, 30.250)); 
    markupsPoints.push_back(cv::Point3d(446.062, 24.572, 8.713)); 
    markupsPoints.push_back(cv::Point3d(441.346, 5.823, -10.997)); 
    markupsPoints.push_back(cv::Point3d(436.670, -13.135, -31.428)); 
    markupsPoints.push_back(cv::Point3d(432.367, -31.428, -51.785)); 
    markupsPoints.push_back(cv::Point3d(427.745, -50.016, -72.519)); 
    markupsPoints.push_back(cv::Point3d(464.824, 101.129, 52.251)); 
    markupsPoints.push_back(cv::Point3d(458.628, 81.864, 30.810)); 
    markupsPoints.push_back(cv::Point3d(454.120, 63.546, 10.458)); */ 


    // Calculate camera pose 
    cv::solvePnP(cv::Mat(markupsPoints), cv::Mat(imagePoints), CamMat, DistMat, rvec, tvec); 
    cv::Rodrigues(rvec, R); 

    // Invert results of Rodrigues by transposing rotation matrix and calculating inverted tvec 
    R = R.t(); 
    tvec = -R * tvec; // translation of inverse 

    std::cout << "Tvec = " << std::endl << tvec << std::endl; 

    // Copy R and tvec into the extrinsic matrix 
    extrinsic(cv::Range(0,3), cv::Range(0,3)) = R * 1; 
    extrinsic(cv::Range(0,3), cv::Range(3,4)) = tvec * 1; 

    // Fill last row of homogeneous transform (0,0,0,1) 
    double *p = extrinsic.ptr<double>(3); 
    p[0] = p[1] = p[2] = 0; p[3] = 1; 

    std::cout << "Extrinsic = " << std::endl << extrinsic << std::endl << std::endl; 
    std::cout << "Extrinsic (inv) = " << std::endl << extrinsic.inv() << std::endl; 
    std::cin >> tempImage[0]; 
    return 0; 
} 

EDIT 1:私はチー徐の方法(XN =(X-CX)/ F、YN =(Y-CY)/ F)を用いて画素値を正規化しようとしました。いいえ運:(

EDIT 2:solvePnPを使用するほとんどの人がチェッカーボードのコーナーをワールドフレームと原点のベクトルとしてマークする方法を使用しているように見えますが、私はトラッカーをこれが期待通りに機能するならば、最初の座標Iマークは約< 0,0>になります。solvePnPから得られた変換にワールド・ツー・カメラ・マーカー変換の逆数を掛け合わせると(うまくいけば)

EDIT 3:チェッカーボードに座標系を設定した後、チェッカーボード空間からワールド空間への変換cTwを計算し、レンダリング環境で検証しました。 それhenは、カメラ空間からチェッカーボード空間への変換を表す外来mTcを計算しました。トラッキングシステムからカメラマーカー変換wTrを取得した。最後に、私はカメラの原点からカメラマーカーまでずっと私の転換を移動する変換のすべてを取り、次のようにそれらを乗じ:

mTc*cTw*wTr

ローと、それを見よまったく同じ結果を与えました。私は間違っていることの兆候があればここで死にそうです。誰かが何か手がかりを持っているなら、私はあなたに手伝ってくれるよう頼みます。

編集4:今日、私はチェッカーボードの軸が左利きの座標系にあるように構成していることに気付きました。 OpenCVは標準の右手形式を使用しているので、私はチェッカーボードのフレーム軸を右手に設定してテストを再試行することにしました。結果はほぼ同じですが、世界とチェッカーボードの変換は非標準の変換形式になっています。つまり、3x3回転行列は斜めの対角線上にもはやほぼ対称ではありませんでした。これは、私の追跡システムが右手座標系を使用していないことを示しています(もしそれが文書化されていれば素晴らしいでしょう。私はこれを解決する方法がわかりませんが、私はそれが問題の一部であると確信しています。私はそれを打ち続けるだろうし、ここで誰かが何をすべきか知っていることを願っています。また、OpenCVボードの図をEduardoで追加しました。ありがとうエドゥアルド!

+0

*その他何でも問題ありません* +1 – null

答えて

3

私は問題を理解しました。当然のことながら、それは理論ではなく実装であった。プロジェクトが最初に設計されたとき、私たちはウェブカメラベースのトラッカーを利用しました。このトラッカーにはz軸がマーカー平面から出ていました。光トラッカーに移動したとき、コードはほとんどの場合そのまま移植されました。残念なことに私たちにとっては(私の人生の2ヶ月のRIP)、z軸がマーカー平面からまだ出ていないかどうかは決して確認しませんでした。したがって、レンダリングパイプラインに間違ったビューが割り当てられ、シーンカメラへのベクトルが表示されました。翻訳が何らかの理由でオフになっていることを除いて、現在ほとんど動作しています。しかし、まったく別の問題です!

関連する問題