要理解直方图,绕不开“亮度”这个概念。人们把亮度分为0到255共256个数值,数值越大,代表的亮度越高。其中0代表纯黑色的最暗区域,255表示最亮的纯白色,而中间的数字就是不同亮度的灰色。人们还进一步把这些亮度分为了5个区域,分别是黑色,阴影,中间调,高光和白色。
在图像处理上,直方图是图像信息统计的有力工具。其实也就是统计一幅图某个亮度像素数量。
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 );
参数解释:
• images:输入的图像的指针;
• nimages:输入图像个数;
• channels:需要统计直方图的第几通道;
• mask:掩模,mask必须是一个8位(CV_8U)的数组并且和images的数组大小相同;
• hist:直方图计算的输出值;
• dims:输出直方图的维度(由channels指定);
• histSize:直方图中每个dims维度需要分成多少个区间(如果把直方图看作一个一个竖条的话,就是竖条的个数);
• ranges:统计像素值的区间;
• uniform=true:是否对得到的直方图数组进行归一化处理;
• accumulate=false:在多个图像时,是否累积计算像素值的个数;
【注】:在计算图像直方图的时候一般配合minMaxLoc()和normalize()函数一起使用。
只看函数说明肯定看不明白,下面来对函数进行代码演示。绘制直方图主要有三步:
相关代码如下:
Mat src, gray;
src = imread("D:/Desktop/16.png");
if (!src.data)
{
cout << "读取图片错误,请重新输入正确路径!\n";
system("pause");
return -1;
}
cvtColor(src, gray, CV_BGR2GRAY);//转换为灰度图像
imshow("【灰度图】", gray);
当然,如果觉得麻烦,可以直接用以下代码代替:
Mat src, gray;
grayImage = imread("D:/Desktop/16.png",0);
imshow("【灰度图】", gray);
//需要计算的图像的通道,灰度图像为0,BGR图像需要指定B,G,R
const int channels[] = { 0 };
Mat hist;//定义输出Mat类型
int dims = 1;//设置直方图维度
const int histSize[] = { 256 }; //直方图每一个维度划分的柱条的数目
//每一个维度取值范围
float pranges[] = { 0, 255 };//取值区间
const float* ranges[] = { pranges };
calcHist(&gray, 1, channels, Mat(), hist, dims, histSize, ranges, true, false);//计算直方图
方法一:
int scale = 2;
int hist_height = 256;
Mat hist_img = Mat::zeros(hist_height, 256 * scale, CV_8UC3); //创建一个黑底的8位的3通道图像,高256,宽256*2
double max_val;
minMaxLoc(hist, 0, &max_val, 0, 0);//计算直方图的最大像素值
//将像素的个数整合到 图像的最大范围内
//遍历直方图得到的数据
for (int i = 0; i < 256; i++)
{
float bin_val = hist.at<float>(i); //遍历hist元素(注意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));//绘制直方图
}
方法二:
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通道图像,高300,宽500
normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());//将直方图归一化到[0,histImage.rows]
for (int i = 1; i < nHistSize; i++)
{
line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(hist.at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(hist.at<float>(i))), Scalar(255, 0, 0));
}
输出结果:
ok,以上便是OpenCV绘制灰度直方图的全部内容了,自行天剑opencv库即可运行全程序,如果实在无法独立完成,可以参考资源:
https://download.csdn.net/download/didi_ya/14989048
上面介绍了灰度直方图的绘制,下面介绍一下BGR彩色直方图的绘制过程。
关于为什么叫BGR而不叫RGB,网上有许多误导性的博客,相信大家一定也有所了解,这里就不过多介绍了。
同上
利用split函数进行分通道显示:
vector<Mat>bgr_planes;
split(src, bgr_planes);
int histSize = 256;
float range[] = { 0,255 };
const float*Ranges = { range };
Mat b_hist, g_hist, r_hist;
calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &Ranges, true, false);
calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &Ranges, true, false);
calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &Ranges, true, false);
【注意】:这里说明一下,第一行有两种写法,如果按照3.2所写:const int histSize[] = { 256 }; ,则后面计算直方图时直接写histSize即可,如果按照4.3所写:int histSize = 256;,那么下面调用计算直方图的calcHist()函数的时候,该变量需要写 &histSize
选择一种自己喜欢的方法即可,切记不要用混了!
//归一化
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));//绘制直方图显示的图像
normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());//归一化
normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
for (int i = 1; i < nHistSize; i++)
{
//绘制蓝色分量直方图
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(b_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0),2);
//绘制绿色分量直方图
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0),2);
//绘制红色分量直方图
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(r_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255),2);
}
完整代码也可参考:
https://download.csdn.net/download/didi_ya/14989174