zoukankan      html  css  js  c++  java
  • OpenCV 常用图像拼接方法(三):基于特征匹配拼接

        OpenCV常用图像拼接方法将分为四个部分与大家共享,这里是第三种方法,欢迎关注后续。

        OpenCV的常用图像拼接方法(三):基于特征匹配的图像拼接,本次介绍SIFT特征匹配拼接方法,OpenCV版本为4.4.0。特点和适用范围:图像有足够重合相同特征区域,且待拼接图像之间无明显尺度变换和畸变。

    优点:适应部分倾斜变化情况。缺点:需要有足够的相同特征区域进行匹配,速度较慢,拼接较大图片容易崩溃。

        如下是待拼接的两张图片:

     

    特征匹配图:

     拼接结果图:

     拼接缝处理后(拼接处过渡更自然):

    核心代码:

      1 /********************直接图像拼接函数*************************/
      2 bool ImageOverlap0(Mat &img1, Mat &img2)
      3 {
      4   Mat g1(img1, Rect(0, 0, img1.cols, img1.rows));  // init roi 
      5   Mat g2(img2, Rect(0, 0, img2.cols, img2.rows));
      6  
      7   cvtColor(g1, g1, COLOR_BGR2GRAY);
      8   cvtColor(g2, g2, COLOR_BGR2GRAY);
      9  
     10   vector<cv::KeyPoint> keypoints_roi, keypoints_img;  /* keypoints found using SIFT */
     11   Mat descriptor_roi, descriptor_img;                           /* Descriptors for SIFT */
     12   FlannBasedMatcher matcher;                                   /* FLANN based matcher to match keypoints */
     13  
     14   vector<cv::DMatch> matches, good_matches;
     15   cv::Ptr<cv::SIFT> sift = cv::SIFT::create();
     16   int i, dist = 80;
     17  
     18   sift->detectAndCompute(g1, cv::Mat(), keypoints_roi, descriptor_roi);      /* get keypoints of ROI image */
     19   sift->detectAndCompute(g2, cv::Mat(), keypoints_img, descriptor_img);         /* get keypoints of the image */
     20   matcher.match(descriptor_roi, descriptor_img, matches);  //实现描述符之间的匹配
     21  
     22   double max_dist = 0; double min_dist = 5000;
     23   //-- Quick calculation of max and min distances between keypoints 
     24   for (int i = 0; i < descriptor_roi.rows; i++)
     25   {
     26     double dist = matches[i].distance;
     27     if (dist < min_dist) min_dist = dist;
     28     if (dist > max_dist) max_dist = dist;
     29   }
     30   // 特征点筛选
     31   for (i = 0; i < descriptor_roi.rows; i++)
     32   {
     33     if (matches[i].distance < 3 * min_dist)
     34     {
     35       good_matches.push_back(matches[i]);
     36     }
     37   }
     38  
     39   printf("%ld no. of matched keypoints in right image
    ", good_matches.size());
     40   /* Draw matched keypoints */
     41  
     42   Mat img_matches;
     43   //绘制匹配
     44   drawMatches(img1, keypoints_roi, img2, keypoints_img,
     45     good_matches, img_matches, Scalar::all(-1),
     46     Scalar::all(-1), vector<char>(),
     47     DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
     48   imshow("matches", img_matches);
     49  
     50   vector<Point2f> keypoints1, keypoints2;
     51   for (i = 0; i < good_matches.size(); i++)
     52   {
     53     keypoints1.push_back(keypoints_img[good_matches[i].trainIdx].pt);
     54     keypoints2.push_back(keypoints_roi[good_matches[i].queryIdx].pt);
     55   }
     56   //计算单应矩阵(仿射变换矩阵) 
     57   Mat H = findHomography(keypoints1, keypoints2, RANSAC);
     58   Mat H2 = findHomography(keypoints2, keypoints1, RANSAC);
     59  
     60  
     61   Mat stitchedImage;  //定义仿射变换后的图像(也是拼接结果图像)
     62   Mat stitchedImage2;  //定义仿射变换后的图像(也是拼接结果图像)
     63   int mRows = img2.rows;
     64   if (img1.rows > img2.rows)
     65   {
     66     mRows = img1.rows;
     67   }
     68  
     69   int count = 0;
     70   for (int i = 0; i < keypoints2.size(); i++)
     71   {
     72     if (keypoints2[i].x >= img2.cols / 2)
     73       count++;
     74   }
     75   //判断匹配点位置来决定图片是左还是右
     76   if (count / float(keypoints2.size()) >= 0.5)  //待拼接img2图像在右边
     77   {
     78     cout << "img1 should be left" << endl;
     79     vector<Point2f>corners(4);
     80     vector<Point2f>corners2(4);
     81     corners[0] = Point(0, 0);
     82     corners[1] = Point(0, img2.rows);
     83     corners[2] = Point(img2.cols, img2.rows);
     84     corners[3] = Point(img2.cols, 0);
     85     stitchedImage = Mat::zeros(img2.cols + img1.cols, mRows, CV_8UC3);
     86     warpPerspective(img2, stitchedImage, H, Size(img2.cols + img1.cols, mRows));
     87  
     88     perspectiveTransform(corners, corners2, H);
     89     /*
     90     circle(stitchedImage, corners2[0], 5, Scalar(0, 255, 0), 2, 8);
     91     circle(stitchedImage, corners2[1], 5, Scalar(0, 255, 255), 2, 8);
     92     circle(stitchedImage, corners2[2], 5, Scalar(0, 255, 0), 2, 8);
     93     circle(stitchedImage, corners2[3], 5, Scalar(0, 255, 0), 2, 8); */
     94     cout << corners2[0].x << ", " << corners2[0].y << endl;
     95     cout << corners2[1].x << ", " << corners2[1].y << endl;
     96     imshow("temp", stitchedImage);
     97     //imwrite("temp.jpg", stitchedImage);
     98  
     99     Mat half(stitchedImage, Rect(0, 0, img1.cols, img1.rows));
    100     img1.copyTo(half);
    101     imshow("result", stitchedImage);
    102   }
    103   else  //待拼接图像img2在左边
    104   {
    105     cout << "img2 should be left" << endl;
    106     stitchedImage = Mat::zeros(img2.cols + img1.cols, mRows, CV_8UC3);
    107     warpPerspective(img1, stitchedImage, H2, Size(img1.cols + img2.cols, mRows));
    108     imshow("temp", stitchedImage);
    109  
    110     //计算仿射变换后的四个端点
    111     vector<Point2f>corners(4);
    112     vector<Point2f>corners2(4);
    113     corners[0] = Point(0, 0);
    114     corners[1] = Point(0, img1.rows);
    115     corners[2] = Point(img1.cols, img1.rows);
    116     corners[3] = Point(img1.cols, 0);
    117  
    118     perspectiveTransform(corners, corners2, H2);  //仿射变换对应端点
    119     /*
    120     circle(stitchedImage, corners2[0], 5, Scalar(0, 255, 0), 2, 8);
    121     circle(stitchedImage, corners2[1], 5, Scalar(0, 255, 255), 2, 8);
    122     circle(stitchedImage, corners2[2], 5, Scalar(0, 255, 0), 2, 8);
    123     circle(stitchedImage, corners2[3], 5, Scalar(0, 255, 0), 2, 8); */
    124     cout << corners2[0].x << ", " << corners2[0].y << endl;
    125     cout << corners2[1].x << ", " << corners2[1].y << endl;
    126  
    127     Mat half(stitchedImage, Rect(0, 0, img2.cols, img2.rows));
    128     img2.copyTo(half);
    129     imshow("result", stitchedImage);
    130  
    131   }
    132   imwrite("result.bmp", stitchedImage);
    133   return true;
    134 }
  • 相关阅读:
    vue项目 axios封装第二弹
    封装axios
    css基于文件格式使用不同的样式
    vue使用过程中的一些小技巧
    element-ui中单独引入Message组件的问题
    vue中axios复用封装
    OTA“多角恋”:携程闪电入股同程、途牛
    OTA(Online Travel Agent)
    网络时代
    互联网技术
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/14182696.html
Copyright © 2011-2022 走看看