この記事では、シーン内のオブジェクトの見つかった位置を使用して、 拡張現実アプリケーションで3D空間を構築する方法を説明します。 これを行うには、2つのマトリックスを取得する必要があります。たとえば、OpenGLでの作業用の投影(GL_PROJECTION)とモデル(GL_MODELVIEW)です。 OpenCVライブラリを使用してこれを行います 。
最近、私はこの問題を解決しなければなりませんでしたが、その方法を段階的に説明しているリソースを見つけられませんでした(たぶん見苦しかったかもしれません)が、この問題には十分な落とし穴があります。 いずれにせよ、このタスクを説明するハブに関する記事は害を与えません。
エントリー
私自身はiOSプログラマーであり、空き時間には自分の拡張現実エンジンを開発しています。 ベースは、コンピュータービジョン用のオープンソースライブラリであるOpenCVが採用しました。
一般的に、この分野でのモバイルデバイス(iOS / Android)の最も興味深い提案は、Qualcommの開発です。
最近では、 Vuforiaという名前で独自のAR SDKをリリースしました。 同時に、ライセンスのパラグラフが誇らしげに述べているように、SDKの使用は開発とアプリケーションのストア(AppStore、AndroidMarket)の両方に無料で使用できます。 同時に、このSDKが匿名情報を収集してQualcommサーバーに送信する可能性があることをエンドユーザーに警告する必要があると書いています。 このセクションを見つけるには、右側のメニューのSDK入門のこのリンクをクリックします->ステップ3:コンパイルと実行...->アプリケーションの公開。 これに加えて、あなたは私が妄想的であると考えることができますが、SDKが一定の割合で人気を得ると、90%の確信があります。
したがって、自分のエンジンの開発は時間の無駄ではないと思います。
実際には、ポイントまで!
理論
この時点で、プロジェクトにOpenCVを実装し( 方法は? )、カメラからのフレーム内のオブジェクトを認識するメソッドをすでに作成していると考えています。 つまり、次のようなものがあります。
この主題に関する理論は、多くの情報源で見つけることができます。 以下に主なリンクを提供しました。 多くの読み物の質問がありますが、最初のリソースはOpenCVドキュメントページです。
簡単に言えば、見つかった2Dホモグラフィから3D空間を構築するには、2つのマトリックスを知る必要があります。
- 内部行列(固有行列)、またはカメラ行列-この行列は、カメラパラメータ-2つの軸に沿った焦点距離( fx 、 fy )と焦点中心座標( cx 、 cy )で構成されます。
このマトリックスの構造:
- 外部マトリックス、またはモデルマトリックスは、モデルのストレッチ、回転、およびトランスポートマトリックスです。 実際、空間内のオブジェクトの位置を一意に設定します。 構造:
、
ここで、対角要素はストレッチ、残りの要素rは回転、要素tは輸送を担います。
注:一般に、このマトリックスの構造はモデル(座標変換方程式)によって異なる場合があります。 たとえば、同じOpenGLでは、わずかに異なる構造のマトリックスが送信されますが、キー要素は常にそこに存在します。
練習する
実際には、OpenGLがオブジェクトの上に3Dモデルをレンダリングするために、それを設定する必要があります。
- 射影行列はGL_PROJECTIONです。
- モデルマトリックスはGL_MODELVIEWです。
注: iOSでは、OpenGLESの2つのバージョン-1.1および2.0を使用できます。 主な違いは、2番目のバージョンのシェーダーの存在です。 どちらの場合でも、2つの行列を指定する必要があります。最初の場合のみ、それらは型の構造によって定義されます。
glMatrixMode(GL_PROJECTION); glLoadMatrixf(projectionMatrix); glMatrixMode(GL_MODELVIEW); glLoadMatrixf(modelViewMatrix);
2番目の方法では、シェーダーへの入力にそれらを渡します。
次に、カメラから取得するフレームのサイズがcameraSize =(width、height)であると判断します。 私の場合、cameraSize =(640、480)。
各マトリックスの作成方法を理解しましょう。
射影行列
このマトリックスは、カメラマトリックスに基づいています。 上記のように、後者は特定のカメラパラメータで構成されています。 理論的には、これらのパラメーターはカメラの技術的特性に基づいて計算できますが、実際には誰も計算しません。
カメラパラメータを見つけるプロセスは、キャリブレーションと呼ばれます。 OpenCVには、キャリブレーションを実行するために必要なすべての機能が含まれています。 また、ウェブカメラをオンラインで簡単に調整できる例もあります。 ただし、デバイス(iPhone / iPad / iPod)のカメラを調整する必要があります。 そしてここで私はここで説明された道に沿って行った 。
カメラを「オフライン」で較正します。 つまり、デバイスのカメラでキャリブレーションテンプレート(チェッカーボード)の写真を撮影し、写真をコンピューターに転送して、これらの写真からパラメーターを計算します。 いくつかのポイント:
- チェッカーボードテンプレートをA4シートに印刷します。 シートはきれいである必要があります。プリンタが「流れない」ことが非常に望ましいです。
- 写真では、チェス盤のシートが正確に表面上にあることを確認してください-エッジが曲がっていない、シートの曲がりが存在していなかった。 これを行うには、段ボールに貼り付けるか、重いものを端に置いて、テンプレートの外観を妨げないようにします。
- カメラからの画像は、cameraSizeのサイズに変換する必要があります。 これは明らかなステップではありません-たとえば、iPhone 4では、640 x 480カメラのフレームサイズを認識に使用します。簡単なカメラで写真を撮ってコンピューターにドロップすると、大きな画像(2592 x 1936)が得られ、640 x 480に圧縮して、プログラムで使用しました。
- テンプレートのショット数は、さまざまな角度から撮影した12〜16でなければなりません。 個人的には、16枚のショットを使用しましたが、12と16に基づいて作成されたパラメーターを変更しながら、大きくはありませんが存在しています。 一般的に、画像が多いほど、パラメーターの精度が高くなり、この精度は3Dオブジェクトを構築する際のシフトの有無にさらに影響します。
- カメラマトリックスに加えて、この段階で歪み係数を見つけます。 一言で言えば、これらの係数はカメラレンズによる画像の歪みを表します。 詳細については、 OpenCVのドキュメントとWikipediaをご覧ください 。 モバイルデバイスに関しては、ほとんどのカメラには高品質のカメラが搭載されており、これらの比率は比較的小さいため、無視できます。
Xcodeプロジェクトキャリブレーションプログラムは、 こちらからダウンロードできます 。 この場合、OpenCVをコンパイルしておく必要があります。 または、ここからコンパイル済みフレームワークをダウンロードできます。
写真自体は、コンパイル済みのバイナリを含むフォルダーに入れ、順番に名前を変更する必要があります。
すべてが正常であれば、プログラム出力に2つのファイルが表示されます。
- Intrinsics.xml-ここでは、3x3カメラマトリックスが行ごとに記録されます
- Distortion.xml-計算された係数があります。
一部の画像でテンプレートが見つからない場合は、これらの画像を他の画像に置き換えてみてください。照明の角度を変えて、テンプレートに対してあまり鋭角にしないでください。 OpenCVは、テンプレートのすべての内部ポイントを簡単に見つけるはずです。
これらのファイルから数値を取得して、OpenGLの射影行列を作成できます。
float cameraMatrix[9] = {6.24860291e+02, 0., cameraSize.width*0.5f, 0., 6.24860291e+02, cameraSize.height*0.5f, 0., 0., 1.}; - (void)buildProjectionMatrix { // Camera parameters double f_x = cameraMatrix[0]; // Focal length in x axis double f_y = cameraMatrix[4]; // Focal length in y axis (usually the same?) double c_x = cameraMatrix[2]; // Camera primary point x double c_y = cameraMatrix[5]; // Camera primary point y double screen_width = cameraSize.width; // In pixels double screen_height = cameraSize.height; // In pixels double near = 0.1; // Near clipping distance double far = 1000; // Far clipping distance projectionMatrix[0] = 2.0 * f_x / screen_width; projectionMatrix[1] = 0.0; projectionMatrix[2] = 0.0; projectionMatrix[3] = 0.0; projectionMatrix[4] = 0.0; projectionMatrix[5] = 2.0 * f_y / screen_height; projectionMatrix[6] = 0.0; projectionMatrix[7] = 0.0; projectionMatrix[8] = 2.0 * c_x / screen_width - 1.0; projectionMatrix[9] = 2.0 * c_y / screen_height - 1.0; projectionMatrix[10] = -( far+near ) / ( far - near ); projectionMatrix[11] = -1.0; projectionMatrix[12] = 0.0; projectionMatrix[13] = 0.0; projectionMatrix[14] = -2.0 * far * near / ( far - near ); projectionMatrix[15] = 0.0; }
いくつかのメモ:
- カメラ行列の係数( cx 、 cy )をフレームの中心に置き換えます。 そうすると、3Dモデルはフレーム内のオブジェクトに対して移動しません。 投稿の著者は同じ結論に達しました(更新:記事の最後を参照)。
- ここから射影行列を得るための公式を取りました。 実際、この方法で透視投影が設定され、カメラのパラメーターとフレームサイズが考慮されます。
- 提示されたカメラのマトリックスは、iPhone 4用に私が取得したものです。他のデバイスでは、マトリックスは異なりますが、それほど多くはないと思います。
行列モデル
このマトリックスを作成するときに、StackOverflowのこの質問が役に立ちました。
幸いなことに、OpenCVでは必要な機能はすでに実装されています。
だからコード:
float cameraMatrix[9] = {6.24860291e+02, 0., cameraSize.width*0.5f, 0., 6.24860291e+02, cameraSize.height*0.5f, 0., 0., 1.}; float distCoeff[5] = {1.61426172e-01, -5.95113218e-01, 7.10574386e-04, -1.91498715e-02, 1.66041708e+00}; - (void)buildModelViewMatrixUseOld:(BOOL)useOld { clock_t timer; startTimer(&timer); CvMat cvCameraMatrix = cvMat( 3, 3, CV_32FC1, (void*)cameraMatrix ); CvMat cvDistortionMatrix = cvMat( 1, 5, CV_32FC1, (void*)distCoeff ); CvMat* objectPoints = cvCreateMat( 4, 3, CV_32FC1 ); CvMat* imagePoints = cvCreateMat( 4, 2, CV_32FC1 ); // Defining object points and image points int minDimension = MIN(detector->modelWidth, detector->modelHeight)*0.5f; for (int i=0; i<4; i++) { float objectX = (detector->x_corner[i] - detector->modelWidth/2.0f)/minDimension; float objectY = (detector->y_corner[i] - detector->modelHeight/2.0f)/minDimension; cvmSet(objectPoints, i, 0, objectX); cvmSet(objectPoints, i, 1, objectY); cvmSet(objectPoints, i, 2, 0.0f); cvmSet(imagePoints, i, 0, detector->detected_x_corner[i]); cvmSet(imagePoints, i, 1, detector->detected_y_corner[i]); } CvMat* rvec = cvCreateMat(1, 3, CV_32FC1); CvMat* tvec = cvCreateMat(1, 3, CV_32FC1); CvMat* rotMat = cvCreateMat(3, 3, CV_32FC1); cvFindExtrinsicCameraParams2(objectPoints, imagePoints, &cvCameraMatrix, &cvDistortionMatrix, rvec, tvec); // Convert it CV_MAT_ELEM(*rvec, float, 0, 1) *= -1.0; CV_MAT_ELEM(*rvec, float, 0, 2) *= -1.0; cvRodrigues2(rvec, rotMat); GLfloat RTMat[16] = {cvmGet(rotMat, 0, 0), cvmGet(rotMat, 1, 0), cvmGet(rotMat, 2, 0), 0.0f, cvmGet(rotMat, 0, 1), cvmGet(rotMat, 1, 1), cvmGet(rotMat, 2, 1), 0.0f, cvmGet(rotMat, 0, 2), cvmGet(rotMat, 1, 2), cvmGet(rotMat, 2, 2), 0.0f, cvmGet(tvec, 0, 0) , -cvmGet(tvec, 0, 1), -cvmGet(tvec, 0, 2), 1.0f}; cvReleaseMat(&objectPoints); cvReleaseMat(&imagePoints); cvReleaseMat(&rvec); cvReleaseMat(&tvec); cvReleaseMat(&rotMat); printTimerWithPrefix((char*)"ModelView matrix computation", timer); }
最初に、オブジェクトの4組のポイントとフレーム内の対応する位置を定義する必要があります。
フレーム内の位置ポイントは、フレーム内の(境界)オブジェクトを記述する四角形の頂点です。 変換Hのホモグラフィを使用してポイントデータを取得するには、このホモグラフィを使用してテンプレートの極端なポイントに単純に対処できます。
オブジェクト自体のポイントに関して、いくつかのポイントがあります。
- オブジェクトのポイントは3Dで設定され、フレーム上のポイントは2Dで設定されます。 したがって、 zにゼロ以外の値を指定すると仮定すると、 zの原点はフレーム内のオブジェクトの平面に対してシフトします。 2つの写真のデータから理解するのが簡単です。
z = 1.0
z = 0.0
- また、この3D空間での作業がより便利になるように、オブジェクトのポイントを設定します。 たとえば、原点をテンプレートの中央に配置し、長さの単位を小さい方の半分(minDimensionコード内)にします。 この場合、ピクセル単位のテンプレートの特定のサイズに依存せず、3Dスペースは小さい側でスケーリングされます。
構築された行列は、 cvFindExtrinsicCameraParams2関数に渡されます。 彼女は、回転ベクトルと転送ベクトルを作成します。 回転ベクトルから、回転行列を取得する必要があります。 これは、2番目と3番目の要素に-1を掛けて回転ベクトルを少し変換した後、 cvRodrigues2関数を使用して行われます。 さらに、取得したデータをOpenGLのモデルマトリックスに保存するだけです。 この場合、OpenGLマトリックスを転置する必要があります。
これですべて、一時オブジェクトが削除され、モデルのマトリックスが取得されます。
合計
2つの行列を作成する手順があれば、GLViewを安全に作成し、そこにモデルを描画できます。 モデルのマトリックスを見つける機能は、iPhone 4で10ミリ秒以下で実行されることに注意してください。つまり、その使用は認識のFPSを大幅に低下させません。
ご清聴ありがとうございました。
詳細:
1.http : //old.uvr.gist.ac.kr/wlee/web/techReports/ar/Camera%20Models.html
2. http://www.hitl.washington.edu/artoolkit/mail-archive/message-thread-00653-Re--Questions-concering-.html
3.http: //sightations.wordpress.com/2010/08/03/simulating-calibrated-cameras-in-opengl/
4.http ://www.songho.ca/opengl/gl_projectionmatrix.html