【拜小白opencv】45-二维H-S直方图绘制----calcHist()函数、minMaxLoc()函数

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

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

 

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

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

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

 

本节以HSV空间的H-S的二维联合直方图为例。需要说明的是,二维直方图并不是一个通道一个通道的分别独立平行计算,而是将两个通道的数据整合起来计算出的二维直方图。

 

前言:

 

直方图的定义

直方图(Histogram)又称柱状图、质量分布图,是一种统计报告图。直方图由一系列高度不等的纵向条纹或线段表示数据分布的情况。一般用横轴表示数据类型,纵轴表示分布情况。在图像处理上,直方图是图像信息统计的有力工具。其实也就是统计一幅图某个亮度像素数量。

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

第一个就是当我们面对图像的时候,我们面对的是抽象的矩阵,如下图,下面是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,那么你就可以用(最大的高度/最大的像素量)统计到的像素量来进行缩放。这样就简单多了。我这里提到的缩放方式只是一种,你可以随便定义喜欢的缩放方式。

 

 

一维直方图的结构表示为:

 

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

 

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

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

2-H-S直方图

  • 为了刻画图像中颜色的直观特性,常常需要分析图像HSV空间下的直方图特征。
  • HSV空间由色调、饱和度以及亮度构成,因此在进行直方图计算时,需要先将源RGB图像转换为HSV颜色空间图像,然后将对应的H和S通道进行单元划分,在其二维空间上计算对应直方图,其次再通过计算直方图空间上的最大值,归一化绘制相应的直方图信息。
  • 色相-饱和度直方图常常应用在目标检测、特征分析及目标特征跟踪等场景中。

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

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,直方图在配置阶段不会被清零。此功能主要是允许从多个阵列中计算单个直方图,或者用于在特定的时间更新直方图。

=====================间隔线======================

寻找最值:minMaxLoc()函数

功能:查找全局最小和最大数组元素并返回它们的值和它们的位置。

 
  1. void minMaxLoc(InputArray src, CV_OUT double* minVal,

  2. CV_OUT double* maxVal=0, CV_OUT Point* minLoc=0,

  3. CV_OUT Point* maxLoc=0, InputArray mask=noArray());

参数解释

  • 参数1:InputArray类型的src,输入单通道数组(图像)。
  • 参数2:double*类型的minVal,返回最小值的指针。若无须返回,此值置为NULL。
  • 参数3:double*类型的maxVal,返回最大值的指针。若无须返回,此值置为NULL。
  • 参数4:Point*类型的minLoc,返回最小位置的指针(二维情况下)。若无须返回,此值置为NULL。
  • 参数5:Point*类型的maxLoc,返回最大位置的指针(二维情况下)。若无须返回,此值置为NULL。
  • 参数6:InputArray类型的mask,用于选择子阵列的可选掩膜。

=====================间隔线======================

绘图方法详情请看官方文档:  基本绘图

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

4-代码演示

 
  1. //--------------------------------------------------------------

  2. //功能:绘制H-S直方图(二维)

  3. //--------------------------------------------------------------

  4.  
  5. #include <opencv2/core/core.hpp>

  6. #include <opencv2/highgui/highgui.hpp>

  7. #include <opencv2/imgproc/imgproc.hpp>

  8. #include <iostream>

  9. using namespace std;

  10. using namespace cv;

  11.  
  12. int main()

  13. {

  14. //------------【1】读取源图像并转化为HSV颜色模型------------

  15. Mat srcImage, hsvImage;

  16. srcImage = imread("D:/OutPutResult/ImageTest/jianzhu6.jpg");

  17. if (!srcImage.data)

  18. {

  19. cout << "读取图片错误,请重新输入正确路径!\n";

  20. system("pause");

  21. return -1;

  22. }

  23. cvtColor(srcImage, hsvImage, COLOR_BGR2HSV); //RGB颜色空间转换为HSV颜色空间

  24. //------------【2】参数准备------------------------------------

  25. //将灰度量化为30个等级,将饱和度量化为32个等级

  26. int hueBinNum = 30; //灰度色调的直方图直条数量

  27. int saturationBinNum = 32; //饱和度的直方图直条数量

  28. int histSize[] = { hueBinNum, saturationBinNum };

  29. //定义灰度的变化范围为0到179

  30. float hueRanges[] = { 0, 180 };

  31. //定义饱和度的变化范围为0(黑,白,灰)到255(纯光谱颜色)

  32. float saturationRanges[] = { 0, 256 };

  33. const float* ranges[] = { hueRanges, saturationRanges };

  34. MatND dstHist;

  35. //选取计算直方图通道。calcHist函数中将计算第0通道和第1通道的直方图

  36. int channels[] = { 0, 1 };

  37. //------------【3】正式calcHist,进行直方图计算------------

  38. calcHist(&hsvImage, //输入的数组

  39. 1, //数组个数为1

  40. channels, //通道索引

  41. Mat(), //不使用掩膜

  42. dstHist, //输出的目标直方图

  43. 2, //需要计算的直方图的维度为2

  44. histSize, //存放每个维度的直方图尺寸的数组

  45. ranges, //每一维数值的取值范围数组

  46. true, //指示直方图是否均匀的标识符,true表示均匀的直方图

  47. false); //累计标识符,false表示直方图在配置阶段会被清零

  48.  
  49. //------------【4】为绘制直方图准备参数------------------------------------

  50. double maxValue = 0;

  51. minMaxLoc(dstHist, 0, &maxValue, 0, 0); //查找到直方图的最大值

  52. int scale = 10;

  53. Mat histImg = Mat::zeros(saturationBinNum*scale, hueBinNum * 10, CV_8UC3);

  54. //------------【5】双层循环,进行直方图绘制。遍历H、S通道------------

  55. for (int hue = 0; hue < hueBinNum; hue++)

  56. {

  57. for (int saturation = 0; saturation < saturationBinNum; saturation++)

  58. {

  59. float binValue = dstHist.at<float>(hue, saturation); //直方图直条的值

  60. int intensity = cvRound(binValue * 255 / maxValue); //强度

  61. //正式进行绘制

  62. rectangle(histImg, Point(hue*scale, saturation*scale), Point((hue + 1)*scale - 1,

  63. (saturation + 1)*scale - 1), Scalar::all(intensity), CV_FILLED);

  64. }

  65. }

  66. //------------【6】显示效果图------------------------

  67. imshow("【源图像-RGB颜色空间】", srcImage);

  68. imshow("【源图像转HSV颜色空间】", hsvImage);

  69. imshow("H-S直方图", histImg);

  70. waitKey(0);

  71. return 0;

  72. }

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

5-显示结果

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

6-程序说明

注意 :色调(Hue),饱和度(Saturation)。 所以“H-S直方图”就是“色调-饱和度直方图” 。

 

参考文献:

【OpenCV】绘制直方图

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

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

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值