图像直方图是图像处理中非常重要的像素统计结果,图像直方图不再表征任何的图像纹理信息,而是对图像像素的统计。由于同一物体无论是旋转还是平移在图像中都具有相同的灰度值,因此直方图具有平移不变性、放缩不变性等优点,因此可以用来查看图像整体的变化形式,例如图像是否过暗、图像像素灰度值主要集中在哪些范围等,在特定的条件下也可以利用图像直方图进行图像的识别,例如对数字的识别。
图像直方图简单来说就是统计图像中每个灰度值的个数,之后将图像灰度值作为横轴,以灰度值个数或者灰度值所占比率作为纵轴绘制的统计图。通过直方图可以看出图像中哪些灰度值数目较多,哪些较少,可以通过一定的方法将灰度值较为集中的区域映射到较为稀疏的区域,从而使得图像在像素灰度值上分布更加符合期望状态。通常情况下,像素灰度值代表亮暗程度,因此通过图像直方图可以分析图像亮暗对比度,并调整图像的亮暗程度。
在OpenCV 4中只提供了图像直方图的统计函数calcHist(),该函数能够统计出图像中每个灰度值的个数,但是对于直方图的绘制需要使用者自行绘制。我们首先学习统计灰度值数目的函数**calcHist()**的使用,该函数的原型在代码清单4-1中给出。
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
)
该函数用于统计图像中每个灰度值像素的个数,例如统计一张CV_8UC1的图像,需要统计灰度值从0到255中每一个灰度值在图像中的像素个数,如果某个灰度值在图像中没有,那么该灰度值的统计结果就是0。由于该函数具有较多的参数,并且每个参数都较为复杂,因此作者建议读者在使用该函数时只统计单通道图像的灰度值分布,对于多通道图像可以将图像每个通道分离后再进行统计。
为了使读者更加了解函数的使用方法,我们在代码清单4-2中提供了绘制灰度图像的图像直方图的示例程序。在程序中我们首先使用calcHist()函数统计灰度图像里面每个灰度值的数目,之后通过不断绘制矩形的方式实现直方图的绘制。由于图像中部分灰度值像素数目较多,因此我们将每个灰度值数目缩小了20倍后再进行绘制,绘制的直方图在图4-1中所示。在程序中我们使用了OpenCV 4提供的四舍五入的取整函数cvRound(),该函数输入参数为double类型的变量,返回值为对该变量四舍五入后的int型数值。
#include
#include
using namespace cv;
using namespace std;
int main(){
Mat img=imread("699342568.jpg");
if(img.empty()){
cout<<"请确认输入的图片路径是否正确"<<endl;
return -1;
}
Mat gray;
cvtColor(img,gray,COLOR_BGR2GRAY);
//设置提取直方图的相关变量
Mat hist;//用于存放直方图计算结果
const int channels[1]={0};//通道索引
float inRanges[2]={0,255};
const float*ranges[1]={inRanges};//像素灰度值范围
const int bins[1]={256};//直方图的维度,其实就是像素灰度值的最大值
calcHist(&img,1,channels,Mat(),hist,1,bins,ranges);//计算图像直方图
//准备绘制直方图
int hist_w=512;
int hist_h=400;
int width=2;
Mat histImage=Mat::zeros(hist_h,hist_w,CV_8UC3);
for(int i=1;i<=hist.rows;++i){
rectangle(histImage,Point(width*(i-1),hist_h-1),
Point(width*i-1,hist_h-cvRound(hist.at<float>(i-1)/20)),
Scalar(255,255,255),-1);
}
namedWindow("histImage",WINDOW_AUTOSIZE);
imshow("histImage",histImage);
imshow("gray",gray);
waitKey(0);
return 0;
}