一、 函数原型
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 | 用来计算直方图的dims通道的列表 |
mask | 可选的掩码 |
hist | 输出直方图 |
dims | 直方图的维数(不大于CV_MAX_DIMS) |
histSize | 每个维度的直方图尺寸的数组(256) |
ranges | 每个维度的直方图范围的数组[dims]的阵列(0-255) |
uniform | 表示直方图是否是统一的标志 |
accumulate | 累积标志 |
uniform:是否对灰度值范围进行均匀划分的标志
ccumulate:累积标志。如果它被设置,直方图在开始分配时不会被清除。
分配时不会被清除。这个特性使你可以从几组数组中计算出一个单一的直方图,或者更新直方图。
数组,或者及时更新柱状图。
二、参数具体解释
1. channels
用来计算直方图的dims通道的列表。
第一个通道索引值:0到images[0].channels()-1的数字
(如果是三通道,就是0 ~(3-1) 即 0 ~ 2)
第二个通道索引值:images[0].channels() 到 images[0].channels() + images[1].channels()-1
例:三张三通道图像,
则第一张图像三个通道索引值:0、1、2
第一张图像三个通道索引值:3、4、5
第一张图像三个通道索引值:6、7、8
计算二维直方图(第一张图像第二通道,第二张图象第三通道)
const int channels[1] = { 0,5 };
2. mask
可选的操作掩码矩阵,
如果是空矩阵则表示图像中所有位置的像素都计入直方图中,
如果矩阵不为空,则该操作掩码矩阵必须与输入图像尺寸相同且数据类型为CV_8U。当矩阵不为空的时候,那些掩码值不为0的掩码对应的像素被纳入统计范围,而那些掩码值为0的掩码对应的像素则不被纳入统计。
空矩阵:Mat();
3. dims
需要计算的直方图的维度(不大于CV_MAX_DIMS)
一般为数组channels大小(三通道为3),当写为1的时候计算的是通道索引数组中的第1个通道的直方图,当写为2的时候计算的是通道索引数组中的第1个通道和第2个通道的二维直方图。
4. histSize
每个维度的直方图尺寸,直方图中每个dims维度需要分成多少个区间(如果把直方图看作一个一个竖条的话,就是竖条的个数);
例如:
计算二维直方图(第一张图像第二通道,第二张图象第一通道)两个图象分别为RGB和HSV
const int histSize[2] = { 256 ,181 };
可以分为256个区间(也就是一个像素值一个区间)
const int histSize[2] = { 3 ,3 };
(也可以分为3个区间,也就是256/3个像素值一个区间,画出来的直方图就是三个点两个折线)
5. ranges
例如:
计算二维直方图(第一张图像第二通道,第二张图象第一通道)两个图象分别为RGB和HSV(也就是一维根据第一个图象决定,二维根据第二个图像决定)
float hranges1[2] = { 0,255 };
float hranges2[2] = { 0,180 };
const float* ranges[2] = { hranges1, hranges2};
三、 代码
1. 一维直方图
// 图像直方图
void QuickDemo::showHistogram(Mat& image)
{
// 三通道分离
std::vector<Mat> bgr_plane; // Mat有三个通道
split(image, bgr_plane);
// 定义参数变量
const int channels[1] = { 0 };
const int bins[1] = { 256 }; // 每个维度分成256个区间
float hranges[2] = { 0,255 };
const float* ranges[1] = { hranges };
Mat b_hist;
Mat g_hist;
Mat r_hist;
// 计算Blue,GReen,Red通道的直方图
// 输入图像 输入图像个数 统计直方图第几通道 输出直方图维度 每个维度分成多少区间 统计像素值的区间
calcHist(&bgr_plane[0], 1, channels,Mat() , b_hist, 1, bins, ranges);
calcHist(&bgr_plane[1], 1, channels,Mat() , g_hist, 1, bins, ranges);
calcHist(&bgr_plane[2], 1, channels,Mat() , r_hist, 1, bins, ranges);
// 显示直方图
int hist_w = 512;
int hist_h = 400;
int bin_w = cvRound((double)hist_w / bins[0]); // 一个区间的长度(总长度/区间)
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3); // 定义一个Mat图像显示直方图
// 1. 归一化直方图数据
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());
// 2. 绘制直方图曲线
for (int i = 1; i < bins[0]; i++) // 分别绘制每个区间
{
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);
line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(b_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(histImage, Point(bin_w * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),
Point(bin_w * (i), hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, 8, 0);
}
// 3. 显示出来
namedWindow("一维直方图", WINDOW_AUTOSIZE);
imshow("一维直方图", histImage);
}
2. 二维直方图
// 二维直方图
void QuickDemo::showHistogram_2d_demo(Mat& image)
{
// 直方图矩阵计算
Mat hsv, hs_hist; // hsv:将图像转换为HSV类型 hs_hist:直方图计算输出矩阵
cvtColor(image, hsv, COLOR_BGR2HSV);
int hs_channels[] = {0,1}; // 参数channels:需要统计直方图的第几通道;h s
int hbins = 30, sbins = 32; // 参数histSize:直方图中每个dims维度需要分成多少个区间(如果把直方图看作一个一个竖条的话,就是竖条的个数);
int hist_bins[] = { hbins, sbins };
float h_range[] = { 0, 180 }, s_range[] = { 0,255 };
const float* hs_ranges[] = { h_range,s_range }; // ranges:统计像素值的区间;
calcHist(&hsv, 1, hs_channels, Mat(), hs_hist, 2, hist_bins, hs_ranges, true, false);
// 直方图输出
double maxVal;
minMaxLoc(hs_hist, 0, &maxVal, 0, 0); // 获取像素最大值
int scale = 10; // 确定输出直方图图像大小范围
Mat hist2d_image = Mat::zeros(sbins * scale, hbins * scale, CV_8UC3); // 用作输出的图像
for (int h = 0; h < hbins; h++) // 参数histSize:直方图中每个dims维度需要分成多少个区间(如果把直方图看作一个一个竖条的话,就是竖条的个数);
{
for (int s = 0; s < sbins; s++)
{
float binVal = hs_hist.at<float>(h, s); // 获取像素值
int intensity = cvRound(binVal * 255 / maxVal); // 实现将hs_hist中对应第h行第s列的数值限制到0到255。如果只除以maxval(用minMaxLoc找到的矩阵中的最大值)就是归一化,限制到0到1,但乘上255就限制到255了。
rectangle(hist2d_image, Point(h * scale, s * scale), Point((h + 1) * scale - 1, (s + 1) * scale - 1), Scalar::all(intensity), -1);
}
}
applyColorMap(hist2d_image, hist2d_image, COLORMAP_JET);
imshow("hs 二维直方图", hist2d_image);
imwrite("D:\\software\\hist_2d.png", hist2d_image);
}
参考链接