OpenCV を使用して基本的な拡張現実を実行しようとしています。私が行っている方法はfindChessboardCorners
、カメラ画像から一連のポイントを取得するために使用しています。次に、z = 0 平面に沿って 3D 四角形を作成し、これを使用solvePnP
して、画像化された点と平面点の間のホモグラフィを取得します。そこから、画像の上に適切なポーズで立方体をレンダリングできるモデルビュー マトリックスを設定できるはずだと思います。
のドキュメントにsolvePnP
は、「([並進ベクトル]と一緒に)モデル座標系からカメラ座標系にポイントをもたらす」回転ベクトルを出力すると書かれています。それは私が望んでいることの反対だと思います。私のクワッドは平面 z = 0 にあるので、そのクワッドを適切な 3D 平面に変換するモデルビュー マトリックスが必要です。
反対の回転と平行移動を反対の順序で実行することで、正しいモデル ビュー マトリックスを計算できると考えましたが、うまくいかないようです。レンダリングされたオブジェクト (立方体) はカメラ イメージと共に移動し、平行移動はおおよそ正しいように見えますが、回転はまったく機能しません。1 つのみで回転する必要があるときに複数の軸で回転し、場合によっては間違った方向に回転します。これが私がこれまでにやっていることです:
std::vector<Point2f> corners;
bool found = findChessboardCorners(*_imageBuffer, cv::Size(5,4), corners,
CV_CALIB_CB_FILTER_QUADS |
CV_CALIB_CB_FAST_CHECK);
if(found)
{
drawChessboardCorners(*_imageBuffer, cv::Size(6, 5), corners, found);
std::vector<double> distortionCoefficients(5); // camera distortion
distortionCoefficients[0] = 0.070969;
distortionCoefficients[1] = 0.777647;
distortionCoefficients[2] = -0.009131;
distortionCoefficients[3] = -0.013867;
distortionCoefficients[4] = -5.141519;
// Since the image was resized, we need to scale the found corner points
float sw = _width / SMALL_WIDTH;
float sh = _height / SMALL_HEIGHT;
std::vector<Point2f> board_verts;
board_verts.push_back(Point2f(corners[0].x * sw, corners[0].y * sh));
board_verts.push_back(Point2f(corners[15].x * sw, corners[15].y * sh));
board_verts.push_back(Point2f(corners[19].x * sw, corners[19].y * sh));
board_verts.push_back(Point2f(corners[4].x * sw, corners[4].y * sh));
Mat boardMat(board_verts);
std::vector<Point3f> square_verts;
square_verts.push_back(Point3f(-1, 1, 0));
square_verts.push_back(Point3f(-1, -1, 0));
square_verts.push_back(Point3f(1, -1, 0));
square_verts.push_back(Point3f(1, 1, 0));
Mat squareMat(square_verts);
// Transform the camera's intrinsic parameters into an OpenGL camera matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Camera parameters
double f_x = 786.42938232; // Focal length in x axis
double f_y = 786.42938232; // Focal length in y axis (usually the same?)
double c_x = 217.01358032; // Camera primary point x
double c_y = 311.25384521; // Camera primary point y
cv::Mat cameraMatrix(3,3,CV_32FC1);
cameraMatrix.at<float>(0,0) = f_x;
cameraMatrix.at<float>(0,1) = 0.0;
cameraMatrix.at<float>(0,2) = c_x;
cameraMatrix.at<float>(1,0) = 0.0;
cameraMatrix.at<float>(1,1) = f_y;
cameraMatrix.at<float>(1,2) = c_y;
cameraMatrix.at<float>(2,0) = 0.0;
cameraMatrix.at<float>(2,1) = 0.0;
cameraMatrix.at<float>(2,2) = 1.0;
Mat rvec(3, 1, CV_32F), tvec(3, 1, CV_32F);
solvePnP(squareMat, boardMat, cameraMatrix, distortionCoefficients,
rvec, tvec);
_rv[0] = rvec.at<double>(0, 0);
_rv[1] = rvec.at<double>(1, 0);
_rv[2] = rvec.at<double>(2, 0);
_tv[0] = tvec.at<double>(0, 0);
_tv[1] = tvec.at<double>(1, 0);
_tv[2] = tvec.at<double>(2, 0);
}
次に、描画コードで...
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, 0.0f);
modelViewMatrix = GLKMatrix4Translate(modelViewMatrix, -tv[1], -tv[0], -tv[2]);
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -rv[0], 1.0f, 0.0f, 0.0f);
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -rv[1], 0.0f, 1.0f, 0.0f);
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, -rv[2], 0.0f, 0.0f, 1.0f);
私がレンダリングしている頂点は、原点の周りに単位長さの立方体を作成します (つまり、各エッジに沿って -0.5 から 0.5 まで)。OpenGL 変換関数で変換が「逆順」で実行されることを知っているので、上記は立方体をz 軸、y 軸、x 軸の順に移動し、それを平行移動します。ただ、最初に翻訳してから回転させているように見えるので、Apple のGLKMatrix4
動作は異なるのでしょうか?
この質問は私のものと非常に似ているようで、特に coder9 の回答は、多かれ少なかれ私が探しているもののようです。しかし、私はそれを試してみて、結果を私の方法と比較したところ、どちらの場合も到達した行列は同じでした. その答えは正しいと思いますが、重要な詳細がいくつか欠けています。