【opencv】goodFeaturesToTrack源码分析-2-Shi-Tomasi角点检测
本文章是【opencv】goodFeaturesToTrack源码分析-1的后续,主要描述Shi-Tomasi角点检测算法原理及opencv实现。
1、算法原理
Shi-Tomasi算法是Harris算法的改进,在Harris算法中,是根据协方差矩阵M的两个特征值的组合来判断是否角点。而在Shi-Tomasi算法中,是根据较小的特征值是否大于阈值来判断是否角点。
这个判断依据是:较小的特征值表示在该特征值方向上的方差较小,较小的方差如果都能够大于阈值,在这个方向上的变化满足是角点的判断要求。
协方差矩阵MM如下表示:w(x,y)w(x,y)表示窗函数,通常不使用。Ix,IyIx,Iy分别表示在x及y方向上的差分。
M= \sum_{x,y}w(x,y) \left[ \begin{matrix} I_x^2 & I_xI_y \\ I_xI_y & I_y^2 \end{matrix} \right]
这里我们需要关心的是,如何求解矩阵M的特征值?
求解特征值,一般求解这样的一个 λ\lambda,使得:
det(λI−A)=0det(\lambda I-A)=0
对于 AA这样一个2x2矩阵来说,我们可以分解为:
det(\lambda \left[ \begin{matrix} 1 & 0 \\ 0 & 1 \end{matrix} \right]-A )= det( \left[ \begin{matrix} \lambda& 0 \\ 0 & \lambda \end{matrix} \right]-\left[\begin{matrix}a &b \\d& c \end{matrix}\right] )=0
(\lambda-a)(\lambda-c)-bd=\lambda^2-(a+c)\lambda +ac-bd=0
根据求根公式,有:
\lambda = \frac{{(a+c)\pm \sqrt{(a+c)^2-4(ac-bd)}}}{2}=\frac{{(a+c)\pm \sqrt{(a-c)^2+4bd}}}{2}
带入具体的 MM,较小的特征值为:
\lambda =\frac{{(\sum I_x^2+\sum I_y^2)- \sqrt{(\sum I_x^2-\sum I_y^2)^2+4(\sum I_xI_y)^2}}}{2}
后续在具体的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 IyMat 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 IxIyfor( 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_NEONfor( ; 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 ∑IxIyboxFilter(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、先求Ix、IyI_x 、I_y;
2、再求对应的I2x、I2y、IxIyI_x^2、I_y^2、I_xI_y及其∑I2x \sum I_x^2 、∑IxIy \sum I_xI_y 、∑I2y \sum I_y^2
3、最后按照公式计算特征值
在计算特征值时,是调用calcMinEigenVal() 。该接口是根据以上公式
\lambda =\frac{{(\sum I_x^2+\sum I_y^2)- \sqrt{(\sum I_x^2-\sum I_y^2)^2+4(\sum I_xI_y)^2}}}{2}= {(\frac{\sum I_x^2}{2}+\frac{\sum I_y^2}{2})- \sqrt{(\frac{\sum I_x^2}{2}-\frac{\sum I_y^2}{2})^2+(\sum I_xI_y)^2}} 计算特征值。
从下面的代码可以看出, a=∑I2x2a=\frac{\sum I_x^2}{2} b=∑IxIyb= \sum I_xI_y c=∑I2y2c=\frac{\sum I_y^2}{2}
static void calcMinEigenVal( const Mat& _cov, Mat& _dst )
{int i, j;Size size = _cov.size();
#if CV_SSEvolatile bool simd = checkHardwareSupport(CV_CPU_SSE);
#endifif( _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_NEONfloat32x4_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)));}#endiffor( ; 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进行分析。
【opencv】goodFeaturesToTrack源码分析-2-Shi-Tomasi角点检测相关推荐
- Redis源码分析(十一)--- memtest内存检测
今天我们继续redis源码test测试包下的其他文件,今天看完的是memtest文件,翻译器起来,就是memory test 内存检测的意思,这个文件虽然说代码量不是很多,但是里面的提及了很多东西,也 ...
- 决策树(七)--Boost及源码分析
原文:http://blog.csdn.net/zhaocj/article/details/50536385 一.原理 AdaBoost(Adaptive Boosting,自适应提升)算法是由来自 ...
- HRNet 源码分析
论文 Deep High-Resolution Representation Learning for Human Pose Estimation 也是 一个 top-down得对于人体姿态估计得检测 ...
- opencv源码解析之(6):hog源码分析
一.网上一些参考资料 在博客目标检测学习_1(用opencv自带hog实现行人检测) 中已经使用了opencv自带的函数detectMultiScale()实现了对行人的检测,当然了,该算法采 ...
- 基于边缘的图像分割——分水岭算法(watershed)算法分析(附opencv源码分析)
最近需要做一个图像分割的程序,查了opencv的源代码,发现opencv里实现的图像分割一共有两个方法,watershed和mean-shift算法.这两个算法的具体实现都在segmentation. ...
- SVM算法及OpenCV源码分析
关于SVM原理,请参看: 系统学习机器学习之SVM(一) 系统学习机器学习之SVM(二) 系统学习机器学习之SVM(三)--Liblinear,LibSVM使用整理,总结 系统学习机器学习之SVM(四 ...
- KNN(七)--最近邻及OpenCV源码分析
原文: http://blog.csdn.net/zhaocj/article/details/50764093 一.原理 K近邻算法(KNN,K-NearestNeighbors)是一种非常简单的机 ...
- 决策树(十)--GBDT及OpenCV源码分析
一.原理 梯度提升树(GBT,Gradient Boosted Trees,或称为梯度提升决策树)算法是由Friedman于1999年首次完整的提出,该算法可以实现回归.分类和排序.GBT的优点是特征 ...
- 决策树(八)--随机森林及OpenCV源码分析
原文: http://blog.csdn.net/zhaocj/article/details/51580092 一.原理 随机森林(Random Forest)的思想最早是由Ho于1995年首次提出 ...
最新文章
- 详解H3C交换机“端口安全”功能
- PHP中常见的几种运行代码的方式
- python运行卡死_快速解决jupyter启动卡死的问题
- IOS对plist配置文件的读写操作
- 多媒体调度系统如何实现对水库大坝的防洪调度
- Java解析XML汇总(DOM/SAX/JDOM/DOM4j/XPath)
- Fiori里前后台ETAG处理
- P2469-[SDOI2010]星际竞速【费用流】
- 前端学习(2976):路由钩子函数
- SQL SERVER7应用
- hadoop-2.6.5安装
- C++ 中的Virtual Function (虚函数)
- MyBatis使用动态SQL语句
- 基于 Keras 用深度学习预测时间序列
- redis整理の配置
- 2020 CUMCM全国大学生数学建模竞赛 B题 Notes
- 戴尔笔记本win10系统迁移到新固态硬盘
- Qt 语言家实现中英文切换(解决纯代码添加部件的中英文转换问题)
- linux suse11 sp3安装,SUSE Linux Enterprise Server 11 SP3安装教程详解
- 自动化运维工具——Ansible
热门文章
- 漂亮的css网站js资源无限下载
- 故障诊断专家系统研究之一-----绪论
- Unity Shader Graph 制作Grid网格效果
- 【计算机与UNIX汇编原理⑫】——汇编考前复习【重要知识点 + 基础题 + 易错题 + 难题解析】
- 基于tabular包的Latex表格尺寸设置方法(列宽和行高)
- 学java难不难?java应该怎么学?
- Data too long for column ‘data‘ at row 1以及设置成longblob造成的乱码解决。node-mysql
- Linux 命令行模式下退出 vim
- java龙世界禁忌之恋灵魂大殿_《龙世界-禁忌之恋》完美图文攻略
- C# 获取适配器网络连接IP地址,子网掩码,DNS,数据包等信息