2013年8月22日 星期四

EmguCV Image Process: Estimating Projective Relations in Images part4


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

上一篇取出了兩張不同角度影像的特徵值

這次利用所取出的特徵值來做homography

中文在維基翻譯作:單應性

說實在的看到這個詞根本不知道到底是什麼意思

維基中解釋:

單應性是一個從實射影平面到射影平面的可逆變換,直線在該變換下仍映射為直線

而這裡的目的就是將兩張不同角度的影像

使其中一張的平面射影到另一張的平面

進而接合這兩張不同角度的影像


接續前一篇所寫的RobustMatcher

這裡的實作不需要進行RANSAC這個方法

因為這個方法會在homography中被實作

因此我們在RobustMatcher中新增一個方法Homography

所需的引數就跟RANSACTest的方法一樣

void Homography(Matrix<float> matches, VectorOfKeyPoint keyPoints1, VectorOfKeyPoint keyPoints2, int matchesNumber, Image<Gray, Byte> image1, Image<Gray, Byte> image2)
{ 

    Matrix<float> selPoints1 = new Matrix<float>(matchesNumber, 2);
    Matrix<float> selPoints2 = new Matrix<float>(matchesNumber, 2);

    int selPointsIndex = 0;
    for (int i = 0; i < matches.Rows; i++)
    {
        if (matches[i, 0] == 0 && matches[i, 1] == 0)
        {
            continue;
        }
        //Get the position of left keypoints
        float x = keyPoints1[(int)matches[i, 0]].Point.X;
        float y = keyPoints1[(int)matches[i, 0]].Point.Y;
        selPoints1[selPointsIndex, 0] = x;
        selPoints1[selPointsIndex, 1] = y;
        //Get the position of right keypoints
        x = keyPoints2[(int)matches[i, 1]].Point.X;
        y = keyPoints2[(int)matches[i, 1]].Point.Y;
        selPoints2[selPointsIndex, 0] = x;
        selPoints2[selPointsIndex, 1] = y;
        selPointsIndex++;
    }
    ...
}
前面的程式與RANSACTest一樣

透過for loop把真正需要的key points取出,存到selPoints1、selPoints1中

這裡我們試著把selPoints1與selPoints2分別畫到兩張影像上

那由於之前是直拍影像

不適合用來此主題,因此這次使用兩張橫向影像如下所示:

一張影像偏左

一張影像偏右

因為桌面剛好有這兩瓶物件,就直接利用拍攝

打上key points的結果如下:

 第一張(image1)影像的key points

第二張(image2)影像的key points

接著我們要找出這兩張影像的homography

Matrix<float> homography = new Matrix<float>(3, 3);
//Find the homography between image 1 and image 2
CvInvoke.cvFindHomography(
    selPoints2, //corresponding
    selPoints1, //points
    homography,    //homography
    HOMOGRAPHY_METHOD.RANSAC, //RANSAC method
    1,          // max distance to reprojection point
    IntPtr.Zero); //mask in null

使用的方法很簡單

先建立一個3X3的Matrix

然後把key points、homography matrix帶入、

方法設定為HOMOGRAPHY_METHOD.RANSAC、

不使用mask

結果就會回存到homography之中


這裡要注意一點就是key points帶入的順序

第一組key points是用來當作source

第二組kry points是用來當作destination

意思就是說:

將第一組key points的所屬平面影像投射到第二組key points的平面影像上

在這邊的實作也就是說

把第二張影像經過homography過後會得到與第一張影像同視角的影像

此時利用cvWarpPerspective方法

//Warp image 2 to image 1
Image<Gray, Byte> result = new Image<Gray, byte>(image1.Width*2, image1.Height);
CvInvoke.cvWarpPerspective(
    image2,
    result,
    homography,
    (int)Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR,
    new MCvScalar());
將第二張影像做homography投影後

並把結果存在result上

帶入homography matrix

設定其變形的方法為:INTER.CV_INTER_LINEAR

擴充的元素填入new MCvScalar()
(也就是黑色)

所得結果如下:
可以和原始的第二張影像對比一下

會發現視角的呈現略有不同

右側隔板被矯正拉直

由於這是利用第二張影像的key points去修正成第一張影像的視角

所以會發現這張處理後的影像的左邊留有一塊黑邊

而兩個物件的位置似乎與第一張影像雷同

因此在第一張影像套用在這張上

結果就會變成:

經過上下比對

你會發現物件的位置相當準確喔!!

只是因為視角的關係

在正中間的接縫中還是有些地方怪怪的

但你整體上看起來

在桌腳、隔板的角度等等

是不是很自然呢!?


當然你也可以手動嘗試兩張影像的接合

就會如下:

這樣比對的話,是不是就能看出其中的差異了呢!!


Homography完整程式:(result就是結果)

void Homography(Matrix<float> matches, VectorOfKeyPoint keyPoints1, VectorOfKeyPoint keyPoints2, int matchesNumber, Image<Gray, Byte> image1, Image<Gray, Byte> image2)
{ 

    Matrix<float> selPoints1 = new Matrix<float>(matchesNumber, 2);
    Matrix<float> selPoints2 = new Matrix<float>(matchesNumber, 2);

    int selPointsIndex = 0;
    for (int i = 0; i < matches.Rows; i++)
    {
        if (matches[i, 0] == 0 && matches[i, 1] == 0)
        {
            continue;
        }
        //Get the position of left keypoints
        float x = keyPoints1[(int)matches[i, 0]].Point.X;
        float y = keyPoints1[(int)matches[i, 0]].Point.Y;
        selPoints1[selPointsIndex, 0] = x;
        selPoints1[selPointsIndex, 1] = y;
        //Get the position of right keypoints
        x = keyPoints2[(int)matches[i, 1]].Point.X;
        y = keyPoints2[(int)matches[i, 1]].Point.Y;
        selPoints2[selPointsIndex, 0] = x;
        selPoints2[selPointsIndex, 1] = y;
        selPointsIndex++;
    }

    Matrix homography = new Matrix(3, 3);
    //Find the homography between image 1 and image 2
    CvInvoke.cvFindHomography(
        selPoints2, //corresponding
        selPoints1, //points
        homography,    //homography
        HOMOGRAPHY_METHOD.RANSAC, //RANSAC method
        1,          // max distance to reprojection point
        IntPtr.Zero); //mask in null
    
    //Warp image 2 to image 1
    Image result = new Image(image1.Width*2, image1.Height);
    CvInvoke.cvWarpPerspective(
        image2,
        result,
        homography,
        (int)Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR,
        new MCvScalar());

}

將上一節的RobustMatcher類別中Match的方法,最後一行

Matrix<float> fundementalMatrix = RANSACTest(symMatches, keypoints1, keypoints2, symNumber, image1, image2);

換成Homography的方法即可

Homography(symMatches, keypoints1, keypoints2, symNumber, image1, image2);

Homography方法裡面最後的Image result就會是結果


沒有留言:

張貼留言