opencv3.2——使用鼠标选择ROI进行SURF匹配

1.目的

匹配图片的时候,总有其他物体干扰着匹配成功率。所以选择需要的物体进行匹配就现得很重要。所以,本文尝试选择了使用鼠标交互的方法选择物体,并进行运算速度快而且匹配率高的SURF算法进行匹配。本程序优点:鼠标交互,重复选择物体框直到选择到最佳物体框。

2.代码实现

[cpp] view plain copy

  1. #include<opencv2/opencv.hpp>  
  2. #include<iostream>  
  3. #include"opencv2/xfeatures2d.hpp"  
  4. #include<opencv2/highgui/highgui.hpp>  
  5.   
  6. using namespace cv;  
  7. using namespace std;  
  8. using namespace cv::xfeatures2d;  
  9. using namespace cv::ml;  
  10.   
  11. #define WINDOW_NAME "【程序窗口】"  
  12.   
  13. void on_MouseHandle(int event, int x, int y, int flags, void*param);  
  14. void DrawRectangle(cv::Mat& img, cv::Rect box);  
  15.   
  16. Rect g_rectangle;  
  17. bool g_bDrawingBox = false;//是否进行绘制  
  18. RNG g_rng(12345);  
  19.   
  20. int main(int argc, char **argv)  
  21. {  
  22.     //【1】准备参数  
  23.     Mat srcImage = imread("1.jpg");  
  24.     g_rectangle = Rect(-1, -1, 0, 0);  
  25.   
  26.     //【2】设置鼠标操作回调函数  
  27.     namedWindow(WINDOW_NAME);  
  28.     setMouseCallback(WINDOW_NAME, on_MouseHandle, (void*)&srcImage);  
  29.   
  30.     while (1)  
  31.     {  
  32.         if (waitKey(10) == 27) { break; }//esc键,程序退出  
  33.         imshow(WINDOW_NAME, srcImage);  
  34.   
  35.     }  
  36.   
  37.     //ROI  
  38.     Mat imageROI = srcImage(g_rectangle);  
  39.     Mat imageROIG;  
  40.         //预备  
  41.    cvtColor ( imageROI ,imageROIG, CV_RGB2GRAY);  
  42.   
  43.     //SURF  
  44.     Mat a = imageROIG;    //读取灰度图像  
  45.     Mat b = imread("2.jpg", 0);  
  46.   
  47.     Ptr<SURF> surf;                   //创建方式和opencv2中的不一样  
  48.                                       //    Ptr<SIFT> sift;  
  49.     surf = SURF::create(900, 5, 4);       //阈值  
  50.   
  51.     BFMatcher matcher;                //匹配器  
  52.     Mat c, d;  
  53.     vector<KeyPoint> key1, key2;  
  54.     vector<DMatch> matches;  
  55.   
  56.     //结果为一个Mat矩阵,它的行数与特征点向量中元素个数是一致的。每行都是一个N维描述子的向量  
  57.     surf->detectAndCompute(a, Mat(), key1, c);      //检测关键点和匹配描述子  
  58.     surf->detectAndCompute(b, Mat(), key2, d);  
  59.   
  60.     matcher.match(c, d, matches);         // 匹配,得到匹配向量  
  61.   
  62.     sort(matches.begin(), matches.end());  // 匹配点排序  
  63.     vector< DMatch > good_matches;            // 匹配两幅图像的描述子  
  64.     int ptsPairs = min(50, (int)(matches.size() * 0.15));  
  65.     cout << ptsPairs << endl;  
  66.     for (int i = 0; i < ptsPairs; i++)       // 将匹配较好的特征点存入good_matches中  
  67.     {  
  68.         good_matches.push_back(matches[i]);  
  69.     }  
  70.     Mat outimg;  
  71.     drawMatches(                               // 绘制匹配点  
  72.         a,                                    // 原图像1  
  73.         key1,                                 // 原图像1的特征点  
  74.         b,                                    // 原图像2  
  75.         key2,                                 // 原图像2的特征点  
  76.         good_matches,                         // 原图像1的特征点匹配原图像2的特征点[matches[i]]  
  77.         outimg,                               // 输出图像具体由flags决定  
  78.         Scalar::all(-1),                    // 匹配的颜色(特征点和连线),若matchColor==Scalar::all(-1),颜色随机  
  79.         Scalar::all(-1),                    // 单个点的颜色,即未配对的特征点,若matchColor==Scalar::all(-1),颜色随机  
  80.         vector<char>(),                       // Mask决定哪些点将被画出,若为空,则画出所有匹配点  
  81.         DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);  //Fdefined by DrawMatchesFlags  
  82.   
  83.   
  84.     vector<Point2f> obj;  
  85.     vector<Point2f> scene;  
  86.   
  87.     for (size_t i = 0; i < good_matches.size(); i++)  
  88.     {  
  89.         ////good_matches[i].queryIdx保存着第一张图片匹配点的序号,keypoints_1[good_matches[i].queryIdx].pt.x 为该序号对应的点的x坐标  
  90.         obj.push_back(key1[good_matches[i].queryIdx].pt);  
  91.         scene.push_back(key2[good_matches[i].trainIdx].pt);  
  92.     }  
  93.   
  94.     vector<Point2f> scene_corners(4);  
  95.     vector<Point2f> obj_corners(4);  
  96.     obj_corners[0] = Point(0, 0);  
  97.     obj_corners[1] = Point(a.cols, 0);  
  98.     obj_corners[2] = Point(a.cols, a.rows);  
  99.     obj_corners[3] = Point(0, a.rows);  
  100.   
  101.     Mat H = findHomography(              // 在两个平面之间寻找单映射变换矩阵  
  102.         obj,                            // 在原平面上点的坐标  
  103.         scene,                          // 在目标平面上点的坐标  
  104.         4);                        // 用于计算单映射矩阵的方法  
  105.   
  106.     perspectiveTransform(                // 向量组的透视变换  
  107.         obj_corners,                    // 输入两通道或三通道的浮点数组,每一个元素是一个2D/3D 的矢量转换  
  108.         scene_corners,                  // 输出和src同样的size和type  
  109.         H);                             // 3x3 或者4x4浮点转换矩阵  
  110.   
  111.   
  112.                                         // 绘制  
  113.     line(outimg, scene_corners[0] + Point2f((float)a.cols, 0), scene_corners[1] + Point2f((float)a.cols, 0), Scalar(0, 255, 0), 2, LINE_AA);  
  114.     line(outimg, scene_corners[1] + Point2f((float)a.cols, 0), scene_corners[2] + Point2f((float)a.cols, 0), Scalar(0, 255, 0), 2, LINE_AA);  
  115.     line(outimg, scene_corners[2] + Point2f((float)a.cols, 0), scene_corners[3] + Point2f((float)a.cols, 0), Scalar(0, 255, 0), 2, LINE_AA);  
  116.     line(outimg, scene_corners[3] + Point2f((float)a.cols, 0), scene_corners[0] + Point2f((float)a.cols, 0), Scalar(0, 255, 0), 2, LINE_AA);  
  117.   
  118.     namedWindow("匹配图", 0);  
  119.     imshow("匹配图", outimg);  
  120.     waitKey(-1);  
  121.     return 0;  
  122. }  
  123.   
  124. void on_MouseHandle(int event, int x, int y, int falgs, void* param)  
  125. {  
  126.     Mat& image = *(cv::Mat*)param;  
  127.     switch (event)  
  128.     {  
  129.         //鼠标移动消息  
  130.     case EVENT_MOUSEMOVE:  
  131.     {  
  132.   
  133.         if (g_bDrawingBox) //标识符为真,则记录下长和宽到Rect型变量中  
  134.         {  
  135.             g_rectangle.width = x - g_rectangle.x;  
  136.             g_rectangle.height = y - g_rectangle.y;  
  137.         }  
  138.     }  
  139.     break;  
  140.     //左键按下信号  
  141.     case EVENT_LBUTTONDOWN:  
  142.     {  
  143.         cout << " EVENT_LBUTTONDOWN" << endl;  
  144.         g_bDrawingBox = true;  
  145.         g_rectangle = Rect(x, y, 0, 0);//记录起点  
  146.     }  
  147.     break;  
  148.     //左键抬起信号  
  149.     case EVENT_LBUTTONUP:  
  150.     {  
  151.         cout << " EVENT_LBUTTONUP" << endl;  
  152.         g_bDrawingBox = false;  
  153.         //对宽高小于0的处理  
  154.         if (g_rectangle.width < 0)  
  155.         {  
  156.             g_rectangle.x += g_rectangle.width;  
  157.             g_rectangle.width *= -1;  
  158.         }  
  159.         if (g_rectangle.height < 0)  
  160.         {  
  161.             g_rectangle.y += g_rectangle.height;  
  162.             g_rectangle.height *= -1;  
  163.   
  164.         }  
  165.   
  166.         //调用绘制函数  
  167.         DrawRectangle(image, g_rectangle);  
  168.     }  
  169.     break;  
  170.     }  
  171. }  
  172.   
  173. void DrawRectangle(cv::Mat& img, cv::Rect box)  
  174. {  
  175.     rectangle(img, box.tl(), box.br(), Scalar(0, 0, 225));  
  176.   
  177.   
  178. }  

效果图

(1)不同的物体匹配的效果图:

匹配结果显示不是同一物体,无识别框形成,识别线凌乱。

(2)同一物体,旋转拍摄得不同画面,两个画面进行匹配的效果图:

匹配成功,识别框明显,识别线整齐。

注意事项

1.图片的像素要较高。(电脑摄像头以上,电脑摄像头拍摄的图片要靠近摄像头,或者匹配的物体特征明显如文字。)

2.没有xfreatures2d模块的访客可以借鉴博主以前的博客,opencv3.2的xfeatures2d模块(nofree模块)现放置到第三方库。

3.鼠标交互可以重复选择所需识别物体框,取最后一次物体框为最终物体框。按esc键退出选择物体框,并进行识别,识别窗口按任意键退出,程序结束。

心得体会

opencv的函数库比较强大,大大减少了编写程序的复杂程度。对于初学者而言是不错的机器视觉入门的途径。

猜你喜欢

转载自blog.csdn.net/cyf15238622067/article/details/80452488