1.calcHist() 函数的理解
函数原型: void calcHist(const Mat* arrays, int narrays, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate= false );
参数解释:
arrays 输入图像的指针,可以是多幅图像,所有的图像都必须有相同的深度( CV_8U or CV_32F),同时一幅图像可以有多个 channels
narrays 输入图像的个数
channels 计算直方图的 channels 的数组,当输入图像为多通道图像时, channels[0] = {0}, 表示的是取其第一个通道, channels[0] = {1}表示取其第二个通道,以此类推, channels[2] = {1, 2}表示的是取其第2个和第3个通道的图像 进行直方图统计
mask 掩码,如果 mask 不为空,那么它必须是一个8位(CV_8U)的数组,并且它的大小和 arrays[i] 的大小相同,值为1的点将用来计算直方图
hist 计算出来的直方图
dims 需要统计的特征的数目,计算出来的直方图的维数,直方图的维数就是图像的通道数,每一维的长度是灰度级数,一维直方图就是灰度图的直方图,直方图的尺寸是 256; 高维直方图可以理解为图像在每个维度上灰度级分布的直方图,常见的是二维 直方图,如红-蓝直方图的两个分量分别表示红光图像的灰度值和蓝光图像的灰度值的函数,其图像坐标(Dr,Db)处对应在红光图像中具有灰度级 Dr 同时在蓝光图像中具有灰度级 Db 的像素个数。
histSize 在每一维上直方图的个数,把直方图看作一个一个的竖条的话,就是每一维竖条的个数
ranges 用来统计的灰度值的范围
2. 例程
绘制 二维 H-S 直方图
#include "stdafx.h" #include "highgui/highgui.hpp" #include "opencv2/nonfree/nonfree.hpp" #include "opencv2/legacy/legacy.hpp" #include <iostream> using namespace cv; using namespace std; int main() { // 载入原图 转化为 HSV 模型 Mat srcImage, hsvImage; srcImage = imread("01.jpg"); cvtColor(srcImage, hsvImage, CV_BGR2HSV); // 参数准备 // 将色调量化为 30 个等级,将饱和度量化为 32 个等级 int hueBinNum = 30; //色调直方图直条数量 int saturationBinNum = 32; //饱和度的直方图直条数量 int histSize[] = { hueBinNum, saturationBinNum}; // 定义色调的变换范围为 0 到 179 float hueRanges[] = {0, 179}; // 定义饱和度的变化范围为 0 (黑、白、灰) 到 255(纯光谱颜色) float saturationRanges[] = {0, 256}; const float* Ranges[] = {hueRanges, saturationRanges}; MatND dstHist; // 参数准备, calcHist 函数中将计算第 0 通道和第 1 通道的直方图 int channels[] = {0, 1}; // 正式调用 calcHist, 进行直方图的计算 calcHist( &hsvImage, 1, channels, Mat(), dstHist, 2, histSize, Ranges, true, false); // 为绘制直方图准备参数 double maxValue = 0; // 最大值 minMaxLoc(dstHist, 0, &maxValue, 0, 0); // 找出全局最小值 int scale = 10; Mat histImg = Mat::zeros(saturationBinNum * scale, hueBinNum * scale, CV_8UC3); // 双层循环,进行直方图绘制 for (int hue = 0; hue < hueBinNum; hue++) { for (int saturation = 0; saturation < saturationBinNum; saturation ++) { float binValue = dstHist.at<float>(hue, saturation); // 直方图直条的值 int intensity = cvRound(binValue * 255 / maxValue); // 强度 // 正式进行绘制 rectangle(histImg, Point(hue * scale, saturation * scale), Point((hue + 1)*scale-1, (saturation + 1)*scale-1), Scalar(intensity), CV_FILLED); } } // 显式效果图 namedWindow("H-S直方图", WINDOW_NORMAL); imshow("素材图", srcImage); imshow("H-S直方图", histImg); waitKey(0); return 0;
}
绘制 RGB 三色直方图
#include "stdafx.h" #include "highgui/highgui.hpp" #include "opencv2/nonfree/nonfree.hpp" #include "opencv2/legacy/legacy.hpp" #include <iostream> using namespace cv; using namespace std; int main() { // 载入原图并显示 Mat srcImage; srcImage = imread("01.jpg"); imshow("素材图", srcImage); // 参数准备 int bins = 256; int hist_size[] = {bins}; float range[] = {0, 255}; const float* ranges[] = {range}; MatND blueHist, greenHist, redHist; // 进行蓝色分量直方图计算 int channel_b[] = {0}; calcHist(&srcImage, 1, channel_b, Mat(), blueHist, 1, hist_size, ranges, true, false); // 进行绿色分量直方图计算 int channel_g[] = {1}; calcHist(&srcImage, 1, channel_g, Mat(), greenHist, 1, hist_size, ranges, true, false); // 进行红色分量直方图计算 int channel_r[] = {2}; calcHist(&srcImage, 1, channel_r, Mat(), redHist, 1, hist_size, ranges, true, false); // 绘制三色直方图 // 准备参数 double maxValue_r = 0; double maxValue_g = 0; double maxValue_b = 0; minMaxLoc( blueHist, 0, &maxValue_b, 0, 0); minMaxLoc( greenHist, 0, &maxValue_g, 0,0); minMaxLoc( redHist, 0, &maxValue_r, 0, 0); int scale = 1; int histHeight = 256; Mat histImage = Mat::zeros(histHeight, bins*3, CV_8UC3); // 正式开始绘制 for (int i = 0; i < bins; i++) { // 参数准备 float binValue_blue = blueHist.at<float>(i); float binValue_green = greenHist.at<float>(i); float binValue_red = redHist.at<float>(i); int intensity_blue = cvRound(binValue_blue * 255 / maxValue_b); int intensity_green = cvRound(binValue_green * 255 / maxValue_g); int intensity_red = cvRound(binValue_red * 255 / maxValue_r); // 绘制蓝色分量直方图 rectangle( histImage,Point(i * scale, histHeight-1), Point((i+1)*scale, histHeight-intensity_blue), Scalar(255, 0 ,0), CV_FILLED); rectangle( histImage,Point(i*scale+bins, histHeight-1), Point((i+1)*scale+bins, histHeight-intensity_green), Scalar(0, 255 ,0), CV_FILLED); rectangle( histImage,Point(i*scale+2*bins, histHeight-1), Point((i+1)*scale+2*bins, histHeight-intensity_red), Scalar(0, 0 ,255), CV_FILLED); } // 显示直方图 namedWindow("BGR直方图", WINDOW_NORMAL); imshow("BGR直方图", histImage); waitKey(0); return 0; }
直方图对比
#include "stdafx.h" #include "highgui/highgui.hpp" #include "opencv2/nonfree/nonfree.hpp" #include "opencv2/legacy/legacy.hpp" #include <iostream> using namespace cv; using namespace std; int main() { // 定义储存基准图像和另外两张对比图像的矩阵(RGB 和 HSV) Mat srcImage_base, hsvImage_base; Mat srcImage_test1, hsvImage_test1; Mat srcImage_test2, hsvImage_test2; Mat hsvImage_halfDown; // 载入基准图像(srcImage_base) 和两张测试图像 srcImage_test1、srcImage_test2,并显示 srcImage_base = imread("01.jpg"); srcImage_test1 = imread("02.jpg"); srcImage_test2 = imread("03.jpg"); // 显示载入的3张图片 imshow("基准图像",srcImage_base); imshow("测试图像1",srcImage_test1); imshow("测试图像2",srcImage_test2); // 创建包含基准图像下半部的半身图像(HSV格式) hsvImage_halfDown = hsvImage_base(Range(hsvImage_base.rows/2,hsvImage_base.rows-1), Range(0, hsvImage_base.cols-1)); // 初始化直方图需要计算的实参 // 对 hue 通道使用30个 bin ,对 saturation 通道使用32个 bin int h_bins = 50; int s_bins = 60; int hist_size[] = { h_bins, s_bins}; // hue 的取值范围从 0 到 255, saturation 取值范围从 0 到 180 float h_ranges[] = {0, 255}; float s_ranges[] = {0, 180}; const float* ranges[] = {h_ranges, s_ranges}; // 使用第 0 和 第 1 通道 int channels[] = {0,1}; // 创建储存直方图的 MatND 实例 MatND baseHist; MatND halfDownHist; MatND testHist1; MatND testHist2; // 计算基准图像,两张测试图像,半身基准图像的 HSV 直方图 calcHist( &hsvImage_base, 1, channels, Mat(), baseHist, 2, hist_size, ranges, true, false); normalize( baseHist, baseHist, 0, 1, NORM_MINMAX, -1, Mat()); calcHist( &hsvImage_halfDown, 1, channels, Mat(), halfDownHist, 2, hist_size, ranges, true, false); normalize( halfDownHist, halfDownHist, 0, 1, NORM_MINMAX, -1, Mat()); calcHist( &hsvImage_test1, 1,channels, Mat(), testHist1, 2, hist_size, ranges, true, false); normalize( testHist1, testHist1, 0, 1, NORM_MINMAX, -1, Mat()); calcHist( &hsvImage_test2, 1, channels, Mat(), testHist2, 2, hist_size,ranges, true, false); normalize( testHist2, testHist2, 0, 1, NORM_MINMAX, -1, Mat()); // 按顺序使用 4 种对比标准将基准图像的直方图与其余各直方图进行对比 for (int i = 0; i < 4; i++) { // 进行直方图的对比 int compare_method = i; double base_base = compareHist( baseHist, baseHist, compare_method); double base_half = compareHist( baseHist, halfDownHist, compare_method); double base_test1 = compareHist( baseHist, testHist1, compare_method); double base_test2 = compareHist( baseHist, testHist2, compare_method); // 输出结果 printf("方法 【%d】的匹配结果如下: 【基准图 - 基准图】: %f, 【基准图 - 半身图】: %f, 【基准图 - 侧视图】: %f, 【基准图 - 侧视图2】: %f ",i, base_base, base_half, base_test1, base_test2); } printf("检测结束"); waitKey(0); return 0; }