OpenCV实战(17)——FAST特征点检测

Posted 盼小辉丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV实战(17)——FAST特征点检测相关的知识,希望对你有一定的参考价值。

OpenCV实战(17)——FAST特征点检测

0. 前言

Harris 算子根据两个垂直方向上的强度变化率给出了角点(或更一般地说,兴趣点)的数学定义。但使用这种定义需要计算图像导数,计算代价较为高昂,特别是兴趣点检测通常只是更复杂算法的先决步骤。
在本中,我们将学习另一个特征点检测算子 FAST (Features from Accelerated Segment Test)。其专门设计用于快速检测图像中的兴趣点;关键点检测仅基于几个像素的比较。

1. FAST 特征点检测

使用 OpenCV 通用接口进行特征点检测,能够轻松使用任意特征点检测器。本节将介绍 FAST 检测器,顾名思义,其被设计为进行快速计算。

(1) 创建一个关键点向量来存储结果:

std::vector<cv::KeyPoint> keypoints;

(2) 创建一个阈值为 40 的 FAST 检测器:

// FAST 检测器
cv::Ptr<cv::FastFeatureDetector> ptrFAST = new cv::FastFeatureDetector(40);

(3) 检测加载图像的所有关键点:

ptrFAST->detect(image, keypoints);

OpenCV 还提供了一个通用函数来在图像上绘制关键点:

cv::drawKeypoints(image, keypoints, image, cv::Scalar(255, 255, 255), cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);

通过指定绘图标志,将关键点绘制在输入图像上,可以得到以下输出结果:


当我们将关键点颜色指定为负值时,绘制的每个圆圈将使用不同的随机颜色。
Harris 角点一样,FAST 特征算法源于角点定义,该定义基于特征点周围的图像强度。关键点的检测通过检查以候选点为中心的像素圆进行,如果找到长度大于圆周长 3/4 的连续点(弧),其所有像素都与中心点的强度显着不同,则该候选点为一个关键点。
这是一个可以快速计算的检测过程。此外,在其公式中,该算法使用了一个额外的技巧来进一步加快计算过程。事实上,如果我们首先测试圆上相距 90 度的四个点(例如,上、下、右、左),为了满足检测条件,其中至少三个点都必须比中心像素更亮或更暗,否则,可以立即否定该点,而无需检查圆周上的其他点。这是一个非常高效的检测技巧,因为在实践中,大多数图像点都无法满足此四点检测条件。
要检查像素圆的半径是该方法需要考虑的一个因素,在实践中发现,半径为 3 时检测效果较好,效率也更高,此时,需要考虑圆周上的 16 个像素:
R ≈ [ 16 1 2 15 3 14 4 13 0 5 12 6 11 7 10 9 8 ] R\\approx\\left[ \\beginarrayccc & & 16 & 1 & 2 & &\\\\ & 15 & & & & 3& \\\\ 14&&&&&&4\\\\ 13&&&0&&&5\\\\ 12&&&&&&6\\\\ & 11 & & & & 7&\\\\ & & 10 & 9 & 8 & &\\\\\\endarray\\right] R 141312151116101092837456

在以上示例中,用于预测试的四个点的像素值为 15913,所需满足条件的连续较暗或较亮的像素点数为 12。但是,可以观察到通过将连续段的长度减少到 9,可以更好的检测到角点在不同图像上的可重复性。这种变体通常称为 FAST-9 角点检测器,这也是 OpenCV 中所采用的。此外,OpenCV 中的 cv::FASTX 函数实现了 FAST 检测器的另一个变体。
一个像素点的强度必须与中心像素的强度相差指定的量才会被视为满足更暗或更亮的检测条件,相差的量使用函数中的阈值参数指定,阈值越大,检测到的角点就越少。
而对于 Harris 特征而言,通常需要对已经检测到的角点进行非极大值抑制。因此,需要定义角点强度度量。也可以考虑以下替代方案,角点强度由中心像素与所识别的连续弧上的像素之间的绝对差之和给出,该算法也可通过直接函数调用实现:

cv::FAST(image,     // 输入图像
        keypoints,  // 输出关键点向量
        40,         // 阈值
        false);     // 是否使用非极大值抑制

但是,推荐使用 cv::FeatureDetector 接口,提高应用程序的灵活性。
FAST 特征点检测算法实现了非常快的兴趣点检测,因此,当程序需要效率优先时,应该首选该算法,例如,在实时视觉跟踪或对象识别应用程序中,必须在实时视频流中跟踪或匹配多个特征点。
为了改进特征点的检测,OpenCV 提供使用许多类适配器,以更好地控制关键点的提取。

2. 自适应特征检测

如果希望更好地控制特征点的数量,可以使用 cv::FeatureDetector 类的子类 cv::DynamicAdaptedFeatureDetector,其可以指定检测的兴趣点数量,在 FAST 特征检测器中用法如下:

cv::DynamicAdaptedFeatureDetector fastD(
    new cv::FastAdjuster(40),   // 特征检测器
    150,                        // 最少特征数量
    200,                        // 最大特征数量
    50);                        // 最大迭代次数
fastD->detect(image, keypoints);

然后迭代地检测兴趣点,每次迭代后,检查检测到的兴趣点数量,并相应地调整检测器阈值以产生更多或更少的点;重复此过程,直到检测到的兴趣点数位于指定的区间内。
通过指定最大迭代次数,避免过多检测耗费太多时间。要以通用方式实现此方法,使用的 cv::FeatureDetector 类必须实现 cv::AdjusterAdapter 接口,该类包括一个 tooFew 方法和一个 tooMany 方法,这两个方法都会修改检测器的内部阈值以产生更多或更少的关键点;此外,还有一个断言方法,当检测器阈值仍然可以调整时返回 true
虽然可以使用 cv::DynamicAdaptedFeatureDetector 类获得适当数量的特征点,但是这需要以降低效率为代价;此外,该类无法保证一定会在指定的迭代次数内获得所需数量的特征点。
可以看到,我们将动态分配对象的地址作为参数传递,以指定适配器类将使用的特征检测器。我们无需手动释放分配的内存来避免内存泄漏,这是因为指针会被转移到 cv::Ptr<FeatureDetector> 参数,它会自动释放所指向的对象。
另一个有用的类适配器是 cv::GridAdaptedFeatureDetector 类,它会在图像上定义网格,然后,可以限制每个单元格包含的最大元素数量,以将检测到的关键点散布在图像上。在检测图像中的关键点时,通常会在特定纹理区域中看到集中的兴趣点。例如,在以上图像的眼睛周围检测到非常密集的 FAST 特征点,通过使用此类适配器可以改进检测结果:

cv::GridAdaptedFeatureDetector fastG(
    new cv::FastFeatureDetector(10),   // 特征检测器
    1200,                              // 最大特征点数量
    5,                                 // 网格行数
    2);                                // 网格列数
fastG->detect(image, keypoints);

类适配器通过使用提供的 cv::FeatureDetector 对象检测每个单元格上的特征点,还可以指定最大总特征点数,在每个单元格中只保留强度最大的数个点,以免超过指定的最大值。
cv::PyramidAdaptedFeatureDetector 适配器可以在图像金字塔上应用特征检测器,结果组合在关键点的输出向量中:

cv::PyramidAdaptedFeatureDetector fastP(
    new cv::FastFeatureDetector(60),   // 特征检测器
    3);                                // 金字塔层数
fastP->detect(image, keypoints);

每个点的坐标通过原始图像坐标指定,此外设置 cv::Keypoint 类的 size 属性,以便在原始分辨率的一半处检测到的点的大小是原始图像中检测到的点的大小的两倍。将 cv::drawKeypoints 函数中的 flag 参数设为 cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS,可以令绘制的半径等于关键点 size 属性。

3. 完整代码

头文件 (harrisDetector.h) 完整代码参考 Harris 特征点检测一节,主函数文件 (fastCorners.cpp) 完整代码如下所示:

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/xfeatures2d.hpp>

#include "harrisDetector.h"

int main() 
    // 读取图像
    cv::Mat image = cv::imread("1.png", 0);
    if (!image.data) return 0;
    cv::transpose(image, image);
    cv::flip(image, image, 0);
    cv::namedWindow("Original");
    cv::imshow("Original",image);
    std::vector<cv::KeyPoint> keypoints;
    // FAST 特征
    image = cv::imread("1.png", 0);
    cv::transpose(image, image);
    cv::flip(image, image, 0);
    keypoints.clear();
    // FAST 检测器
    cv::Ptr<cv::FastFeatureDetector> ptrFAST = cv::FastFeatureDetector::create(40);
    ptrFAST->detect(image, keypoints);
    cv::drawKeypoints(image, keypoints, image, cv::Scalar(255, 255, 255), cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);
    std::cout << "Number of keypoints (FAST): " << keypoints.size() << std::endl;
    cv::namedWindow("FAST");
    cv::imshow("FAST",image);
    // 未使用非极大值抑制的 FAST 特征
    image = cv::imread("1.png", 0);
    cv::transpose(image, image);
    cv::flip(image, image, 0);
    keypoints.clear();
    ptrFAST->setNonmaxSuppression(false);
    ptrFAST->detect(image, keypoints);
    cv::drawKeypoints(image, keypoints, image, cv::Scalar(255, 255, 255), cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);
    cv::namedWindow("FAST Features (all)");
    cv::imshow("FAST Features (all)",image);
    // 读取图像
    image = cv::imread("1.png", 0);
    cv::transpose(image, image);
    cv::flip(image, image, 0);
    int total(100);         // 关键点数量
    int hstep(5), vstep(3); // 4x3 的网格
    int hsize(image.cols/hstep), vsize(image.rows/vstep);
    int subtotal(total/(hstep*vstep));  // 每个网格中的关键点数量
    cv::Mat imageROI;
    std::vector<cv::KeyPoint> gridpoints;
    std::cout << "Grid of " << vstep << " by " << hstep << " each of size " << vsize << " by " << hsize << std::endl;
    // 使用低阈值探测
    ptrFAST->setThreshold(20);
    // 非极大值抑制
    ptrFAST->setNonmaxSuppression(true);
    keypoints.clear();
    for (int i=0; i<vstep; i++) 
        for (int j=0; j<hstep; j++) 
            // 在当前网格上创建ROI
            imageROI = image(cv::Rect(j*hsize, i*vsize, hsize, vsize));
            // 在网格中检测关键点
            gridpoints.clear();
            ptrFAST->detect(imageROI, gridpoints);
            std::cout << "Number of FAST in grid " << i << "," << j << ": " << gridpoints.size() << std::endl;
            if (gridpoints.size()>subtotal) 
                for (auto it=gridpoints.begin(); it!=gridpoints.end()+subtotal; ++it) 
                    std::cout << "  " << it->response << std::endl;
                
            
            // 获取最强 FAST 特征
            auto itEnd(gridpoints.end());
            if (gridpoints.size()>subtotal) 
                std::nth_element(gridpoints.begin(), 
                                gridpoints.end()+subtotal, 
                                gridpoints.end(),
                                [](cv::KeyPoint& a, cv::KeyPoint& b) return a.response>b.response;);
                itEnd = gridpoints.begin() + subtotal;
            
            // 添加到全局关键点向量
            for (auto it=gridpoints.begin(); it!=itEnd; ++it) 
                it->pt += cv::Point2f(j*hsize, i*vsize);
                keypoints.push_back(*it);
                std::cout << " " << it->response << std::endl;
            
        
    
    cv::drawKeypoints(image, keypoints, image, cv::Scalar(255, 255, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    cv::namedWindow("FAST Features (grid)");
    cv::imshow("FAST Features (grid)", image);
    cv::waitKey();
    return 0;

小结

为了解决 Harris 角点检测算法计算代价较为高昂的缺点,特征点检测算子 FAST (Features from Accelerated Segment Test) 被专门设计用于快速检测图像中的角点。本节,我们介绍了如何使用通用接口调用 OpenCV 中的 FAST 特征点检测算法。

系列链接

OpenCV实战(1)——OpenCV与图像处理基础
OpenCV实战(2)——OpenCV核心数据结构
OpenCV实战(3)——图像感兴趣区域
OpenCV实战(4)——像素操作
OpenCV实战(5)——图像运算详解
OpenCV实战(6)——OpenCV策略设计模式
OpenCV实战(7)——OpenCV色彩空间转换
OpenCV实战(8)——直方图详解
OpenCV实战(9)——基于反向投影直方图检测图像内容
OpenCV实战(10)——积分图像详解
OpenCV实战(11)——形态学变换详解
OpenCV实战(12)——图像滤波详解
OpenCV实战(13)——高通滤波器及其应用
OpenCV实战(14)——图像线条提取
OpenCV实战(15)——轮廓检测详解
OpenCV实战(16)——角点检测详解

opencv动态背景下运动目标检测 FAST+SURF+FLANN配准差分 17/12/13更新图片

FAST检测特征点+SURF描述特征点速度上要比SURF在多尺度下检测特征点后描述要快的多

在自己的电脑上做了两种实验的对比,通过VS性能分析可以看到结果

配置I5 2.7GHZ X64 VS2012 OPENCV249

代码中大津法二值化可以直接用opencv提供的大津法接口

 

 

代码功能

SURF提取描述

FAST提取SURF描述

特征点提取

24.2%

0.9%

特征点描述

25%

14.7%

特征点匹配

12.2%

8.9%

 

 

检测效果:


原软件界面:

 

 

 

怪自己手残,不应该中文命名的,调试会出现很多问题
 

 

// 动态背景目标探测Dlg.cpp : 实现文件
//

#include "stdafx.h"
#include "动态背景目标探测.h"
#include "动态背景目标探测Dlg.h"
#include "afxdialogex.h"
#include <opencv2/opencv.hpp>
#include <opencv2/nonfree/nonfree.hpp> 

using namespace cv;
using namespace std;

 

CString strFilePath; //视频文件名
VideoCapture capture;//视频源
Mat image01,image02;
bool bExit,bGetTemplat;
bool bFirst = true;//第一次SURF循环

Point g_pt(-1,-1);
C动态背景目标探测Dlg *dlg;
int picSize = 912;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// C动态背景目标探测Dlg 对话框



C动态背景目标探测Dlg::C动态背景目标探测Dlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(C动态背景目标探测Dlg::IDD, pParent)

	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);


void C动态背景目标探测Dlg::DoDataExchange(CDataExchange* pDX)

	CDialogEx::DoDataExchange(pDX);


BEGIN_MESSAGE_MAP(C动态背景目标探测Dlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_CHOSEFILE, &C动态背景目标探测Dlg::OnBnClickedChosefile)
    ON_BN_CLICKED(IDC_TARGET, &C动态背景目标探测Dlg::OnBnClickedTarget)
    ON_BN_CLICKED(IDC_STOP, &C动态背景目标探测Dlg::OnBnClickedStop)
END_MESSAGE_MAP()


// C动态背景目标探测Dlg 消息处理程序

BOOL C动态背景目标探测Dlg::OnInitDialog()

	CDialogEx::OnInitDialog();

	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

    HWND hWnd ;
    HWND hParent;

    namedWindow("pic", WINDOW_AUTOSIZE);
    hWnd = (HWND)cvGetWindowHandle("pic");
    hParent = ::GetParent(hWnd);
    ::SetParent(hWnd, GetDlgItem(IDC_PIC)->m_hWnd);
    ::ShowWindow(hParent, SW_HIDE);

    namedWindow("diff", WINDOW_AUTOSIZE);
    hWnd = (HWND)cvGetWindowHandle("diff");
    hParent = ::GetParent(hWnd);
    ::SetParent(hWnd, GetDlgItem(IDC_DIFF)->m_hWnd);
    ::ShowWindow(hParent, SW_HIDE);

    SetDlgItemInt(IDC_THRESHOLD, 21);
    dlg = (C动态背景目标探测Dlg*)theApp.m_pMainWnd;

    bExit = 0;
    bGetTemplat = 0;

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE


// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void C动态背景目标探测Dlg::OnPaint()

	if (IsIconic())
	
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	
	else
	
		CDialogEx::OnPaint();
	


//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR C动态背景目标探测Dlg::OnQueryDragIcon()

	return static_cast<HCURSOR>(m_hIcon);




void C动态背景目标探测Dlg::OnBnClickedChosefile()

    // 设置过滤器   
    TCHAR szFilter[] = _T("|所有文件(*.*)|*.*||");   
    // 构造打开文件对话框   
    CFileDialog fileDlg(TRUE, _T(""), NULL, 0, szFilter, this);   
      
    // 显示打开文件对话框   
    if (IDOK == fileDlg.DoModal())   
       
        // 如果点击了文件对话框上的“打开”按钮,则将选择的文件路径显示到编辑框里   
        strFilePath = fileDlg.GetPathName();   

        capture.open(string(strFilePath));
        bExit = 0;
        bFirst = TRUE;
       


//对轮廓按面积降序排列  
bool biggerSort(vector<Point> v1, vector<Point> v2)  
  
    return contourArea(v1)>contourArea(v2);  
  

void on_MouseHandle(int event, int x, int y, int flags, void* param)

    Mat& image = *(cv::Mat*) param;
    switch(event)
    
        //左键按下消息
    case EVENT_LBUTTONDOWN: 
        
            g_pt = Point(x, y);
            CString str;
            
            CRect rect;
            dlg->GetDlgItem(IDC_PIC)->GetClientRect(&rect);
            str.Format("%d,%d", int(g_pt.x*picSize/rect.right), int(g_pt.y*picSize/rect.bottom));
            dlg->SetDlgItemText(IDC_POS, str);

            g_pt.x = int(g_pt.x*picSize/rect.right);
            g_pt.y = int(g_pt.y*picSize/rect.bottom);

            bGetTemplat = TRUE;
            break;
        
    case EVENT_RBUTTONDOWN:
        
            bGetTemplat = FALSE;
            break;
        
        break;
    


//大津法求阈值函数
int otsuThreshold(IplImage* img)

	int T = 0;//阈值
	int height = img->height;
	int width  = img->width;
	int step      = img->widthStep;
	int channels  = img->nChannels;
	uchar* data  = (uchar*)img->imageData;
	double gSum0;//第一类灰度总值
	double gSum1;//第二类灰度总值
	double N0 = 0;//前景像素数
	double N1 = 0;//背景像素数
	double u0 = 0;//前景像素平均灰度
	double u1 = 0;//背景像素平均灰度
	double w0 = 0;//前景像素点数占整幅图像的比例为ω0
	double w1 = 0;//背景像素点数占整幅图像的比例为ω1
	double u = 0;//总平均灰度
	double tempg = -1;//临时类间方差
	double g = -1;//类间方差
	double Histogram[256]=0;// = new double[256];//灰度直方图
	double N = width*height;//总像素数
	for(int i=0;i<height;i++)
	//计算直方图
		for(int j=0;j<width;j++)
		
			double temp =data[i*step + j * 3] * 0.114 + data[i*step + j * 3+1] * 0.587 + data[i*step + j * 3+2] * 0.299;
			temp = temp<0? 0:temp;
			temp = temp>255? 255:temp;
			Histogram[(int)temp]++;
		 
	
	//计算阈值
	for (int i = 0;i<256;i++)
	
		gSum0 = 0;
		gSum1 = 0;
		N0 += Histogram[i];         
		N1 = N-N0;
		if(0==N1)break;//当出现前景无像素点时,跳出循环
		w0 = N0/N;
		w1 = 1-w0;
		for (int j = 0;j<=i;j++)
		
			gSum0 += j*Histogram[j];
		
		u0 = gSum0/N0;
		for(int k = i+1;k<256;k++)
		
			gSum1 += k*Histogram[k];
		
		u1 = gSum1/N1;
		//u = w0*u0 + w1*u1;
		g = w0*w1*(u0-u1)*(u0-u1);
		if (tempg<g)
		
			tempg = g;
			T = i;
		
	
	return T; 


//检测与点选线程函数
UINT C动态背景目标探测Dlg::ThreadFunc(LPVOID pParam)  
  
    //线程函数实现  
    C动态背景目标探测Dlg *dlg = (C动态背景目标探测Dlg*)pParam;  

    Mat image1,image2;   
    Mat imagetemp1,imagetemp2;
    Mat temp,image02temp;
    vector<Rect> target;
    Mat imageGray1,imageGray2;


    namedWindow("pic");
    setMouseCallback("pic",on_MouseHandle,(void*)&image02temp);

    FastFeatureDetector fast(130);//FAST特征点检测
    //SurfFeatureDetector surfDetector(4000);//SURF特征点检测

    vector<KeyPoint> keyPoint1,keyPoint2;//两幅图中检测到的特征点
   
    SurfDescriptorExtractor SurfDescriptor;//SURF特征点描述 
    Mat imageDesc1,imageDesc2;

    while (!bExit)
    
        if (!bGetTemplat)
        
            target.clear();

            if (bFirst)//第一次处理
            
                //前一帧图
                capture >> imagetemp1;
                if (imagetemp1.empty())
                
                    bExit = TRUE;
					bFirst = TRUE;
					bGetTemplat = FALSE;
                    break;
                
                //这里因为我处理需要,从原视频中抠出picSize*picSize部分
                if (imagetemp1.cols > picSize + 200 && imagetemp1.rows > picSize)
                
                    image01 = imagetemp1(Rect(200, 0, picSize, picSize));
                
                else
                
                    image01 = imagetemp1.clone();
                
                
                //后一帧图
                capture >> imagetemp2;
                capture >> imagetemp2;
                capture >> imagetemp2;
                if (imagetemp2.empty())
                
                    bExit = TRUE;
					bFirst = TRUE;
					bGetTemplat = FALSE;
                    break;
                
                //这里因为我处理需要,从原视频中抠出picSize*picSize部分
                if (imagetemp2.cols > picSize + 200 && imagetemp2.rows > picSize)
                
                    image02 = imagetemp2(Rect(200, 0, picSize, picSize));
                
                else
                
                    image02 = imagetemp2.clone();
                


                //灰度图转换 
                cvtColor(image01,image1,CV_RGB2GRAY);  
                cvtColor(image02,image2,CV_RGB2GRAY);  

                //提取特征点  
                fast.detect(image1,keyPoint1);//FAST特征点提取
                fast.detect(image2,keyPoint2);//FAST特征点提取
                //surfDetector.detect(image1,keyPoint1);//SURF特征点提取
                //surfDetector.detect(image2,keyPoint2);//SURF特征点提取    

                //特征点描述,为下边的特征点匹配做准备    
                SurfDescriptor.compute(image1,keyPoint1,imageDesc1);//SURF特征点描述    
                SurfDescriptor.compute(image2,keyPoint2,imageDesc2);//SURF特征点描述       

                bFirst = false;
            
            else//对于后面的处理,只需要提取一帧图像的特征点就可以了,把上次的结果给这次的第一帧
            
                image01 = image02.clone();
                imageDesc1 = imageDesc2.clone();
                keyPoint1 = keyPoint2;

                //后一帧图
                capture >> imagetemp2;
                capture >> imagetemp2;
                capture >> imagetemp2;
                if (imagetemp2.empty())
                
                    bExit = TRUE;
					bFirst = TRUE;
					bGetTemplat = FALSE;
                    break;
                
                //这里因为我处理需要,从原视频中抠出picSize*picSize部分
                if (imagetemp2.cols > picSize + 200 && imagetemp2.rows > picSize)
                
                    image02 = imagetemp2(Rect(200, 0, picSize, picSize));
                
                else
                
                    image02 = imagetemp2.clone();
                

                //灰度图转换 
                cvtColor(image02,image2,CV_RGB2GRAY);  

                double time0 = static_cast<double>(getTickCount());//开始计时,需要计时的是FAST-SURF配准的时间

                //提取特征点       
                fast.detect(image2,keyPoint2);
                //surfDetector.detect(image2,keyPoint2);    

                //特征点描述,为下边的特征点匹配做准备      
                SurfDescriptor.compute(image2,keyPoint2,imageDesc2);      

                //获得匹配特征点,并提取最优配对     
                FlannBasedMatcher matcher;  
                vector<DMatch> matchePoints;    
                matcher.match(imageDesc1,imageDesc2,matchePoints,Mat());  
                sort(matchePoints.begin(),matchePoints.end()); //特征点排序    

                vector<Point2f> imagePoints1,imagePoints2;      
                if (matchePoints.size()<50)//对特征点的数量做一个限制
                
                    continue;
                

                //筛除误匹配特征点
                for(int i=0; i<matchePoints.size()*0.5; i++)  
                         
                    imagePoints1.push_back(keyPoint1[matchePoints[i].queryIdx].pt);       
                    imagePoints2.push_back(keyPoint2[matchePoints[i].trainIdx].pt);       
                  

                //获取图像1到图像2的投影映射矩阵 尺寸为3*3  
                Mat homo=findHomography(imagePoints1,imagePoints2,CV_RANSAC);  
                也可以使用getPerspectiveTransform方法获得透视变换矩阵,不过要求只能有4个点,效果稍差  
                //Mat   homo=getPerspectiveTransform(imagePoints1,imagePoints2);     

                //cout<<"变换矩阵为:\\n"<<homo<<endl<<endl; //输出映射矩阵  
                //图像配准  
                Mat imageTransform1,imgpeizhun,imgerzhi;  
                warpPerspective(image01,imageTransform1,homo,Size(image02.cols,image02.rows));    
                //imshow("经过透视矩阵变换后",imageTransform1);  

                absdiff(image02, imageTransform1, imgpeizhun);
                //imshow("配准diff", imgpeizhun);  

				int t = otsuThreshold(&IplImage(imgpeizhun));//大津法得到差分图的二值化阈值
                threshold(imgpeizhun, imgerzhi, t, 255.0 , CV_THRESH_BINARY);
                //imshow("配准二值化", imgerzhi);

                image02temp = image02.clone();

                cvtColor(imgerzhi,temp,CV_RGB2GRAY);  

                //检索连通域
				Mat se=getStructuringElement(MORPH_RECT, Size(3,3));
				morphologyEx(temp, temp, MORPH_OPEN, se);
				int dialate_size = dlg->GetDlgItemInt(IDC_THRESHOLD);
                Mat se2=getStructuringElement(MORPH_RECT, Size(dialate_size,dialate_size));
                morphologyEx(temp, temp, MORPH_DILATE, se2);


                vector<vector<Point>> contours;
                findContours(temp, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

                //轮廓数量的筛选
                if (contours.size()<1)
                
                    continue;
                

                //std::sort(contours.begin(), contours.end(), biggerSort);//轮廓大小的排序,这里注释了因为计算费事

                float m_BiLi = 0.8;//由于两幅图配准,边缘不会一致,因此对原图大小0.8的比例中搜索检测到的目标

                for (int k = 0; k < contours.size(); k++)
                
                    Rect bomen = boundingRect(contours[k]);

                    //省略由于配准带来的边缘无效信息
                    if (bomen.x > image02temp.cols * (1 - m_BiLi) && bomen.y > image02temp.rows * (1 - m_BiLi) 
                        && bomen.x + bomen.width < image02temp.cols * m_BiLi && bomen.y + bomen.height < image02temp.rows * m_BiLi
                        /*&& contourArea(contours[k]) > contourArea(contours[0])/10*/
                        && contourArea(contours[k]) > 900 && contourArea(contours[k]) < 30000)
                    
                        rectangle(image02temp, bomen, Scalar(0,0,255), 4, 8, 0);
                        target.push_back(bomen);
                    

                

                //输出帧率
                time0 = ((double)getTickCount()-time0)/getTickFrequency();
                dlg->SetDlgItemInt(IDC_EDIT_FRE, (int)(1/time0), 1);

                //显示图像
                CRect rect;
                dlg -> GetDlgItem(IDC_DIFF)->GetClientRect(&rect);
                resize(temp, temp, cv::Size(rect.Width(), rect.Height()));
                imshow("diff", temp);

                dlg -> GetDlgItem(IDC_PIC)->GetClientRect(&rect);
                resize(image02temp, image02temp, cv::Size(rect.Width(), rect.Height()));
                imshow("pic", image02temp);

                waitKey(20);  
                      
        
        else//鼠标点击 选择目标,可以进行跟踪(这里没写跟踪部分,只写了点选部分)
        
            int minIndex;
            int minDis = 9999999;
            int aimDis;

            //距离与点选的坐标最近的目标是哪个
            for (int i = 0; i < target.size(); i++)
            
                aimDis = sqrt((target[i].x + target[i].width/2 - g_pt.x) * (target[i].x + target[i].width/2 - g_pt.x) + (target[i].y + target[i].height/2 - g_pt.y) * (target[i].y + target[i].height/2 - g_pt.y));
                if (aimDis < minDis)
                
                    minDis = aimDis;
                    minIndex = i;
                
            

            //绘制点选目标
            rectangle(image02, target[minIndex], Scalar(0,255,255), 4, 8, 0);

            //显示点选目标
            CRect rect;
            Mat image02show;
            dlg -> GetDlgItem(IDC_PIC)->GetClientRect(&rect);
            resize(image02, image02show, cv::Size(rect.Width(), rect.Height()));
            imshow("pic", image02show);

            waitKey(20);  
        
        
    
    return 0;  
  


void C动态背景目标探测Dlg::OnBnClickedTarget()

    AfxBeginThread(ThreadFunc, this); //启动线程



void C动态背景目标探测Dlg::OnBnClickedStop()

    capture.release();
    bExit = TRUE;
	bGetTemplat = FALSE;

 

 

 

 

                FAST(image1, keyPoint1, dlg->GetDlgItemInt(IDC_EDIT_FAST));
                FAST(image2, keyPoint2, dlg->GetDlgItemInt(IDC_EDIT_FAST));
                //fast.detect(image1,keyPoint1);//FAST特征点提取
                //fast.detect(image2,keyPoint2);//FAST特征点提取
  17/12/13:测试图片,背景是动态的,车辆也是动态的

 

以上是关于OpenCV实战(17)——FAST特征点检测的主要内容,如果未能解决你的问题,请参考以下文章

opencv实现一种改进的Fast特征检测算法

OpenCV 例程 300篇243. 特征检测之 FAST 算法

OpenCV 例程 300篇243. 特征检测之 FAST 算法

OpenCV 例程 300篇243. 特征检测之 FAST 算法

OpenCV-Python教程:38.FAST角点检测算法

OpenCV中ORB特征提取与匹配