# Harris角点检测

# 基础理论

在处理适用于多幅图像的特征描述子时,主要有两种特征检测方法,一种是基于角的检测,另一种是处理图像中的所有区域。这里主要讨论一下基于角的检测。

1988年,Harris and Stephens 提出了一种角检测算法, HS角检测器,见论文A Combined Corner and Edge Detector (opens new window)

HS检测器原理如下,对于灰度变化有三种情况,1是各个方向上灰度均不变化, 2是在某个方向上变化, 但在另一个方向灰度不变,3是两个方向都发生变化。HS角检测器是使用数学公式来区分这三种情况。

在这里插入图片描述

f表示图像,f(s, t)表示由 (s, t)的值定义的一小块图像 patch,f(s+x, t+y)是尺寸相同但移动了 (x,y)的小块图像 patch,两个图像块每个像素像素值间差的平方的加权和表示为:

是一个加权函数。有角的地方,即C(x,y)取最大值。取一个像素点,在其相邻像素计算

表示泰勒展开的近似,

然后:

上述方程可以表示为矩阵形式:

C(x,y) = [x,y]M\begin{bmatrix} x\\ y \end{bmatrix}

其中,

A = \begin{bmatrix} f_x^2 & f_xf_y\\ f_xf_y & f_y^2 \end{bmatrix}

有时被称为Harris矩阵。

矩阵的特征向量指向最大的数据扩展方向,且对应的特征值与特征向量方向上的数据扩展量成正比。由此可以知道,两个小特征值表示几乎恒定的灰度,一个小特征值和一个大特征值表示存在垂直边界或水平边界,两个大特征值表示存在一个孤立的点或角。

然而,由于求解特征值计算开销较大,HS角检测器并未使用特征值,而是利用了方阵的性质,迹等于该矩阵的特征值之和,行列式等于特征值的积,

在这里插入图片描述

是一个常数,通常取

两个特征值都大时, R取较大的正值, 一个特征值较大一个特征值较小时,R取较大负值,两个特征值都较小时,R的绝对值较小

k可以看作一个敏感因子,k越小找到的角越多

Shi-Tomasi 角点检测器

1994年,Jianbo Shi and Carlo Tomasi提出Good features to track (opens new window)
直接使用作为度量,避免了超参数

# OpenCV API

# cornerHarris (opens new window)

void cv::cornerHarris(
    InputArray 	src,
    OutputArray 	dst,
    int 	blockSize,
    int 	ksize,
    double 	k,
    int 	borderType = BORDER_DEFAULT 
)	
  • src:输入,8位单通道图像,灰度图
  • dst:存储Harris检测结果的数据,数据类型CV_32FC1
  • blockSize:计算C(x,y)时邻域的大小
  • ksize:使用Sobel算子计算梯度时,卷积核的大小
  • k:计算R时的超参数,即检测角的敏感度的调解因子,小则角多,大则角少
  • borderType:边界像素的处理方式

# goodFeaturesToTrack (opens new window)

void cv::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.先使用cornerHarris计算每个角的度量

2.在每个像素3x3的邻域范围执行极大值抑制

3.每个角对应的Harris矩阵的最小特征值小于,该角将被舍弃

4.将余下的角根据quality measure降序排列

5.移除那些距离小于maxDistance的角点

  • image:单通道图像
  • corners:角的输出向量
  • maxCorners: 最多支持检测到的角的个数
  • qualityLevel: 控制角的质量水平,例如最好的是1500, qualityLevel0.01,则quality measure小于1500*0.01的将被舍弃
  • minDistance: 角与角之间的最小距离
  • mask:掩码,控制对图像哪一部分进行角点检测
  • blockSize: 计算梯度相关矩阵时使用的邻域大小,参考cornerEigenValsAndVecs
  • useHarrisDetector:是否使用Harris角点检测
  • k:Harris角点检测的超参数

# 示例

#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream>

using namespace cv;
using namespace std;

Mat src, src_gray;


int maxCorners = 23;
int maxTrackbar = 100;
RNG rng(12345);

int thresh = 200;
int max_thresh = 255;
const char* source_window = "Source image";
const char* corners_window = "Corners detected";

void cornerHarris_demo( int, void* );
void goodFeaturesToTrack_Demo( int, void* );

int main( int argc, char** argv )
{
    CommandLineParser parser( argc, argv, "{@input | building.jpg | input image}" );
    src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
    if ( src.empty() )
    {
        cout << "Could not open or find the image!\n" << endl;
        cout << "Usage: " << argv[0] << " <Input image>" << endl;
        return -1;
    }
    cvtColor( src, src_gray, COLOR_BGR2GRAY );
    namedWindow( source_window );
    createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo );
    imshow( source_window, src );
    // cornerHarris_demo( 0, 0 );
    goodFeaturesToTrack_Demo( 0, 0 );
    waitKey();
    return 0;
}

void cornerHarris_demo( int, void* )
{
    int blockSize = 2;
    int apertureSize = 3;
    double k = 0.04;
    Mat dst = Mat::zeros( src.size(), CV_32FC1 );
    cornerHarris( src_gray, dst, blockSize, apertureSize, k );
    Mat dst_norm, dst_norm_scaled;
    normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
    convertScaleAbs( dst_norm, dst_norm_scaled );
    for( int i = 0; i < dst_norm.rows ; i++ )
    {
        for( int j = 0; j < dst_norm.cols; j++ )
        {
            if( (int) dst_norm.at<float>(i,j) > thresh )
            {
                circle( dst_norm_scaled, Point(j,i), 5,  Scalar(0), 2, 8, 0 );
            }
        }
    }
    namedWindow( corners_window );
    imshow( corners_window, dst_norm_scaled );
    imwrite("corner_grid.png", dst_norm_scaled);
}


void goodFeaturesToTrack_Demo( int, void* )
{
    maxCorners = MAX(maxCorners, 1);
    vector<Point2f> corners;
    double qualityLevel = 0.01;
    double minDistance = 10;
    int blockSize = 3, gradientSize = 3;
    bool useHarrisDetector = false;
    double k = 0.04;
    Mat copy = src.clone();
    goodFeaturesToTrack( src_gray,
                         corners,
                         maxCorners,
                         qualityLevel,
                         minDistance,
                         Mat(),
                         blockSize,
                         gradientSize,
                         useHarrisDetector,
                         k );
    cout << "** Number of corners detected: " << corners.size() << endl;
    int radius = 8;
    for( size_t i = 0; i < corners.size(); i++ )
    {
        circle( copy, corners[i], radius, Scalar(0, 0, rng.uniform(0, 256)), FILLED );
    }
    namedWindow( source_window );
    imwrite("corner_grid_st.png", copy);
    imshow( source_window, copy );
}

在这里插入图片描述

# reference