1. calcHist函数
1.1 calcHist函数原型
void cv::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:待统计直方:图的图像数组,数组中所有的图像应具有相同的尺寸和数据类型,并且数据类型只能是 CV_8U、CV_16U 和 CV_32F 这 3 种中的一种,但是不同图像的通道数可以不同。
- nimages:输入的图像数量。
- channels:需要统计的通道索引数组,第一个图像的通道索引从 0 到 images[0].channels()−1,第二个图像通道索引从 images[0].channels()到 images[0].channels()+ images[1].channels()−1,依此类推。
- mask:可选的操作掩码。如果是空矩阵,那么表示图像中所有位置的像素都计入直方图中;如果矩阵不为空,那么必须与输入图像尺寸相同且数据类型为 CV_8U。
- hist:输出的统计直方图结果,是一个 dims 维度的数组。
- dims:需要计算直方图的维度,必须是整数,并且不能大于 CV_MAX_DIMS,在 OpenCV 4.0 和 OpenCV 4.1 版中为 32。
- histSize:存放每个维度直方图的数组的尺寸。
- ranges:每个图像通道中灰度值的取值范围。
- uniform:直方图是否均匀的标志符,默认状态下为均匀(true)。
- accumulate:是否累积统计直方图的标志,如果累积(true),那么,在统计新图像的直方图时,之前图像的统计结果不会被清除,该参数主要用于统计多个图像整体的直方图。
该函数用于统计图像中每个灰度值像素的个数,例如统计一幅 CV_8UC1
的图像,需要统计灰度值从 0
至
255
中每一个灰度值在图像中的像素个数,如果某个灰度值在图像中没有,那么该灰度值的统计结果就是 0
。由于该函数具有较多的参数,并且每个参数都较为复杂,因此作者建议在使用该函数时只统计单通道图像的灰度值分布,对于多通道图像,可以将图像每个通道分离后再进行统计。
为了使读者更加了解函数的使用方法,代码清单 4-2 中提供了绘制灰度图像的图像直方图的示例程序。在该程序中,首先使用 calcHist()
函数统计灰度图像中每个灰度值的数目,之后通过不断绘制矩形的方式实现直方图的绘制。由于图像中部分灰度值像素数目较多,因此将每个灰度值数目缩小为原来的 1/20
后再进行绘制,绘制的直方图如图
4-1
所示。在该程序中,使用了
OpenCV 4
提供的四舍五入的取整函数 cvRound()
,该函数输入参数为
double
类型的变量,返回值为对该变量四舍五入后的 int
型数值。
2. normalize()函数
2.1 normalize()函数原型
void cv::normalize(InputArray src,
InputOutputArray dst,
double alpha = 1,
double beta = 0,
int norm_type = NORM_L2,
int dtype = -1,
InputArray mask = noArray()
)
- src:输入数组矩阵。
- dst:输入与 src 相同大小的数组矩阵。
- alpha:在范围归一化的情况下,归一化到下限边界的标准值。
- beta:范围归一化时的上限范围,它不用于标准规范化。
- norm_type:归一化过程中数据范数种类标志,常用的可选择参数在表 4-1 中给出。
- dtype:输出数据类型选择标志。如果其为负数,那么输出数据与 src 拥有相同的类型,否则与 src 具有相同的通道数,但是数据类型不同。
- mask:掩码矩阵。
该函数输入一个存放数据的矩阵,通过参数 alpha
设置将数据缩放到最大范围,然后通过norm_type 参数选择计算范数的种类,之后将输入矩阵中的每个数据分别除以求取的范数数值,最 后得到缩放的结果。输出结果是一个 CV_32F
类型的矩阵,可以将输入矩阵作为输出矩阵,或者重新定义一个新的矩阵用于存放输出结果。该函数的第 5
个参数用于选择计算数据范数的种类,常用的可选择参数以及计算范数的公式在表 4-1
中给出。计算不同的范数,最后的结果也不相同,例如选择 NORM_L1
标志,输出结果为每个灰度值所占的比例;选择
NORM_INF
参数,输出结果为除以数据中最大值,将所有的数据归一化为 0
~
1
。
normalize()函数归一化常用标志参数
3. 代码示例
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
vector<double> positiveData = { 2.0, 8.0, 10.0 };
vector<double> normalized_L1, normalized_L2, normalized_Inf, normalized_L2SQR,normalized_MINMAX;
//测试不同归一化方法
normalize(positiveData, normalized_L1, 1.0, 0.0, NORM_L1); //绝对值求和归一化
cout <<"normalized_L1=["<< normalized_L1[0]<<", "
<< normalized_L1[1]<<", "<< normalized_L1[2] <<"]"<< endl;
normalize(positiveData, normalized_L2, 1.0, 0.0, NORM_L2); //模长归一化
cout << "normalized_L2=[" << normalized_L2[0] << ", "
<< normalized_L2[1] << ", " << normalized_L2[2] << "]" << endl;
normalize(positiveData, normalized_Inf, 1.0, 0.0, NORM_INF); //最大值归一化
cout << "normalized_Inf=[" << normalized_Inf[0] << ", "
<< normalized_Inf[1] << ", " << normalized_Inf[2] << "]" << endl;
normalize(positiveData, normalized_MINMAX, 1.0, 0.0, NORM_MINMAX); //偏移归一化
cout << "normalized_MINMAX=[" << normalized_MINMAX[0] << ", "
<< normalized_MINMAX[1] << ", " << normalized_MINMAX[2] << "]" << endl;
Mat img = imread("../pic/gril.jpg");
if (img.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat gray;
//cvtColor(img, gray, COLOR_BGR2GRAY);
Mat imgs[3],imgs0,imgs1,imgs2;
split(img, imgs);
imgs0 = imgs[0];
imgs1 = imgs[1];
imgs2 = imgs[2];
//设置提取直方图的相关变量
Mat b_hist,g_hist,r_hist; //用于存放直方图计算结果
const int channels[1] = { 0 }; //通道索引
float inRanges[2] = { 0,255 };
const float* ranges[1] = { inRanges }; //像素灰度值范围
const int bins[1] = { 256 }; //直方图的维度,其实就是像素灰度值的最大值
calcHist(&imgs[0], 1, 0, Mat(), b_hist, 1, bins, ranges, true, false); //计算图像直方图
calcHist(&imgs[1], 1, 0, Mat(), g_hist, 1, bins, ranges, true, false); //计算图像直方图
calcHist(&imgs[2], 1, 0, Mat(), r_hist, 1, bins, ranges, true, false); //计算图像直方图
//准备绘制直方图
int hist_w = 500;
int hist_h = 400;
int width = 2;
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
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 <= 255; i++)
{
line(histImage, Point(width*(i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),Point(width*(i), hist_h - cvRound(b_hist.at<float>(i)/2)), Scalar(255, 0, 0),2);
line(histImage, Point(width*(i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),Point(width*(i), hist_h - cvRound(g_hist.at<float>(i)/2)), Scalar(0, 255, 0),2);
line(histImage, Point(width*(i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),Point(width*(i), hist_h - cvRound(r_hist.at<float>(i)/2)), Scalar(0, 0, 255),2);
}
namedWindow("histImage", WINDOW_AUTOSIZE);
imshow("histImage", histImage);
imshow("img", img);
waitKey(0);
return 0;
}
4. 测试结果