很多時候我們不知道攝像機的內參數矩陣,並且我們也不太關注內參數到底是多少,因爲我們僅僅關心如何得到兩幅圖像的稠密匹配,或者兩幅圖像的差別——例如我們只想計算兩幅圖像的視差圖,或者說得到兩幅立體圖像對的深度圖就足夠了。既然不知道攝像機的內參數,那麼就只能藉助對極約束來達到目的了。通過計算兩幅圖像的基礎矩陣F,然後利用對極約束矯正極線爲平行線的方法,可以很好的實現這個目標,該方法也被稱爲Hartly方法,在OpenCV中由cv::stereoRectifyUncalibrated函數實現。
立體圖像的極線矯正需要三個步驟:
(1)提取特徵點並匹配,參考http://blog.sina.com.cn/s/blog_4298002e01013w4z.html
(2)計算基本矩陣F,參考http://blog.sina.com.cn/s/blog_4298002e01013w9a.html
(3)極線矯正。
Hartly方法的函數原型如下:
//! computes the rectification transformation for an uncalibrated stereo camera (zero distortion is assumed)
CV_EXPORTS_W bool stereoRectifyUncalibrated( const Mat& points1, const Mat& points2,
const
Mat& F, Size imgSize,
CV_OUT
Mat& H1, CV_OUT Mat& H2,
double
threshold=5 );
該函數輸入參數爲兩幅圖像的匹配特徵點,基本矩陣F以及圖像的尺寸,返回的參數是兩幅圖像各自對應的單應變換矩陣H1和H2。只需要對兩幅圖像按照H1和H2做單應變換,即可得到矯正後圖像。假設I爲圖像,變換如下:
I_recty = H*I
需要說明一點,該函數的前兩個參數Mat& points1, Mat& points2與cv::findFundamentalMat的前兩個參數並不是相同的數據結構。它們雖然可以是同一個匹配點集,但是他們的數據結構是完全不同的!cv::findFundamentalMat中傳入的匹配點集要求是2xN或者Nx2的矩陣,但是cv::stereoRectifyUncalibrated中要求傳入的匹配點集必須是1x2N或者2Nx1的矩陣!在很多文檔中都說他們的參數是一樣的,這其實是一個天大的錯誤,如果用計算F的匹配點集直接傳給圖像矯正函數,程序將直接崩潰。正確的做法是利用cv::Mat的構造函數,直接從vector<Point2f>構造一個cv::Mat傳入。
cv::stereoRectifyUncalibrated函數默認原始圖像是沒有徑向畸變的,因此在矯正圖像之前,最好先對原始圖像做徑向矯正。
另外需要注意的一點,函數返回的單應變換矩陣H1和H2都是double類型,也即CV_64F類型,若不是該類型的矩陣,與之相乘會報錯。下面是示例代碼:
// 假設前面我們已經得到兩幅圖像的匹配特徵點,並計算出了基本矩陣F,同時得到了匹配特徵點的inlier
// Mat m_matLeftImage;
// Mat m_matRightImage;
// vector<Point2f> m_LeftInlier;
// vector<Point2f> m_RightInlier;
// Mat m_Fundamental;
// 計算圖像矯正的單應變換矩陣
Mat m_LeftH;
Mat m_RightH;
stereoRectifyUncalibrated(Mat(m_LeftInlier), Mat(m_RightInlier), m_Fundamental,
Size(m_matLeftImage.cols, m_matLeftImage.rows),
m_LeftH, m_RightH);
// 任意指定一個內參數矩陣K,不會影響計算結果,此處設爲單位陣。
Mat K = Mat::eye(3, 3, CV_64F); // 注意一定是double類型
Mat invK = K.inv(DECOMP_SVD);
Mat LeftR = invK*m_LeftH*K; // 根據單應變換矩陣計算左圖攝像機在空間中的變換矩陣R1
Mat RightR = invK*m_RightH*K; // 計算右圖攝像機在空間中的變換矩陣R2
Mat LeftMap1, LeftMap2;
Mat RightMap1, RightMap2;
Mat Distort; // 徑向畸變爲0,設爲空矩陣
Size UndistSize(m_matLeftImage.cols, m_matLeftImage.rows);
// 計算左右兩幅圖像的映射矩陣
initUndistortRectifyMap(K, Distort, LeftR, K, UndistSize, CV_32FC1, LeftMap1, LeftMap2);
initUndistortRectifyMap(K, Distort, RightR, K, UndistSize, CV_32FC1, RightMap1, RightMap2);
// 把原始圖像投影到新圖像上,得到矯正圖像
Mat m_LeftRectyImage;
Mat m_RightRectyImage;
remap(m_matLeftImage, m_LeftRectyImage, LeftMap1, LeftMap2, INTER_LINEAR);
remap(m_matRightImage, m_RightRectyImage, RightMap1, RightMap2, INTER_LINEAR);
// 顯示結果
cvNamedWindow( "left image", 1);
cvShowImage("left image", &(IplImage(m_LeftRectyImage)));
cvNamedWindow( "right image", 1);
cvShowImage("right image", &(IplImage(m_RightRectyImage)));
cvWaitKey( 0 );
cvDestroyWindow( "left image" );
cvDestroyWindow( "right image" );
程序計算的結果如下圖所示:
原始圖像對:
極線矯正後的圖像:
得到上面的矯正圖像之後,就可以計算視差或者進行稠密匹配了。下面以視差計算的應用爲例,分別用GC和SGBM算法計算視差,結果如下圖:
SGBM算法:
from: http://blog.sina.com.cn/s/blog_4298002e01013yb8.html