Opencv Harris角点检测源码分析

一.简介

这两天学习了Harris角点检测的算法原理,想深入了解一下opencv是如何实现的Harris角点检测算法,就产生了这篇文章供大家参考。在opencv中Harris角点检测的函数一共分为3大类:1)AVX优化过的;2)SIMD128优化过的;3)普通的串行处理。我仅仅分析了普通的串行处理,去理解Harris算法原理。至于AVX,IPP或者SIMD128优化的,仅仅是增加了数据的并行处理,优化了运行时间,本质上的原理与串行还是相同的。如果有错误,希望大家给予指正。我对源码进行了相应的注释供大家参考。

二,opencv中Harris角点检测算法流程

1.根据输入的kernel_size的正负,选择sobel算子或者scharr算子对灰度图像进行梯度计算,获得dx,dy。并将dxdx,dydy,dx*dy存入3通道的cov矩阵
2.对cov矩阵利用boxfliter函数进行均值滤波
3.调用calcHarris( );计算角点响应函数R = det(M) - k(trace(M))^2;

三.源码分析

void cv::cornerHarris( InputArray _src, OutputArray _dst, int blockSize, int ksize, double k, int borderType )
{
    CV_INSTRUMENT_REGION();//opencv的profiling,可以在内部追踪函数执行状况,默认情况下是关闭的,不会产生性能开销.

    CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(),
               ocl_cornerMinEigenValVecs(_src, _dst, blockSize, ksize, k, borderType, HARRIS))
			   //CV_OCL_RUN就是对OpenCL的检测,若OpenCL可用则执行OpenCL版本的cornerHarris。 http://www.sohu.com/a/244149199_823210

    Mat src = _src.getMat();
    _dst.create( src.size(), CV_32FC1 );
    Mat dst = _dst.getMat();

#ifdef HAVE_IPP
    int borderTypeNI = borderType & ~BORDER_ISOLATED;//(4byte)BORDER_ISOLATED=16~按位取反后得到01111;bordertype与01111=bordertype
    bool isolated = (borderType & BORDER_ISOLATED) != 0;//bordertype只有是border_ioslated的时候isolated = 1
#endif
    CV_IPP_RUN(((ksize == 3 || ksize == 5) && (_src.type() == CV_8UC1 || _src.type() == CV_32FC1) &&
        (borderTypeNI == BORDER_CONSTANT || borderTypeNI == BORDER_REPLICATE) && CV_MAT_CN(_src.type()) == 1 &&
        (!_src.isSubmatrix() || isolated)) && IPP_VERSION_X100 >= 810, ipp_cornerHarris( src, dst, blockSize, ksize, k, borderType ));
	//判断是否有IPP,若有利用IPP优化过的函数进行计算加快运行速度
    cornerEigenValsVecs( src, dst, blockSize, ksize, HARRIS, k, borderType );//enum { MINEIGENVAL=0, HARRIS=1, EIGENVALSVECS=2 };
}


static void
( const Mat& src, Mat& eigenv, int block_size,//窗口大小3*3||5*5
							int aperture_size, //进行sobel算子运算的掩码模板大小3*3
							int op_type, //运算类型:enum { MINEIGENVAL=0, HARRIS=1, EIGENVALSVECS=2 };
							double k=0.,//与角点响应度度有关的参数
                     int borderType=BORDER_DEFAULT )//复制边界border_replicate
{
#if CV_TRY_AVX
    bool haveAvx = CV_CPU_HAS_SUPPORT_AVX;//判别cpu是否支持Intel AVX指令集,AVX指令集用于提升CPU的浮点型运算速度
#endif
#if CV_SIMD128//判断cpu是否支持simd128
    bool haveSimd = hasSIMD128();
#endif

    int depth = src.depth();
    double scale = (double)(1 << ((aperture_size > 0 ? aperture_size : 3) - 1)) * block_size;//1左移x位,右边为补0;*的优先级高于<<
    if( aperture_size < 0 )
        scale *= 2.0;
    if( depth == CV_8U )
        scale *= 255.0;
    scale = 1.0/scale;//scale的目的是整型转换为浮点型的缩放比例

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

    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算子scharr算子与Sobel的不同点是在平滑部分,
	//这里所用的平滑算子是1/16∗[3,10,3],相比于1/4∗[1,2,1],中心元素占的权重更重,
	//这可能是相对于图像这种随机性较强的信号,邻域相关性不大,
	//所以邻域平滑应该使用相对较小的标准差的高斯函数,也就是更瘦高的模板
        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 );//cov是3通道的
    int i, j;

    for( i = 0; i < size.height; i++ )
    {
        float* cov_data = cov.ptr<float>(i);//指向矩阵中第i行的float指针
        const float* dxdata = Dx.ptr<float>(i);
        const float* dydata = Dy.ptr<float>(i);

#if CV_TRY_AVX//如果支持AVX,就是用AVX优化过的函数操作,加快浮点型的运算速度
        if( haveAvx )
            j = cornerEigenValsVecsLine_AVX(dxdata, dydata, cov_data, size.width);
        else
#endif // CV_TRY_AVX
            j = 0;

#if CV_SIMD128//如果支持SIMD128,就是用SIMD128优化过的函数操作,加快浮点型的运算速度
        if( haveSimd )
        {
            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_dst0, v_dst1, v_dst2;
                v_dst0 = v_dx * v_dx;
                v_dst1 = v_dx * v_dy;
                v_dst2 = v_dy * v_dy;

                v_store_interleave(cov_data + j * 3, v_dst0, v_dst1, v_dst2);
            }
        }
#endif // CV_SIMD128
      //不进行任何cpu处理优化,直接串行处理的函数,j=0在上面已经初始化了
        for( ; j < size.width; j++ )
        {
            float dx = dxdata[j];
            float dy = dydata[j];

            cov_data[j*3] = dx*dx;//cov是3通道的
            cov_data[j*3+1] = dx*dy;
            cov_data[j*3+2] = dy*dy;
        }
    }

    boxFilter(cov, cov, cov.depth(), Size(block_size, block_size),//对cov进行均值滤波
        Point(-1,-1), false, borderType );

    if( op_type == MINEIGENVAL )
        calcMinEigenVal( cov, eigenv );
    else if( op_type == HARRIS )
        calcHarris( cov, eigenv, k );
    else if( op_type == EIGENVALSVECS )
        calcEigenValsVecs( cov, eigenv );
}
static void calcHarris( const Mat& _cov, Mat& _dst, double k )
{
    int i, j;
    Size size = _cov.size();
#if CV_TRY_AVX//如果支持AVX,就是用AVX优化过的函数操作,加快浮点型的运算速度
    bool haveAvx = CV_CPU_HAS_SUPPORT_AVX;
#endif
#if CV_SIMD128//如果支持SIMD128,就是用SIMD128优化过的函数操作,加快浮点型的运算速度
    bool haveSimd = hasSIMD128();
#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);

#if CV_TRY_AVX//如果支持AVX,就是用AVX优化过的函数操作,加快浮点型的运算速度
        if( haveAvx )
            j = calcHarrisLine_AVX(cov, dst, k, size.width);
        else
#endif // CV_TRY_AVX
            j = 0;

#if CV_SIMD128//如果支持SIMD128,就是用SIMD128优化过的函数操作,加快浮点型的运算速度
        if( haveSimd )
        {
            v_float32x4 v_k = v_setall_f32((float)k);
            for( ; j <= size.width - v_float32x4::nlanes; j += v_float32x4::nlanes )
            {
                v_float32x4 v_a, v_b, v_c;
                v_load_deinterleave(cov + j * 3, v_a, v_b, v_c);

                v_float32x4 v_ac_bb = v_a * v_c - v_b * v_b;
                v_float32x4 v_ac = v_a + v_c;
                v_float32x4 v_dst = v_ac_bb - v_k * v_ac * v_ac;
                v_store(dst + j, v_dst);
            }
        }
#endif // CV_SIMD128
 //不进行任何cpu处理优化,直接串行处理的函数,j=0在上面已经初始化了
        for( ; j < size.width; j++ )
        {
            float a = cov[j*3];//a = dx*dx;
            float b = cov[j*3+1];//b = dx*dy;
            float c = cov[j*3+2];//c = dy*dy;
            dst[j] = (float)(a*c - b*b - k*(a + c)*(a + c));//计算角点响应函数;2*2的矩阵detM = a*c - b*b;traceM =(a + c)*(a + c)
        }
    }
}

你可能感兴趣的:(学生,opencv,图像处理,c++)