OpenCV之图像特征提取与检测(十六) 平面对象识别

对象形变与位置变换
    Mat findHomography( // 发现两个平面的透视变换,生成变换矩阵。即根据srcPoints的顶点数据与dstPoints的顶点数据,返回有srcPoints变换到dstPoints的变换矩阵
        InputArray srcPoints, // srcPoints 与 dstPoints 应该是同尺寸
        InputArray dstPoints, 
        int method = 0, // 发现变换矩阵的算法 RANSAC
        double ransacReprojThreshold = 3, 
        OutputArray mask=noArray(), 
        const int maxIters = 2000, 
        const double confidence = 0.995
    );
    void perspectiveTransform( // 透视变换
        InputArray src, 
        OutputArray dst, 
        InputArray m // 变换矩阵
    );

代码

    #include "../common/common.hpp"

    using namespace cv::xfeatures2d;

    void main(int argc, char** argv)
    {
        Mat img1 = imread(getCVImagesPath("images/box.png"), IMREAD_GRAYSCALE);
        Mat img2 = imread(getCVImagesPath("images/box_in_scene.png"), IMREAD_GRAYSCALE);
        imshow("object image", img1);
        imshow("object in scene", img2);

        // surf featurs extraction
        int minHessian = 400;
        Ptr<SURF> detector = SURF::create(minHessian); // 也可以用 SIFT 特征
        vector<KeyPoint> keypoints_obj; // 保存特征点
        vector<KeyPoint> keypoints_scene;
        Mat descriptor_obj, descriptor_scene; // 特征描述子
        detector->detectAndCompute(img1, Mat(), keypoints_obj, descriptor_obj, false); // SURF特征检测,同时计算生成对应描述子
        detector->detectAndCompute(img2, Mat(), keypoints_scene, descriptor_scene, false);
        cout << "keypoints_obj.size=" << keypoints_obj.size() << endl; // keypoints_obj.size=786
        cout << "keypoints_scene.size=" << keypoints_scene.size() << endl; // keypoints_scene.size = 1040
        // descriptor_obj depth=5, type=5, size=[64 x 786]   CV_32F单通道   每个关键点用64来描述?
        cout << "descriptor_obj depth=" << descriptor_obj.depth() << ", type=" << descriptor_obj.type() << ", size=" << descriptor_obj.size() << endl;
        // descriptor_scene depth = 5, type = 5, size = [64 x 1040]
        cout << "descriptor_scene depth=" << descriptor_scene.depth() << ", type=" << descriptor_scene.type() << ", size=" << descriptor_scene.size() << endl;

        // matching
        FlannBasedMatcher matcher; // Flann 匹配类
        vector<DMatch> matches; // 保存匹配结果
        // 在descriptor_scene中匹配descriptor_obj的特征描述子,结果放到matches中, matches的长度与descriptor_obj的行数一致
        matcher.match(descriptor_obj, descriptor_scene, matches); // descriptor_obj中的特征描述子都会在descriptor_scene找到一个匹配点(不管是否真的准确)
        //descriptor_obj rows=786, cols=64
        cout << "descriptor_obj rows=" << descriptor_obj.rows << ", cols=" << descriptor_obj.cols << endl;
        cout << "matches.size=" << matches.size() << endl; // matches.size = 786

        // find good matched points
        double minDist = 1000;
        double maxDist = 0;
        for (int i = 0; i < descriptor_obj.rows; i++) {
            cout << "matches[" << i << "].queryIdx=" << matches[i].queryIdx << ", "; // matches[i].queryIdx  要查询的特征描述子的下标
            cout << "matches[" << i << "].trainIdx=" << matches[i].trainIdx << ", "; // matches[i].trainIdx  匹配到的要训练的特征描述子的下标
            cout << "matches[" << i << "].distance=" << matches[i].distance << endl; // 这两个描述特征子的距离      匹配度?
            double dist = matches[i].distance; // 要查询的特征描述子与匹配到的要训练的特征描述子之间的距离, 距离越小,匹配的越准确
            if (dist > maxDist) {
                maxDist = dist;
            }
            if (dist < minDist) {
                minDist = dist;
            }
        }
        printf("max distance=%f\n", maxDist); // max distance=0.671626
        printf("min distance=%f\n", minDist); // min distance=0.055168
        vector<DMatch> goodMatches; // 保存匹配度高的匹配点
        for (int i = 0; i < descriptor_obj.rows; i++) {
            double dist = matches[i].distance;
            if (dist < max(3 * minDist, 0.02)) { // 阈值的选取 实际情况实际分析?
                goodMatches.push_back(matches[i]);
            }
        }

        Mat matchesImg; // 显示匹配的结果
        drawMatches(img1, keypoints_obj, img2, keypoints_scene, goodMatches, matchesImg, Scalar::all(-1),
            Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); // Single keypoints will not be drawn.
        imshow("Flann Matching Result", matchesImg);


        // 发现查询描述子的特征所在的顶点与训练描述子的特征所在的顶点 之间的变换矩阵
        vector<Point2f> obj;
        vector<Point2f> objInScene;
        for (size_t t = 0; t < goodMatches.size(); t++) {
            obj.push_back(keypoints_obj[goodMatches[t].queryIdx].pt); // 获取要查询的描述子的关键点的位置
            objInScene.push_back(keypoints_scene[goodMatches[t].trainIdx].pt); // 获取要训练的描述子的关键点的位置
        }
        Mat H = findHomography(obj, objInScene, RANSAC); // 发现 透视变换矩阵
        cout << "goodMatches.size=" << goodMatches.size() << endl; // goodMatches.size=31
        // H depth = 6, type = 6, size = [3 x 3]    CV_64F 单通道
        cout << "H depth=" << H.depth() << ", type=" << H.type() << ", size=" << H.size() << endl;

        vector<Point2f> obj_corners(4); // 书本的四个顶点
        vector<Point2f> scene_corners(4); // 保存训练图像中书本的顶点位置
        obj_corners[0] = Point(0, 0); // 因为图1中书本的顶点就是图像的四个角,所以就这么设置以下的值了
        obj_corners[1] = Point(img1.cols, 0);
        obj_corners[2] = Point(img1.cols, img1.rows);
        obj_corners[3] = Point(0, img1.rows);
        perspectiveTransform(obj_corners, scene_corners, H); // 透视变换,通过变换矩阵找到训练图像中书本的顶点位置

        // draw line   因为matchesImg是两张图像的合成,所以若要在matchesImg上显示找到的书本的位置,x坐标需要偏移img1.cols
        line(matchesImg, scene_corners[0] + Point2f(img1.cols, 0), scene_corners[1] + Point2f(img1.cols, 0), Scalar(0, 0, 255), 2, 8, 0);
        line(matchesImg, scene_corners[1] + Point2f(img1.cols, 0), scene_corners[2] + Point2f(img1.cols, 0), Scalar(0, 0, 255), 2, 8, 0);
        line(matchesImg, scene_corners[2] + Point2f(img1.cols, 0), scene_corners[3] + Point2f(img1.cols, 0), Scalar(0, 0, 255), 2, 8, 0);
        line(matchesImg, scene_corners[3] + Point2f(img1.cols, 0), scene_corners[0] + Point2f(img1.cols, 0), Scalar(0, 0, 255), 2, 8, 0);

        Mat dst;
        cvtColor(img2, dst, COLOR_GRAY2BGR);
        line(dst, scene_corners[0], scene_corners[1], Scalar(0, 0, 255), 2, 8, 0); // 在训练图像上绘制找到的书本
        line(dst, scene_corners[1], scene_corners[2], Scalar(0, 0, 255), 2, 8, 0);
        line(dst, scene_corners[2], scene_corners[3], Scalar(0, 0, 255), 2, 8, 0);
        line(dst, scene_corners[3], scene_corners[0], Scalar(0, 0, 255), 2, 8, 0);

        imshow("find known object demo", matchesImg); // 在合成的matchesImg上显示找到的书本
        imshow("Draw object", dst); // 在原训练图上显示找到的书本

        waitKey(0);
    }

效果图

这里写图片描述

猜你喜欢

转载自blog.csdn.net/huanghuangjin/article/details/81290190
今日推荐