图像直方图的绘制
图像直方图是图像处理中非常重要的像素统计结果
由于同一物体无论是旋转还是平移,在图像中都具有相同的灰度值
因此直方图具有平移不变性、放缩不变性等优点,可以查看图像的整体变化形式,例如图像是否过暗、图像像素灰度值主要集中范围
在特定条件下,也可以利用图像直方图进行图像的识别,例如对数字的识别
简单来说,图像直方图就是统计图像中每一个灰度值的个数
之后将图像灰度值作为横轴,灰度值个数或者灰度值所占比率作为纵轴绘制直方图
通过直方图,可以看出图像中哪些灰度值数目较多,哪些较少,可以通过一定的方法将灰度值较为集中的区域映射到较为稀疏的区域
从而使图像在像素在像素值上的更加分布符合期望状态
通常情况下,像素灰度值表示亮暗程度,因此,可以通过图像直方图,分析图像亮暗对比度,并调整图像的亮暗程度
OpenCV4提供了图像直方图的统计函数calcHist(),该函数可以统计出图像中每个灰度值的个数
calcHist()函数原型:
void calcHist(const Mat* images,
int nimages,
const int* channels,
InputArray mask,
OutputArray hist,
int dims,
const int* histSize,
const float** ranges,
bool uniform = ture,
bool accumulate = false
)
- images:待统计直方图的图像数组,是一个MAT类型的数组,数组中所有的图像应具有相同的尺寸和数据类型,并且数据类型只能 是CV_8U、CV_16U和CV_32F三种中的一种,但是不同图像的通道数可以不同
- nimages:待统计直方图的图像数量
channels:需要参与形成多维直方图的通道索引数组,第一个图像的通道索引从0到images[0].channels()-1,第二个图像通道索引从
images[0].channels()到images[0].channels()+ images[1].channels()-1,以此类推
假如有三张图像,都是三通道。那么第一张图像三个通道的索引值分别为0、1、2;第二张图像的三个通道的索引值分别 为3、4、5;第三张图像的三个通道的索引值分别为6、7、8
现在我们要计算一个二维直方图,参与计算的通道为第一张图像的第一通道和第三张图像的第三通道,那么数组channels 的定义和初始化如下:
int channels[2]={0,8}mask: 可选的操作掩码,如果是空矩阵则表示图像中所有位置的像素都计入直方图中,如果矩阵不为空,则必须与输入图像尺寸
相同且数据类型为CV_8U。当不为空的时候,那些掩码值不为0的掩码对应的像素被纳入统计范围,而那些掩码值为0的掩码
对应的像素则不被纳入统计
- hist:存储直方图统计结果,是一个dims维度的数组
- dims:需要计算的直方图的维度,必须是整数,并且不能大于CV_MAX_DIMS,在OpenCV3和OpenCV 4.0中都为32
- histSize:每个维度的直方图尺寸
- ranges:每个维度的灰度值取值范围
- uniform:直方图是否均匀划分的标志符,默认状况下为均值(ture)
- accumulate:是否累加统计直方图的标志,如果累积(ture),那么在统计新图像的直方图时,之前图像的统计结果不会被
统计,该参数主要用于统计多个图像整体的直方图
该函数用于统计图像中每个灰度值像素个数,例如统计一幅CV_8UC1的图图像,需要统计从0到255中每一个灰度值在图像中的像素个数
如果某个灰度值在图像中没有,那么该灰度值的统计结果为0
示例程序:使用calcHsit()函数统计灰度图像中每个灰度值的数目,之后通过不断绘制矩阵的方式实现直方图的绘制
由于图像中部分灰度值数目较多,因此将每个灰度值缩小为原来的1/20后再进行绘制
OpenCV4提供了四舍五入的取整函数cvRound()
该函数输入参数double类型的变量,返回值为该变量四舍五入后的int型数值
示例:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("E:\\CLion\\opencv_xin\\SG\\SG_0.jpg");
if (img.empty()) {
cout << "Error" << 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(&gray,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 = 0; 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)/15 ) ),
Scalar(255,255,255),
-1
);
}
namedWindow("histImage",WINDOW_AUTOSIZE);
imshow("histImage",histImage);
imshow("gray",gray);
waitKey(0);
return 0;
}
矩形绘制函数:(2.4.4)
void cv::rectangle(InputOutArray img,
Point pt1,
Point pt2,
const Sclar& color,
int thickness = 1,
int lineType = LINE_8,
int shift = 0
)