0
点赞
收藏
分享

微信扫一扫

(八)HS角点检测

1.基础理论

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

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

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

在这里插入图片描述

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

C ( x , y ) = ∑ ∑ ω ( s , t ) [ f ( s + x , t + y ) − f ( s , t ) ] 2 C(x,y) = \sum\sum\omega(s,t)[f(s+x, t+y)-f(s,t)]^2 C(x,y)=ω(s,t)[f(s+x,t+y)f(s,t)]2

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

f ( s + x , t + y ) f(s+x, t+y) f(s+x,t+y)表示泰勒展开的近似,

f ( s + x , t + y ) ≈ f ( s , t ) + x f x ( s , t ) + y f y ( s , t ) f(s+x, t+y)\approx f(s,t)+xf_x(s,t)+yf_y(s,t) f(s+x,t+y)f(s,t)+xfx(s,t)+yfy(s,t)

然后:

C ( x , y ) = ∑ ∑ ω ( s , t ) [ x f x ( s , t ) + y f y ( s , t ) ] 2 C(x,y) = \sum\sum\omega(s,t)[xf_x(s,t)+yf_y(s,t)]^2 C(x,y)=ω(s,t)[xfx(s,t)+yfy(s,t)]2

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

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

其中,

M = ∑ ∑ ω ( s , t ) A M = \sum\sum\omega(s,t)A M=ω(s,t)A

A = [ f x 2 f x f y f x f y f y 2 ] A = \begin{bmatrix} f_x^2 & f_xf_y\\ f_xf_y & f_y^2 \end{bmatrix} A=[fx2fxfyfxfyfy2]

M M M有时被称为Harris矩阵。

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

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

R = λ x λ y − k ( λ x + λ y ) 2 = d e t ( M ) − k t r a c e 2 ( M ) R=\lambda_x\lambda_y - k(\lambda_x + \lambda_y)^2=det(M)-ktrace^2(M) R=λxλyk(λx+λy)2=det(M)ktrace2(M)

在这里插入图片描述

k k k是一个常数,通常取 ( 0 , 0.25 ) (0, 0.25) (0,0.25)

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

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

Shi-Tomasi 角点检测器

1994年,Jianbo Shi and Carlo Tomasi提出Good features to track
直接使用 R = m i n ( λ x , λ y ) R=min(\lambda_x,\lambda_y) R=min(λx,λy)作为度量,避免了超参数 k k k

2.OpenCV API

2.1cornerHarris

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:边界像素的处理方式

2.2 goodFeaturesToTrack

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矩阵的最小特征值小于 q u a l i t y L e v e l ∗ m a x ( q u a l i t y M e a s u r e M a p ( x , y ) ) qualityLevel*max(qualityMeasureMap(x,y)) qualityLevelmax(qualityMeasureMap(x,y)),该角将被舍弃

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角点检测的超参数

3.示例

#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 );
}

在这里插入图片描述

举报

相关推荐

0 条评论