opencv3.1的HOG特征检测参数详解(HOG梯度直方图的高质量文章*****)

编程入门 行业动态 更新时间:2024-10-28 18:34:31

opencv3.1的HOG特征检测参数详解(HOG梯度<a href=https://www.elefans.com/category/jswz/34/1763343.html style=直方图的高质量文章*****)"/>

opencv3.1的HOG特征检测参数详解(HOG梯度直方图的高质量文章*****)

HOG特征的源码中给出的检测窗口大小的默认值为128*64,网上很多范例都会将原图resize为该尺寸。那么HOG特征检测时,检测窗口尺寸一定要处理为128*64吗?No,我们只要遵守必要的规则,检测窗口可以设置为任意尺寸。本文记录此问题点的验证方法和使用HOG特征点提取时的注意事项。

      1. HOGDescriptor参数详解

      HOGDescriptor是HOG特征类,使用该类时,涉及到的参数有:ImageSize,winSize,blockSize,cellSize,blockStride, winStride。我们先通过一张图来大致了解一下HOG特征提取和参数设置的规则。这张图来源于参考文献opencv源码解析之(6):hog源码分析,感谢原作者,我在这张图上添加了一些参数设置规则的说明。

      上图中的规则在HOG的源码中可以找到,HOG源码的路径为D:\opencv源码路径\sources\modules\objdetect\src\hog.cpp。本文中的”opencv源码路径“指的是正在使用的opencv的sources所在的路径。该语句在函数getDescriptorSize中,该函数用于计算HOG特征点的维度。

1 2 3 4      CV_Assert ( blockSize . width % cellSize . width == 0 &&          blockSize . height % cellSize . height == 0 ) ;      CV_Assert ( ( winSize . width - blockSize . width ) % blockStride . width == 0 &&          ( winSize . height - blockSize . height ) % blockStride . height == 0 ) ;

      在opencv3.1的hog源码的注释中,有提到HOG检测算法的原理,大致如下:
            检测算法以4层嵌套循环在计算,每一层循环均为二维,理论上其动作分别如下:
                  用设置的winSize以winStride为单位遍历ImageSize
                        用设置的blockSize以blockStride为单位遍历winSize
                              用cellSize为单位遍历blockSize
                                    遍历cellSize中的每一个像素

      但实际上源码中并未这样做,因为这样计算的速度将会非常非常慢。为了加快HOG提取的速度,HOG算法的作者使用了查表法,将pixData和blockData计算完成后存在内存中,在遍历过程中用到相应数据时,直接查表即可,这样可以省去大量重复计算的时间。

      HOGDescriptor其中一个包含默认参数的构造函数如下,这些默认值也是我们在很多范例中见到的设定值。HOGDescriptor的构造函数在opencv3.1源码中的路径为D:\opencv源码路径\sources\modules\objdetect\include\opencv2\objdetect.hpp。

1 2 3 4 5    CV_WRAP HOGDescriptor ( ) : winSize ( 64 , 128 ) , blockSize ( 16 , 16 ) , blockStride ( 8 , 8 ) ,          cellSize ( 8 , 8 ) , nbins ( 9 ) , derivAperture ( 1 ) , winSigma ( - 1 ) ,          histogramNormType ( HOGDescriptor :: L2Hys ) , L2HysThreshold ( 0.2 ) , gammaCorrection ( true ) ,          free_coef ( - 1.f ) , nlevels ( HOGDescriptor :: DEFAULT_NLEVELS ) , signedGradient ( false )      { }

     2. HOGDescriptor参数设置实例

     在opencv3.1的源码中,有一个名为train_HOG的范例,其路径为D:\opencv源码路径\sources\samples\cpp\train_HOG.cpp,该范例中有一个将HOG特征可视化的函数。我对该文件进行改造后用来验证HOGDescriptor尺寸设置的问题。

     源图像尺寸为448*280,我设置的参数如下,winSize为图像尺寸,blockSize,blockStride,cellSize的设置则遵守本文图1的规则,其他参数保持默认值。这样既可以得到图像的HOG特征点:9个方向的梯度直方图。

1 2 3 4 5 HOGDescriptor hog ; hog . winSize = Size ( 448 , 280 ) ; hog . blockSize = Size ( 48 , 24 ) ; hog . blockStride = Size ( 8 , 8 ) ; hog . cellSize = Size ( 24 , 12 ) ;

      其可视化结果得到的结果如下图所示,每一个cell中都有一只绿色“小蜘蛛”。我们可以观察到图像右侧和下方有一部分无特征点,因为这部分的值不足以构成一个cell。

      上图的特征点过于粗糙,而且图像中还有一部分区域并未检测到特征点。我将参数进行调整,调整后参数和图像如下所示。在这一组参数下,特征点覆盖到了整幅图像,特征点更加密集。

1 2 3 4 5 HOGDescriptor hog ; hog . winSize = Size ( 448 * 280 ) ; hog . blockSize = Size ( 16 , 16 ) ; hog . blockStride = Size ( 2 , 2 ) ; hog . cellSize = Size ( 8 , 8 ) ;

        实验证明,只要遵守响应的规则,我们可以根据实际需求自行调整各个参数。

3. HOG特征点可视化函数

    在train_HOG.cpp中,HOG特征点可视化函数名为get_hogdescriptor_visu,该函数实现算法的链接为.php?id=public:hog_descriptor_computation_and_visualization (需fq)。

    该函数在使用时需要注意两点:该函数会把原图扩大3被后绘制HOG特征点,若要调整尺寸,需要自行修改响应代码;该函数默认cellSize为8*8,因此若需要显示任意尺寸的HOG特征点,还需要将函数中使用的cellSize修正为实际使用值。我将该函数进行了调整,在调用时可以设置放大倍数和cellSize,代码如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 // From .php?id=public:hog_descriptor_computation_and_visualization Mat get_hogdescriptor_visu ( const Mat & color_origImg , vector < float > & descriptorValues , const Size & size , const Size & cellSize , const float & zoomFac ) { const int DIMX = size . width ; const int DIMY = size . height ; Mat visu ; resize ( color_origImg , visu , Size ( ( int ) ( color_origImg . cols* zoomFac ) , ( int ) ( color_origImg . rows* zoomFac ) ) ) ; //int cellSizeW = cellSize.width; int gradientBinSize = 9 ; float radRangeForOneBin = ( float ) ( CV_PI / ( float ) gradientBinSize ) ; // dividing 180 into 9 bins, how large (in rad) is one bin? // prepare data structure: 9 orientation / gradient strenghts for each cell int cells_in_x_dir = DIMX / cellSize . width ; int cells_in_y_dir = DIMY / cellSize . height ; float * * * gradientStrengths = new float * * [ cells_in_y_dir ] ; int * * cellUpdateCounter = new int * [ cells_in_y_dir ] ; for ( int y = 0 ; y < cells_in_y_dir ; y ++ ) { gradientStrengths [ y ] = new float * [ cells_in_x_dir ] ; cellUpdateCounter [ y ] = new int [ cells_in_x_dir ] ; for ( int x = 0 ; x < cells_in_x_dir ; x ++ ) { gradientStrengths [ y ] [ x ] = new float [ gradientBinSize ] ; cellUpdateCounter [ y ] [ x ] = 0 ; for ( int bin = 0 ; bin < gradientBinSize ; bin ++ ) gradientStrengths [ y ] [ x ] [ bin ] = 0.0 ; } } // nr of blocks = nr of cells - 1 // since there is a new block on each cell (overlapping blocks!) but the last one int blocks_in_x_dir = cells_in_x_dir - 1 ; int blocks_in_y_dir = cells_in_y_dir - 1 ; // compute gradient strengths per cell int descriptorDataIdx = 0 ; int cellx = 0 ; int celly = 0 ; for ( int blockx = 0 ; blockx < blocks_in_x_dir ; blockx ++ ) { for ( int blocky = 0 ; blocky < blocks_in_y_dir ; blocky ++ ) { // 4 cells per block ... for ( int cellNr = 0 ; cellNr < 4 ; cellNr ++ ) { // compute corresponding cell nr cellx = blockx ; celly = blocky ; if ( cellNr == 1 ) celly ++ ; if ( cellNr == 2 ) cellx ++ ; if ( cellNr == 3 ) { cellx ++ ; celly ++ ; } for ( int bin = 0 ; bin < gradientBinSize ; bin ++ ) { float gradientStrength = descriptorValues [ descriptorDataIdx ] ; descriptorDataIdx ++ ; gradientStrengths [ celly ] [ cellx ] [ bin ] += gradientStrength ; } // for (all bins) // note: overlapping blocks lead to multiple updates of this sum! // we therefore keep track how often a cell was updated, // to compute average gradient strengths cellUpdateCounter [ celly ] [ cellx ] ++ ; } // for (all cells) } // for (all block x pos) } // for (all block y pos) // compute average gradient strengths for ( celly = 0 ; celly < cells_in_y_dir ; celly ++ ) { for ( cellx = 0 ; cellx < cells_in_x_dir ; cellx ++ ) { float NrUpdatesForThisCell = ( float ) cellUpdateCounter [ celly ] [ cellx ] ; // compute average gradient strenghts for each gradient bin direction for ( int bin = 0 ; bin < gradientBinSize ; bin ++ ) { gradientStrengths [ celly ] [ cellx ] [ bin ] /= NrUpdatesForThisCell ; } } } // draw cells for ( celly = 0 ; celly < cells_in_y_dir ; celly ++ ) { for ( cellx = 0 ; cellx < cells_in_x_dir ; cellx ++ ) { int drawX = cellx * cellSize . width ; int drawY = celly * cellSize . height ; int mx = drawX + cellSize . width / 2 ; int my = drawY + cellSize . height / 2 ; rectangle ( visu , Point ( ( int ) ( drawX* zoomFac ) , ( int ) ( drawY* zoomFac ) ) , Point ( ( int ) ( ( drawX + cellSize . width ) * zoomFac ) , ( int ) ( ( drawY + cellSize . height ) * zoomFac ) ) , Scalar ( 100 , 100 , 100 ) , 1 ) ; // draw in each cell all 9 gradient strengths for ( int bin = 0 ; bin < gradientBinSize ; bin ++ ) { float currentGradStrength = gradientStrengths [ celly ] [ cellx ] [ bin ] ; // no line to draw? if ( currentGradStrength == 0 ) continue ; float currRad = bin * radRangeForOneBin + radRangeForOneBin / 2 ; float dirVecX = cos ( currRad ) ; float dirVecY = sin ( currRad ) ; float maxVecLen = ( cellSize . width > cellSize . height ) ? ( float ) ( cellSize . width / 2.f ) : ( float ) ( cellSize . height / 2.f ) ; float scale = 2.5 ; // just a visualization scale, to see the lines better // compute line coordinates float x1 = mx - dirVecX * currentGradStrength * maxVecLen * scale ; float y1 = my - dirVecY * currentGradStrength * maxVecLen * scale ; float x2 = mx + dirVecX * currentGradStrength * maxVecLen * scale ; float y2 = my + dirVecY * currentGradStrength * maxVecLen * scale ; // draw gradient visualization line ( visu , Point ( ( int ) ( x1* zoomFac ) , ( int ) ( y1* zoomFac ) ) , Point ( ( int ) ( x2* zoomFac ) , ( int ) ( y2* zoomFac ) ) , Scalar ( 0 , 255 , 0 ) , 1 ) ; } // for (all bins) } // for (cellx) } // for (celly) // don't forget to free memory allocated by helper data structures! for ( int y = 0 ; y < cells_in_y_dir ; y ++ ) { for ( int x = 0 ; x < cells_in_x_dir ; x ++ ) { delete [ ] gradientStrengths [ y ] [ x ] ; } delete [ ] gradientStrengths [ y ] ; delete [ ] cellUpdateCounter [ y ] ; } delete [ ] gradientStrengths ; delete [ ] cellUpdateCounter ; return visu ; } // get_hogdescriptor_visu

更多推荐

opencv3.1的HOG特征检测参数详解(HOG梯度直方图的高质量文章*****)

本文发布于:2024-03-09 20:47:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1726075.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:直方图   梯度   高质量   详解   特征

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!