直方图统计 opencv

      版权声明:本文为博主原创文章,欢迎阅读与转载,转载请注明出处与链接。          https://blog.csdn.net/sinat_36264666/article/details/78847003        </div>
        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
                          <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
      <div class="htmledit_views" id="content_views">

常言道“温故而知新”,写此文章就是对自己目前学习内容的小小的总结与记录。

本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上有些瑕疵,还望读者能够指教探讨,大家共同进步。

博主机器配置为:VS2013+opencv2.4.13+Win-64bit。

若本文能给读者带来一点点启示与帮助,我就很开心了。

====================分割线====================


1-BGR直方图

  • 在OpenCV中,彩色图像存储是通过多通道的数组来实现的,对CV_8UF3来言,其每个数组通道中的元素可取值为0到255。
  • 颜色分布直方图描述的是不同色彩在整幅图像中所占的比例,而并不关心每种色彩所处的空间位置。
  • 因此,对彩色图像求其直方图,可先提取彩色图像的各个通道,然后对每个通道进行直方图计算,最后利用图像融合技术合并通道信息,求解出图像颜色分布直方图。
===========================分割线=========================

2-换个角度认识图像(直方图)

第一个就是当我们面对图像的时候,我们面对的是抽象的矩阵, 如下图,下面是0-255的灰度图像的表示 ,密密麻麻的。


那么我们做的直方图,其实就是对这些像素值的统计。例如:首先,我们需要把0-255分成 17 个 区域(bin),如下图所示:


我们对每个范围中的灰度值进行统计排序,做出如下的表格:


我们是以图像的灰度为例子说明这个直方图,当然直方图不仅仅用于灰度特种统计排序,还可以用于图像的梯度、方向等特征。

灰度直方图是灰度级的函数,描述图像中该灰度级的像素个数(或该灰度级像素出现的频率):其横坐标是灰度级,纵坐标表示图像中该灰度级出现的个数(频率)。

在以上的过程中,我们使用到一些重要的参数,理解这些参数帮助我们更好的使用API函数。

  • dims:需要统计的特征的数目,我们上面只统计了 灰度值这个特征,所以, dims =1。
  • bins:一般翻译为箱子,看上图,一共有16个bins,其实就和我们平时见得简单函数差不多。在图像直方图中,你可以把一个灰度值设置为一个bins,0~255强度的灰度值一共就需要256个bins,是不是很简单。 
  • Range:就是范围啦,规定一个bins能够达到的最大和最小的范围。比如一张图片10*10,那么就有100个像素。然后前面已经说过,直方图是按照亮度统计像素数量,那么范围就是0~100啦。这里有一个地方要说一下,刚刚0~100还是对于比较小的图像,那么对于比较大的图像1000*1000,那么范围太大了。我们统计像素数量的时候肯定没有问题,但是要画直方图的时候,难道有一个包含100000个像素,岂不是要化的很长?所以,一般在画直方图的时候,会有一个比例缩放的过程,比如我提前定好我直方图最大的高度只能够是256,那么你就可以用(最大的高度/最大的像素量)统计到的像素量来进行缩放。这样就简单多了。我这里提到的缩放方式只是一种,你可以随便定义喜欢的缩放方式。

一维直方图的结构表示为


再结合上面画的示意图,应该就很好理解了。


基本的概念其实很简答,想的时候要不要想复杂了,那么基本概念就到这里了。

===============分割线===============

3-相关函数说明

当然了,首先介绍的是计算直方图的函数了。

直方图计算:calcHist()函数


  
  
  1. void calcHist( const Mat* images, int nimages,
  2. const int* channels, InputArray mask,
  3. OutputArray hist, int dims, const int* histSize,
  4. const float** ranges, bool uniform=true, bool accumulate=false );
参数解释:

  • 参数1:输入源图像。注意这里的格式是const Mat*,也就是说,你要传入一个地址,输入的数组(图片)或者数组集(一堆图片)需要为相同的深度(CV_8U或CV_32F)和相同的尺寸。
  • 参数2:int类型的nimages,输入数组的个数,也就是第一个参数中存放了多少张“图像”,有几个原数组。
  • 参数3:const int*类型的channels,用来计算直方图的channes的数组,需要统计的通道(dim)索引。第一个数组通道从0到images[0].channels()-1,而第二个数组通道从images[0]计算到images[0].channels()+images[1].channels()-1,以此类推。比如输入是2副图像,第一副图像有0,1,2共三个channel,第二幅图像只有0一个channel,那么输入就一共有4个channes,如果int channels[3] = {3, 2, 0},那么就表示是使用第二副图像的第一个通道和第一副图像的第2和第0个通道来计算直方图。(这句表示没看懂)
  • 参数4:InputArray类型的mask,可选的操作掩码。如果此掩码不为空,那么它必须为8位(CV_8U)的数组,并且与images[i]有同样大小的尺寸,值为1的点将用来计算直方图。这里的非零掩码元素用于标记出统计直方图的数组元素数据。
  • 参数5:OutputArray类型的hist,输出的计算出来的直方图,一个二维数组。
  • 参数6:int类型dims,需要计算的直方图的维度,必须是正数,且不大于CV_MAX_DIMS。(32)
  • 参数7:const int*类型的histSize,存放每个维度的直方图尺寸的数组。简单把直方图看作一个一个的竖条的话,就是每一维上竖条的个数。
  • 参数8:const float**类型的ranges,表示每一个维度数组(第6个参数dims)的每一维的边界阵列,可以理解为每一维数值的取值范围。比如 float rang1[] = {0, 20};float rang2[] = {30, 40};  const float*rangs[] = {rang1, rang2};那么就是对0,20和30,40范围的值进行统计。
  • 参数9:bool类型的uniform,表示直方图是否均匀的标识符,即每一个竖条的宽度是否相等。有默认值true
  • 参数10:bool类型的accumulate,累计标识符,有默认值false。若其为true,直方图在配置阶段不会被清零。此功能主要是允许从多个阵列中计算单个直方图,或者用于在特定的时间更新直方图。
=====================间隔线======================
归一化:normalize()函数
功能:缩放和移位数组元素,以便指定的标准(alpha)或最小(alpha)和最大(beta)数组值获得指定的值。

  
  
  1. void normalize( InputArray src, OutputArray dst, double alpha=1, double beta=0,
  2. int norm_type=NORM_L2, int dtype= -1, InputArray mask=noArray());
参数解释:
  • 参数1:InputArray类型的src,输入数组(图像)
  • 参数2:OutputArray类似的dst,输出数组(图像),与输入图像类型尺寸一样。
  • 参数3:alpha,表示range normalization模式的最小值。有默认值为1。
  • 参数4:beta,表示range normalization模式的最大值,不用于norm normalization(范数归一化)模式。有默认值为0。
  • 参数5:normType,表示归一化的类型,可以有以下的取值:
---------------NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化,一般较常用。
---------------NORM_INF:此类型的定义没有查到,根据OpenCV 1的对应项,可能是归一化数组的C-范数(绝对值的最大值)。
---------------NORM_L1:归一化数组的L1-范数(绝对值的和)。
---------------NORM_L2:归一化数组的(欧几里德)L2-范数。
  • 参数6:有默认值为-1。dtype为负数时,输出数组的type与输入数组的type相同;否则,输出数组与输入数组只是通道数相同,而tpye=CV_MAT_DEPTH(dtype)。
  • 参数7:操作掩膜,用于指示函数是否仅仅对指定的元素进行操作。
=====================间隔线======================
绘图方法详情请看官方文档:  基本绘图
=======================分割线=====================

4-代码演示


  
  
  1. //----------------------------------------------------------
  2. //功能:BGR三通道直方图实现
  3. //----------------------------------------------------------
  4. #include <opencv2/core/core.hpp>
  5. #include <opencv2/highgui/highgui.hpp>
  6. #include <opencv2/imgproc/imgproc.hpp>
  7. #include <iostream>
  8. using namespace std;
  9. using namespace cv;
  10. int main()
  11. {
  12. //------------【1】读取源图像并检查图像是否读取成功--------------------------------
  13. Mat srcImage = imread( "D:/OutPutResult/ImageTest/aurora.jpg");
  14. if (!srcImage.data)
  15. {
  16. cout << "读取图片错误,请重新输入正确路径!\n";
  17. system( "pause");
  18. return -1;
  19. }
  20. namedWindow( "【源图像-RGB颜色空间】");
  21. imshow( "【源图像-RGB颜色空间】", srcImage);
  22. //-------------【2】图像通道的分离,3个通道B、G、R------------------------
  23. vector<Mat> rgb_channel;
  24. split(srcImage, rgb_channel);
  25. //-------------【3】初始化直方图计算参数---------------------------------------
  26. int bins = 256;
  27. int histsize[] = { bins };
  28. float range[] = { 0, 256 };
  29. const float* histRange = { range };
  30. Mat b_Hist, g_Hist, r_Hist;
  31. //-------------【4】计算各个通道的直方图--------------------------------------
  32. calcHist(&rgb_channel[ 0], 1, 0, Mat(), b_Hist, 1, histsize, &histRange, true, false); //B-通道
  33. calcHist(&rgb_channel[ 1], 1, 0, Mat(), g_Hist, 1, histsize, &histRange, true, false); //G-通道
  34. calcHist(&rgb_channel[ 2], 1, 0, Mat(), r_Hist, 1, histsize, &histRange, true, false); //R-通道
  35. //-------------【5】设置直方图绘图参数----------------------------------------------------
  36. int hist_h = 360;
  37. int hist_w = bins * 3;
  38. int bin_w = cvRound(( double)hist_w / bins);
  39. Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0)); //创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像
  40. //-------------【6】将直方图归一化到[0,histImage.rows] ------------------------------------------------------------------------------
  41. normalize(b_Hist, b_Hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); //B-通道
  42. normalize(g_Hist, g_Hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); //G-通道
  43. normalize(r_Hist, r_Hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); //R-通道
  44. //--------------【7】绘制直方图 ----------------------------------------------------------------
  45. for ( int i = 1; i < bins; i++)
  46. {
  47. //绘制B通道的直方图信息
  48. line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(b_Hist.at< float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(b_Hist.at< float>(i))), Scalar( 255, 0, 0), 2, 8, 0);
  49. //绘制G通道的直方图信息
  50. line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(g_Hist.at< float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(g_Hist.at< float>(i))), Scalar( 0, 255, 0), 2, 8, 0);
  51. //绘制R通道的直方图信息
  52. line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(r_Hist.at< float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(r_Hist.at< float>(i))), Scalar( 0, 0, 255), 2, 8, 0);
  53. }
  54. namedWindow( "【RGB直方图】");
  55. imshow( "【RGB直方图】", histImage);
  56. waitKey( 0);
  57. return 0;
  58. }
=================================分割线======================

5-显示结果


==============================分割线================================

6-程序说明

先把源图像的通道进行分离,0-通道为B;1-通道为G;2-通道为R。然后计算每个通道的直方图并绘制显示处理。


参考文献:

【拜小白opencv】44-灰度直方图(一维直方图)calcHist()函数、minMaxLoc()函数、normalize()函数

【OpenCV】绘制直方图

Opencv2系列学习笔记4(灰度直方图)

OpenCV从入门到放弃(七):直方图那些事儿

Opencv图像识别从零到精通(8)-----灰度直方图

==========================END======================
      版权声明:本文为博主原创文章,欢迎阅读与转载,转载请注明出处与链接。          https://blog.csdn.net/sinat_36264666/article/details/78847003        </div>
        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
                          <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
      <div class="htmledit_views" id="content_views">

猜你喜欢

转载自blog.csdn.net/weixin_43124720/article/details/90245504