記事ディレクトリ
1. 問題の導入
現在のコンピュータビジョン技術は十分に成熟した段階にまで発展していると言わざるを得ませんが、著者が働き始めた頃、カメラキャリブレーションはまだ非常に謎に満ちた技術で、それを実行できるのはほんのわずかな専門家だけで、何もありませんでした。インターネット上のそれに関連する資料。しかし、現在、カメラのキャリブレーションは非常に一般的な技術であり、参考になる情報がたくさんあります。そこで、筆者は突然、これらの大型カメラがキャリブレーションできるのであれば、私たちが使用している携帯電話のカメラもキャリブレーションする必要があると考えました。そこで、当時この技術を学ばなかった後悔を補うために、筆者が携帯電話のカメラを使った具体的な実践を記録します。結局のところ、技術を学ぶ最善の方法は、自分で実践することです。
2. 準備
2.1 校正フィールド
著者は多くの正式な校正フィールドを見てきましたが、その中にはマーカーとカメラ機器を運ぶためのトラックを備えた多数の垂直バーを備えた非常に大きなフィールドもあります。ただし、現在最も一般的で安価な方法は、チェッカーボード校正ボードを使用することです。これは、いわゆる張正佑校正方法です。
それでは、チェッカーボード校正ボードはどこから来たのでしょうか?紙に印刷するのも 1 つの方法ですが、2 つの問題がある可能性があります。1 つは、印刷後に各グリッドのサイズをピクセルからメートル単位に変換する必要があることです。もう 1 つは、整数を見つける必要があることです。貼る壁が盛り上がってしまうと、スムーズに平らに貼るのがかなり難しいです。そこで筆者はこの方法は選択せず、最終的にネット通販でキャリブレーションボードを見つけました。携帯電話のカメラのセンサーサイズはそれほど大きくないので、キャリブレーションボードもそれほど大きくする必要はありません。最終的に選んだキャリブレーションボードのサイズは次のとおりです。
各グリッドは 5 mm、合計 12X9 グリッドです。全体のサイズは比較的小さく、手のひらほどの大きさです。材質はガラス基板で価格は約50元。私の実際の経験ではこのサイズはまだ少し小さいですが、それより大きい場合は費用が高くなりますので、経済的に余裕のある学生はより大きなサイズを選択することをお勧めします。
2.2 カメラ撮影
次のステップは、携帯電話のカメラを使用してチェッカーボード キャリブレーション ボードの写真を撮影することです。理論的には、キャリブレーションと計算に必要な制御点のセットは 6 つだけですが、特定された制御点にはすべてエラーがあるため、精度を向上させるには複数の点のセットが必要になります。 1 枚の写真を撮影して取得したコントロール ポイントでは十分ではなく、局所最適問題を回避し、解決プロセスの信頼性を向上させるには、通常、複数の写真でコントロール ポイントを取得する必要があります。可能であれば、複数の視野角と、異なる距離にある複数のキャリブレーション プレートの写真を使用します。また、歪みやその他のパラメータをより適切に推定できるように、キャリブレーション プレートが画像面全体の異なる領域を確実にカバーするようにすることも最善です。
ここで著者は、以下に示すように、前、後、左、右、上、下の 6 つの異なる位置と角度から、チェッカーボード キャリブレーション ボードの 6 枚の写真を撮影しました。
取られたキャリブレーションプレートの領域が中央に近すぎることがわかりますが、使用したキャリブレーションプレートのサイズが確かに少し小さいため、これについてはどうすることもできません。撮影時に非常に近づくと、携帯電話のカメラ プログラムが自動的にクローズアップ撮影に切り替わります。筆者も接写に切り替えた後にカメラパラメータが変更されるかどうかが分からないので、あまり寄ることはできませんでした。でも、あまり遠くから撮るとちょっとぼやけてしまうので、今のエフェクトしか使えません。
最近の携帯電話のカメラ機能は、フィルターや広角補正など自動で写真を補正してくれる機能が多くありますが、これらの機能はオフにするか、できるだけ使用しないようにしましょう。また、撮影中にフォーカス調整を行わず、カメラのズーム倍率を表す0.6x、1x、2x、3xなどのパラメータをそのまま使用します。較正。もちろん、レンズと焦点面の位置を変更しないで、オートフォーカス機能もオフにする必要があります。
もう1つの質問は、写真を撮るために携帯電話を静止させてキャリブレーションボードを動かすか、それとも写真を撮るためにキャリブレーションボードを静止させて携帯電話を動かすかということです。どちらも原理的には実装可能ですが、実装が簡単なため、キャリブレーション プレートは移動せず、カメラを移動する方が一般的です。著者は、市松模様の校正ボードを両面テープで壁に貼り付けることでこれを実現しました。これは、最も低コストのマイクロ校正フィールドとみなすことができます。
実際、キャリブレーションボードをテーブルの上に置いて撮影したこともありますが、室内で撮影する場合は写真に影が入りやすいので、壁に固定した方が良いでしょう。また、最高の撮影効果を得るには、照明の良い壁に設置し、日光が十分にある日中に撮影するのが最善です。
3. 基本原則
3.1 撮像原理
カメラ キャリブレーションでは内部パラメータが計算されますが、実際には外部パラメータも計算されます。カメラ キャリブレーション ソリューションで使用されるカメラ イメージング原理により、内部パラメータと外部パラメータがこのプロセスで一緒に計算に参加します。歪みを考慮しない場合、カメラの結像原理は次の式(1)で表すことができます。
s [ uv 1 ] = K [ R ∣ t ] [ X w Y w Z w 1 ] (1) s \begin{bmatrix} u\\ v\\ 1\\ \end{bmatrix} = K \begin{bmatrix} R|t\\ \end{bmatrix} \begin{bmatrix} X_w\\ Y_w\\ Z_w\\ 1\\ \end{bmatrix} \tag{1}s でv1 =け[R ∣ t] バツでそしてでとで1 (1)
この式では次のようになります。
- [ X w Y w Z w ] T {\begin{bmatrix}X_w & Y_w & Z_w\\\end{bmatrix}}^T[バツでそしてでとで]T はワールド空間内の 3 次元の点を表し、オブジェクト空間点とも呼ばれます。
- [ uv ] T {\begin{bmatrix}u & v\\\end{bmatrix}}^T[でで]T は画像平面上のピクセル座標を表し、画像点とも呼ばれます。
- [ R ∣ t ] \begin{bmatrix}R|t\\\end{bmatrix}[R ∣ t] はカメラの外部パラメータ行列です。具体的には、回転変換と平行移動変換を組み合わせたもので、RRR は3X3 回転行列です。ttt は3 次元ベクトルです。回転変換はオイラー角で表現できるため、3次元ベクトルで表現することもできます。 3 つの回転量と 3 つの平行移動量は、カメラの 6 つの外部パラメータの原点です。
- KKK はカメラの内部パラメータ行列で、通常は次の式 (2) で表されます。
K = [ fx 0 cx 0 fycy 0 0 1 ] (2) K = \begin{bmatrix} f_x & 0 & c_x\\ 0 & f_y & c_y \\ 0 & 0 & 1\\ \end{bmatrix} \tag{2}け= ふx000ふそして0cxcそして1 (2)- ファクスふx和fy f_yふそしてこれらは、それぞれ水平方向と垂直方向の焦点距離 (ピクセル単位) です。
- cxc_xcxそしてcy c_ycそしてαは像主点(つまり結像面の光軸との交点)の座標であり、単位はピクセルである。
- sssはスケール係数です。このパラメータは同次座標の変換を実現し、その後の 3 次元座標を 2 次元座標に変換します。
著者の観点から見ると、上記のカメラ画像の原理は、実際には他の分野の知識と似ています。
-
コンピュータグラフィックス。グラフィックス レンダリングにおける幾何学的変換には、モデル変換、ビュー変換、投影変換が含まれます。これらは合わせて一般に MVP マトリックスとして知られています。モデル変換には回転変換と平行移動変換が含まれ、ビュー変換はモデル変換の逆変換であり、方程式 ( の外部パラメーター行列[ R ∣ t ] \begin{bmatrix}R|t\\\end{bmatrix}に対応します) 1)[R ∣ t]。ただし、射影行列は式(1)の内部パラメータ行列KKK は点をカメラ座標系から画像座標系に変換するもので、グラフィックス レンダリングにおける射影行列は点をカメラ座標系からクリッピング座標系に変換するものです。
-
写真測量。写真測量では、この結像原理の公式セットは共線方程式としてまとめられます。異なる表現形式に加えて、最も重要な違いは、内部パラメーターが 3 つだけであることです。焦点距離と画像主成分の 2 次元座標です。ポイント。個人的には、この式はあまり直感的ではないと思いますが、調整の計算は簡単です。
上記の 2 つを経験した読者は、それらを比較して理解することができますが、それらは少し異なりますが、それらの原理はすべて同じであり、状況に応じて異なる反応を示すだけです。説明。
3.2 歪み補正
上記のイメージング原理では、歪みの影響は考慮されていません。歪みはなぜ起こるのでしょうか?簡単に言えば、カメラのレンズは完全に平坦な光学系ではなく、光線は透過する際に複雑な曲がりを経験し、その結果画像の直線が画像の端で歪む原因となります。一般的な歪みには、半径方向の歪みと接線方向の歪みが含まれます。
歪み補正は非常に謎めいているように見えますが、端的に言えば、歪み補正に使用される有理関数モデルという 1 つのことを理解するだけで十分です。いわゆる有理関数モデルは、補正前の位置 x 間の高次多項式 (y = ax 3 + bx 2 + cx + dy=ax^3+bx^2+cx+d の形式) を使用します。と補正後の位置 y です。そして=x3+b ×2+c ×+d ) を表現するには、物理的原理はなく、純粋に数学的フィッティングによって行われ、最終的に各高次項の係数 (a、b、c、d) が得られます。
歪み補正によりキャリブレーション ソリューションの複雑さが増すため、ここではこれ以上説明しません。初心者にとっては、結像原理の式 (1) を理解することがより重要です。
4. 校正液
4.1 コードの実装
キャリブレーション計算は、上記で紹介した基本原理を使用して実行できますが、計算方法は、主に OpenCV ライブラリを使用して説明します。
#include <filesystem>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#ifdef _WIN32
#include <Windows.h>
#endif
using namespace cv;
using namespace std;
int main() {
#ifdef _WIN32
SetConsoleOutputCP(65001);
#endif
vector<std::filesystem::path> imgPaths = {
"C:/Work/CalibrateCamera/Data/front.jpg",
"C:/Work/CalibrateCamera/Data/left.jpg",
"C:/Work/CalibrateCamera/Data/right.jpg",
"C:/Work/CalibrateCamera/Data/up.jpg",
"C:/Work/CalibrateCamera/Data/down.jpg",
"C:/Work/CalibrateCamera/Data/back.jpg"};
size_t imageNum = imgPaths.size();
// 定义棋盘格尺寸 (内角点数)
int boardWidth = 11; // 列数
int boardHeight = 8; // 行数
cv::Size boardSize(boardWidth, boardHeight);
double cellSize = 0.005;
Size imageSize(3072, 4096); // 图像尺寸
// 准备标定所需的物方点和像方点
vector<vector<Point3f>> objectPoints(imageNum); // 多张图像的3D物方点
vector<vector<Point2f>> imagePoints(imageNum); // 多张图像的2D像方点
for (size_t ii = 0; ii < imageNum; ++ii) {
// 加载棋盘格图像
cv::Mat image = cv::imread(imgPaths[ii].string().c_str());
if (image.empty()) {
std::cerr << "Error: Could not load image!" << std::endl;
return -1;
}
// 存储角点坐标
std::vector<cv::Point2f> corners;
// 转换图像为灰度
cv::Mat grayImage;
cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);
// 寻找棋盘格角点
// cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE
bool found = cv::findChessboardCorners(grayImage, boardSize, corners,
cv::CALIB_CB_FAST_CHECK);
// 如果找到角点,进行进一步处理
if (found) {
std::cout << "Chessboard corners found!" << std::endl;
// 增加角点的精度
cv::cornerSubPix(
grayImage, corners, cv::Size(11, 11), cv::Size(-1, -1),
cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER,
30, 0.001));
// 绘制角点
std::string cornerImgPath = imgPaths[ii].parent_path().generic_string() +
"/corner/" + imgPaths[ii].stem().string() +
"_corner" + imgPaths[ii].extension().string();
cv::drawChessboardCorners(image, boardSize, corners, found);
cv::imwrite(cornerImgPath.c_str(), image);
cout << corners.size() << endl;
imagePoints[ii].resize(corners.size());
for (size_t ci = 0; ci < corners.size(); ++ci) {
imagePoints[ii][ci] = corners[ci];
}
objectPoints[ii].resize(corners.size());
for (int hi = 0; hi < boardHeight; ++hi) {
for (int wi = 0; wi < boardWidth; ++wi) {
int ci = hi * boardWidth + wi;
objectPoints[ii][ci].x = cellSize * wi;
objectPoints[ii][ci].y = cellSize * hi;
objectPoints[ii][ci].z = 0;
}
}
} else {
std::cerr << "Chessboard corners not found!" << std::endl;
}
}
// 内参矩阵和畸变系数
Mat cameraMatrix = Mat::eye(3, 3, CV_64F); // 初始化为单位矩阵
Mat distCoeffs = Mat::zeros(8, 1, CV_64F); // 初始化为零
// 外参的旋转和位移向量
vector<Mat> rvecs, tvecs;
// 执行标定
double reprojectionError =
calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix,
distCoeffs, rvecs, tvecs);
cout << u8"重投影误差:" << reprojectionError << endl;
cout << u8"内参矩阵:" << cameraMatrix << endl;
cout << u8"畸变系数:" << distCoeffs << endl;
return 0;
}
4.2 詳細な分析
4.2.1 ソリューションの実装
コードを実装する手順は非常に簡単です。findChessboardCorners
関数を通じてチェッカーボード イメージのコーナー ポイントを抽出し、それをcalibrateCamera
関数に渡して、内部パラメータ マトリックスである最終的な解決結果を取得します。その鍵はcalibrateCamera
この関数にあります。その関数プロトタイプを見てみましょう。
CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints, Size imageSize,
InputOutputArray cameraMatrix, InputOutputArray distCoeffs,
OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
int flags = 0, TermCriteria criteria = TermCriteria(
TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON) );
そのパラメータの詳細は次のとおりです。
- objectPoints: 3D 空間内のオブジェクト空間点座標のセット。これは[X w Y w Z w] T {\begin{bmatrix}X_w & Y_w & Z_w\\\end{bmatrix}}^T[バツでそしてでとで]T.複数の画像からの複数の点セットのコレクションであるため、そのタイプは実際には です
std::vector<std::vector<cv::Point3f>>
。 - imagePoints: 画像内のピクセル座標のコレクション。式 (1) の[uv] T {\begin{bmatrix}u & v\\\end{bmatrix}}^Tに対応します。[でで]T の型も double 配列である必要があります
std::vector<std::vector<cv::Point2f>>
。 - imageSize: 画像のサイズ (幅と高さ) をピクセル単位で入力します。
- CameraMatrix: 出力カメラの内部パラメータ行列。式 (1) のKKです。K は3X3 行列です。
- distCoeffs: 出力カメラの歪み係数。通常は 1X5 または 1X8 ベクトルで、半径方向と接線方向の歪み係数が含まれます。
- rvecs: 出力回転ベクトルセット。式 (1) のRRに変換できます。R.各回転ベクトルは画像に対応するため、タイプは です
std::vector<cv::Mat>
。 - tvecs: 出力平行移動ベクトルセット、式 (1) のttに対応と。各変換ベクトルはイメージに対応し、タイプも同様です
std::vector<cv::Mat>
。 - 戻り値: キャリブレーション結果の精度を測定するために使用される、キャリブレーションされた再投影誤差。誤差が小さいほど、キャリブレーション結果はより正確になります。
calibrateCamera
この関数の分析を通じて、読者はなぜ著者が最初に式 (1) の結像原理について話したいのかを容易に理解できると思います。この解パラメータの入力と出力は式 (1) に基づいていますが、別の疑問が生じます。入力されたオブジェクトの正方形の点と画像の正方形の点はどこから来るのでしょうか?
4.2.2 ポイントの抽出
答えは簡単、チェス盤のコーナーポイントです。チェッカーボードは白と黒のグリッドで構成されているため、角の点を抽出するのが簡単ですが、チェッカーボードも規則的であり、各グリッドのサイズが同じである限り、オブジェクトの座標を知るのは簡単です。 。理論的には、コーナー ポイントが画像から抽出され、チェス盤以外のコーナー ポイントが削除されている限り、カメラ キャリブレーションの画像ポイントとして使用できます。ただし、OpenCV は、チェッカーボードの内側のコーナー ポイントの数を直接入力することにより、画像ポイントを自動的に検出できる追加のインターフェイスを提供しますfindChessboardCorners
。以下の図に示すように、作成者が画像上で抽出した画像ポイントは次のとおりです。
上図に示すように、findChessboardCorners
抽出されるのは 12X9 チェッカーボードなどの内側のコーナー ポイントであり、抽出された内側のコーナー ポイントは 11X8 であり、結果は左から右、上から下の順に並べ替えられます。なぜこのように注文するのでしょうか?オブジェクトの正方形の点を計算するのが簡単だからです。カメラ キャリブレーションのアプリケーションでは、カメラの外部パラメータは重要ではないため、チェッカーボード キャリブレーション ボードの左上隅を世界座標系の原点として使用できます。最初の点の座標は (0,0) です。 ,0)、2 番目の点の座標は (0.005,0,0)、3 番目の点の座標は (0.010,0,0)...12 番目の点の座標は (0,0.005,0) 、13 番目の点の座標は ( 0.005,0.005,0)... というように計算して、すべてのコーナー ポイントに対応するワールド空間座標系の座標を取得します。
読者に注意していただきたいもう 1 つの点は、findChessboardCorners
ここで設定したパラメーターは cv::CALIB_CB_FAST_CHECK であり、cv::CALIB_CB_ADAPTIVE_THRESH および cv::CALIB_CB_NORMALIZE_IMAGE が画像の前処理を行うため、チェッカーボードのコーナー ポイントを抽出する機能が向上します。 。ただし、効率が悪いのか、OpenCV の問題なのかはわかりませんが、実際に使用しているときにプログラムが停止することがわかりました。そのため、この 2 つのオプションは使用しませんでした。
4.3 解決策の結果
最終的に著者の和解結果は以下の通り。
重投影误差:0.166339
内参矩阵:[2885.695162446343, 0, 1535.720945173723;
0, 2885.371543143629, 2053.122840953737;
0, 0, 1]
畸变系数:[0.181362004467736;
-3.970106972775221;
0.0005157812878172198;
0.0004644406171824815;
23.559069196518]
解の結果の再投影誤差は 0.166339 です。これは、画像上に再投影したときに各オブジェクト点と実際に検出されたコーナー点の位置との間の誤差が 0.166339 ピクセルであることを意味します。一般に、このような誤差は非常に小さいと考えられ、キャリブレーション結果がより正確であることを示しています。
ただし、著者はまた、誤差が 0.166339 ピクセルであるため、具体的には何メートルなのかという疑問を考えています。以前、測量や地図作成のソフトウェアを開発していたときも、調整結果はピクセル単位で表示されていました。「具体的には何メートルですか?」と尋ねられることがありました。今回、著者はこの問題にも注目しましたが、カメラのキャリブレーションなどのアプリケーション シナリオでは、このアルゴリズムの結果は再投影されたピクセルの差を測定することになるため、精度を表現するために物理単位を直接使用することは実際には不可能であると思います。これは、カメラの外部パラメータの方向によって誤差の尺度が変化することと一致します。
内部パラメータ行列と比較すると、計算された焦点距離はfx = 2885.695 f_x=2885.695となります。ふx=2885.695和fy = 2885.372 f_y=2885.372ふそして=2885.372、単位もピクセルです。では、この焦点距離を物理単位に換算すると何メートルになるのでしょうか?筆者が調べた情報によると、焦点距離のピクセルとミリメートルの換算式は次の通りです。
焦点距離 (mm) = 焦点距離 (ピクセル) × センサーサイズ (mm) 画像解像度 (ピクセル) 焦点距離 (mm) = \frac{焦点距離 (ピクセル) × センサーサイズ (mm)} {画像解像度 (ピクセル)}焦点距離(mm)=画像解像度 (ピクセル)焦点距離 (ピクセル)×センサーサイズ (mm )
つまり、カメラのセンサーサイズに関係するのですが、センサーサイズの記述がちょっと辛いですね、例えば私の携帯電話のカメラのセンサーはネット上では1/1.49インチと表示されています。通常、センサーの対角線の長さを示します。カメラ センサーの物理サイズは、対角線の長さとアスペクト比 (4:3 や 16:9 など) に基づいて計算でき、特定の物理単位の焦点距離の値を知ることができます。ただし、業界の慣例や歴史的な基準により、センサーの公称対角長と実際の物理的サイズには差異があるため、計算された値が正しくない可能性がありますので、公式に問い合わせて確認することをお勧めします。ただし、焦点距離をピクセル単位で調整するだけで、その後の使用シナリオに十分対応できます。著者はここでその真相に迫ります。
5. 補足質問
最後に、まだ解決していない、または理解していない質問をいくつか追加させてください。
- 結像原理に挙げた式(1)の内部パラメータ行列の部分については、実は焦点距離をなぜX方向にfx f_xに分割するのかは筆者も解明できていない。ふxそしてy方向にfy f_yふそして、一部の材料の内部パラメータ行列はこの方法でリストされておらず、「写真測量」の教科書にリストされている共線方程式には 1 つの焦点距離値ffしかありません。f。
- 著者は、固定焦点距離ffを使用できる「カメラの再キャリブレーション」という操作があるようであることを覚えていますf、画像の主点を画像の中心に調整し、再投影して歪みを除去すると、後続の空間計算が簡素化され、計算がより便利になります。時間の関係はその後の研究に委ねられます。
- この記事の著者は、ソリューションのアルゴリズム原理については詳しく説明しません。これは、測量と地図作成では、調整と呼ばれることもある特別な用語で説明することができないためです。状態推定や最尤推定、非線形最適化などについて議論を続けるには、少なくとも最小二乗法の原理を知っておく必要があります。それについては後続の記事での議論に任せましょう。
参考としていくつかの記事をリストします。