计算机视觉(二)图像的匹配

一.引言

如何判断两个物体是不是同一个?

  • 我们可以根据物体的颜色、纹理、大小、形状等特征来判断。然而,有时候在多种因素的影响下,如物体旋转、尺度变化、仿射变换,会改变物体的多数特征,涉及物体的鲁棒性(Robust),这里暂且不提,但可以知道的是,纹理是物体不变的特征。因此,检测纹理即可判断物体是否为同一个。

那么,如何检测纹理?

  • 角点是纹理的最好反映。

接着,什么是角点?

  • 目前为止还没有明确的数学定义,可以理解为极值点,即在某方面属性突出的点。或者说是特征点,具有很好的不会被破坏的特征的可描述点。具体情况具体分析,由于角上的点比较精确,一般不会因外界因素影响而改变,所以特征点常常是角点。

最后,如何进行角点检测与匹配?

  • 接下来简单介绍三种算法:Fast算法、Harris算法以及两者相结合的ORB算法。

二.Fast算法

1.优点:
可以进行实时的特征检测。
2.原理:
-在图像中任选一像素点p,设其像素值为I
-设定一个阈值t
-以3为半径画圆,覆盖p点周围的16个像素点(如图所示)
-如果这周围的16个像素中有连续的n个像素的像素值都小于I-t,或者有连续的n个像素都大于I+t,那么这个点就被判断为角点。opencv中n被设定为12。
-对以上步骤进行优化,可以先检测p点周围的四个点,即1, 5, 9, 12四个点中是否有三个点满足超过I+t,如果不满足,则直接跳过,如果满足,则继续使用前面的算法,全部判断16个点中是否有12个满足条件。
图片如下:
这里写图片描述
这里写图片描述
3.代码:

#include<opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;

//判断两个像素点的像素值的绝对值是否小于30的函数
bool isdif(uchar a, uchar b)
{
    if (abs(a - b) < 30)
        return false;
    else
        return true;
}

void main()
{
    VideoCapture cap(0);
    while (true)
    {
        Mat frame;
        cap >> frame;
        Mat frame_rgb;
        frame.copyTo(frame_rgb);
        cvtColor(frame, frame, CV_64FC1);
        //cout<<frame.at<uchar>(0, 0);
        //定义待检测点周围四个点的相对位置,pos[x][y],x取4表示4个点,y取0或1表示x轴和y轴的变化
        int pos[4][2];
        //第0号点在x轴不变,y轴变
        pos[0][0] = 0;
        pos[0][1] = -3;
        //第1号点在x轴变,y轴不变
        pos[1][0] = 3;
        pos[1][1] = 0;
        //第2号点在x轴不变,y轴变
        pos[2][0] = 0;
        pos[2][1] = 3;
        //第3号点在x轴变,y轴不变
        pos[3][0] = -3;
        pos[3][1] = 0;
        //边缘3个点无法检测,从3开始循环,防止溢出
        for (int i = 3; i < frame.rows - 3; i++)
        {
            for (int j = 3; j < frame.cols - 3; j++)
            {
                int num = 0;
                uchar cen = frame.at<uchar>(i, j);//中心像素点
                for (int k = 0; k < 4; k++)
                {
                    uchar temp = frame.at<uchar>(i + pos[k][0], j + pos[k][1]);//周围几个点的坐标等于中心像素点的坐标加上相对位置
                    if (isdif(cen, temp))
                    {
                        num++;
                    }
                }
                if ((num == 1) || (num == 3) || (num == 4))
                //差别很大的点有1或3或4个时均为角点
                {
                    circle(frame_rgb, cvPoint(j, i), 2, CV_RGB(255, 0, 0), 2);//圈出这个点
                }
            }
        }
        imshow("frame", frame_rgb);
        waitKey(10);

三.Harris算法

1.原理:
简单说,角点在各个方向的变化都是最大的,而边缘区域在只是某一方向有明显变化,如下图所示
这里写图片描述
从数学上讲,基本公式如下
这里写图片描述
这里写图片描述
w(x,y)表示移动窗口,I(x,y)表示像素灰度值强度,范围为0~255。
根据泰勒级数展开计算偏导数可得Harris矩阵,如下图
这里写图片描述
这里写图片描述
再计算Harris角点响应值,R=det(M)-k*(trace(M)^2),据学长实际操作,发现实际运算只计算k*(trace(M)^2),并不需要前半部分。
根据下图加以理解
这里写图片描述
个人还不是很理解这个算法,以上解释不保证正确。
2.代码:
一般算法:

#include<opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;

void main()
{
    VideoCapture cap(0);
    while (true)
    {
        Mat frame;
        cap >> frame;
        Mat frame_rgb;
        frame.copyTo(frame_rgb);
        cvtColor(frame, frame, CV_64FC1);
        //cout<<frame.at<uchar>(0, 0);
        for (int i = 1; i < frame.rows-1; i++)
        {
            for (int j = 1; j < frame.cols-1; j++)
            {
                double IX, IY;
                IX = frame.at<uchar>(i, j - 1) - frame.at<uchar>(i, j + 1);
                IY = frame.at<uchar>(i - 1, j) - frame.at<uchar>(i + 1, j);
                double R = IX*IX*IY*IY - 0.23*(IX*IX + IY*IY);
                //套用公式,其中0.23的值可变,范围0.1-0.5
                //cout << R << endl;
                if (R > 10000)//这里的10000的值也可变
                {
                    circle(frame_rgb, cvPoint(j, i), 1, CV_RGB(255, 0, 0));
                    //圈出这个点
                }
            }
        }
        imshow("frame", frame_rgb);
        waitKey(10);
}       

调用API的算法:

#include<opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;

void main()
{
    VideoCapture cap(0);
    while (true)
    {
        Mat frame;
        cap >> frame;
        cvtColor(frame, frame, CV_64FC1);
        Mat con;
        Mat cornerStrength;
        cornerHarris(frame, cornerStrength, 2, 3, 0.1);
        Mat harrisCorner;
        threshold(cornerStrength, harrisCorner, 0.00001, 255, THRESH_BINARY);
        imshow("角点检测后的二值效果图", harrisCorner);
        waitKey(10);
}

四.ORB算法

1.原理:
首先用Harris fast算法进行角点检测,再进行角点匹配,从而完成图像匹配;
在角点检测步骤,先用fast算法选取一定量的可能点,再用Harris算法进一步筛选,得到更为精确的角点。
2.代码:
暂且放上调用API的版本:

#include<iostream>
#include<opencv.hpp>
using namespace std;
using namespace cv;

void FeatureMatch(Mat FrameROI1, Mat FrameROI2, vector <Point2f> &kp1, vector <Point2f> &kp2, int Flag, float ratio);
void DrawFeature(Mat &rematch, Mat Frame1, Mat Frame2, vector <Point2f> &kp1, vector <Point2f> &kp2);

void main()
{
    Mat img = imread("pipei.jpg", 1);
    //记得把匹配图片命名为"pipei.jpg"(名字和格式均可自定义)放在程序对应文件夹中(可右键项目名称,选择在文件资源管理器中打开文件夹)
    resize(img, img, cvSize(640, 480));
    Mat img_gray;
    cvtColor(img, img_gray, CV_RGB2GRAY);
    VideoCapture cap(0);
    while (true)
    {
        Mat frame;
        cap >> frame;
        Mat frame_gray;
        cvtColor(frame, frame_gray, CV_RGB2GRAY);
        vector<Point2f> kp1;
        vector<Point2f> kp2;
        FeatureMatch(img_gray, frame_gray, kp1, kp2, 0, 0.8);
        Mat rematch;
        DrawFeature(rematch, img, frame, kp1, kp2);
        imshow("frame", rematch);
        waitKey(10);
    }
}

void FeatureMatch(Mat FrameROI1, Mat FrameROI2, vector <Point2f> &kp1, vector <Point2f> &kp2, int Flag, float ratio)
{
    Mat des1;
    Mat des2;
    vector <KeyPoint> keypoint1;
    vector <KeyPoint> keypoint2;


    Ptr <FeatureDetector> dector = ORB::create(500, 1.2f, 8, 31, 0, 2, ORB::FAST_SCORE, 31, 30);
    dector->detect(FrameROI1, keypoint1, Mat());
    dector->detect(FrameROI2, keypoint2, Mat());


    //Ptr <FeatureDetector> dector2 =ORB::create(50, 1.2f, 1, 31, 0, 2, ORB::FAST_SCORE, 31, 30);
    if (Flag == 0)
    {
        Ptr<DescriptorExtractor> pd0 = ORB::create(500, 1.2f, 8, 31, 0, 2, ORB::FAST_SCORE, 31, 30);
        pd0->compute(FrameROI1, keypoint1, des1);
        pd0->compute(FrameROI2, keypoint2, des2);
    }
    if (Flag == 1)
    {
        Ptr<DescriptorExtractor> pd1 = BRISK::create();
        pd1->compute(FrameROI1, keypoint1, des1);
        pd1->compute(FrameROI2, keypoint2, des2);
    }


    if ((keypoint1.size()>1) && (keypoint2.size()>1))
    {

        vector <vector<DMatch> > Vmatch;
        Ptr <DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
        matcher->knnMatch(des1, des2, Vmatch, 2);

        vector <DMatch> KNmatch;
        for (int i = 0; i < Vmatch.size(); i++)
        {
            double rat;
            if (Vmatch.at(i).at(1).distance<0.05) {
                rat = 1;
            }
            else {
                rat = Vmatch.at(i).at(0).distance / Vmatch.at(i).at(1).distance;
            }

            if (rat < ratio) {
                KNmatch.push_back(Vmatch.at(i).at(0));
            }

        }

        kp1.clear();
        kp2.clear();

        if (KNmatch.size()>0)
        {
            for (int i = 0; i<KNmatch.size(); i++)
            {
                int quaryx = KNmatch.at(i).queryIdx;
                int trainx = KNmatch.at(i).trainIdx;
                float mx = keypoint1.at(quaryx).pt.x;
                float my = keypoint1.at(quaryx).pt.y;
                float kx = keypoint2.at(trainx).pt.x;
                float ky = keypoint2.at(trainx).pt.y;
                Point2f p1;
                Point2f p2;
                p1.x = mx;
                p1.y = my;
                p2.x = kx;
                p2.y = ky;

                if (abs(ky - my)<5)
                {
                    kp1.push_back(p1);
                    kp2.push_back(p2);
                }
            }
        }
    }
    else
    {
        kp1.clear();
        kp2.clear();
    }
}

void DrawFeature(Mat &rematch, Mat Frame1, Mat Frame2, vector <Point2f> &kp1, vector <Point2f> &kp2)
{
    rematch = Mat((Frame1.rows >= Frame2.rows) ? Frame1.rows : Frame2.rows, Frame1.cols + Frame2.cols, CV_8UC3);
    Rect rectleft(0, 0, Frame1.cols, Frame1.rows);
    Rect rectright(Frame1.cols, 0, Frame2.cols, Frame2.rows);
    Frame1.copyTo(rematch(rectleft));
    Frame2.copyTo(rematch(rectright));

    for (int i = 0; i<kp1.size(); i++)
    {
        line(rematch, cvPoint(kp1[i].x, kp1[i].y), cvPoint(Frame1.cols + kp2[i].x, kp2[i].y), CV_RGB(255, 0, 0), 2);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值