特征匹配目标跟踪(FAST+ORB)

文档下载链接:https://download.csdn.net/download/OEMT_301/12093490

FAST算法
FAST特征检测思想:其核心就是判断以检测点为中心,其周围16个点形成的圆的灰度差值绝对值连续个数,是否大于判断条件,如果是,那么该点为特征点,否则非特征点。
在这里插入图片描述
在这里插入图片描述
当FAST_N = FAST_15,可以检测小于45°角特征点
当FAST_N = FAST_13,可以检测小于90°角特征点
当FAST_N = FAST_11,可以检测小于135°角特征点
在实际中为了尽可能多的获取待匹配的特征点,因此经常将FAST_N设置为FAST_11。

在实际处理中,为了提高检测速度,需要对获取的特征点进行非极大值抑制,其思路如下:
(1) 以某一特征点为中心为邻域(如3×3或5×5窗口)
(2) 如果该窗口内存在多个特征点,保留多个特征点中得分最高的特征点,其它点进行抑制,即删除。
特征点得分计算方法:
在这里插入图片描述

ORB算法
ORB中BRIEF描述子
首先介绍一下BRIEF描述子,该描述子就是在特征点P的周围以R为半径的邻域内,以一定模式选取N个点对,把这N个点对比较结果组合起来作为描述子。如下图所示的点对:
在这里插入图片描述
在这里插入图片描述
这里注意到,该特征描述子不具备旋转不变性和尺度一致性。其中尺度一致性可以利用图像金字塔进行改善。而旋转不变性可以用如下方法解决:
在这里插入图片描述
首先获取特征点P的FAST窗口邻域内灰度重心Q,其中重心Q计算方法如下:
在这里插入图片描述
其中,I(x,y)为像素点(x,y)灰度值。Q(x,y)为获取的重心点坐标。θ为坐标系旋转角度。
那么(PQ) ⃑向量,即为该特征点描述子的方向向量,然后建立以该向量为坐标轴的坐标系(相对原坐标系旋转θ角度)的描述子窗口,如下图:
在这里插入图片描述
在这里插入图片描述
此时,以新坐标系,重新选取点对,和计算新的描述子结果。这样就解决了尺度一致性和旋转不变性问题,保证在领域内的点对都是相对应。
ORB中特征点匹配
在这里插入图片描述

代码(直接调用opencv3.0.0自带函数):

#include <iostream>
#include <signal.h>
#include <vector>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

bool  tracking = false;
bool flag_s = true;

bool leftButtonDownFlag=false;  //左键单击后的标志位
bool leftButtonUpFlag=false;    //左键单击后松开的标志位
Point Point_s;                  //矩形框起点
Point Point_e;                  //矩形框鼠标左键弹起来的终点
Point processPoint;             //矩形框移动的终点
//鼠标回调函数
void onMouse(int event,int x,int y,int flags,void *ustc)
{
    if(event==CV_EVENT_LBUTTONDOWN)
    {
        tracking = false;
        leftButtonDownFlag = true; //标志位
        leftButtonUpFlag = false;
        processPoint=Point(x,y);  //设置左键按下点的矩形起点
        Point_s=processPoint;
    }
    else if(event == CV_EVENT_MOUSEMOVE && leftButtonDownFlag)
    {
        processPoint=Point(x,y);
    }
    else if(event==CV_EVENT_LBUTTONUP && leftButtonDownFlag)
    {
        leftButtonDownFlag=false;
        processPoint=Point(x,y);
        Point_e=processPoint;
        tracking = true;
        leftButtonUpFlag = true;
        flag_s = true;
    }
}

//
int main()
{
    Rect rect_s;
    Mat img_mould, frame, mould;

    Mat ShowMatches;

    vector<KeyPoint> Keypoints, Keypoints1, Keypoints2;
    Mat descriptors, descriptors1, descriptors2, mask;
    Ptr<ORB> orb = ORB::create();                                           // 特征提取算法
    vector<DMatch> matches;
    Ptr<DescriptorMatcher> matcher =DescriptorMatcher::create("BruteForce");

    const int minNumbermatchesAllowed = 8;
    //Prepare data for findHomography
    vector<Point2f> Keypoints_r1, Keypoints_r2;

    vector<double> xx, yy;      // 最终匹配特征点坐标

    //打开摄像头或者特定视频
    VideoCapture cap;
    cap.open(0);//或cap.open("文件名")

    //读入视频是否为空
    if (!cap.isOpened())
    {
        return -1;
    }

    namedWindow("输出视频", 1);
    setMouseCallback("输出视频", onMouse, 0);//鼠标回调函数,响应鼠标以选择跟踪区域

    while (1)
    {
        cap >> frame;
        if (frame.empty())
        {
            return -1;
        }

        if(tracking && leftButtonUpFlag)
        {
            leftButtonUpFlag = false;
            rect_s.x = Point_s.x;
            rect_s.y = Point_s.y;
            rect_s.width = Point_e.x - Point_s.x;
            rect_s.height = Point_e.y - Point_s.y;
            img_mould = frame.clone();
            mould = Mat(img_mould, rect_s);     //目标窗口
            Keypoints.clear();
            orb->detectAndCompute(mould, Mat(), Keypoints, descriptors);        // 获取目标窗口特征点
            if(Keypoints.size() < minNumbermatchesAllowed)
            {
                cout << "重新选框" << endl;
                break;
            }

        }

        if(leftButtonDownFlag)
        {
            rect_s.x = Point_s.x;
            rect_s.y = Point_s.y;
            rect_s.width = processPoint.x - Point_s.x;
            rect_s.height = processPoint.y - Point_s.y;
            rectangle(frame, rect_s, Scalar(0, 255, 0), 3, 8, 0);   //显示跟踪结果,框出
        }

        if(tracking)
        {
            // 图像合并
            ShowMatches = Mat::zeros(frame.rows, rect_s.width+frame.cols, frame.type());
            mould.copyTo(ShowMatches(Rect(0, 0, rect_s.width, rect_s.height)));
            frame.copyTo(ShowMatches(Rect(rect_s.width, 0, frame.cols, frame.rows)));

            Keypoints1.clear();
            Keypoints1.assign(Keypoints.begin(), Keypoints.end());      // 赋值目标窗口特征点
            descriptors1 = descriptors.clone();
            Keypoints2.clear();
            orb->detectAndCompute(frame, Mat(), Keypoints2, descriptors2);      // 获取检测帧特征点

            //Matching
            matches.clear();
            matcher->match(descriptors1, descriptors2, matches);            // 特征点匹配 matches为匹配索引值

            Keypoints_r1.clear();Keypoints_r2.clear();
            if (matches.size() < minNumbermatchesAllowed)                   // 特征点阈值判断
            {
                continue;
            }
            // 将初次匹配的特征点进行格式转化,是RANSAC进行后续处理
            for(int i=0; i<matches.size();i++)
            {
                Keypoints_r1.push_back(Keypoints1.at(matches.at(i).queryIdx).pt);           // 目标matches.at(i).queryIdx个点与
                Keypoints_r2.push_back(Keypoints2.at(matches.at(i).trainIdx).pt);           // 检测图像中matches.at(i).trainIdx个点是匹配对用
            }

            Mat H = findHomography(Keypoints_r1, Keypoints_r2, CV_RANSAC, 3, mask);         // 利用RANSAC算法对特征点进行过滤,H为单应性矩阵
                                                                                            // mask为RANSAC处理后匹配特征标记结果

            xx.clear();yy.clear();

            for(int i=0; i<matches.size();i++)
            {
                if(mask.at<bool>(0, i))                                     // 该点为匹配特征点
                {
                    double ss = H.at<double>(2, 0)*Keypoints_r1.at(i).x + H.at<double>(2, 1)*Keypoints_r1.at(i).y
                            + H.at<double>(2, 2);                           // 单应性矩阵倍率
                    // 利用单应性计算匹配后的点
                    xx.push_back(abs((H.at<double>(0, 0)*Keypoints_r1.at(i).x + H.at<double>(0, 1)*Keypoints_r1.at(i).y
                            + H.at<double>(0, 2))/ss - Keypoints_r2.at(i).x));
                    yy.push_back(abs((H.at<double>(1, 0)*Keypoints_r1.at(i).x + H.at<double>(1, 1)*Keypoints_r1.at(i).y
                            + H.at<double>(1, 2))/ss - Keypoints_r2.at(i).y));

                    // 计算点偏移值
                    double dx = xx.back() - Keypoints_r2.at(i).x;
                    double dy = yy.back() - Keypoints_r2.at(i).y;

                    circle(ShowMatches, Point(Keypoints_r1.at(i).x, Keypoints_r1.at(i).y), 2, Scalar(0, 0, 255));   //绘制目标匹配特征点
                    circle(ShowMatches, Point(Keypoints_r2.at(i).x + rect_s.width, Keypoints_r2.at(i).y), 2, Scalar(0, 0, 255));    // 绘制检测匹配特征点
                    line(ShowMatches, Point(Keypoints_r1.at(i).x, Keypoints_r1.at(i).y),
                         Point(Keypoints_r2.at(i).x + rect_s.width, Keypoints_r2.at(i).y), Scalar(255, 0, 0), 1);   // 匹配特征点连线
                }
                else
                {
                    // 不是匹配特征点处理过程
                }
            }
            imshow("输出视频", ShowMatches);
        }
        else
        {
            imshow("输出视频", frame);
        }
        waitKey(5);
    }

    return 0;
}

注:
KeyPoint结构:

class KeyPoint
{
	Point2f  pt;    //该图像特征点的坐标
	float  size;     //特征点邻域直径
	float  angle; //特征点的方向,值为[零,三百六十),负值表示不使用,有了这个方向,能够让特征点拥有更高的辨识度,否则仅仅坐标和直径有时会误判特征点
	float  response;//响应程度,代表该点的强壮程度,也就是该点角点程度,用于后期使用和排序
	int  octave; //特征点所在的图像金字塔的组
	int  class_id; //用于聚类的id
}

参考:
https://blog.csdn.net/weixin_37697191/article/details/89090686
https://blog.csdn.net/qq_40213457/article/details/80848794
https://blog.csdn.net/sinat_31337047/article/details/54379213
https://blog.csdn.net/qq_32998593/article/details/79221641
https://blog.csdn.net/lyl771857509/article/details/79661483
https://www.cnblogs.com/dengxiaojun/p/5302778.html
https://blog.csdn.net/qq_35721810/article/details/85158020

发布了37 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/OEMT_301/article/details/103894556