HOG特征可视化

ゝ一世哀愁。 2022-05-26 22:19 413阅读 0赞

可视化说明

在之前博客HOG原理及OpenCV实现中,我们解释了HOG算法的原理。最终提取到的特征就是一串向量,其实我们并不知道它具体是什么样子,也不知道它到底是不是能体现目标区域与非目标区域的差异。为了解决这个问题,我们需要对HOG特征做可视化处理。
HOG特征首先去计算每个像素的梯度,然后建立滑动窗口,在滑动窗中建立滑动块,在块中建立等分的单元(cell)。我们仔细思考下这个过程,一个块在滑动时,每次包含的单元是不同的,但是对于一个单元而言,它是不随块滑动而改变的。这就意味着,如果块尺寸,块步长,单元尺寸确定了,一个窗口中的单元数目与它们中分别包含的像素就确定了。HOG的可视化就是利用这一点,它可视化的东西就是一个单元内的bin投票结果。
为了让下面的过程变得更直观,我们将整个图像作为一个检测窗来使用,也就是说不存在滑动窗的概念。
特别要注意,这应该是HOG最容易产生异常的地方。下面的图片本来是个900×600 900 × 600 的尺寸,但是我对它做了缩放,调整成904×600 904 × 600 。这是为了让904的范围内可以滑出整数个块,因为此时HOG在使用默认参数,即块尺寸16×16 16 × 16 ,块步长8×8 8 × 8 。

900−168=110.5 900 − 16 8 = 110.5

904−168=111 904 − 16 8 = 111


代码实现

  1. #include <iostream>
  2. #include <opencv2/core/core.hpp>
  3. #include <opencv2/highgui/highgui.hpp>
  4. #include <opencv2/opencv.hpp>
  5. using namespace cv;
  6. using namespace std;
  7. // HOGDescriptor visual_imagealizer
  8. // adapted for arbitrary size of feature sets and training images
  9. Mat get_hogdescriptor_visual_image(Mat& origImg,
  10. vector<float>& descriptorValues,
  11. Size winSize,
  12. Size cellSize,
  13. int scaleFactor,
  14. double viz_factor)
  15. {
  16. Mat visual_image;
  17. resize(origImg, visual_image, Size(origImg.cols*scaleFactor, origImg.rows*scaleFactor));
  18. int gradientBinSize = 9;
  19. // dividing 180° into 9 bins, how large (in rad) is one bin?
  20. float radRangeForOneBin = 3.14/(float)gradientBinSize;
  21. // prepare data structure: 9 orientation / gradient strenghts for each cell
  22. int cells_in_x_dir = winSize.width / cellSize.width;
  23. int cells_in_y_dir = winSize.height / cellSize.height;
  24. int totalnrofcells = cells_in_x_dir * cells_in_y_dir;
  25. float*** gradientStrengths = new float**[cells_in_y_dir];
  26. int** cellUpdateCounter = new int*[cells_in_y_dir];
  27. for (int y=0; y<cells_in_y_dir; y++)
  28. {
  29. gradientStrengths[y] = new float*[cells_in_x_dir];
  30. cellUpdateCounter[y] = new int[cells_in_x_dir];
  31. for (int x=0; x<cells_in_x_dir; x++)
  32. {
  33. gradientStrengths[y][x] = new float[gradientBinSize];
  34. cellUpdateCounter[y][x] = 0;
  35. for (int bin=0; bin<gradientBinSize; bin++)
  36. gradientStrengths[y][x][bin] = 0.0;
  37. }
  38. }
  39. // nr of blocks = nr of cells - 1
  40. // since there is a new block on each cell (overlapping blocks!) but the last one
  41. int blocks_in_x_dir = cells_in_x_dir - 1;
  42. int blocks_in_y_dir = cells_in_y_dir - 1;
  43. // compute gradient strengths per cell
  44. int descriptorDataIdx = 0;
  45. int cellx = 0;
  46. int celly = 0;
  47. for (int blockx=0; blockx<blocks_in_x_dir; blockx++)
  48. {
  49. for (int blocky=0; blocky<blocks_in_y_dir; blocky++)
  50. {
  51. // 4 cells per block ...
  52. for (int cellNr=0; cellNr<4; cellNr++)
  53. {
  54. // compute corresponding cell nr
  55. int cellx = blockx;
  56. int celly = blocky;
  57. if (cellNr==1) celly++;
  58. if (cellNr==2) cellx++;
  59. if (cellNr==3)
  60. {
  61. cellx++;
  62. celly++;
  63. }
  64. for (int bin=0; bin<gradientBinSize; bin++)
  65. {
  66. float gradientStrength = descriptorValues[ descriptorDataIdx ];
  67. descriptorDataIdx++;
  68. gradientStrengths[celly][cellx][bin] += gradientStrength;
  69. } // for (all bins)
  70. // note: overlapping blocks lead to multiple updates of this sum!
  71. // we therefore keep track how often a cell was updated,
  72. // to compute average gradient strengths
  73. cellUpdateCounter[celly][cellx]++;
  74. } // for (all cells)
  75. } // for (all block x pos)
  76. } // for (all block y pos)
  77. // compute average gradient strengths
  78. for (int celly=0; celly<cells_in_y_dir; celly++)
  79. {
  80. for (int cellx=0; cellx<cells_in_x_dir; cellx++)
  81. {
  82. float NrUpdatesForThisCell = (float)cellUpdateCounter[celly][cellx];
  83. // compute average gradient strenghts for each gradient bin direction
  84. for (int bin=0; bin<gradientBinSize; bin++)
  85. {
  86. gradientStrengths[celly][cellx][bin] /= NrUpdatesForThisCell;
  87. }
  88. }
  89. }
  90. cout << "descriptorDataIdx = " << descriptorDataIdx << endl;
  91. // draw cells
  92. for (int celly=0; celly<cells_in_y_dir; celly++)
  93. {
  94. for (int cellx=0; cellx<cells_in_x_dir; cellx++)
  95. {
  96. int drawX = cellx * cellSize.width;
  97. int drawY = celly * cellSize.height;
  98. int mx = drawX + cellSize.width/2;
  99. int my = drawY + cellSize.height/2;
  100. rectangle(visual_image,
  101. Point(drawX*scaleFactor,drawY*scaleFactor),
  102. Point((drawX+cellSize.width)*scaleFactor,
  103. (drawY+cellSize.height)*scaleFactor),
  104. CV_RGB(100,100,100),
  105. 1);
  106. // draw in each cell all 9 gradient strengths
  107. for (int bin=0; bin<gradientBinSize; bin++)
  108. {
  109. float currentGradStrength = gradientStrengths[celly][cellx][bin];
  110. // no line to draw?
  111. if (currentGradStrength==0)
  112. continue;
  113. float currRad = bin * radRangeForOneBin + radRangeForOneBin/2;
  114. float dirVecX = cos( currRad );
  115. float dirVecY = sin( currRad );
  116. float maxVecLen = cellSize.width/2;
  117. float scale = viz_factor; // just a visual_imagealization scale,
  118. // to see the lines better
  119. // compute line coordinates
  120. float x1 = mx - dirVecX * currentGradStrength * maxVecLen * scale;
  121. float y1 = my - dirVecY * currentGradStrength * maxVecLen * scale;
  122. float x2 = mx + dirVecX * currentGradStrength * maxVecLen * scale;
  123. float y2 = my + dirVecY * currentGradStrength * maxVecLen * scale;
  124. // draw gradient visual_imagealization
  125. line(visual_image,
  126. Point(x1*scaleFactor,y1*scaleFactor),
  127. Point(x2*scaleFactor,y2*scaleFactor),
  128. CV_RGB(0,0,255),
  129. 1);
  130. } // for (all bins)
  131. } // for (cellx)
  132. } // for (celly)
  133. // don't forget to free memory allocated by helper data structures!
  134. for (int y=0; y<cells_in_y_dir; y++)
  135. {
  136. for (int x=0; x<cells_in_x_dir; x++)
  137. {
  138. delete[] gradientStrengths[y][x];
  139. }
  140. delete[] gradientStrengths[y];
  141. delete[] cellUpdateCounter[y];
  142. }
  143. delete[] gradientStrengths;
  144. delete[] cellUpdateCounter;
  145. return visual_image;
  146. }
  147. int main()
  148. {
  149. HOGDescriptor hog;
  150. hog.winSize=Size(904,600);
  151. vector<float> des;
  152. Mat src = imread("timg.jpg");
  153. Mat dst ;
  154. resize(src,dst,Size(904,600));
  155. imshow("src",src);
  156. hog.compute(dst,des);
  157. cout<<des.size()<<endl;
  158. Mat background = Mat::zeros(Size(904,600),CV_8UC1);
  159. Mat background_hog = get_hogdescriptor_visual_image(background,des,hog.winSize,hog.cellSize,1,2.0);
  160. imshow("HOG特征1",background_hog);
  161. imwrite("特征可视化1.jpg",background_hog);
  162. Mat src_hog = get_hogdescriptor_visual_image(src,des,hog.winSize,hog.cellSize,1,2.0);
  163. imshow("HOG特征2",src_hog);
  164. imwrite("特征可视化2.jpg",src_hog);
  165. waitKey();
  166. return 0;
  167. }

这里写图片描述
这里写图片描述

这里写图片描述

发表评论

表情:
评论列表 (有 0 条评论,413人围观)

还没有评论,来说两句吧...

相关阅读

    相关 CAM:特征技术

    众所周知,深度学习是一个"黑盒"系统。它通过“end-to-end”的方式来工作,输入数据例如RGB图像,输出目标例如类别标签、回归值等,中间过程不可得知。如何才能打开“黑盒”

    相关 HOG特征

    引言 HOG 全名为Histogram of Gragient,表示梯度方向直方图; 梯度,作为一个向量,包括大小(幅值)和方向(角度)两部分。图像中像素点的

    相关 HOG特征

    可视化说明 在之前博客[HOG原理及OpenCV实现][HOG_OpenCV]中,我们解释了HOG算法的原理。最终提取到的特征就是一串向量,其实我们并不知道它具体是什么样

    相关 Tensorboard特征

    前言 实现孪生卷积神经网络时,验证是否共享了参数,进行了特征图可视化。由于卷积层特征图通道都很大,需要进行重新reshape成Tensorboard可以显示的通道数(1或