分享

图像处理之Harris角度检测算法

 薇薇微微笑 2014-01-06

图像处理之Harris角度检测算法

Harris角度检测是通过数学计算在图像上发现角度特征的一种算法,而且其具有旋转不

性的特质。OpenCV中的Shi-Tomasi角度检测就是基于Harris角度检测改进算法。

基本原理:

角度是一幅图像上最明显与重要的特征,对于一阶导数而言,角度在各个方向的变化是

最大的,而边缘区域在只是某一方向有明显变化。一个直观的图示如下:


数学原理:

基本数学公式如下:


其中W(x, y)表示移动窗口,I(x, y)表示像素灰度值强度,范围为0~255。根据泰勒级数

计算一阶到N阶的偏导数,最终得到一个Harris矩阵公式:


根据Harris的矩阵计算矩阵特征值,然后计算Harris角度响应值:


其中K为系数值,通常取值范围为0.04 ~ 0.06之间。

算法详细步骤

第一步:计算图像X方向与Y方向的一阶高斯偏导数Ix与Iy

第二步:根据第一步结果得到Ix^2 , Iy^2与Ix*Iy值

第三步:高斯模糊第二步三个值得到Sxx, Syy, Sxy

第四部:定义每个像素的Harris矩阵,计算出矩阵的两个特质值

第五步:计算出每个像素的R值

第六步:使用3X3或者5X5的窗口,实现非最大值压制

第七步:根据角度检测结果计算,最提取到的关键点以绿色标记,显示在原图上。

程序关键代码解读

第一步计算一阶高斯偏导数的Ix与Iy值代码如下:

  1. filter.setDirectionType(GaussianDerivativeFilter.X_DIRECTION);  
  2. BufferedImage xImage = filter.filter(grayImage, null);  
  3. getRGB( xImage, 0, 0, width, height, inPixels );  
  4. extractPixelData(inPixels, GaussianDerivativeFilter.X_DIRECTION, height, width);  
  5.   
  6. filter.setDirectionType(GaussianDerivativeFilter.Y_DIRECTION);  
  7. BufferedImage yImage = filter.filter(grayImage, null);  
  8. getRGB( yImage, 0, 0, width, height, inPixels );  
  9. extractPixelData(inPixels, GaussianDerivativeFilter.Y_DIRECTION, height, width);  

关于如何计算高斯一阶与二阶偏导数请看这里:

http://blog.csdn.net/jia20003/article/details/16369143

http://blog.csdn.net/jia20003/article/details/7664777

第三步:分别对第二步计算出来的三个值,单独进行高斯

模糊计算,代码如下:

  1. private void calculateGaussianBlur(int width, int height) {  
  2.        int index = 0;  
  3.        int radius = (int)window_radius;  
  4.        double[][] gw = get2DKernalData(radius, sigma);  
  5.        double sumxx = 0, sumyy = 0, sumxy = 0;  
  6.        for(int row=0; row<height; row++) {  
  7.         for(int col=0; col<width; col++) {                 
  8.             for(int subrow =-radius; subrow<=radius; subrow++)  
  9.             {  
  10.                 for(int subcol=-radius; subcol<=radius; subcol++)  
  11.                 {  
  12.                     int nrow = row + subrow;  
  13.                     int ncol = col + subcol;  
  14.                     if(nrow >= height || nrow < 0)  
  15.                     {  
  16.                         nrow = 0;  
  17.                     }  
  18.                     if(ncol >= width || ncol < 0)  
  19.                     {  
  20.                         ncol = 0;  
  21.                     }  
  22.                     int index2 = nrow * width + ncol;  
  23.                     HarrisMatrix whm = harrisMatrixList.get(index2);  
  24.                     sumxx += (gw[subrow + radius][subcol + radius] * whm.getXGradient());  
  25.                     sumyy += (gw[subrow + radius][subcol + radius] * whm.getYGradient());  
  26.                     sumxy += (gw[subrow + radius][subcol + radius] * whm.getIxIy());  
  27.                 }  
  28.             }  
  29.             index = row * width + col;  
  30.             HarrisMatrix hm = harrisMatrixList.get(index);  
  31.             hm.setXGradient(sumxx);  
  32.             hm.setYGradient(sumyy);  
  33.             hm.setIxIy(sumxy);  
  34.               
  35.             // clean up for next loop  
  36.             sumxx = 0;  
  37.             sumyy = 0;  
  38.             sumxy = 0;  
  39.         }  
  40.        }          
  41. }  

第六步:非最大信号压制(non-max value suppression)

这个在边源检测中是为了得到一个像素宽的边缘,在这里则

是为了得到准确的一个角点像素,去掉非角点值。代码如下:

  1. /*** 
  2.  * we still use the 3*3 windows to complete the non-max response value suppression 
  3.  */  
  4. private void nonMaxValueSuppression(int width, int height) {  
  5.        int index = 0;  
  6.        int radius = (int)window_radius;  
  7.        for(int row=0; row<height; row++) {  
  8.         for(int col=0; col<width; col++) {  
  9.             index = row * width + col;  
  10.             HarrisMatrix hm = harrisMatrixList.get(index);  
  11.             double maxR = hm.getR();  
  12.             boolean isMaxR = true;  
  13.             for(int subrow =-radius; subrow<=radius; subrow++)  
  14.             {  
  15.                 for(int subcol=-radius; subcol<=radius; subcol++)  
  16.                 {  
  17.                     int nrow = row + subrow;  
  18.                     int ncol = col + subcol;  
  19.                     if(nrow >= height || nrow < 0)  
  20.                     {  
  21.                         nrow = 0;  
  22.                     }  
  23.                     if(ncol >= width || ncol < 0)  
  24.                     {  
  25.                         ncol = 0;  
  26.                     }  
  27.                     int index2 = nrow * width + ncol;  
  28.                     HarrisMatrix hmr = harrisMatrixList.get(index2);  
  29.                     if(hmr.getR() > maxR)  
  30.                     {  
  31.                         isMaxR = false;  
  32.                     }  
  33.                 }                     
  34.             }  
  35.             if(isMaxR)  
  36.             {  
  37.                 hm.setMax(maxR);  
  38.             }  
  39.         }  
  40.        }  
  41.       
  42. }  

运行效果:


程序完整源代码:

  1. package com.gloomyfish.image.harris.corner;  
  2.   
  3. import java.awt.image.BufferedImage;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6.   
  7. import com.gloomyfish.filter.study.GrayFilter;  
  8.   
  9. public class HarrisCornerDetector extends GrayFilter {  
  10.     private GaussianDerivativeFilter filter;  
  11.     private List<HarrisMatrix> harrisMatrixList;  
  12.     private double lambda = 0.04; // scope : 0.04 ~ 0.06  
  13.       
  14.     // i hard code the window size just keep it' size is same as   
  15.     // first order derivation Gaussian window size  
  16.     private double sigma = 1; // always  
  17.     private double window_radius = 1; // always  
  18.     public HarrisCornerDetector() {  
  19.         filter = new GaussianDerivativeFilter();  
  20.         harrisMatrixList = new ArrayList<HarrisMatrix>();  
  21.     }  
  22.   
  23.     @Override  
  24.     public BufferedImage filter(BufferedImage src, BufferedImage dest) {  
  25.         int width = src.getWidth();  
  26.         int height = src.getHeight();  
  27.         initSettings(height, width);  
  28.         if ( dest == null )  
  29.             dest = createCompatibleDestImage( src, null );  
  30.           
  31.         BufferedImage grayImage = super.filter(src, null);  
  32.         int[] inPixels = new int[width*height];  
  33.           
  34.         // first step  - Gaussian first-order Derivatives (3 × 3) - X - gradient, (3 × 3) - Y - gradient  
  35.         filter.setDirectionType(GaussianDerivativeFilter.X_DIRECTION);  
  36.         BufferedImage xImage = filter.filter(grayImage, null);  
  37.         getRGB( xImage, 0, 0, width, height, inPixels );  
  38.         extractPixelData(inPixels, GaussianDerivativeFilter.X_DIRECTION, height, width);  
  39.           
  40.         filter.setDirectionType(GaussianDerivativeFilter.Y_DIRECTION);  
  41.         BufferedImage yImage = filter.filter(grayImage, null);  
  42.         getRGB( yImage, 0, 0, width, height, inPixels );  
  43.         extractPixelData(inPixels, GaussianDerivativeFilter.Y_DIRECTION, height, width);  
  44.                   
  45.         // second step - calculate the Ix^2, Iy^2 and Ix^Iy  
  46.         for(HarrisMatrix hm : harrisMatrixList)  
  47.         {  
  48.             double Ix = hm.getXGradient();  
  49.             double Iy = hm.getYGradient();  
  50.             hm.setIxIy(Ix * Iy);  
  51.             hm.setXGradient(Ix*Ix);  
  52.             hm.setYGradient(Iy*Iy);  
  53.         }  
  54.           
  55.         // 基于高斯方法,中心点化窗口计算一阶导数和,关键一步 SumIx2, SumIy2 and SumIxIy, 高斯模糊  
  56.         calculateGaussianBlur(width, height);  
  57.   
  58.         // 求取Harris Matrix 特征值   
  59.         // 计算角度相应值R R= Det(H) - lambda * (Trace(H))^2  
  60.         harrisResponse(width, height);  
  61.           
  62.         // based on R, compute non-max suppression  
  63.         nonMaxValueSuppression(width, height);  
  64.           
  65.         // match result to original image and highlight the key points  
  66.         int[] outPixels = matchToImage(width, height, src);  
  67.           
  68.         // return result image  
  69.         setRGB( dest, 0, 0, width, height, outPixels );  
  70.         return dest;  
  71.     }  
  72.       
  73.       
  74.     private int[] matchToImage(int width, int height, BufferedImage src) {  
  75.         int[] inPixels = new int[width*height];  
  76.         int[] outPixels = new int[width*height];  
  77.         getRGB( src, 0, 0, width, height, inPixels );  
  78.         int index = 0;  
  79.         for(int row=0; row<height; row++) {  
  80.             int ta = 0, tr = 0, tg = 0, tb = 0;  
  81.             for(int col=0; col<width; col++) {  
  82.                 index = row * width + col;  
  83.                 ta = (inPixels[index] >> 24) & 0xff;  
  84.                 tr = (inPixels[index] >> 16) & 0xff;  
  85.                 tg = (inPixels[index] >> 8) & 0xff;  
  86.                 tb = inPixels[index] & 0xff;  
  87.                 HarrisMatrix hm = harrisMatrixList.get(index);  
  88.                 if(hm.getMax() > 0)  
  89.                 {  
  90.                     tr = 0;  
  91.                     tg = 255; // make it as green for corner key pointers  
  92.                     tb = 0;  
  93.                     outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;  
  94.                 }  
  95.                 else  
  96.                 {  
  97.                     outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;                    
  98.                 }  
  99.                   
  100.             }  
  101.         }  
  102.         return outPixels;  
  103.     }  
  104.     /*** 
  105.      * we still use the 3*3 windows to complete the non-max response value suppression 
  106.      */  
  107.     private void nonMaxValueSuppression(int width, int height) {  
  108.         int index = 0;  
  109.         int radius = (int)window_radius;  
  110.         for(int row=0; row<height; row++) {  
  111.             for(int col=0; col<width; col++) {  
  112.                 index = row * width + col;  
  113.                 HarrisMatrix hm = harrisMatrixList.get(index);  
  114.                 double maxR = hm.getR();  
  115.                 boolean isMaxR = true;  
  116.                 for(int subrow =-radius; subrow<=radius; subrow++)  
  117.                 {  
  118.                     for(int subcol=-radius; subcol<=radius; subcol++)  
  119.                     {  
  120.                         int nrow = row + subrow;  
  121.                         int ncol = col + subcol;  
  122.                         if(nrow >= height || nrow < 0)  
  123.                         {  
  124.                             nrow = 0;  
  125.                         }  
  126.                         if(ncol >= width || ncol < 0)  
  127.                         {  
  128.                             ncol = 0;  
  129.                         }  
  130.                         int index2 = nrow * width + ncol;  
  131.                         HarrisMatrix hmr = harrisMatrixList.get(index2);  
  132.                         if(hmr.getR() > maxR)  
  133.                         {  
  134.                             isMaxR = false;  
  135.                         }  
  136.                     }                     
  137.                 }  
  138.                 if(isMaxR)  
  139.                 {  
  140.                     hm.setMax(maxR);  
  141.                 }  
  142.             }  
  143.         }  
  144.           
  145.     }  
  146.       
  147.     /*** 
  148.      * 计算两个特征值,然后得到R,公式如下,可以自己推导,关于怎么计算矩阵特征值,请看这里: 
  149.      * http://www./matrix/eigen1/eigen1.html 
  150.      *  
  151.      *  A = Sxx; 
  152.      *  B = Syy; 
  153.      *  C = Sxy*Sxy*4; 
  154.      *  lambda = 0.04; 
  155.      *  H = (A*B - C) - lambda*(A+B)^2; 
  156.      * 
  157.      * @param width 
  158.      * @param height 
  159.      */  
  160.     private void harrisResponse(int width, int height) {  
  161.         int index = 0;  
  162.         for(int row=0; row<height; row++) {  
  163.             for(int col=0; col<width; col++) {  
  164.                 index = row * width + col;  
  165.                 HarrisMatrix hm = harrisMatrixList.get(index);  
  166.                 double c =  hm.getIxIy() * hm.getIxIy();  
  167.                 double ab = hm.getXGradient() * hm.getYGradient();  
  168.                 double aplusb = hm.getXGradient() + hm.getYGradient();  
  169.                 double response = (ab -c) - lambda * Math.pow(aplusb, 2);  
  170.                 hm.setR(response);  
  171.             }  
  172.         }         
  173.     }  
  174.   
  175.     private void calculateGaussianBlur(int width, int height) {  
  176.         int index = 0;  
  177.         int radius = (int)window_radius;  
  178.         double[][] gw = get2DKernalData(radius, sigma);  
  179.         double sumxx = 0, sumyy = 0, sumxy = 0;  
  180.         for(int row=0; row<height; row++) {  
  181.             for(int col=0; col<width; col++) {                 
  182.                 for(int subrow =-radius; subrow<=radius; subrow++)  
  183.                 {  
  184.                     for(int subcol=-radius; subcol<=radius; subcol++)  
  185.                     {  
  186.                         int nrow = row + subrow;  
  187.                         int ncol = col + subcol;  
  188.                         if(nrow >= height || nrow < 0)  
  189.                         {  
  190.                             nrow = 0;  
  191.                         }  
  192.                         if(ncol >= width || ncol < 0)  
  193.                         {  
  194.                             ncol = 0;  
  195.                         }  
  196.                         int index2 = nrow * width + ncol;  
  197.                         HarrisMatrix whm = harrisMatrixList.get(index2);  
  198.                         sumxx += (gw[subrow + radius][subcol + radius] * whm.getXGradient());  
  199.                         sumyy += (gw[subrow + radius][subcol + radius] * whm.getYGradient());  
  200.                         sumxy += (gw[subrow + radius][subcol + radius] * whm.getIxIy());  
  201.                     }  
  202.                 }  
  203.                 index = row * width + col;  
  204.                 HarrisMatrix hm = harrisMatrixList.get(index);  
  205.                 hm.setXGradient(sumxx);  
  206.                 hm.setYGradient(sumyy);  
  207.                 hm.setIxIy(sumxy);  
  208.                   
  209.                 // clean up for next loop  
  210.                 sumxx = 0;  
  211.                 sumyy = 0;  
  212.                 sumxy = 0;  
  213.             }  
  214.         }         
  215.     }  
  216.       
  217.     public double[][] get2DKernalData(int n, double sigma) {  
  218.         int size = 2*n +1;  
  219.         double sigma22 = 2*sigma*sigma;  
  220.         double sigma22PI = Math.PI * sigma22;  
  221.         double[][] kernalData = new double[size][size];  
  222.         int row = 0;  
  223.         for(int i=-n; i<=n; i++) {  
  224.             int column = 0;  
  225.             for(int j=-n; j<=n; j++) {  
  226.                 double xDistance = i*i;  
  227.                 double yDistance = j*j;  
  228.                 kernalData[row][column] = Math.exp(-(xDistance + yDistance)/sigma22)/sigma22PI;  
  229.                 column++;  
  230.             }  
  231.             row++;  
  232.         }  
  233.           
  234. //      for(int i=0; i<size; i++) {  
  235. //          for(int j=0; j<size; j++) {  
  236. //              System.out.print("\t" + kernalData[i][j]);  
  237. //          }  
  238. //          System.out.println();  
  239. //          System.out.println("\t ---------------------------");  
  240. //      }  
  241.         return kernalData;  
  242.     }  
  243.   
  244.     private void extractPixelData(int[] pixels, int type, int height, int width)  
  245.     {  
  246.         int index = 0;  
  247.         for(int row=0; row<height; row++) {  
  248.             int ta = 0, tr = 0, tg = 0, tb = 0;  
  249.             for(int col=0; col<width; col++) {  
  250.                 index = row * width + col;  
  251.                 ta = (pixels[index] >> 24) & 0xff;  
  252.                 tr = (pixels[index] >> 16) & 0xff;  
  253.                 tg = (pixels[index] >> 8) & 0xff;  
  254.                 tb = pixels[index] & 0xff;  
  255.                 HarrisMatrix matrix = harrisMatrixList.get(index);  
  256.                 if(type == GaussianDerivativeFilter.X_DIRECTION)  
  257.                 {  
  258.                     matrix.setXGradient(tr);  
  259.                 }  
  260.                 if(type == GaussianDerivativeFilter.Y_DIRECTION)  
  261.                 {  
  262.                     matrix.setYGradient(tr);  
  263.                 }  
  264.             }  
  265.         }  
  266.     }  
  267.       
  268.     private void initSettings(int height, int width)  
  269.     {  
  270.         int index = 0;  
  271.         for(int row=0; row<height; row++) {  
  272.             for(int col=0; col<width; col++) {  
  273.                 index = row * width + col;  
  274.                 HarrisMatrix matrix = new HarrisMatrix();  
  275.                 harrisMatrixList.add(index, matrix);  
  276.             }  
  277.         }  
  278.     }  
  279.   
  280. }  
最后注意:

我是把彩色图像变为灰度图像来计算,这个计算量小点

处理容易点,此外很多图像处理软件都会用标记来显示

关键点像素,我没有,只是将关键点像素改为绿色。

所以可以从这些方面有很大的提高空间。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多