常言道“温故而知新”,写此文章就是对自己目前学习内容的小小的总结与记录。
本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上有些瑕疵,还望读者能够指教探讨,大家共同进步。
博主机器配置为:VS2013+opencv2.4.13+Win-64bit。
若本文能给读者带来一点点启示与帮助,我就很开心了。
====================分割线====================
直方图的定义
直方图(Histogram)又称柱状图、质量分布图,是一种统计报告图。直方图由一系列高度不等的纵向条纹或线段表示数据分布的情况。一般用横轴表示数据类型,纵轴表示分布情况。在图像处理上,直方图是图像信息统计的有力工具。其实也就是统计一幅图某个亮度像素数量。
第一个就是当我们面对图像的时候,我们面对的是抽象的矩阵,如下图,下面是0-255的灰度图像的表示,密密麻麻的。
那么我们做的直方图,其实就是对这些像素值的统计。例如:首先,我们需要把0-255分成 17 个 区域(bin),如下图所示:
我们对每个范围中的灰度值进行统计排序,做出如下的表格:
我们是以图像的灰度为例子说明这个直方图,当然直方图不仅仅用于灰度特种统计排序,还可以用于图像的梯度、方向等特征。
灰度直方图是灰度级的函数,描述图像中该灰度级的像素个数(或该灰度级像素出现的频率):其横坐标是灰度级,纵坐标表示图像中该灰度级出现的个数(频率)。
在以上的过程中,我们使用到一些重要的参数,理解这些参数帮助我们更好的使用API函数。
一维直方图的结构表示为:
再结合上面画的示意图,应该就很好理解了。
基本的概念其实很简答,想的时候要不要想复杂了,那么基本概念就到这里了。
===============分割线===============
当然了,首先介绍的是计算直方图的函数了。
直方图计算:calcHist()函数
void calcHist( const Mat* images, int nimages,
const int* channels, InputArray mask,
OutputArray hist, int dims, const int* histSize,
const float** ranges, bool uniform=true, bool accumulate=false );
参数解释:
void minMaxLoc(InputArray src, CV_OUT double* minVal,
CV_OUT double* maxVal=0, CV_OUT Point* minLoc=0,
CV_OUT Point* maxLoc=0, InputArray mask=noArray());
参数解释
void normalize( InputArray src, OutputArray dst, double alpha=1, double beta=0,
int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray());
参数解释:
//************************************************
//功能:灰度直方图的实现--即一维直方图 */
// 包括2种方式绘制出直方图图像 */
//************************************************
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat srcImage, grayImage;
//------------【1】读取源图像并检查图像是否读取成功------------
srcImage = imread("D:/OutPutResult/ImageTest/jianzhu6.jpg");
if (!srcImage.data)
{
cout << "读取图片错误,请重新输入正确路径!\n";
system("pause");
return -1;
}
//将彩色图转换为灰度图像
cvtColor(srcImage, grayImage, CV_BGR2GRAY);
imshow("【灰度图】", grayImage);
//------------【2】定义直方图参数并计算直方图------------
//1--计算的图像的通道,就是需要计算图像的哪个通道(BGR空间需要确定计算 B或G或R空间)
const int channels[] = { 0 };
//2--配置输出的结果存储的 空间 ,用MatND类型来存储结果
MatND hist;
//3--设置计算直方图的维度
int dims = 1;
//4--直方图的每一个维度的柱条的数目(就是将数值分组,共有多少组)
const int histSize[] = { 256 }; //如果这里写成int histSize = 256; 那么下面调用计算直方图的calcHist()函数的时候,该变量需要写 &histSize
//5--最后是确定每个维度的取值范围,就是横坐标的总数
float pranges[] = { 0, 255 };//首先得定义一个变量用来存储单个维度的数值的取值范围
const float* ranges[] = { pranges };
//6--计算直方图
calcHist(&grayImage, 1, channels, Mat(), hist, dims, histSize, ranges, true, false);
//------------【3】绘制直方图------------
//*********绘制直方图(绘制方式一)*********
int scale = 2;
int hist_height = 256;
Mat hist_img = Mat::zeros(hist_height, 256 * scale, CV_8UC3); //创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像
//因为任何一个图像的某个像素的总个数,都有可能会有很多,会超出所定义的图像的尺寸,针对这种情况,先对个数进行范围的限制
//先用 minMaxLoc函数来得到计算直方图后的像素的最大个数
double max_val;
minMaxLoc(hist, 0, &max_val, 0, 0);
//将像素的个数整合到 图像的最大范围内
//遍历直方图得到的数据
for (int i = 0; i < 256; i++)
{
float bin_val = hist.at(i); //注意hist中是float类型
int intensity = cvRound(bin_val*hist_height / max_val); //要绘制的高度
rectangle(hist_img, Point(i*scale, hist_height - 1), Point((i + 1)*scale - 1, hist_height - intensity), Scalar(255, 255, 255));
//line(hist_img, Point(i*scale, hist_height - 1), Point((i + 1)*scale - 1, hist_height - intensity), Scalar(255, 255, 255), 2, 8, 0);
}
//*********绘制直方图(绘制方式二)*********
int hist_w = 500;
int hist_h = 300;
int nHistSize = 256;
int bin_w = cvRound((double)hist_w / nHistSize); //区间
Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));//创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像
//将直方图归一化到[0,histImage.rows]
normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
//在直方图画布上画出直方图
for (int i = 1; i < nHistSize; i++)
{
line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(hist.at(i - 1))),
Point(bin_w*(i), hist_h - cvRound(hist.at(i))), Scalar(0, 255, 0), 2, 8, 0);
}
//显示直方图
imshow("【灰度直方图--方式一绘制】", hist_img);
imshow("【灰度直方图--方式二绘制】", histImage);
waitKey(0);
return 0;
}
==================分割线=============
<2>画出直方图其实有多种方式,可以使用line()或rectangle()都可以。
参考文献:
【OpenCV】绘制直方图
Opencv2系列学习笔记4(灰度直方图)
OpenCV从入门到放弃(七):直方图那些事儿
Opencv图像识别从零到精通(8)-----灰度直方图
================END=====================