OpenCV 4.13.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 4.13.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
全方位カメラのキャリブレーション

このモジュールには、全方位カメラのキャリブレーション、平行化(レクティフィケーション)、ステレオ再構成が含まれる。カメラモデルは次の論文で説明されている。

C. Mei and P. Rives, Single view point omnidirectional camera calibration from planar grids, in ICRA 2007.

このモデルは、いずれも非常に広い視野を持ちうるカタディオプトリックカメラと魚眼カメラのモデル化が可能である。

キャリブレーション部分の実装は、Liのキャリブレーションツールボックスに基づいている。

B. Li, L. Heng, K. Kevin and M. Pollefeys, "A Multiple-Camera System Calibration Toolbox Using A Feature Descriptor-Based Calibration Pattern", in IROS 2013.

このチュートリアルでは、全方位カメラキャリブレーションモジュールの次の部分を紹介する。

  • 単一カメラのキャリブレーション。
  • ステレオカメラペアのキャリブレーション。
  • 大きな歪みが除去されるように画像を平行化する。
  • 広い視野を持つ2枚のステレオ画像から3Dを再構成する。
  • opencv/calib3d/ の魚眼モデルとの比較。

単一カメラのキャリブレーション

カメラをキャリブレーションする最初のステップは、キャリブレーションパターンを用意して数枚の写真を撮ることである。OpenCVはチェッカーボードや円グリッドなど、いくつかの種類のパターンをサポートしている。ランダムパターンと呼ばれる新しいパターンも使用でき、詳細は opencv_contrib/modules/ccalib を参照すること。

次のステップは、キャリブレーションパターンからコーナーを抽出することである。チェッカーボードの場合はOpenCV関数 cv::findChessboardCorners を、円グリッドの場合は cv::findCirclesGrid を、ランダムパターンの場合は opencv_contrib/modules/ccalib/src/randomPattern.hpp の randomPatternCornerFinder クラスを使用する。画像中のコーナーの位置を imagePoints のような変数に保存する。imagePoints の型は std::vector<std::vector<cv::Vec2f>> でよく、最初のベクトルは各フレームのコーナーを格納し、2番目のベクトルは個々のフレーム内のコーナーを格納する。型は std::vector<cv::Mat> でもよく、その場合 cv::MatCV_32FC2 である。

また、ワールド(パターン)座標系における対応する3D点も必要である。パターンの物理的なサイズが分かっていれば、自分で計算できる。3D点は objectPoints に保存する。imagePoints と同様に、std::vector<std::vector<Vec3f>> または std::vector<cv::Mat> でよく、その場合 cv::MatCV_32FC3 型である。objectPointsimagePoints は互いに対応しているため、サイズは同じでなければならない点に注意すること。

もう一つ入力すべきものは画像のサイズである。ファイル opencv_contrib/modules/ccalib/tutorial/data/omni_calib_data.xml には objectPoints、imagePoints、imageSize の例が格納されている。以下のコードでこれらを読み込む。

cv::FileStorage fs("omni_calib_data.xml", cv::FileStorage::READ);
std::vector<cv::Mat> objectPoints, imagePoints;
cv::Size imgSize;
fs["objectPoints"] >> objectPoints;
fs["imagePoints"] >> imagePoints;
fs["imageSize"] >> imgSize;

次に、出力パラメータを格納するいくつかの変数を定義し、次のようにキャリブレーション関数を実行する。

cv::Mat K, xi, D, idx;
int flags = 0;
cv::TermCriteria critia(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 200, 0.0001);
std::vector<cv::Mat> rvecs, tvecs;
double rms = cv::omnidir::calibrate(objectPoints, imagePoints, imgSize, K, xi, D, rvecs, tvecs, flags, critia, idx);

KxiD は内部パラメータであり、rvecstvecs はパターンの姿勢を格納する外部パラメータである。これらはすべて CV_64F のビット深度を持つ。xi はMeiのモデルにおける単一値の変数である。idxCV_32S のMatで、実際にキャリブレーションに使用された画像のインデックスを格納する。これは、一部の画像が初期化ステップで失敗するため、最終的な最適化に使用されないことによる。戻り値の rms は再投影誤差の二乗平均平方根である。

キャリブレーションはいくつかの機能をサポートしている。flags は次のようないくつかの機能の列挙体である。

キャリブレーション中にパラメータを固定するには flags を指定する。複数の機能を設定するには'プラス'演算子を使用する。例えば CALIB_FIX_SKEW+CALIB_FIX_K1 は、スキューとK1を固定することを意味する。

criteria は最適化中の停止基準であり、例えば cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 200, 0.0001) のように設定する。これは200回の反復を使用し、相対変化が0.0001より小さくなったときに停止することを意味する。

ステレオキャリブレーション

ステレオキャリブレーションは2台のカメラを一緒にキャリブレーションすることである。出力パラメータには、2台のカメラのカメラパラメータと、それらの相対的な姿勢が含まれる。相対的な姿勢を復元するには、2台のカメラが同時に同じパターンを観測しなければならないため、2台のカメラの objectPoints は同じである。

次に、上述したように両方のカメラの画像コーナーを検出して imagePoints1imagePoints2 を取得する。そして、共有される objectPoints を計算する。

ステレオキャリブレーションデータの例は opencv_contrib/modules/ccalib/tutorial/data/omni_stereocalib_data.xml に格納されている。データは次のように読み込む。

cv::FileStorage fs("omni_stereocalib_data.xml", cv::FileStorage::READ);
std::vector<cv::Mat> objectPoints, imagePoints1, imagePoints2;
cv::Size imgSize1, imgSize2;
fs["objectPoints"] >> objectPoints;
fs["imagePoints1"] >> imagePoints1;
fs["imagePoints2"] >> imagePoints2;
fs["imageSize1"] >> imgSize1;
fs["imageSize2"] >> imgSize2;

次に、次のようにステレオキャリブレーションを行う。

cv::Mat K1, K2, xi1, xi2, D1, D2;
int flags = 0;
cv::TermCriteria critia(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 200, 0.0001);
std::vector<cv::Mat> rvecsL, tvecsL;
cv::Mat rvec, tvec;
double rms = cv::omnidir::stereoCalibrate(objectPoints, imagePoints1, imagePoints2, imgSize1, imgSize2, K1, xi1, D1, K2, xi2, D2, rvec, tvec, rvecsL, tvecsL, flags, critia, idx);

ここで rvectvec は1台目と2台目のカメラ間の変換である。rvecsLtvecsL はパターンと1台目のカメラ間の変換である。

画像の平行化

全方位画像は非常に大きな歪みを持つため、人間の眼球とは相性が悪い。より見やすくするため、カメラパラメータが既知であれば平行化を適用できる。以下は水平視野360度の全方位画像の例である。

image

平行化後は、透視図のようなビューが生成される。以下はこのモジュールで画像の平行化を実行する一例である。

cv::omnidir::undistortImage(distorted, undistorted, K, D, xi, int flags, Knew, new_size)

変数 distortedundistorted は、それぞれ元画像と透視的に平行化された画像である。KDxi はカメラパラメータである。KNewnew_size は平行化後の画像のカメラ行列と画像サイズである。flags は平行化の種類であり、次のいずれかを指定できる。

  • RECTIFY_PERSPECTIVE: 透視画像に平行化する。一部の視野が失われる。
  • RECTIFY_CYLINDRICAL: すべての視野を保持する円筒画像に平行化する。
  • RECTIFY_STEREOGRAPHIC: わずかに視野が失われる可能性のあるステレオ投影画像に平行化する。
  • RECTIFY_LONGLATI: 地球の世界地図のような経度緯度マップに平行化する。この平行化はステレオ再構成に利用できるが、ビューとしては見やすくない場合がある。このマップは次の論文で説明されている: Li S. Binocular spherical stereo[J]. Intelligent Transportation Systems, IEEE Transactions on, 2008, 9(4): 589-600.

次の4枚の画像は、上述した4種類の平行化画像である。

image
image
image
image

透視平行化画像はわずかな視野しか保持せず、見栄えがよくないことが分かる。円筒平行化はすべての視野を保持し、底部の中央でのみシーンが不自然になる。ステレオ投影の底部中央の歪みは円筒よりも小さいが、他の場所の歪みは大きく、すべての視野を保持できない。非常に大きな歪みを持つ画像では、経度緯度平行化は良い結果を与えないが、エピポーラ拘束を一直線上に置くことができるため、全方位画像でステレオマッチングを適用できる。

注意: より良い結果を得るには、Knew を慎重に選ぶべきであり、これはカメラに依存する。一般に、焦点距離が小さいほど視野は狭くなり、その逆も同様である。以下に推奨される設定を示す。

RECTIFY_PERSPECTIVE の場合

Knew = Matx33f(new_size.width/4, 0, new_size.width/2,
0, new_size.height/4, new_size.height/2,
0, 0, 1);

RECTIFY_CYLINDRICAL、RECTIFY_STEREOGRAPHIC、RECTIFY_LONGLATI の場合

Knew = Matx33f(new_size.width/3.1415, 0, 0,
0, new_size.height/3.1415, 0,
0, 0, 1);

より良いビューを得るために (u0, v0) を変更する必要があるかもしれない。

ステレオ再構成

ステレオ再構成は、キャリブレーション済みのステレオカメラペアから3D点を再構成することである。これはコンピュータビジョンの基本的な問題である。しかし全方位カメラでは、大きな歪みのため少々難しく、あまり一般的ではない。従来の手法は画像を透視画像に平行化し、透視画像でステレオ再構成を行う。しかし前節で示したように、透視画像への平行化は視野を失いすぎ、全方位カメラの利点、すなわち広い視野を無駄にしてしまう。

ステレオ再構成の最初のステップは、エピポーラ線が水平線になるようにするステレオ平行化である。ここでは、すべての視野を保持するために経度緯度平行化を使用する。透視平行化も利用可能だが推奨されない。2番目のステップは、視差マップを得るためのステレオマッチングである。最後に、視差マップから3D点を生成できる。

全方位カメラのステレオ再構成のAPIは omnidir::stereoReconstruct である。ここでは、その動作を示す例を用いる。

まず、上述したようにステレオカメラペアをキャリブレーションし、K1D1xi1K2D2xi2rvectvec のようなパラメータを取得する。次に、1台目と2台目のカメラからそれぞれ2枚の画像、例えば image1image2 を読み込む。これらを以下に示す。

image

次に、次のように omnidir::stereoReconstruct を実行する。

cv::Size imgSize = img1.size();
int numDisparities = 16*5;
int SADWindowSize = 5;
cv::Mat disMap;
int flag = cv::omnidir::RECTIFY_LONGLATI;
int pointType = omnidir::XYZRGB;
// the range of theta is (0, pi) and the range of phi is (0, pi)
cv::Matx33d KNew(imgSize.width / 3.1415, 0, 0, 0, imgSize.height / 3.1415, 0, 0, 0, 1);
Mat imageRec1, imageRec2, pointCloud;
cv::omnidir::stereoReconstruct(img1, img2, K1, D1, xi1, K2, D2, xi2, R, T, flag, numDisparities, SADWindowSize, disMap, imageRec1, imageRec2, imgSize, KNew, pointCloud);

ここで変数 flag は平行化の種類を示し、RECTIFY_LONGLATI(推奨)と RECTIFY_PERSPECTIVE のみが意味を持つ。numDisparities は最大視差値であり、SADWindowSizecv::StereoSGBM のウィンドウサイズである。pointType は点群の型を定義するフラグである。omnidir::XYZRGB では各点が6次元ベクトルで、最初の3要素はxyz座標、最後の3要素はrgbの色情報である。もう一つの型 omnidir::XYZ は、各点が3次元でxyz座標のみを持つことを意味する。

さらに、imageRec1imagerec2 は1枚目と2枚目の画像の平行化されたバージョンである。これらのエピポーラ線は同じy座標を持つため、ステレオマッチングが容易になる。以下にそれらの例を示す。

これらがよく整列していることが分かる。変数 disMap は、imageRec1imageRec2 から cv::StereoSGBM によって計算された視差マップである。上記2枚の画像の視差マップは次のとおりである。

image

視差が得られたら、各ピクセルの3D位置を計算できる。点群は pointCloud に格納される。これは3チャンネルまたは6チャンネルの cv::Mat である。点群を次の画像に示す。