参数说明
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 图像数组。每个图像的大小要一致,depth要一致,即数据类型要一致,但通道数可以不一致。
- nimages 图像数组的大小,即images数组的大小
- channels 参与计算的各个通道的索引。由于各图像的通道数并不一定一致,并且此函数也不强制要求所有通道都参与计算,因此此参数用于指定要参与计算的图像的通道索引。索引值包含了图像标识,以及图像的通道标识。具体方法为:
第一个图像的索引标识为 [0, images[0].channels()),
第二个图像的索引标识为 [mages[0].channels(), mages[0].channels()+mages[1].channels())
之后以此类推。
例如:
图像mages[0]有三个通道,其通道索引为{0, 1, 2};
图像mages[1] 有两个通道,其通过索引为 {3, 4};
图像mages[2] 有三个通道,其通过索引为 {5, 6, 7} - mask 此函数支持对图像中指定区域进行直方图统计
- hist 直方图统计结果
- dims 直方图维度。使用最多的也就是1维、2维,更高维应该很少吧。本文后续再详细说明。
- histSize 指定各个通道的的直方图中分类数。此函数的设计是,多个像素取值可以划分为同个分类,例如:像素灰度值取值范围是[0, 255],但不一定要分成256类,可以为了8类,就可以通过histSize 这个参数指定。
- ranges 指定各个通道的像素灰度值的取值范围。也就是说,此函数的设计并不要求各个通道的像素灰度值取值范围一致。例如:可以第一个通道取值是[0, 255], 第二个通道取值是[0, 127]。
此外,此参数地用于与uniform的取值有关。
uniform取值为true时,表示将像素取值平均分配为 histSize[i]个区域(此处的i表示,参与计算的第i个通道),此时,ranges[i]数组的大小通道是2,即ranges[i][0] 表示像素值的最小值,ranges[i][1]表示像素取值的最大值。
uniform取值为false时,表示不平均分配区间。此时,此函数就不知道如何划分区间了,就需要调用者指定。此时,ranges[i]数组的大小为区间个数加1,即histSize[i]+1。
一维颜色直方图
如果要统计一个RGB图像,三个通道的各自的直方图,需要将dims设置为1,并调用三次calcHist函数。
{
READ_IMG_DEF_ERR_RET(img, "img1.jpg");
cv::imshow("原始图像", img);
int channels[1] = { 0 };
int histSize[1] = { 256 };
float range[2] = { 0, 256 };
const float* ranges[1] = { range}; // 指定每个通道的取值范围
// 计算B通道的颜色直方图
cv::Mat bHist;
cv::calcHist(&img, 1, channels, cv::Mat(), bHist, 1, histSize, ranges);
PRINT_IMG_INFO(bHist);
// 计算G通道的颜色直方图
cv::Mat gHist;
cv::calcHist(&img, 1, channels, cv::Mat(), gHist, 1, histSize, ranges);
PRINT_IMG_INFO(gHist);
// 计算R通道的颜色直方图
cv::Mat rHist;
cv::calcHist(&img, 1, channels, cv::Mat(), rHist, 1, histSize, ranges);
PRINT_IMG_INFO(rHist);
cv::Mat bHistImg = ImgTools->buildHistImg(bHist, 600, 600, cv::Scalar(255, 0, 0));
cv::imshow("颜色直方图 B", bHistImg);
cv::Mat gHistImg = ImgTools->buildHistImg(gHist, 600, 600, cv::Scalar(0, 255, 0));
cv::imshow("颜色直方图 G", gHistImg);
cv::Mat rHistImg = ImgTools->buildHistImg(rHist, 600, 600, cv::Scalar(0, 0, 255));
cv::imshow("颜色直方图 R", rHistImg);
cv::waitKey(0);
}
二维颜色直方图
此时需要将dims参数设置为2。
{
cv::Mat imgs[2];
imgs[0] = (cv::Mat_<uchar>(2, 4) << 0, 1, 3, 3, 3, 5, 6, 7);
imgs[1] = (cv::Mat_<uchar>(2, 4) << 8, 9, 10, 11, 11, 13, 14, 15);
ImgTools->dumpImg(imgs[0], "imags[0]: ");
ImgTools->dumpImg(imgs[1], "imags[1]: ");
cv::Mat img;
cv::merge(imgs, sizeof(imgs) / sizeof(imgs[0]), img);
ImgTools->dumpImg(img, "imag: ");
int channels[3] = { 0, 1 };
int histSize[3] = { 8, 8 };
float rRange[2] = { 0, 8 };
float gRange[2] = { 8, 16 };
const float* ranges[2] = { rRange, gRange}; // 指定每个通道的取值范围
cv::Mat hist;
cv::calcHist(&img, 1, channels, cv::Mat(), hist, 2, histSize, ranges);
ImgTools->dumpImg(hist, "hist info:");
}
运行结果如下图所示:
在代码中,计算两个通道,通过参数histSize指定,每个通道都分成8个分区,因此可能有64种组合结果,因此二维直方图的大小为8*8的矩阵。
在图像中(3,10)这个组合的数据只有一个,对应的分区编号是(3,2),因此二维直方图中(3,2)位置的数据值为1,即上图中绿色标记的位置。
在图像中 (3, 11)这个组合的数据有两个, (3, 11)对应的分区编号是(3,3),因此上图中的红色标记的位置的统计值为2