2013年7月3日 星期三

EmguCV Image Process: Estimating Projective Relations in Images part2


EmguCV Image Process: Estimating Projective Relations in Images

參考OpenCV 2 Computer Vision Application Programming Cookbook第九章

介紹內容如下:

Calibrating a camera

Computing the fundamental matrix of an image pair

Matching images using random sample consensus

Computing a homography between two images

前一篇介紹透過chessboard來做影像的矯正(calibrate)

這裡要透過兩張不同角度,或是不同攝影機拍攝的兩張同主題的影像

透過數學的計算求出fundamental matrix

再利用fundamental matrix畫出epipolar line

這之間的原理相當複雜

有興趣的朋友可以參考這篇寫得非常詳細

簡單來說

就是利用一個fundamental matrix的運算

就可以把第一張影像的某個點,映射到第二張影像的對應點上


而影像上的特徵點在上一個章節有介紹過

這次一樣利用SURF擷取所要的特徵點

並將特徵點做一個轉換

Image<Gray, Byte> Image1 = new Image<Gray, byte>("image1.jpg");
Image<Gray, Byte> Image2 = new Image<Gray, byte>("image2.jpg");
//Construct the SURF feature detector object
SURFDetector surf = new SURFDetector(
    5000,   //threshold
    false); //extended descriptors
//Detect the SURF features
VectorOfKeyPoint image1Keypoints = surf.DetectKeyPointsRaw(Image1, null);
VectorOfKeyPoint image2Keypoints = surf.DetectKeyPointsRaw(Image2, null);

//Convert keypoints into matrix
int count = image2Keypoints.Size;
Matrix<float> selPoints1 = ConvertToMatrix(image1Keypoints, count);
Matrix<float> selPoints2 = ConvertToMatrix(image2Keypoints, count);
取得的特徵點後

透過ConvertToMatrix這麼方法把VectorOfKeyPoint轉成Matrix<float>

方法如下:

Matrix<float> ConvertToMatrix(VectorOfKeyPoint keypoints, int count)
{
    Matrix<float> result = new Matrix<float>(count, 2);

    for (int i = 0; i < count; i++)
    {
        float x = keypoints[i].Point.X;
        float y = keypoints[i].Point.Y;
        result[i, 0] = x;
        result[i, 1] = y;
    }
    return result;
}
這裡要注意的是

兩張影像取出的特徵點若是不一樣多

之後帶入求fundamental matrix會有問題

因此ConvertToMatrix方法帶入的count必須一致

所以先判斷是第一張影像的特徵點多,還是第二張的特徵點多

並以少的特徵點的數量作為基準

Matrix<float> fundamentalMatrix = new Matrix<float>(3, 3);
IntPtr status = CvInvoke.cvCreateMat(1, count, MAT_DEPTH.CV_8U);

//Compute F matrix from 7 matches
CvInvoke.cvFindFundamentalMat(
    selPoints1, //points in first image
    selPoints2, //points in second image
    fundamentalMatrix,  //fundamental matrix 
    CV_FM.CV_FM_7POINT, //7-point method
    3.0,  //Use 3.0 for default. The parameter is used for RANSAC method only.
    0.99, //Use 0.99 for default. The parameter is used for RANSAC or LMedS methods only. 
    status);//The array is computed only in RANSAC and LMedS methods.

//lines in right image
Matrix<float> lines = new Matrix<float>(count, 3);
CvInvoke.cvComputeCorrespondEpilines(
    selPoints1,  //image points 
    1,           //in image 1      
    fundamentalMatrix, //fundamental matrix
    lines);      //epipolar lines
帶入CvInvoke.cvFindFundamentalMat方法求出fundamental matrix

這裡注意是帶入第一張影像的特徵點

最後兩個參數就以預設值3.0以及0.99帶入

最後再透過CvInvoke.cvComputeCorrespondEpilines

取得epipolar line

這裡的lines是一個N*3的矩陣,N表示特徵點的數量

接著再把epipolar line畫在第二張的影像上

//draw the left points corresponding epipolar for all epipolar lines
for (int i = 0; i < count; i++)
{ 
    //draw the line between first and last column
    Point p1 = new Point(0, -(int)(lines[i,2]/lines[i,1]));
    Point p2 = new Point(Image2.Width, -(int)((lines[i,2]+lines[i,0]*Image2.Width)/lines[i,1]));
    LineSegment2D line = new LineSegment2D(p1, p2);
    Image2.Draw(line, new Gray(255), 1);
}
利用一個for迴圈把所有的line畫上去

這裡epipolar line的點的算法要注意一下

計算方式非常特別

畫完的結果如下:

epipolar lines

那其實計算出來的結果應該是不太準

因為這兩張影像本是同一張影像

所以還是要挑選一下兩張影像的狀況會比較適合


下一篇將介紹如何利用random sample consensus來做影像特徵點配對~!

沒有留言:

張貼留言