
本文共 17979 字,大约阅读时间需要 59 分钟。
1. 特征检测方法
有许多用于特征检测和提取的算法,我们将会对其中大部分进行介绍。OpenCV最常使用的特征检测和提取算法有:
Harris:该算法用于检测角点;SIFT:该算法用于检测斑点;SURF:该算法用于检测角点;FAST:该算法用于检测角点;BRIEF:该算法用于检测斑点;ORB:该算法代表带方向的FAST算法与具有旋转不变性的BRIEF算法;
通过以下方法进行特征匹配:
暴力(Brute-Force)匹配法;基于FLANN匹配法;
可以采用单应性进行空间验证。
1.1. 特征定义
那么,究竟什么是特征呢?为什么一副图像的某个特定区域可以作为一个特征,而其他区域不能呢?粗略的讲,特征就是有意义的图像区域,该区域具有独特特征和易于识别性。因此角点及高密度区域都是很好的特征,而大量重复的模式或低密度区域(例如图像中的蓝色天空)则不是很好的特征。边缘可以将图像分为两个区域,因此也可以看做好的特征。斑点是与周围有很大差别的像素区域,也是有意义的特征。
大多数特征检测算法都会涉及图像的角点、边和斑点的识别,也有一些涉及脊向的概念,可以认为脊向是细长物体的对称轴,例如识别图像中的一条路。角点和边都好理解,那什么是斑点呢?斑点通常是指与周围有着颜色和灰度差别的区域。在实际地图中,往往存在着大量这样的斑点,如一颗树是一个斑点,一块草地是一个斑点,一栋房子也可以是一个斑点。由于斑点代表的是一个区域,相比单纯的角点,它的稳定性要好,抗噪声能力要强,所以它在图像配准上扮演了很重要的角色。
由于某些算法在识别和提取某类型特征的时候有较好的效果,所以知道输入图像是什么很重要,这样做有利于选择最合适的OpenCV工具包。
1.2. 角点检测原理
角点在保留图像图形重要特征的同时,可以有效地减少信息的数据量,使其信息的含量很高,有效地提高了计算的速度,有利于图像的可靠匹配,使得实时处理成为可能。它的各种应用,这里我就不再赘述了。
1.2.1. Harris角点检测原理
我们先来看一幅图片,了解一下什么是角点?
上图中E,F中的角我们通常称作角点(corner points),他们具有以下特征:
-
轮廓之间的交点;
-
对于同一场景,即使视角发生变化,通常具备稳定性质的特征;
-
该点附*区域的像素点无论在梯度方向上还是其梯度幅值上有着较大变化;
harris特征角最早在paper A Combined Corner and Edge Detector中被Chris Harris & Mike Stephens提出。
Harris角点检测的基本思想:算法基本思想是使用一个固定窗口在图像上进行任意方向上的滑动,比较滑动前与滑动后两种情况,窗口中的像素灰度变化程度,如果存在任意方向上的滑动,都有着较大灰度变化,那么我们可以认为该窗口中存在角点
1、灰度变化描述
当窗口发生[u,v]
移动时,那么滑动前与滑动后对应的窗口中的像素点灰度变化描述如下:
其中,u,v是窗口在水平,竖直方向的偏移,
图中蓝线圈出的部分我们称之为结构张量(structure tensor),用M表示。
讲到这里,先明确一点,我们的目的是什么?我们的目的是寻找这样的像素点,它使得我们的u,v无论怎样取值,E(u,v)都是变化比较大的,这个像素点就是我们要找的角点。不难观察,上式近似处理后的E(u,v)是一个二次型,而由下述定理可知,
椭圆的长短轴是与结构张量M的两个特征值相对应的量。通过判断的情况我们就可以区分出‘flat’,‘edge’,‘corner’这三种区域,因为最直观的印象:
corner:在水平、竖直两个方向上变化均较大的点,即Ix、Iy都较大;
edge :仅在水平、或者仅在竖直方向有较大的点,即Ix和Iy只有其一较大 ;
flat : 在水平、竖直方向的变化量均较小的点,即Ix、Iy都较小;
而结构张量M是由Ix,Iy构成,它的特征值正好可以反映Ix,Iy的情况,下面我以一种更容易理解的方式来讲述椭圆的物理意义。
当然,大牛们并没有止步于此,这样通过判断两个变量的值来判断角点毕竟不是很方便。于是他们想出了一种更好的方法,对,就是定义角点响应函数R(corner response function),
至此,我们就可以通过判断R的值来判断某个点是不是角点了。
角点:R为大数值整数
边缘:R为大数值负数
平坦区:绝对值R是小数值
/*@param src Input single-channel 8-bit or floating-point image.@param dst Image to store the Harris detector responses. It has the type CV_32FC1 and the samesize as src .@param blockSize Neighborhood size (see the details on #cornerEigenValsAndVecs ).@param ksize Aperture parameter for the Sobel operator.@param k Harris detector free parameter. See the formula above.@param borderType Pixel extrapolation method. See #BorderTypes. */CV_EXPORTS_W void cornerHarris( InputArray src, OutputArray dst, int blockSize, int ksize, double k, int borderType = BORDER_DEFAULT );
/* * @Descripttion: * @version: * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2021-05-02 23:45:24 * @LastEditors: Yueyang * @LastEditTime: 2021-05-05 07:27:48 */#include <opencv2/opencv.hpp>using namespace cv;int main(){ Mat srcImage,dstImage,normal_dst,norScale; srcImage=imread("F:/SDK/OpenCV/HiKivision/picture/chessboard.png"); Mat out=srcImage.clone(); cvtColor(srcImage,srcImage,CV_BGR2GRAY); //imshow("src",srcImage); dstImage=Mat::zeros(srcImage.size(),CV_32FC1); cornerHarris(srcImage,dstImage,2,3,0.04,BORDER_DEFAULT); normalize(dstImage,normal_dst,0,255,NORM_MINMAX,CV_32FC1,Mat()); convertScaleAbs(normal_dst,norScale); for(int row=0;row<out.rows;row++){ uchar* currentPtr=norScale.ptr(row); for(int col=0;col<out.cols;col++) { int value=(int)*currentPtr;//一般100-200之间 就是上面提到的角点响应函数R if(value>100)circle(out,Point(col,row),20,Scalar(0,0,255)); currentPtr++; } } imwrite("F:/SDK/OpenCV/HiKivision/picture/out.jpg",out); imshow("out.jpg",out); waitKey(0); return 0;}
1.2.2. Tomasi角点检测原理
R=min(Y1,Y2);
CV_EXPORTS_W void goodFeaturesToTrack( InputArray image, OutputArray corners, int maxCorners, double qualityLevel, double minDistance, InputArray mask = noArray(), int blockSize = 3, bool useHarrisDetector = false, double k = 0.04 );
下面都不讲原理,看不懂了
1.2.3. SURF特征检测
/* * @Descripttion: * @version: * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2021-05-05 07:51:35 * @LastEditors: Yueyang * @LastEditTime: 2021-05-05 08:04:08 */#include <iostream>#include <opencv2/opencv.hpp>#include <opencv2/xfeatures2d.hpp>using namespace std;using namespace cv;using namespace cv::xfeatures2d;int main(){ Mat srcImage = imread("F:/SDK/OpenCV/HiKivision/picture/board.jpg"); //判断图像是否读取成功 if (srcImage.empty()) { cout << "图像加载失败" << endl; return -1; } else { cout << "图像加载成功" << endl << endl; } namedWindow("原图像",WINDOW_AUTOSIZE); imshow("原图像",srcImage); Mat imageMid; //定义滤波后图像 int minHessian = 400; //定义Hessian矩阵阈值特征点检测算子 Ptr<SURF> deteror=SURF::create(minHessian); vector<KeyPoint> keypoints; //定义KeyPoint类型的矢量容器vector存储检测到的特征点 deteror->detect(srcImage,keypoints); //调用detect检测特征点 //绘制检测到的特征点 Mat dstImage; drawKeypoints(srcImage, keypoints, dstImage, Scalar::all(-1), DrawMatchesFlags::DEFAULT); namedWindow("特征点检测",WINDOW_AUTOSIZE); imshow("特征点检测",dstImage); waitKey(0); return 0;}
1.2.4. SIFT特征点检测
/* * @Descripttion: * @version: * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2021-05-05 08:50:08 * @LastEditors: Yueyang * @LastEditTime: 2021-05-05 08:57:02 */#include "opencv2/opencv.hpp"#include <opencv2/xfeatures2d.hpp>#include <vector>using namespace std;using namespace cv;using namespace cv::xfeatures2d;int main(){ Mat srcImg1 = imread("F:/SDK/OpenCV/HiKivision/picture/board.jpg"); //Mat srcImg2 = imread("F:/SDK/OpenCV/HiKivision/picture/chessboard.png"); //定义SIFT特征检测类对象 Ptr<SIFT> deteror=SIFT::create(); vector<KeyPoint>keyPoints1; vector<KeyPoint>keyPoints2; //特征点检测 deteror->detect(srcImg1, keyPoints1); //deteror->detect(srcImg2, keyPoints2); //绘制特征点(关键点) Mat feature_pic1, feature_pic2; //drawKeypoints(srcImg2, keyPoints2, feature_pic2, Scalar(0, 0, 255)); drawKeypoints(srcImg1, keyPoints1, feature_pic1, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);//颜色随机,带有方向 //显示原图 imshow("src1", srcImg1); //imshow("src2", srcImg2); //显示结果 imshow("feature1", feature_pic1); //imshow("feature2", feature_pic2); waitKey(0);}
1.2.5. HOG 特征检测
/* * @Descripttion: * @version: * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2021-05-05 09:00:19 * @LastEditors: Yueyang * @LastEditTime: 2021-05-05 09:09:55 */#include <iostream>#include "opencv2/opencv.hpp" using namespace cv;using namespace std; int main(int argc, char** argv) { Mat src = imread("F:/SDK/OpenCV/HiKivision/picture/warker.png"); if (src.empty()) { cout << "Load image error..." << endl; return -1; } HOGDescriptor hog = HOGDescriptor(); hog.setSVMDetector(hog.getDefaultPeopleDetector()); vector<Rect> foundLocations; hog.detectMultiScale(src, foundLocations, 0, Size(8, 8), Size(32, 32), 1.05, 2); Mat result = src.clone(); for (size_t t = 0; t < foundLocations.size(); t++) rectangle(result, foundLocations[t], Scalar(0, 0, 255), 2, 8, 0); imwrite("hog.jpg",result); imshow("HOG SVM Detector Demo", result); waitKey(); return 0;}
1.2.6. LBP 特征提取
1.2.6.1. 原始LBP
原始LBP是在3*3的窗口内,以窗口中心元素为阈值,比较周围8个像素,若大于中心像素点,则标记为1,否则为0。然后这8个点就可以产生一个8位的二进制数(共有2^8=256种),转换为10进制数后,这个值就用来代替像素中心值。
1.2.6.2. 圆形LBP算子
由于原始LBP只是覆盖了一个固定半径范围内的小区域,不能满足不同尺寸和频率纹理的需要。对此,将3*3领域扩展到任意领域,用圆形领域代替正方形领域。
(1)将检测窗口划分为16*16的小区域(cell)。
(2)对于每个cell中的一个像素,计算得到它的LBP值。
(3)计算每个cell的直方图,即每个LBP值出现的频率,然后归一化。
(4)统计每个cell的直方图,连接成一个向量。
/* * @Descripttion: * @version: * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2021-05-05 07:51:35 * @LastEditors: Yueyang * @LastEditTime: 2021-05-05 09:59:42 */#include <iostream>#include <opencv2/opencv.hpp>#include <opencv2/xfeatures2d.hpp>using namespace std;using namespace cv;using namespace cv::xfeatures2d;int main(){ Mat srcImage = imread("F:/SDK/OpenCV/HiKivision/picture/board.jpg"); Mat gray_src; namedWindow("原图像",WINDOW_AUTOSIZE); imshow("原图像",srcImage); cvtColor(srcImage,gray_src,CV_BGR2GRAY); int width=gray_src.cols; int heights=gray_src.rows; Mat dst =Mat::zeros(heights-2,width-2,CV_8UC1); for(int row =1;row<heights-1;row++){ for(int col=1;col<width-1;col++) { uchar c=gray_src.at<uchar>(row,col); uchar code=0; code|=(gray_src.at<uchar>(row-1,col-1)>c)<<7; code|=(gray_src.at<uchar>(row-1,col)>c)<<6; code|=(gray_src.at<uchar>(row-1,col+1)>c)<<5; code|=(gray_src.at<uchar>(row,col-1)>c)<<4; code|=(gray_src.at<uchar>(row,col+1)>c)<<3; code|=(gray_src.at<uchar>(row+1,col-1)>c)<<2; code|=(gray_src.at<uchar>(row+1,col)>c)<<1; code|=(gray_src.at<uchar>(row+1,col+1)>c)<<0; dst.at<uchar>(row-1,col-1)=code; } } imshow("dst",dst); waitKey(0); return 0;}
1.3. HAAR特征提取
1.3.1. 1.什么是haar特征?
特征 = 某个区域的像素点经过某种四则运算之后得到的结果。
这个结果可以是一个具体的值也可以是一个向量,矩阵,多维。实际上就是矩阵运算
1.3.2. 2.如何利用特征 区分目标?
阈值判决,如果大于某个阈值,认为是目标。小于某个阈值认为是非目标。
1.3.3. 3.如何得到这个判决?
使用机器学习,我们可以得到这个判决门限
Haar特征的计算原理
这些是在opencv中使用的haar特征。基础类型(5种) 核心(3种)all(6种),这里的14个图片分别对应十四种特征。
如何使用呢?
蓝色区域表明我们所得到的图片。黑白矩形框是我们的特征模板。
比如: 我们的模板是一个(10,10)的矩阵,共覆盖了100个像素点。黑白各占50个像素点
将当前模板放在图像上的任意位置上,在这里,用白色区域覆盖的50个像素之和减去黑色区域的50个像素之和得到我们的特征。
推导公式的一样性。
公式二:
这里的整个区域指黑白两色覆盖的整个100个像素。这两个权重是不一样的。
整体的权重值为1 黑色部分权重值为-2
整个区域的像素值 * 权重1 + 黑色部分 * 权重2
= 整个区域 * 1 + 黑色部分 * -2
=(黑 + 白) * 1 + 黑色部分 * -2
= 白色 - 黑色
Haar特征遍历
计算整幅图的Haar特征
如果想要计算整幅图的Haar特征,我们就需要遍历,
假设这个haar特征的模板是(10,10)共100个像素。图片的大小是(100,100)
如果想获取这个图片上所有的harr特征。使用当前模板沿着水平和竖直方向进行滑动。从上到下,从左到右进行遍历。
遍历的过程中还要考虑步长问题。这个模板一次滑动几个像素。一次滑动10个像素,就需要9次。加上最开始的第0次。
10个模板。
计算这100个模板才能将haar特征计算完毕。
如果我们的步长设置为5.那么就要滑动20次。400个模板。运算量增加4倍。
其实仅仅这样的一次滑动并没有结束,对于每一个模板还要进行几级缩放,才能完成。
需要三个条件:100100,1010,step=10,模板 1
模板 :缩放,这就需要再次进行遍历
运算量太大 :
计算量 = 14个模版20级缩放(1080/2*720/2) * (100点+ -) = 50-100亿
(50-100)*15 = 1000亿次
积分图
特征:计算矩形方块中的像素
有ABCD四个区域,每个区域都是一个矩形方块。
A区域是左上角那一块小区域,而B区域是包含A区域的长条。
C区域又是包含A的竖直长条。D区域是四个方块之和。
1,2,3,4表明这四个小区域。
进行快速计算:
A 1 B 1 2 C 1 3 D 1 2 3 4
4 = A-B-C+D = 1+1+2+3+4-1-2-1-3 = 4(10*10 变成3次±)
任意一个方框,可由周围的矩形进行相减得到。
问题: 在计算每一个方块之前,需要将图片上所有的像素全部遍历一次。至少一次。
特征=(p1-p2-p3+p4)*w
p1 p2 p3 p4 分别是指某一个特征相邻的abcd的模块指针。
adaboost分类器
haar特征一般会和adaboost一起使用
haar特征 + Adaboost是一个非常常见的组合,在人脸识别上取的非常大成功
Adaboost分类器优点在于前一个基本分类器分出的样本,在下一个分类器中会得到加权。加权后的全体样本再次被用于训练下一个基本分类器。
就是说它可以加强负样本。
例如:我们有 苹果 苹果 苹果 香蕉 找出苹果
第一次训练权值分别为: 0.1 0.1 0.1 0.5
因为香蕉是我们不需要的所以0.5,我们正确的样本系数减小,负样本得到加强。
把出错的样本进行加强权值。再把整个结果送到下一个基本分类器
训练终止条件:
-
for迭代的最大次数 count
-
每次迭代完的检测概率p(p是最小的检测概率,当大于p就终止)
分类器的结构
haar特征计算完之后要进行阈值判决,实际是一个个判决过程。
if(haar> T1) 苹果[第一级分类器];[第二级分类器]and haar>T2
两级都达成了,才会被我们认定为苹果。
两级分类器的阈值分别是t1 和 t2,对于每一级的分类器我们称之为强分类器。
2个强分类器组成。一般有15-20强分类器。 要连续满足15-20个强分类器才能认证为目标。
3个强分类器,每个强分类器会计算出一个独立的特征点。使用每一个独立的特征进行阈值判。
强分类器1 特征x1 阈值t1 强分类器2 特征x2 阈值t2 3同理(阈值是通过训练终止时得到的)
进行判决过程: x1>t1 and x2>t2 and x3>t3
三个判决同时达成,目标-》苹果
三个强分类器只要有一个没通过就会被判定为非苹果。作用:判决。而弱分类器作用:计算强分类器特征x1 x2 x3
每个强分类器由若干个弱分类器组成
1强 对多个弱分类器 对应多个特征节点
弱分类器作用:计算强分类器特征x1 x2 x3
强分类器的输入特征是多个弱分类器输出特征的综合处理。
x2 = sum(y1,y2,y3)
y1 弱分类器特征谁来计算的?
node节点来计算
一个弱分类器最多支持三个haar特征,每个haar特征构成一个node节点
Adaboost分类器计算过程
3个haar -》 3个node
node1 haar1 > node的阈值T1 z1 = a1
node1 haar1 < node的阈值T1 z1 = a2
Z = sum(z1,z2,z3) > 弱分类器T y1 = AA
Z = sum(z1,z2,z3) < 弱分类器T y1 = BB
从node向强分类器
第一层分类器:haar->Node z1 z2 z3 得到的就是z1 z2 z3
第二层分类器:Z>T y1 y2 y3: 弱分类器的计算特征
第三层分类器:x = sum(y1,y2,y3) > T1 obj
训练过程
1.初始化数据 权值分布 苹果 苹果 苹果 香蕉 0.1 0.1 0.1 0.1 (第一次权值都是相等的)
2.遍历阈值 p计算出误差概率,找到最小的minP t
3.计算出G1(x)
4.权值分布 update更新权重分布: 0.2 0.2 0.2 0.7
5.训练终止条件。
1.3.4. 基于Haar特征与级联分类器的人脸检测
/* * @Descripttion: * @version: * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2021-05-05 11:19:53 * @LastEditors: Yueyang * @LastEditTime: 2021-05-05 11:23:38 */#include <opencv2/opencv.hpp>#include <iostream>using namespace std;using namespace cv;String fileName = "/home/pi/Desktop/WorkSpace/Study/data/haarcascade_frontalface_default.xml";//当前项目文件下的xml文件//opencv自带训练好的人脸识别级联器CascadeClassifier face_cascade;//定义用来做目标检测的级联分类器的一个类int main(int argc, char** argv){ VideoCapture cap(0); if (!face_cascade.load(fileName)) //检测级联器是否加载成功 { printf("could not load face feature data.../n"); return -1; } Mat src,gray; namedWindow("src", WINDOW_AUTOSIZE); while(true){ cap>>src; cvtColor(src, gray, COLOR_BGR2GRAY);//转换为灰度图 //直方图均衡化 equalizeHist(gray, gray);//因为积分图像特征基于矩形区域的差,如果直方图是不平衡的,这些差异就有可能由于整体光照或者测试图像的曝光而倾斜,所以这一步非常重要 vector<Rect> faces; //利用detectMultiScale搜索图像 face_cascade.detectMultiScale(gray, //灰度图像 faces, //vector<Rect>边界矩形 1.1,//scaleFactor表示每次图像尺寸减小的比例 3, //minNeighbors表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大小都可以检测到人脸) 0, //flags 旧版本OpenCV 1.x级联工具 Size(30, 30)); //目标的最小尺寸 for (size_t i = 0; i < faces.size(); i++) { //检索到人脸则画圆形 Point center(faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5); ellipse(src, center, Size(faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar(0,255,255), 4, 8, 0); } imshow("Face_detect", src); waitKey(30); } return 0;}
1.3.5. 基于Haar特征的模型训练与识别
关键在于数据的采集与标记,这是一个非常麻烦的过程
给一个狗脸识别的例子
链接:https://pan.baidu.com/s/1d2eThpD0bbU0ZnZeV5XZAA
提取码:06cv
/* * @Descripttion: * @version: * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2021-05-05 11:19:53 * @LastEditors: Yueyang * @LastEditTime: 2021-05-05 11:23:38 */#include <opencv2/opencv.hpp>#include <iostream>using namespace std;using namespace cv;String fileName = "F:/SDK/OpenCV/HiKivision/data/Train/haarresult/cascase.xml";//当前项目文件下的xml文件//opencv自带训练好的人脸识别级联器CascadeClassifier face_cascade;//定义用来做目标检测的级联分类器的一个类int main(int argc, char** argv){ VideoCapture cap(0); if (!face_cascade.load(fileName)) //检测级联器是否加载成功 { printf("could not load face feature data.../n"); return -1; } Mat src,gray; namedWindow("src", WINDOW_AUTOSIZE); while(true){ cap>>src; cvtColor(src, gray, COLOR_BGR2GRAY);//转换为灰度图 //直方图均衡化 equalizeHist(gray, gray);//因为积分图像特征基于矩形区域的差,如果直方图是不平衡的,这些差异就有可能由于整体光照或者测试图像的曝光而倾斜,所以这一步非常重要 vector<Rect> faces; //利用detectMultiScale搜索图像 face_cascade.detectMultiScale(gray, //灰度图像 faces, //vector<Rect>边界矩形 1.1,//scaleFactor表示每次图像尺寸减小的比例 3, //minNeighbors表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大小都可以检测到人脸) 0, //flags 旧版本OpenCV 1.x级联工具 Size(30, 30)); //目标的最小尺寸 for (size_t i = 0; i < faces.size(); i++) { //检索到人脸则画圆形 Point center(faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5); ellipse(src, center, Size(faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar(0,255,255), 4, 8, 0); } imshow("Face_detect", src); waitKey(30); } return 0;}
1.4. 特征匹配
/* * @Descripttion: * @version: * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2021-05-05 07:51:35 * @LastEditors: Yueyang * @LastEditTime: 2021-05-05 12:56:57 */#include <iostream>#include <opencv2/opencv.hpp>#include <opencv2/xfeatures2d.hpp>using namespace std;using namespace cv;using namespace cv::xfeatures2d;int main(){ Mat srcImage1 = imread("/home/pi/Desktop/WorkSpace/OpenCV/picture/pic1.jpg");//OBJ Mat srcImage2 = imread("/home/pi/Desktop/WorkSpace/OpenCV/picture/pic2.jpg");//SCENE if(srcImage1.channels()>1) cvtColor(srcImage1,srcImage1,COLOR_BGR2GRAY); if(srcImage2.channels()>1) cvtColor(srcImage2,srcImage2,COLOR_BGR2GRAY); int minHessian = 400; //定义Hessian矩阵阈值特征点检测算子 Ptr<SURF> deteror=SURF::create(minHessian); vector<KeyPoint> keypoints1; //定义KeyPoint类型的矢量容器vector存储检测到的特征点 vector<KeyPoint> keypoints2; Mat descrp1,descrp2; deteror->detectAndCompute(srcImage1,Mat(),keypoints1,descrp1); //调用detect检测特征点 deteror->detectAndCompute(srcImage2,Mat(),keypoints2,descrp2); //调用detect检测特征点 FlannBasedMatcher matcher; vector<DMatch>mathces; matcher.match(descrp1,descrp2,mathces); double minDist=1000; double maxDist=0; for(int i=0;i<descrp1.rows;i++) { double dist=mathces[i].distance; if(dist>maxDist)maxDist=dist; if(dist<minDist)minDist=dist; } printf("min %f,max %f",minDist,maxDist); vector<DMatch>goodmach; for(int i=0;i<descrp1.rows;i++) { double dist =mathces[i].distance; if(dist<max(2*minDist,0.02)) { goodmach.push_back(mathces[i]); } } Mat dst; drawMatches(srcImage1,keypoints1,srcImage2,keypoints2,goodmach,dst); vector<Point2f>obj; vector<Point2f>scene; for(int t=0;t<goodmach.size();t++) { obj.push_back(keypoints1[goodmach[t].queryIdx].pt); scene.push_back(keypoints2[goodmach[t].trainIdx].pt); } Mat H=findHomography(obj,scene,RANSAC); vector<Point2f>obj_coners(4); vector<Point2f>scene_coners(4); obj_coners[0]=Point(0,0); obj_coners[1]=Point(srcImage1.cols,0); obj_coners[2]=Point(srcImage1.cols,srcImage1.rows); obj_coners[3]=Point(0,srcImage1.rows); perspectiveTransform(obj_coners,scene_coners,H); line(dst,scene_coners[0]+Point2f(srcImage1.cols,0),scene_coners[1]+Point2f(srcImage1.cols,0),Scalar(0,0,255),2,8,0); line(dst,scene_coners[1]+Point2f(srcImage1.cols,0),scene_coners[2]+Point2f(srcImage1.cols,0),Scalar(0,0,255),2,8,0); line(dst,scene_coners[2]+Point2f(srcImage1.cols,0),scene_coners[3]+Point2f(srcImage1.cols,0),Scalar(0,0,255),2,8,0); line(dst,scene_coners[3]+Point2f(srcImage1.cols,0),scene_coners[0]+Point2f(srcImage1.cols,0),Scalar(0,0,255),2,8,0); imshow("match",dst); waitKey(0); return 0;}
发表评论
最新留言
关于作者
