文档下载链接: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