Kitchen和Rosenfeld认为角点是那些边缘曲线曲率和梯度幅值都很大的点,因此他们提出了使用曲率k与梯度幅值g的乘积来计算角点响应函数C的方法:
C = k g = k ( I x 2 − I y 2 ) 1 / 2 = I x x I y 2 + I y y I x 2 − 2 I x y I x I y I x 2 + I y 2 ( 1 ) C=kg=k(I_x^2-I_y^2)^{1/2} = \frac {I_{xx}I_y^2+I_{yy}I_x^2-2I_{xy}I_xI_y}{I_x^2+I_y^2}(1) C=kg=k(Ix2−Iy2)1/2=Ix2+Iy2IxxIy2+IyyIx2−2IxyIxIy(1)
C的极值点所对应的像素即为角点。 I x , I y I_x,I_y Ix,Iy为图像I的一阶导数,即:
I x = ∂ I ∂ x , I y = ∂ I ∂ y I_x=\frac{\partial I}{\partial x},I_y=\frac{\partial I}{\partial y} Ix=∂x∂I,Iy=∂y∂I
I x x , I y y , I x y I_{xx},I_{yy},I_{xy} Ixx,Iyy,Ixy为图像I的二阶导数,即:
I x x = ∂ 2 I ∂ x 2 , I y y = ∂ 2 I ∂ y 2 , I x y = ∂ 2 I ∂ x y I_{xx}=\frac{\partial^2 I}{\partial x^2},I_{yy}=\frac{\partial^2 I}{\partial y^2},I_{xy}=\frac{\partial^2 I}{\partial xy} Ixx=∂x2∂2I,Iyy=∂y2∂2I,Ixy=∂xy∂2I
由于(1)式的分母恒大于0,它不改变角点响应函数的相对值,因此在实际应用中,只计算分子部分。
该方法对噪声比较敏感,因为使用了基于灰度的二阶偏导。
void cv::preCornerDetect(InputArray src, OutputArray dst, int ksize, int borderType = BORDER_DEFAULT)
Parameters
src 输入图像,单通道8位整型或者32位浮点型;
dst 输出32位图像;
ksize Sobel算子核尺寸;
borderType 边缘填充方法,但BORDER_WRAP不支持;
void cv::preCornerDetect( InputArray _src, OutputArray _dst, int ksize, int borderType )
{
CV_INSTRUMENT_REGION();
int type = _src.type();
CV_Assert( type == CV_8UC1 || type == CV_32FC1 );
CV_OCL_RUN( _src.dims() <= 2 && _dst.isUMat(),
ocl_preCornerDetect(_src, _dst, ksize, borderType, CV_MAT_DEPTH(type)))
Mat Dx, Dy, D2x, D2y, Dxy, src = _src.getMat();
_dst.create( src.size(), CV_32FC1 );
Mat dst = _dst.getMat();
//Sobel算子计算一阶导数、二阶偏导
Sobel( src, Dx, CV_32F, 1, 0, ksize, 1, 0, borderType );
Sobel( src, Dy, CV_32F, 0, 1, ksize, 1, 0, borderType );
Sobel( src, D2x, CV_32F, 2, 0, ksize, 1, 0, borderType );
Sobel( src, D2y, CV_32F, 0, 2, ksize, 1, 0, borderType );
Sobel( src, Dxy, CV_32F, 1, 1, ksize, 1, 0, borderType );
double factor = 1 << (ksize - 1);
if( src.depth() == CV_8U )
factor *= 255;
factor = 1./(factor * factor * factor);
#if CV_SIMD128
float factor_f = (float)factor;
v_float32x4 v_factor = v_setall_f32(factor_f), v_m2 = v_setall_f32(-2.0f);
#endif
Size size = src.size();
int i, j;
for( i = 0; i < size.height; i++ )
{
float* dstdata = dst.ptr(i);
const float* dxdata = Dx.ptr(i);
const float* dydata = Dy.ptr(i);
const float* d2xdata = D2x.ptr(i);
const float* d2ydata = D2y.ptr(i);
const float* dxydata = Dxy.ptr(i);
j = 0;
//指令集加速
#if CV_SIMD128
{
for( ; j <= size.width - v_float32x4::nlanes; j += v_float32x4::nlanes )
{
v_float32x4 v_dx = v_load(dxdata + j);
v_float32x4 v_dy = v_load(dydata + j);
v_float32x4 v_s1 = (v_dx * v_dx) * v_load(d2ydata + j);
v_float32x4 v_s2 = v_muladd((v_dy * v_dy), v_load(d2xdata + j), v_s1);
v_float32x4 v_s3 = v_muladd((v_dy * v_dx) * v_load(dxydata + j), v_m2, v_s2);
v_store(dstdata + j, v_s3 * v_factor);
}
}
#endif
//计算每个像素的响应函数
for( ; j < size.width; j++ )
{
float dx = dxdata[j];
float dy = dydata[j];
dstdata[j] = (float)(factor*(dx*dx*d2ydata[j] + dy*dy*d2xdata[j] - 2*dx*dy*dxydata[j]));
}
}
}