最近在研究全景圖像的拼接,閱讀了OpenCV中圖像拼接的部分代碼,閱讀到waveCorrect函數時看的一頭霧水。函數的功能是傳入每個相機的旋轉矩陣,計算一個矯正的旋轉矩陣,對旋轉矩陣進行矯正,使得拼接後的圖像近似爲直線(矯正拼接後圖像的波浪形式)。示例見lowe的論文中相關部分:
該部分在 Lowe[2] 的論文中叫做Automatic Panorama Straightening,在 Szeliski[3,4] 的綜述文章中叫做Up vector selection,OpenCV的代碼是對Lowe算法的實現。
straightening約束的OpenCV代碼如下
void waveCorrect(std::vector<Mat> &rmats, WaveCorrectKind kind)
{
LOGLN("Wave correcting...");
#if ENABLE_LOG
int64 t = getTickCount();
#endif
Mat moment = Mat::zeros(3, 3, CV_32F);
for (size_t i = 0; i < rmats.size(); ++i)
{
Mat col = rmats[i].col(0);
moment += col * col.t();
}
Mat eigen_vals, eigen_vecs;
eigen(moment, eigen_vals, eigen_vecs);
Mat rg1;
if (kind == WAVE_CORRECT_HORIZ)
rg1 = eigen_vecs.row(2).t();
else if (kind == WAVE_CORRECT_VERT)
rg1 = eigen_vecs.row(0).t();
else
CV_Error(CV_StsBadArg, "unsupported kind of wave correction");
Mat img_k = Mat::zeros(3, 1, CV_32F);
for (size_t i = 0; i < rmats.size(); ++i)
img_k += rmats[i].col(2);
Mat rg0 = rg1.cross(img_k);
rg0 /= norm(rg0);
Mat rg2 = rg0.cross(rg1);
double conf = 0;
if (kind == WAVE_CORRECT_HORIZ)
{
for (size_t i = 0; i < rmats.size(); ++i)
conf += rg0.dot(rmats[i].col(0));
if (conf < 0)
{
rg0 *= -1;
rg1 *= -1;
}
}
else if (kind == WAVE_CORRECT_VERT)
{
for (size_t i = 0; i < rmats.size(); ++i)
conf -= rg1.dot(rmats[i].col(0));
if (conf < 0)
{
rg0 *= -1;
rg1 *= -1;
}
}
Mat R = Mat::zeros(3, 3, CV_32F);
Mat tmp = R.row(0);
Mat(rg0.t()).copyTo(tmp);
tmp = R.row(1);
Mat(rg1.t()).copyTo(tmp);
tmp = R.row(2);
Mat(rg2.t()).copyTo(tmp);
for (size_t i = 0; i < rmats.size(); ++i)
rmats[i] = R * rmats[i];
LOGLN("Wave correcting, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
}
主要使用兩個約束條件來計算全局旋轉矩陣:
1. 相機座標系的X軸與世界座標系中的Z軸垂直。
2. 相機的旋轉矩陣的Z軸方向的平均值與世界座標系的Z軸相接近。
參考:
1. OpenCV代碼 motion_estimators.cpp 的 void waveCorrect(std::vector<Mat> &rmats, WaveCorrectKind kind) 函數。
2. Automatic Panoramic Image Stitching using Invariant Features
3. 計算機視覺——算法與應用,第340頁。
4. Image Alignment and Stitching,第50頁。