引言
前面我们说了图像的本质是一堆数据,计算机可不认识图像中的各种人物、景色,只认识0,1,而所有这些都是人为打的标签,计算机看到的都是一个个像素点的像素值,而这些像素值又有一定的取值范围,对于RGB来说就是0-255的取值范围,我们就可以利用直方图统计在这区间内哪一个出现的频率低,哪一个频率高。
一、简述图像直方图
- 优势:图像直方图是图像像素值的统计学特征,表示了图像的各个像素在0-255出现的频率,它的计算代价较小,具有图像旋转、缩放、平移不变性等很多优点
- 缺点:不能表征一张图像。
- 应用场景:灰度图像的阈值分割、基于颜色的图像检索以及图像分类、反向投影跟踪等。
- Bins是指直方图的大小范围, 对于像素值取值在0~255之间的,最少有256个bin,此外还可以有16、32、48、128等,256除以bin的大小应该是整数倍。
- 直方图数据反映的是0-255总共256个灰度等级,每一个灰度等级在这张图像上,这么多像素点出现的次数是多少(x轴为灰度等级,y轴为出现次数),每一个直方图都是对图像的像素点在像素值的取值范围空间出现频次的统计。
二、函数原型
calcHist函数
作用:计算一维或多维图像直方图
c++原型:
参数:
- images:输入图像的指针,一定是同样的深度(CV_8U or CV_32F)。且一个图像可以有多个channes。
- nimages:输入图像的个数
- channels:计算直方图的channes的数组。如果输入的图像的个数为2,第一张图像有0,1,2共三个channel,第二张图像只有0一个channel,那么输入就一共有4个通道,如果int channels[3] = {3, 0, 1},那么就表示是使用第二张图像的第一个通道和第一张图像的第0和第1个通道来算
- mask:掩码。跟前面说的mask一致,非0区域才会用来做直方图的计算。如果不用就表示完整计算整张图像,这时Mat()参数为空。如果要用mask,那么它必须是一个8位(CV_8U)的数组,并且它的大小的和images[i]的大小相同,值为1的点用来计算
- hist:计算出来的直方图
- dims:计算出来的直方图的维数,这里是一维的,如果想要二维可以定义一个数组,数组里有两个值
- histSize:在每一维上直方图的个数。简单把直方图看作一个一个的竖条的话,就是每一维上竖条的个数。
- ranges:用来进行统计的范围 例如:float scope[] = {100, 255};const float *rangs[1] = { scope };那么就是对100-255的范围的值进行统计。多个范围可再定义数组
- uniform:每一个竖条的宽度是否相等
- accumulate:是否累加。如果为true,在下次计算的时候不会首先清空直方图
三、实现思路及效果
1、思路
- 定义参数变量并分离三通道
- calcHist函数计算B\G\R三个通道中各个通道的直方图
- 创建画布用来显示直方图
- normalize函数归一化直方图数据(归一化到数据大小一致的范围,为了不超出画布允许的范围)
- 绘制直方图曲线(图像上的坐标转换成屏幕中的坐标,即实际高度等于整个图像的高度减去像素点的高度,如图所示)
- 显示直方图
2、效果
从直方图可以看出,直方图最左边的一块应该对应头发那块黑色,直方图中最高的那一块应该对应灰色的背景,脸部的亮度主要对应在直方图右边区域
四、代码
void test1::hist_drawing(Mat &image)
{
// 分离三通道
vector<Mat> bgr_passage;
split(image, bgr_passage);
// 定义参数变量
const int channels[1] = { 0 };
const int bins[1] = { 256 };
float scope[2] = { 0,255 };
const float* ranges[1] = { scope };
Mat b_hist;
Mat g_hist;
Mat r_hist;
// 计算B, G, R通道的直方图
calcHist(&bgr_passage[0], 1, 0, Mat(), b_hist, 1, bins, ranges);
calcHist(&bgr_passage[1], 1, 0, Mat(), g_hist, 1, bins, ranges);
calcHist(&bgr_passage[2], 1, 0, Mat(), r_hist, 1, bins, ranges);
// 显示直方图
int hist_w = 512;
int hist_h = 400;
int bin_w = cvRound((double)hist_w / bins[0]);
Mat histImg = Mat::zeros(hist_h, hist_w, CV_8UC3);
// 归一化直方图数据
normalize(b_hist, b_hist, 0, histImg.rows, NORM_MINMAX, -1, Mat());
normalize(g_hist, g_hist, 0, histImg.rows, NORM_MINMAX, -1, Mat());
normalize(r_hist, r_hist, 0, histImg.rows, NORM_MINMAX, -1, Mat());
// 绘制直方图曲线
for (int i = 1; i < bins[0]; i++)
{
line(histImg, 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);
line(histImg, 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);
line(histImg, 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);
}
// 显示直方图
// namedWindow("input", WINDOW_FREERATIO);
imshow("hist_drawing", histImg);
}
原创不易,转载请注明出处:
Qt+OpenCV联合开发(二十三)--图像直方图
版权归原作者 hml111666 所有, 如有侵权,请联系我们删除。