私は opencv と openframeworks (つまり、opengl) を使用して、画像 (および後で三角測量のためのいくつかの画像) からカメラ (世界の変換行列と投影行列) を計算しています。
opencv の目的上、「フロア プラン」は、世界の中心が 0,0,0 のオブジェクト (つまり、チェス盤) になります。世界/床の位置はわかっているので、投影情報 (歪み係数、fov など) とカメラの外部座標を取得する必要があります。
これらのフロアプラン ポイントのビュー位置を、正規化されたビュー空間の 2D イメージにマッピングしました ([0,0] は左上、[1,1] は右下)。
オブジェクト(フロアプラン/ワールドポイント)はxz平面、-yアップ上にあるため、平面である必要があるため、opencvのxy平面に変換します(zアップが負か正かはわかりません...)
ofMatrix4x4 gWorldToCalibration(
1, 0, 0, 0,
0, 0, 1, 0,
0, 1, 0, 0,
0, 0, 0, 1
);
ImageSize として 1,1 を calibrationCamera に渡します。フラグがCV_CALIB_FIX_ASPECT_RATIO|V_CALIB_FIX_K4|CV_CALIB_FIX_K5
calibrateCamera
正常に実行され、エラーが少なくなります (通常は約0.003
)。
を使用するcalibrationMatrixValues
と、通常は約 50 度の適切な FOV が得られるため、固有のプロパティが正しいと確信しています。
ここで、カメラの外部ワールド空間変換を計算します... オブジェクトが 1 つしかないので、使用する必要はないと思いsolvePnP
ます (ただし、以前にこれをすべて試して、同じ結果で戻ってきました)。
// rot and trans output...
cv::Mat& RotationVector = ObjectRotations[0];
cv::Mat& TranslationVector = ObjectTranslations[0];
// convert rotation to matrix
cv::Mat expandedRotationVector;
cv::Rodrigues(RotationVector, expandedRotationVector);
// merge translation and rotation into a model-view matrix
cv::Mat Rt = cv::Mat::zeros(4, 4, CV_64FC1);
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
Rt.at<double>(y, x) = expandedRotationVector.at<double>(y, x);
Rt.at<double>(0, 3) = TranslationVector.at<double>(0, 0);
Rt.at<double>(1, 3) = TranslationVector.at<double>(1, 0);
Rt.at<double>(2, 3) = TranslationVector.at<double>(2, 0);
Rt.at<double>(3, 3) = 1.0;
これで、回転と変換の行列が得られましたが、それは列優先です (転置しないとオブジェクトが完全に歪んでいると思います。上記のコードは列優先に見えます)。
// convert to openframeworks matrix AND transpose at the same time
ofMatrix4x4 ModelView;
for ( int r=0; r<4; r++ )
for ( int c=0; c<4; c++ )
ModelView(r,c) = Rt.at<double>( c, r );
前の逆行列を使用して、平面を座標空間 (y を上) に戻します。
// swap y & z planes so y is up
ofMatrix4x4 gCalibrationToWorld = gWorldToCalibration.getInverse();
ModelView *= gCalibrationToWorld;
これを行う必要があるかどうかわかりません...キャリブレーションに入れるときに飛行機を無効にしませんでした...
// invert y and z planes for -/+ differences between opencv and opengl
ofMatrix4x4 InvertHandednessMatrix(
1, 0, 0, 0,
0, -1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1
);
ModelView *= InvertHandednessMatrix;
そして最後に、モデルビューはオブジェクト相対カメラであり、カメラ相対オブジェクト(0,0,0)になるように反転したい
ModelView = ModelView.getInverse();
これにより、カメラが間違った場所に配置され、間違った回転が行われます。それほど離れていません。カメラは Y 平面の右側にあり、変換は大幅にずれていません。正しい方法だと思います....まだ正しくありません。ペイントで描かれた青い円は、カメラがあると予想される場所です。
私はたくさんのSO回答、ドキュメントを何十回も調べましたが、正しいものは何も見つかりませんでした。スペース変換に関して必要なすべてをカバーしたと確信していますが、明らかな何かを見逃している可能性があります? それとも間違った順序で何かをしていますか?
更新 1 - ワールド空間平面...
openCV の入力と一致するように、ワールド空間のフロア平面を XY(Z アップ) に変更しました。(gWorldToCalibration は恒等行列になりました)。回転はまだ間違っており、投影出力は同じですが、翻訳は正しいと思います (確かにマーカーの正しい側にあります)
Update2 - 実際の画像サイズ
カメラのキャリブレーションに入る画像サイズで遊んでいます。正規化された 1,1 を使用していますが、imageSize パラメーターは整数であるため、これは重要であると思いました...そして、そうであると思います (赤いボックスは、投影されたビュー空間ポイントが z と交差する場所です) =0 床面) 歪み補正なしの結果は次のとおりです (変更されたのは、画像サイズが 1,1 から 640,480 に変更されただけです。正規化された入力ビュー空間の座標にも 640,480 を掛けます)
歪みを追加してみます完全に並んでいるかどうかを確認するための修正...