【图像特征提取18】Harris及Shi-Tomasi原理及源码解析

本文转载自http://blog.csdn.net/luoshixian099/article/details/48244255

本文采用的是opencv2.4.3中的源码。

Harris角点检测

   人眼对角点的识别通常是通过一个局部的小窗口内完成的,如果在各个方向上移动这个小窗口,窗口内的灰度发生了较大的变化,那么说明窗口内存在角点。

  如果在各个方向移动,灰度几乎不变,说明是平坦区域;

  如果只沿着某一个方向移动,灰度几乎不变,说明是直线;

  如果沿各个方向移动,灰度均发生变化,说明是角点。

 

                                                      平坦区域                              直线                               角点              

图像I(x,y),在点(x,y)处平移(u,v)后的自相似性,可以用灰度变化函数E(u,v)表示

  

                  

泰勒展开:

代入得到:

                        

其中:

                         

二次项函数本质上就是一个椭圆函数,椭圆的扁平率和尺寸是由矩阵M的两个特征值决定的。

                                              

矩阵M的两个特征值与图像中的角点,边缘,平坦区域的关系:


Harris定义角点响应函数即,即R=Det(M)-k*trace(M)*trace(M)k为经验常数0.04~0.06 。

定义当R>threshold时且为局部极大值的点时,定义为角点。

Harris角点检测算子对图像亮度和对比度具有部分不变性,且具有旋转不变性,但不具有尺度不变性。

                           

OpenCV中调用cornerHarris函数检测角点:

blockSize:为邻域大小,对每个像素,考虑blockSize×blockSize大小的邻域S(p),在邻域上计算图像的差分的相关矩阵;


ksize: 为Soble算子核尺寸,如果小于0,采用3×3的Scharr滤波器;

k:为角点响应函数中的经验常数(0.04~0.06);

[cpp]  view plain  copy
 print ?
  1. int blockSize = 2;  
  2. int apertureSize =3;  
  3. double k = 0.04;  
  4. /// Detecting corners  
  5. cornerHarris( src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT );   
[cpp]  view plain  copy
 print ?
  1. void cv::cornerHarris( InputArray _src, OutputArray _dst, int blockSize, int ksize, double k, int borderType )  
  2. {  
  3.     Mat src = _src.getMat();  
  4.     _dst.create( src.size(), CV_32F );  
  5.     Mat dst = _dst.getMat();  
  6.     cornerEigenValsVecs( src, dst, blockSize, ksize, HARRIS, k, borderType );//调用函数计算图像块的特征值和特征向量  
  7. }  
[cpp]  view plain  copy
 print ?
  1. static void  
  2. cornerEigenValsVecs( const Mat& src, Mat& eigenv, int block_size,  
  3.                      int aperture_size, int op_type, double k=0.,  
  4.                      int borderType=BORDER_DEFAULT )  
  5. {  
  6. #ifdef HAVE_TEGRA_OPTIMIZATION  
  7.     if (tegra::cornerEigenValsVecs(src, eigenv, block_size, aperture_size, op_type, k, borderType))  
  8.         return;  
  9. #endif  
  10.   
  11.   
  12.     int depth = src.depth();  
  13.     double scale = (double)(1 << ((aperture_size > 0 ? aperture_size : 3) - 1)) * block_size;  
  14.     if( aperture_size < 0 )  
  15.         scale *= 2.;  
  16.     if( depth == CV_8U )  
  17.         scale *= 255.;  
  18.     scale = 1./scale;  
  19.     CV_Assert( src.type() == CV_8UC1 || src.type() == CV_32FC1 );  
  20.   
  21.   
  22.     Mat Dx, Dy;   //保存每个像素点的水平方向和垂直方向的一阶差分  
  23.     if( aperture_size > 0 )//采用Sobel滤波器  
  24.     {  
  25.         Sobel( src, Dx, CV_32F, 1, 0, aperture_size, scale, 0, borderType );  
  26.         Sobel( src, Dy, CV_32F, 0, 1, aperture_size, scale, 0, borderType );  
  27.     }  
  28.     else    //采用3×3的Scharr滤波器,可以给出比3×3 Sobel滤波器更精确的结果  
  29.     {  
  30.         Scharr( src, Dx, CV_32F, 1, 0, scale, 0, borderType );  
  31.         Scharr( src, Dy, CV_32F, 0, 1, scale, 0, borderType );  
  32.     }  
  33.   
  34.   
  35.     Size size = src.size();  
  36.     Mat cov( size, CV_32FC3 );  
  37.     int i, j;  
  38.   
  39.   
  40.     for( i = 0; i < size.height; i++ )  
  41.     {  
  42.         float* cov_data = (float*)(cov.data + i*cov.step);  
  43.         const float* dxdata = (const float*)(Dx.data + i*Dx.step);  
  44.         const float* dydata = (const float*)(Dy.data + i*Dy.step);  
  45.   
  46.   
  47.         for( j = 0; j < size.width; j++ )  
  48.         {  
  49.             float dx = dxdata[j];  
  50.             float dy = dydata[j];  
  51.   
  52.   
  53.             cov_data[j*3] = dx*dx;  //第一个通道存dx*dx,即M矩阵左上角的元素  
  54.             cov_data[j*3+1] = dx*dy;//第二个通道存dx*dy,即M矩阵左下角和右上角的元素  
  55.             cov_data[j*3+2] = dy*dy;//第三个通道存dy*dy,即M矩阵右下角的元素  
  56.         }  
  57.     }  
  58.   
  59.   
  60.     boxFilter(cov, cov, cov.depth(), Size(block_size, block_size), //计算邻域上的差分相关矩阵(block_size×block_size)  
  61.         Point(-1,-1), false, borderType );  
  62.   
  63.   
  64.     if( op_type == MINEIGENVAL )   //计算M矩阵的最小的特征值  
  65.         calcMinEigenVal( cov, eigenv );  
  66.     else if( op_type == HARRIS )//计算Harris角点响应函数R  
  67.         calcHarris( cov, eigenv, k );  
  68.     else if( op_type == EIGENVALSVECS )//计算图像块的特征值和特征向量  
  69.         calcEigenValsVecs( cov, eigenv );  
  70. }  
[cpp]  view plain  copy
 print ?
  1. static void  
  2. calcHarris( const Mat& _cov, Mat& _dst, double k )  
  3. {  
  4.     int i, j;  
  5.     Size size = _cov.size();  
  6.     if( _cov.isContinuous() && _dst.isContinuous() )  
  7.     {  
  8.         size.width *= size.height;  
  9.         size.height = 1;  
  10.     }  
  11.   
  12.     for( i = 0; i < size.height; i++ )  
  13.     {  
  14.         const float* cov = (const float*)(_cov.data + _cov.step*i);  
  15.         float* dst = (float*)(_dst.data + _dst.step*i);  
  16.         j = 0;  
  17.         for( ; j < size.width; j++ )  
  18.         {  
  19.             float a = cov[j*3];  
  20.             float b = cov[j*3+1];  
  21.             float c = cov[j*3+2];  
  22.             dst[j] = (float)(a*c - b*b - k*(a + c)*(a + c));  //计算每个像素对应角点响应函数R  
  23.         }  
  24.     }  
  25. }  

Shi-Tomasi角点检测

由于Harris算法的稳定性和k值有关,Shi-Tomasi发现,角点的稳定性和矩阵M的较小特征值有关,改进的Harris算法即直接计算出矩阵M的特征值,用较小的特征值与阈值比较,大于阈值的即为强特征点。
                       
opencv中对其实现算法在goodFeaturesToTrack()函数中:
[cpp]  view plain  copy
 print ?
  1. CV_EXPORTS_W void goodFeaturesToTrack( InputArray image, OutputArray corners,  
  2.                                      int maxCorners, double qualityLevel, double minDistance,  
  3.                                      InputArray mask=noArray(), int blockSize=3,  
  4.                                      bool useHarrisDetector=falsedouble k=0.04 );  
image:输入图像
corners:输出图像数组
maxCorners:需要的角点数目
qualityLevel:最大,最小特征值的乘法因子。定义可接受图像角点的最小质量因子。
minDistance:容忍距离。角点之间的最小距离,采用欧氏距离。
mask:掩码
blockSize:邻域大小
useHarrisDetector:采用Harris角点检测
k:采用Harris角点检测时的经验常数k(0.04~0.06)
算法原理:调用cornerMinEigenVal()函数求出每个像素点自适应矩阵M的较小特征值,保存在矩阵eig中,然后找到矩阵eig中最大的像素值记为maxVal,然后阈值处理,小于qualityLevel*maxVal的特征值排除掉,最后函数确保所有发现的角点之间具有足够的距离。
[cpp]  view plain  copy
 print ?
  1. void cv::goodFeaturesToTrack( InputArray _image, OutputArray _corners,  
  2.                               int maxCorners, double qualityLevel, double minDistance,  
  3.                               InputArray _mask, int blockSize,  
  4.                               bool useHarrisDetector, double harrisK )  
  5. {  
  6.     Mat image = _image.getMat(), mask = _mask.getMat();  
  7.   
  8.     CV_Assert( qualityLevel > 0 && minDistance >= 0 && maxCorners >= 0 );  
  9.     CV_Assert( mask.empty() || (mask.type() == CV_8UC1 && mask.size() == image.size()) );  
  10.   
  11.     Mat eig, tmp;  
  12.     if( useHarrisDetector )       
  13.         cornerHarris( image, eig, blockSize, 3, harrisK );  //采用Harris角点检测  
  14.     else  
  15.         cornerMinEigenVal( image, eig, blockSize, 3 );  //采用Harris改进算法,eig保存矩阵M较小的特征值。见下面算法实现  
  16.   
  17.     double maxVal = 0;  
  18.     minMaxLoc( eig, 0, &maxVal, 0, 0, mask );//保存eig中最大的值maxVal  
  19.     threshold( eig, eig, maxVal*qualityLevel, 0, THRESH_TOZERO );//阈值处理,小于maxVal*qualityLevel的像素值归为0。  
  20.     dilate( eig, tmp, Mat());//膨胀,3×3的核,为了取局部极大值  
  21.   
  22.     Size imgsize = image.size();  
  23.   
  24.     vector<const float*> tmpCorners;  
  25.   
  26.     // collect list of pointers to features - put them into temporary image  
  27.     forint y = 1; y < imgsize.height - 1; y++ )  
  28.     {  
  29.         const float* eig_data = (const float*)eig.ptr(y);  
  30.         const float* tmp_data = (const float*)tmp.ptr(y);  
  31.         const uchar* mask_data = mask.data ? mask.ptr(y) : 0;  
  32.   
  33.         forint x = 1; x < imgsize.width - 1; x++ )  
  34.         {  
  35.             float val = eig_data[x];  
  36.             if( val != 0 && val == tmp_data[x] && (!mask_data || mask_data[x]) )//局部极大值  
  37.                 tmpCorners.push_back(eig_data + x);  
  38.         }  
  39.     }  
  40.   
  41.     sort( tmpCorners, greaterThanPtr<float>() );  //按值从大到小排序  
  42.     vector<Point2f> corners;  
  43.     size_t i, j, total = tmpCorners.size(), ncorners = 0;  
  44.  /*   
  45.   网格处理,即把图像划分成正方形网格,每个网格边长为容忍距离minDistance 
  46.   以一个角点位置为中心,minDistance为半径的区域内部不允许出现第二个角点 
  47.  */  
  48.     if(minDistance >= 1)  
  49.     {  
  50.          // Partition the image into larger grids  
  51.         int w = image.cols;  
  52.         int h = image.rows;  
  53.           
  54.         const int cell_size = cvRound(minDistance);//划分成网格,网格边长为容忍距离  
  55.         const int grid_width = (w + cell_size - 1) / cell_size;  
  56.         const int grid_height = (h + cell_size - 1) / cell_size;  
  57.   
  58.         std::vector<std::vector<Point2f> > grid(grid_width*grid_height);  
  59.   
  60.         minDistance *= minDistance;  
  61.   
  62.         for( i = 0; i < total; i++ )  //按从大到小的顺序,遍历所有角点  
  63.         {  
  64.             int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);  
  65.             int y = (int)(ofs / eig.step);  
  66.             int x = (int)((ofs - y*eig.step)/sizeof(float));  
  67.   
  68.             bool good = true;  
  69.   
  70.             int x_cell = x / cell_size;  
  71.             int y_cell = y / cell_size;  
  72.   
  73.             int x1 = x_cell - 1;  
  74.             int y1 = y_cell - 1;  
  75.             int x2 = x_cell + 1;  
  76.             int y2 = y_cell + 1;  
  77.   
  78.             // boundary check  
  79.             x1 = std::max(0, x1);  
  80.             y1 = std::max(0, y1);  
  81.             x2 = std::min(grid_width-1, x2);  
  82.             y2 = std::min(grid_height-1, y2);  
  83.   
  84.             forint yy = y1; yy <= y2; yy++ )//检测角点,minDistance半径邻域内,有没有其他角点出现  
  85.             {  
  86.                 forint xx = x1; xx <= x2; xx++ )  
  87.                 {  
  88.                     vector <Point2f> &m = grid[yy*grid_width + xx];  
  89.   
  90.                     if( m.size() )  
  91.                     {  
  92.                         for(j = 0; j < m.size(); j++)  
  93.                         {  
  94.                             float dx = x - m[j].x;  
  95.                             float dy = y - m[j].y;  
  96.                             if( dx*dx + dy*dy < minDistance )//有其他角点,丢弃当前角点  
  97.                             {  
  98.                                 good = false;  
  99.                                 goto break_out;  
  100.                             }  
  101.                         }  
  102.                     }  
  103.                 }  
  104.             }  
  105.   
  106.             break_out:  
  107.   
  108.             if(good)  
  109.             {  
  110.                 // printf("%d: %d %d -> %d %d, %d, %d -- %d %d %d %d, %d %d, c=%d\n",  
  111.                 //    i,x, y, x_cell, y_cell, (int)minDistance, cell_size,x1,y1,x2,y2, grid_width,grid_height,c);  
  112.                 grid[y_cell*grid_width + x_cell].push_back(Point2f((float)x, (float)y));  
  113.   
  114.                 corners.push_back(Point2f((float)x, (float)y));//满足条件的存入corners  
  115.                 ++ncorners;  
  116.   
  117.                 if( maxCorners > 0 && (int)ncorners == maxCorners )  
  118.                     break;  
  119.             }  
  120.         }  
  121.     }  
  122.     else   //不设置容忍距离  
  123.     {  
  124.         for( i = 0; i < total; i++ )  
  125.         {  
  126.             int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);  
  127.             int y = (int)(ofs / eig.step);  
  128.             int x = (int)((ofs - y*eig.step)/sizeof(float));  
  129.   
  130.             corners.push_back(Point2f((float)x, (float)y));  
  131.             ++ncorners;  
  132.             if( maxCorners > 0 && (int)ncorners == maxCorners )  
  133.                 break;  
  134.         }  
  135.     }  
  136.   
  137.     Mat(corners).convertTo(_corners, _corners.fixedType() ? _corners.type() : CV_32F);  
  138.   
  139. }  

求矩阵M最小的特征值


[cpp]  view plain  copy
 print ?
  1. static void  
  2. calcMinEigenVal( const Mat& _cov, Mat& _dst )  
  3. {  
  4.     int i, j;  
  5.     Size size = _cov.size();  
  6.     if( _cov.isContinuous() && _dst.isContinuous() )  
  7.     {  
  8.         size.width *= size.height;  
  9.         size.height = 1;  
  10.     }  
  11.   
  12.     for( i = 0; i < size.height; i++ )//遍历所有像素点  
  13.     {  
  14.         const float* cov = (const float*)(_cov.data + _cov.step*i);  
  15.         float* dst = (float*)(_dst.data + _dst.step*i);  
  16.         j = 0;  
  17.         for( ; j < size.width; j++ )  
  18.         {  
  19.             float a = cov[j*3]*0.5f;//cov[j*3]保存矩阵M左上角元素  
  20.             float b = cov[j*3+1];   //cov[j*3+1]保存左下角和右上角元素  
  21.             float c = cov[j*3+2]*0.5f;//cov[j*3+2]右下角元素  
  22.             dst[j] = (float)((a + c) - std::sqrt((a - c)*(a - c) + b*b));//求最小特征值,一元二次方程求根公式  
  23.         }  
  24.     }  
  25. }  

参考:http://blog.csdn.net/xw20084898/article/details/21180729

         http://wenku.baidu.com/view/f61bc369561252d380eb6ef0.html

         http://blog.csdn.net/crzy_sparrow/article/details/7391511     

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
import cv2 as cv import numpy as np """"" cv2.cornerHarris() 可以用来进行角点检测。参数如下: • img - 数据类型为 float32 的输入图像。 • blockSize - 角点检测中要考虑的领域大小。 • ksize - Sobel 求导中使用的窗口大小 • k - Harris 角点检测方程中的自由参数,取值参数为 [0,04,0.06] """"" src_inital = cv.imread("E:/opencv/picture/building.jpg") src = cv.cvtColor(src_inital,cv.COLOR_BGR2GRAY) src = np.float32(src) dst = cv.cornerHarris(src,3,3,0.04) #R值是由det(M)-K(trace(M))*(trace(M)),当该点是角点时,该点所对应的R值就会很大,通过设置对R的阈值,就可以筛选得到角点 #这里的dst就是R值构成的灰度图像,灰度图像坐标会与原图像对应,R值就是角点分数,当R值很大的时候 就可以认为这个点是一个角点 print(dst.shape) src_inital[dst>0.08*dst.max()]=[0,0,255] """"" src_inital[dst>0.08*dst.max()]=[0,0,255] 这句话来分析一下 dst>0.08*dst.max()这么多返回是满足条件的dst索引值,根据索引值来设置这个点的颜色 这里是设定一个阈值 当大于这个阈值分数的都可以判定为角点 dst其实就是一个个角度分数R组成的,当λ1和λ2都很大,R 也很大,(λ1和λ2中的最小值都大于阈值)说明这个区域是角点。 那么这里为什么要大于0.08×dst.max()呢 注意了这里R是一个很大的值,我们选取里面最大的R,然后只要dst里面的值大于百分之八的R的最大值  那么此时这个dst的R值也是很大的 可以判定他为角点,也不一定要0.08可以根据图像自己选取不过如果太小的话 可能会多圈出几个不同的角点 """"" cv.imshow("inital_window",src_inital) cv.waitKey(0) cv.destroyAllWindows() 目标: 理解Harris角点检测的概念 使用函数cv2.cornerHarris(),cv2.cornerSubPix() 原理Harris 角点检测的方法大概原理就是建立一个窗口区域,然后以当前窗口为中心向各个方向进行偏移。 如上图所示,第一个窗口向各个方向偏移的时候,像素值没有变化,因为窗口偏移的时候没有遇到任何边缘信息。 第二个图,窗口当中有一个直线(即block是在边缘上),如果当前窗口进行上下的移动,也没有像素值发生变化(在其他方向上灰度值也会变化)。 第三个图,窗口覆盖了一个“拐角”,如果窗口进行偏移,任何方向上都会有像素变化。 所以,第三张图片判断为检测到角点。 判断特征点是否为角点的依据:R只与M值有关,R为大数值正数时特征点为角点,R为大数值负数时为边缘,R为小数值时为平坦区 寻找R位于一定阈值之上的局部最大值,去除伪角点。 方向导数IxIx和IyIy可以使用cv2.Sobel()函数得到 Harris角点检测的结果是灰度图,图中的值为角点检测的打分值。需要选取合适的阈值对结果进行二值化来检测角点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值