天天看點

kaze算法的圖像配準研究(2)-比對

在特征點檢測過後,完成圖像間特征點的比對是非常重要的。對于圖像配準工作而言。特征點比對的準确度是最值得關注的點,甯願少比對,也不能誤比對。

我在此圖像配準中使用的是KNN比對:

下面引用自百度百科。

K最近鄰(kNN,k-NearestNeighbor)分類算法是資料挖掘分類技術中最簡單的方法之一,所謂K最近鄰,就是k個最近的鄰居的意思,說的是每個樣本都可以用它最接近的k個鄰居來代表。
kaze算法的圖像配準研究(2)-比對

如上圖所示,如果k=5,那麼對于未知點 xu ,離它最近的5個點有4個屬于 w1 ,1個屬于 w3 ,那麼我們可以認為這個未知點 xu 是屬于 w1 的。

在特征點比對的場景中,Lowe設定的比對規則是1NN,即尋找特征向量離得最近的點進行比對。但是這樣會産生誤比對,是以它又定制了一個規則及1NN/2NN < ratio時,該比對才生效。Lowe推薦的ratio為0.8。

得到比對的一系列點後,接下來就是要産生兩圖像的對應仿射矩陣了。這裡采用的是RANSAC法。

最終圖像比對源碼為:

#include<iostream>

//#include <opencv2/xfeatures2d.hpp>
#include <opencv2/opencv.hpp>


using namespace std;
using namespace cv;

void main()
{
    double start, duration_ms;
    cv::Mat mask1, mask2, imgshowcanny;
    cv::Mat imgShow1, imgShow2, imgShow3;
    // 聲明并從data檔案夾裡讀取兩個rgb與深度圖
    cv::Mat rgb1 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\1.jpg", );
    cv::Mat rgb2 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\2.jpg", );
    cv::Mat rgb3 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\3.jpg", );
    cv::Mat rgb4 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\4.jpg", );
    cv::Mat rgb5 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\5.jpg", );
    cv::Mat rgb6 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\6.jpg", );
    cv::Mat rgb7 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\7.jpg", );
    cv::Mat rgb8 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\8.jpg", );
    cv::Mat rgb9 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\9.jpg", );
    cv::Mat rgb10 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\10.jpg", );
    vector< cv::KeyPoint > kp1, kp2, kp3, kp4, kp5, kp6, kp7, kp8, kp9, kp10; //關鍵點
    mask1 = rgb1.clone();
    //邊緣檢測
    //cout << "Canny edge detection" << endl;
    //mask1 = Mat::zeros(rgb1.size(), CV_8UC1);
    //mask2 = Mat::zeros(rgb2.size(), CV_8UC1);
    cv::Canny(mask1, mask1, , , );
    //cv::Canny(rgb2, mask2, 50, 150, 3);
    cv::Mat element = getStructuringElement(MORPH_RECT, Size(, ));
    dilate(mask1, mask1, element);
    cv::imwrite("F:/study work/Visual Odometry dev/VO_practice_2016.4/算法測試/KAZE算法/data/mask1.jpg", mask1);
    imgshowcanny = mask1.clone();
    cv::resize(imgshowcanny, imgshowcanny, cv::Size(imgshowcanny.cols / , imgshowcanny.rows / ));
    cv::imshow("mask", imgshowcanny);


    //特征檢測算法:AKAZE
    cv::Ptr<cv::AKAZE> akaze = AKAZE::create(AKAZE::DESCRIPTOR_MLDB, , , , , , KAZE::DIFF_PM_G2);
    Mat descriptors_1, descriptors_2, descriptors_3, descriptors_4, descriptors_5, descriptors_6, descriptors_7, descriptors_8, descriptors_9, descriptors_10;


    start = double(getTickCount());
    akaze->detectAndCompute(rgb1, mask1, kp1, descriptors_1, false);
    duration_ms = (double(getTickCount()) - start) *  / getTickFrequency();//計時
    std::cout << "It took " << duration_ms << " ms to detect features in pic1 using AKAZE." << std::endl;

    start = double(getTickCount());
    akaze->detectAndCompute(rgb2, cv::Mat(), kp2, descriptors_2, false);
    duration_ms = (double(getTickCount()) - start) *  / getTickFrequency();//計時
    std::cout << "It took " << duration_ms << " ms to detect features in pic2 using AKAZE." << std::endl;

    akaze->detectAndCompute(rgb3, cv::Mat(), kp3, descriptors_3, false);
    akaze->detectAndCompute(rgb4, cv::Mat(), kp4, descriptors_4, false);
    akaze->detectAndCompute(rgb5, cv::Mat(), kp5, descriptors_5, false);
    akaze->detectAndCompute(rgb6, cv::Mat(), kp6, descriptors_6, false);
    akaze->detectAndCompute(rgb7, cv::Mat(), kp7, descriptors_7, false);
    akaze->detectAndCompute(rgb8, cv::Mat(), kp8, descriptors_8, false);
    akaze->detectAndCompute(rgb9, cv::Mat(), kp9, descriptors_9, false);
    akaze->detectAndCompute(rgb10, cv::Mat(), kp10, descriptors_10, false);
    // 可視化, 顯示特征點
    cout << "Key points of two AKAZE images: " << kp1.size() << ", " << kp2.size() << endl;
    cv::drawKeypoints(rgb1, kp1, imgShow1, cv::Scalar::all(-), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    cv::drawKeypoints(rgb2, kp2, imgShow2, cv::Scalar::all(-), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    cv::drawKeypoints(rgb3, kp3, imgShow3, cv::Scalar::all(-), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    cv::imwrite("F:/study work/Visual Odometry dev/VO_practice_2016.4/算法測試/KAZE算法/data/akaze1.jpg", imgShow1);
    cv::imwrite("F:/study work/Visual Odometry dev/VO_practice_2016.4/算法測試/KAZE算法/data/akaze2.jpg", imgShow2);
    cv::resize(imgShow2, imgShow2, cv::Size(imgShow2.cols / , imgShow2.rows / ));
    cv::resize(imgShow1, imgShow1, cv::Size(imgShow1.cols / , imgShow1.rows / ));
    cv::imshow("AKAZE_keypoints2", imgShow2);
    cv::imshow("AKAZE_keypoints1", imgShow1);
    cv::waitKey(); //暫停等待一個按鍵

                    //用Matcher比對特征 :
    BFMatcher bf_matcher;
    FlannBasedMatcher flann_matcher;
    cv::Ptr<cv::DescriptorMatcher>  knn_matcher = cv::DescriptorMatcher::create("BruteForce-Hamming");
    std::vector< DMatch > flann_matches;
    std::vector< DMatch > bf_matches1;
    std::vector< DMatch > knn_match;
    std::vector< std::vector<cv::DMatch> > knn_matches1, knn_matches2, knn_matches3, knn_matches4, knn_matches5, knn_matches6, knn_matches7, knn_matches8, knn_matches9;

    start = double(getTickCount());
    bf_matcher.match(descriptors_1, descriptors_2, bf_matches1);
    //flann_matcher.match(descriptors_1, descriptors_2, flann_matches);
    duration_ms = (double(getTickCount()) - start) *  / getTickFrequency();//計時
    std::cout << "It took " << duration_ms << " ms to do brutal-force-match on AKAZE features." << std::endl;

    start = double(getTickCount());
    knn_matcher->knnMatch(descriptors_1, descriptors_2, knn_matches1, );
    duration_ms = (double(getTickCount()) - start) *  / getTickFrequency();//計時
    std::cout << "It took " << duration_ms << " ms to do knn-match on AKAZE features." << std::endl;

    knn_matcher->knnMatch(descriptors_1, descriptors_3, knn_matches2, );
    knn_matcher->knnMatch(descriptors_1, descriptors_4, knn_matches3, );
    knn_matcher->knnMatch(descriptors_1, descriptors_5, knn_matches4, );
    knn_matcher->knnMatch(descriptors_1, descriptors_6, knn_matches5, );
    knn_matcher->knnMatch(descriptors_1, descriptors_7, knn_matches6, );
    knn_matcher->knnMatch(descriptors_1, descriptors_8, knn_matches7, );
    knn_matcher->knnMatch(descriptors_1, descriptors_9, knn_matches8, );
    knn_matcher->knnMatch(descriptors_1, descriptors_10, knn_matches9, );

    //用RANSAC求取單應矩陣的方式去掉不可靠的BF比對
    vector< DMatch > inliers1, inliers2;
    cout << "Computing homography (RANSAC)" << endl;
    vector<Point2f> points1(bf_matches1.size());
    vector<Point2f> points2(bf_matches1.size());
    for (size_t i = ; i < bf_matches1.size(); i++)
    {
        points1[i] = kp1[bf_matches1[i].queryIdx].pt;
        points2[i] = kp2[bf_matches1[i].trainIdx].pt;
    }
    /*
    for (int i = 0; i < points1.size(); i++) {
    cout << "points1_x :" << points1[i].x << endl;
    cout << "points1_y :" << points1[i].y << endl;
    }
    */
    //計算單應矩陣并找出inliers  
    vector<uchar> flag1(points1.size(), );
    Mat H1 = findHomography(points1, points2, CV_RANSAC, , flag1);
    Mat H2 = findHomography(points2, points1, CV_RANSAC, , flag1);
    cout << "points1_shape: " << points1.size() << endl;
    cout << "points2_shape: " << points2.size() << endl;
    cout << "H1_shape: " << H2.size() << endl;

    /*for (int i = 0; i < H1.rows; i++) {
    for (int j = 0; j < H1.cols; j++) {
    cout << "H1_value" << H1[i][i] << endl;
    }
    }*/
    //std::cout << H2 << std::endl;

    //cout << H << endl << endl;  
    for (int i = ; i < bf_matches1.size(); i++)
    {
        if (flag1[i])
        {
            inliers1.push_back(bf_matches1[i]);
        }
    }
    cout << "AKAZE BF matches inliers size = " << inliers1.size() << endl;

    //去掉不可靠的HAMMING-KNN比對
    vector< cv::DMatch > goodMatches1;
    for (size_t i = ; i < knn_matches1.size(); i++)
    {
        if (knn_matches1[i][].distance <   *
            knn_matches1[i][].distance)
            goodMatches1.push_back(knn_matches1[i][]);
    }

    vector< cv::DMatch > goodMatches2;
    for (size_t i = ; i < knn_matches2.size(); i++)
    {
        if (knn_matches2[i][].distance <   *
            knn_matches2[i][].distance)
            goodMatches2.push_back(knn_matches2[i][]);
    }
    vector< cv::DMatch > goodMatches3;
    for (size_t i = ; i < knn_matches3.size(); i++)
    {
        if (knn_matches3[i][].distance <   *
            knn_matches3[i][].distance)
            goodMatches3.push_back(knn_matches3[i][]);
    }

    vector< cv::DMatch > goodMatches4;
    for (size_t i = ; i < knn_matches4.size(); i++)
    {
        if (knn_matches4[i][].distance <   *
            knn_matches4[i][].distance)
            goodMatches4.push_back(knn_matches4[i][]);
    }
    vector< cv::DMatch > goodMatches5;
    for (size_t i = ; i < knn_matches5.size(); i++)
    {
        if (knn_matches5[i][].distance <   *
            knn_matches5[i][].distance)
            goodMatches5.push_back(knn_matches5[i][]);
    }
    vector< cv::DMatch > goodMatches6;
    for (size_t i = ; i < knn_matches6.size(); i++)
    {
        if (knn_matches6[i][].distance <   *
            knn_matches6[i][].distance)
            goodMatches6.push_back(knn_matches6[i][]);
    }
    vector< cv::DMatch > goodMatches7;
    for (size_t i = ; i < knn_matches7.size(); i++)
    {
        if (knn_matches7[i][].distance <   *
            knn_matches7[i][].distance)
            goodMatches7.push_back(knn_matches7[i][]);
    }
    vector< cv::DMatch > goodMatches8;
    for (size_t i = ; i < knn_matches8.size(); i++)
    {
        if (knn_matches8[i][].distance <   *
            knn_matches8[i][].distance)
            goodMatches8.push_back(knn_matches8[i][]);
    }
    vector< cv::DMatch > goodMatches9;
    for (size_t i = ; i < knn_matches9.size(); i++)
    {
        if (knn_matches9[i][].distance <   *
            knn_matches9[i][].distance)
            goodMatches9.push_back(knn_matches9[i][]);
    }

    cout << "AKAZE good knn matches size = " << goodMatches1.size() << endl;
    cout << "I am before count point" << endl;
    vector<Point2f> points3(goodMatches1.size());
    vector<Point2f> points4(goodMatches1.size());
    for (size_t i = ; i < goodMatches1.size(); i++)
    {
        points3[i] = kp1[goodMatches1[i].queryIdx].pt;
        points4[i] = kp2[goodMatches1[i].trainIdx].pt;
    }
    cout << "I am before count point 1" << endl;
    vector<Point2f> points5(goodMatches2.size());
    vector<Point2f> points6(goodMatches2.size());
    for (size_t i = ; i < goodMatches2.size(); i++)
    {
        points5[i] = kp1[goodMatches2[i].queryIdx].pt;
        points6[i] = kp3[goodMatches2[i].trainIdx].pt;
    }
    cout << "I am before count point 2" << endl;
    vector<Point2f> points7(goodMatches3.size());
    vector<Point2f> points8(goodMatches3.size());
    cout << "goodMatchers3.size" << goodMatches3.size() << endl;

    for (size_t i = ; i < goodMatches3.size(); i++)
    {
        points7[i] = kp1[goodMatches3[i].queryIdx].pt;
        points8[i] = kp4[goodMatches3[i].trainIdx].pt;
    }

    cout << "I am before count point 3" << endl;
    vector<Point2f> points9(goodMatches4.size());
    vector<Point2f> points10(goodMatches4.size());
    for (size_t i = ; i < goodMatches4.size(); i++)
    {
        points9[i] = kp1[goodMatches4[i].queryIdx].pt;
        points10[i] = kp5[goodMatches4[i].trainIdx].pt;
    }
    cout << "I am before count point 4" << endl;
    vector<Point2f> points11(goodMatches5.size());
    vector<Point2f> points12(goodMatches5.size());
    for (size_t i = ; i < goodMatches5.size(); i++)
    {
        points11[i] = kp1[goodMatches5[i].queryIdx].pt;
        points12[i] = kp6[goodMatches5[i].trainIdx].pt;
    }

    vector<Point2f> points13(goodMatches6.size());
    vector<Point2f> points14(goodMatches6.size());
    for (size_t i = ; i < goodMatches6.size(); i++)
    {
        points13[i] = kp1[goodMatches6[i].queryIdx].pt;
        points14[i] = kp7[goodMatches6[i].trainIdx].pt;
    }
    vector<Point2f> points15(goodMatches7.size());
    vector<Point2f> points16(goodMatches7.size());
    for (size_t i = ; i < goodMatches7.size(); i++)
    {
        points15[i] = kp1[goodMatches7[i].queryIdx].pt;
        points16[i] = kp8[goodMatches7[i].trainIdx].pt;
    }
    cout << "I am before count point 4" << endl;
    vector<Point2f> points17(goodMatches8.size());
    vector<Point2f> points18(goodMatches8.size());
    for (size_t i = ; i < goodMatches8.size(); i++)
    {
        points17[i] = kp1[goodMatches8[i].queryIdx].pt;
        points18[i] = kp9[goodMatches8[i].trainIdx].pt;
    }

    vector<Point2f> points19(goodMatches9.size());
    vector<Point2f> points20(goodMatches9.size());
    for (size_t i = ; i < goodMatches9.size(); i++)
    {
        points19[i] = kp1[goodMatches9[i].queryIdx].pt;
        points20[i] = kp10[goodMatches9[i].trainIdx].pt;
    }
    cout << "I am after count point" << endl;

    Mat H3 = findHomography(points4, points3, CV_RANSAC, , flag1);
    Mat H4 = findHomography(points6, points5, CV_RANSAC, , flag1);
    Mat H5 = findHomography(points8, points7, CV_RANSAC, , flag1);
    Mat H6 = findHomography(points10, points9, CV_RANSAC, , flag1);
    Mat H7 = findHomography(points12, points11, CV_RANSAC, , flag1);
    Mat H8 = findHomography(points14, points13, CV_RANSAC, , flag1);
    Mat H9 = findHomography(points16, points15, CV_RANSAC, , flag1);
    Mat H10 = findHomography(points18, points17, CV_RANSAC, , flag1);
    Mat H11 = findHomography(points20, points19, CV_RANSAC, , flag1);

    cv::Mat imdst_2, imdst_3, imdst_4, imdst_5, imdst_6, imdst_7, imdst_8, imdst_9, imdst_10, imsrc_1, imsrc_2, imsrc_3, imsrc_4, imsrc_5, imsrc_6, imsrc_7, imsrc_8, imsrc_9, imsrc_10, warp_dst, imresult;
    imsrc_1 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\1.jpg");
    imsrc_2 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\2.jpg");
    imsrc_3 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\3.jpg");
    imsrc_4 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\4.jpg");
    imsrc_5 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\5.jpg");
    imsrc_6 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\6.jpg");
    imsrc_7 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\7.jpg");
    imsrc_8 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\8.jpg");
    imsrc_9 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\9.jpg");
    imsrc_10 = cv::imread("C:\\opencv-cpp\\opencvtest\\img_source\\1\\10.jpg");
    //warpAffine(im_src, warp_dst, H2, im_src.size());
    warpPerspective(imsrc_2, imdst_2, H3, imsrc_2.size());
    warpPerspective(imsrc_3, imdst_3, H4, imsrc_3.size());
    warpPerspective(imsrc_4, imdst_4, H5, imsrc_4.size());
    warpPerspective(imsrc_5, imdst_5, H6, imsrc_5.size());
    warpPerspective(imsrc_6, imdst_6, H7, imsrc_6.size());
    warpPerspective(imsrc_7, imdst_7, H8, imsrc_7.size());
    warpPerspective(imsrc_8, imdst_8, H9, imsrc_8.size());
    warpPerspective(imsrc_9, imdst_9, H10, imsrc_9.size());
    warpPerspective(imsrc_10, imdst_10, H11, imsrc_10.size());

    cv::Mat imsrc_1_, imdst_2_, imdst_3_, imdst_4_, imdst_5_, imdst_6_, imdst_7_, imdst_8_, imdst_9_, imdst_10_;
    imsrc_1.convertTo(imsrc_1_, CV_32F,  / );
    imdst_2.convertTo(imdst_2_, CV_32F,  / );
    imdst_3.convertTo(imdst_3_, CV_32F,  / );
    imdst_4.convertTo(imdst_4_, CV_32F,  / );
    imdst_5.convertTo(imdst_5_, CV_32F,  / );
    imdst_6.convertTo(imdst_6_, CV_32F,  / );
    imdst_7.convertTo(imdst_7_, CV_32F,  / );
    imdst_8.convertTo(imdst_8_, CV_32F,  / );
    imdst_9.convertTo(imdst_9_, CV_32F,  / );
    imdst_10.convertTo(imdst_10_, CV_32F,  / );
    imresult = (imsrc_1_ + imdst_2_ + imdst_3_ + imdst_4_ + imdst_5_ + imdst_6_ + imdst_7_ + imdst_8_ + imdst_9_ + imdst_10_) / ;
    cv::imshow("result", imresult);
    cv::imwrite("D:\\opencv_cpp\\opencv_cpp\\source_images\\after_transactions\\2_after_transaction.jpg", imdst_2);
    cv::imwrite("D:\\opencv_cpp\\opencv_cpp\\source_images\\after_transactions\\3_after_transaction.jpg", imdst_3);
    // 可視化:顯示比對的特征
    cv::Mat AKAZE_BF_imgMatches;
    cv::Mat AKAZE_KNN_imgMatches;

     篩選比對,把距離太大的去掉
     這裡使用的準則是去掉大于四倍最小距離的比對
    //vector< cv::DMatch > goodMatches;
    //double minDis = 9999;
    //for (size_t i = 0; i<matches.size(); i++)
    //{
    //  if (matches[i].distance < minDis)
    //      minDis = matches[i].distance;
    //}

    //for (size_t i = 0; i<matches.size(); i++)
    //{
    //  if (matches[i].distance < 8 * minDis)
    //      goodMatches.push_back(matches[i]);
    //}

    // 顯示 good matches
    cv::drawMatches(rgb1, kp1, rgb2, kp2, inliers1, AKAZE_BF_imgMatches);
    cv::imwrite("F:/study work/Visual Odometry dev/VO_practice_2016.4/算法測試/KAZE算法/data/AKAZE-BF-Matches-inliers.jpg", AKAZE_BF_imgMatches);
    cv::drawMatches(rgb1, kp1, rgb2, kp2, goodMatches1, AKAZE_KNN_imgMatches);
    cv::imwrite("F:/study work/Visual Odometry dev/VO_practice_2016.4/算法測試/KAZE算法/data/AKAZE-Good-KNN-Matches.jpg", AKAZE_KNN_imgMatches);
    cv::resize(AKAZE_BF_imgMatches, AKAZE_BF_imgMatches, 
Size(AKAZE_BF_imgMatches.cols / , AKAZE_BF_imgMatches.rows / ));
    cv::imshow("AKAZE-BF-Matches-inliers", AKAZE_BF_imgMatches);
    cv::resize(AKAZE_KNN_imgMatches, AKAZE_KNN_imgMatches, Size(AKAZE_KNN_imgMatches.cols / , AKAZE_KNN_imgMatches.rows / ));
    cv::imshow("AKAZE-Good-KNN-Matches", AKAZE_KNN_imgMatches);
    cv::waitKey();
}

           

代碼中選用了10張圖檔進行配準。最後結果為:

繼續閱讀