【opencv】goodFeaturesToTrack源码分析-2-Shi-Tomasi角点检测_Denny#的博客-程序员宅基地

技术标签: 算法  源码分析  【opencv】  强角点  opencv  shi-tomasi  

本文章是【opencv】goodFeaturesToTrack源码分析-1的后续,主要描述Shi-Tomasi角点检测算法原理及opencv实现。

1、算法原理

Shi-Tomasi算法是Harris算法的改进,在Harris算法中,是根据协方差矩阵M的两个特征值的组合来判断是否角点。而在Shi-Tomasi算法中,是根据较小的特征值是否大于阈值来判断是否角点。
这个判断依据是:较小的特征值表示在该特征值方向上的方差较小,较小的方差如果都能够大于阈值,在这个方向上的变化满足是角点的判断要求。
协方差矩阵 M 如下表示: w(x,y) 表示窗函数,通常不使用。 IxIy 分别表示在x及y方向上的差分。

M=x,yw(x,y)[I2xIxIyIxIyI2y]

这里我们需要关心的是,如何求解矩阵M的特征值?
求解特征值,一般求解这样的一个 λ ,使得:

det(λIA)=0

对于 A 这样一个2x2矩阵来说,我们可以分解为:
det(λ[1001]A)=det([λ00λ][adbc])=0

(λa)(λc)bd=λ2(a+c)λ+acbd=0

根据求根公式,有:
λ=(a+c)±(a+c)24(acbd)2=(a+c)±(ac)2+4bd2

带入具体的 M ,较小的特征值为:
λ=(I2x+I2y)(I2xI2y)2+4(IxIy)22

后续在具体的opencv源码分析中就会看到该公式的应用。

2、算法实现

该算法是作为计算最小特征值被goodFeaturesToTrack调用的,调用的位置为:

void cv::goodFeaturesToTrack( )
{
        ...
// 计算最小特征值
        cornerMinEigenVal( image, eig, blockSize, 3 );
        ...
}

在:..\sources\modules\imgproc\src\Featureselect.cpp中。

可以看到该算法的接口是

void cv::cornerMinEigenVal( InputArray _src, OutputArray _dst, int blockSize, int ksize, int borderType ){


cornerEigenValsVecs( const Mat& src, Mat& eigenv, int block_size,
                     int aperture_size, int op_type, double k=0.,
                     int borderType=BORDER_DEFAULT )
}

具体实现在:…\sources\modules\imgproc\src\Corner.cpp


static void
cornerEigenValsVecs( const Mat& src, Mat& eigenv, int block_size,
                     int aperture_size, int op_type, double k=0.,
                     int borderType=BORDER_DEFAULT )
{
    //计算缩放参数
    int depth = src.depth();
    double scale = (double)(1 << ((aperture_size > 0 ? aperture_size : 3) - 1)) * block_size;
    if( aperture_size < 0 )
        scale *= 2.0;
    if( depth == CV_8U )
        scale *= 255.0;
    scale = 1.0/scale;

    CV_Assert( src.type() == CV_8UC1 || src.type() == CV_32FC1 );

    //sobel或者scharr计算分别在x方向及y方向的梯度,即Ix Iy
    Mat Dx, Dy;
    if( aperture_size > 0 )
    {
        Sobel( src, Dx, CV_32F, 1, 0, aperture_size, scale, 0, borderType );
        Sobel( src, Dy, CV_32F, 0, 1, aperture_size, scale, 0, borderType );
    }
    else
    {
        Scharr( src, Dx, CV_32F, 1, 0, scale, 0, borderType );
        Scharr( src, Dy, CV_32F, 0, 1, scale, 0, borderType );
    }

    Size size = src.size();
    Mat cov( size, CV_32FC3 );
    int i, j;
    // 计算Ix^2 Iy^2 IxIy
    for( i = 0; i < size.height; i++ )
    {
        float* cov_data = cov.ptr<float>(i);
        const float* dxdata = Dx.ptr<float>(i);
        const float* dydata = Dy.ptr<float>(i);
        j = 0;

        #if CV_NEON
        for( ; j <= size.width - 4; j += 4 )
        {
            float32x4_t v_dx = vld1q_f32(dxdata + j);
            float32x4_t v_dy = vld1q_f32(dydata + j);

            float32x4x3_t v_dst;
            v_dst.val[0] = vmulq_f32(v_dx, v_dx);
            v_dst.val[1] = vmulq_f32(v_dx, v_dy);
            v_dst.val[2] = vmulq_f32(v_dy, v_dy);

            vst3q_f32(cov_data + j * 3, v_dst);
        }
        for( ; j < size.width; j++ )
        {
            float dx = dxdata[j];
            float dy = dydata[j];

            cov_data[j*3] = dx*dx;
            cov_data[j*3+1] = dx*dy;
            cov_data[j*3+2] = dy*dy;
        }
    }

    //求 ∑Ix^2 ∑Iy^2 ∑IxIy
    boxFilter(cov, cov, cov.depth(), Size(block_size, block_size),
        Point(-1,-1), false, borderType );

    // 调用 calcMinEigenVal 计算特征值
    if( op_type == MINEIGENVAL )
        calcMinEigenVal( cov, eigenv );
    else if( op_type == HARRIS )
        calcHarris( cov, eigenv, k );
    else if( op_type == EIGENVALSVECS )
        calcEigenValsVecs( cov, eigenv );
}


在上述代码中,我们可以看到,代码实现是按照算法原理来完成的:

1、先求 IxIy
2、再求对应的 I2xI2yIxIy 及其 I2x IxIy I2y
3、最后按照公式计算特征值

在计算特征值时,是调用calcMinEigenVal() 。该接口是根据以上公式

λ=(I2x+I2y)(I2xI2y)2+4(IxIy)22=(I2x2+I2y2)(I2x2I2y2)2+(IxIy)2
计算特征值。
从下面的代码可以看出, a=I2x2 b=IxIy c=I2y2

static void calcMinEigenVal( const Mat& _cov, Mat& _dst )
{
    int i, j;
    Size size = _cov.size();
#if CV_SSE
    volatile bool simd = checkHardwareSupport(CV_CPU_SSE);
#endif

    if( _cov.isContinuous() && _dst.isContinuous() )
    {
        size.width *= size.height;
        size.height = 1;
    }

    for( i = 0; i < size.height; i++ )
    {
        const float* cov = _cov.ptr<float>(i);
        float* dst = _dst.ptr<float>(i);
        j = 0;

    #if CV_NEON
        float32x4_t v_half = vdupq_n_f32(0.5f);
        for( ; j <= size.width - 4; j += 4 )
        {
            float32x4x3_t v_src = vld3q_f32(cov + j * 3);
            float32x4_t v_a = vmulq_f32(v_src.val[0], v_half);
            float32x4_t v_b = v_src.val[1];
            float32x4_t v_c = vmulq_f32(v_src.val[2], v_half);

            float32x4_t v_t = vsubq_f32(v_a, v_c);
            v_t = vmlaq_f32(vmulq_f32(v_t, v_t), v_b, v_b);
            vst1q_f32(dst + j, vsubq_f32(vaddq_f32(v_a, v_c), cv_vsqrtq_f32(v_t)));
        }
    #endif
        for( ; j < size.width; j++ )
        {
            float a = cov[j*3]*0.5f;
            float b = cov[j*3+1];
            float c = cov[j*3+2]*0.5f;
            dst[j] = (float)((a + c) - std::sqrt((a - c)*(a - c) + b*b));
        }
    }
}

值得注意的是opencv中也提供NEON优化的代码,根据是否定义宏CV_NEON来判断是否使用NEON优化。对于学习NEON优化很有启发。

后续将对这里的sobel滤波及其boxfilter进行分析。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jaych/article/details/51146923

智能推荐

搭建Eclipse+MyEclipse开发环境 for j2ee-程序员宅基地

一、安装JDK首先下载JDK 5.0(JDK 5.0的下载页面为:http://java.sun.com/j2se/1.5.0/download.jsp);然后运行JDK 5.0安装程序jdk-1_5_0_06-windows-i586-p.exe,安装过程中所有选项保持默认;最后配置JDK的环境变量:在“我的电脑”上点右键—>“属性”—>“高级”—> “环境变量(N)”。新建系统变量

超简单 安装oracle数据库 (RedHat 5.4 + Oracle 10g)_65000.net-程序员宅基地

超简单安装数据库(RedHat 5.4 + Oracle 10g)1. 创建用户和组:[root@localhost]# groupadd oinstall[root@localhost]# groupadd dba[root@localhost]# useradd -g oinstall -G dba oracle[root@localhost]# passwd oracle_65000.net

css div li等 里面放图片一行n个多余n个依次向下一行三个排列-程序员宅基地

效果图css代码片段display: flex;flex-direction:row;flex-wrap:wrap;align-content: space-between;

odoo中权限的定义-程序员宅基地

odoo中权限的定义一.定义xml文件在quality_control_security.xml中定义 控制权限分类分为user和manager两大类如下:Quality control<record id="group_quality_control_user" model="res.groups"> <field name="name">Use...

一种造成“无法编译所做的编辑”的情况-程序员宅基地

最近有一个winform项目,突然出现在调试时作出修改后(即使只是增加一个空格或敲回车键空一行),不能继续编译,一直提示“无法编译所做的编辑”,必须重新启动。百思不得其解,在网上搜了一圈,遇到这个情况的不少,但好像产生的原因并不一样,不过也是受了些启发,想到可能与最后添加的一个引用程序集有问题,之前从.net4.0降级到.net3.5时,一些引用的“嵌入互操作类型”变为TRUE,导致不能通过编译,_无法编译所做的编辑

SpringBoot中Formatter和Converter用法和区别_current token (value_string) not numeric, can not _云庄clouder的博客-程序员宅基地

功能区别:两者的作用一样,都是类型转换。 org.springframework.format.Formatter只能做String类型到其他类型的转换。 org.springframework.core.convert.converter.Converter可以做任意类型的转换。 Converter是一般工具,可以将一种类型转换成另一种类型。例如,将String转换成Date,或者将Long转换成Date。Converter既可以用在web层,也可以用在其它层中。Formatter只能将Strin_current token (value_string) not numeric, can not use numeric value accessor

随便推点

考研二战日记-第52天——概率论:数理统计的基本概念_考研数理统计的基本概念好难-程序员宅基地

摘要:考研数学重在知识点的理解和综合应用,在做题的过程中,数学公式成为解题的重要工具。因此,扎实掌握考研数学的重点公式可以大大提高我们的做题效率。考研帮分章节整理考研数学概率论与数理统计部分的重点公式,旨在帮助大家理清重点,做到经常回顾,配合习题练习做到知识的灵活应用,今天我们一起了解下数理统计的基本概念有哪些重点公式。以上内容由考研帮根据考研官方教材整理发布..._考研数理统计的基本概念好难

svn 使用采坑记录-程序员宅基地

1、每天下班前编译通过的代码提交:养成下班提交编译通过的代码,上班更新代码的习惯,有助于减少合并bug的产生。

Azure Kinect DK 深度相机,Ubuntu 16.04和18.04安装SDK踩坑记录,ROS版本的驱动安装使用_azure kinect dk 深度相机,ubuntu 16.04-程序员宅基地

安装ROS,ubuntu 16.04 源码安装SDK,ubuntu 18.04官网教程安装SDK,ROS驱动安装使用。_azure kinect dk 深度相机,ubuntu 16.04

【Python基础】python如何把秒换成时分秒_python将秒数转换成分秒-程序员宅基地

1、在python中可以使用下面的方法将秒数转换为时分秒:seconds=5555m, s = divmod(seconds, 60)h, m = divmod(m, 60)print ("%02d:%02d:%02d" % (h, m, s))输出结果:01:32:35python divmod() 函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)。2、使用strftime()与gmtime()函数把秒转换为时分秒from time impor_python将秒数转换成分秒

ES6学习笔记——Promise、Generator、Async解决异步_generator是异步请求_hsy_siying的博客-程序员宅基地

一、Async—异步1.Async是Generator的语法糖,Generator是解决异步请求的基础2. Async函数的执行和普通函数一致,只需要一行代码即可,因为他具有内置的执行。3.Async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。4.await命令只能出现在async函数内部,否则会报错二、Generator1.执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。通过.next()方法来_generator是异步请求

apicloud的使用-程序员宅基地

APICloud官方文档地址:https://docs.apicloud.com/APICloud/creating-first-app——————安装studio和AppLoader——————1、直接安装apicloud studio2(不要搞什么sublime的插件了,太麻烦。)2、安装jdk,百度搜索jdk(java开发运行环境),并下载和安装。3、手机安装...

推荐文章

热门文章

相关标签