图像是由像素组成的,在一个单通道的灰度图像中,每个像素的值是在0~255(白色-黑色)之间。直方图是一个简单的图表,它统计出了一幅图像或一组图像中拥有给定数值的像素量。所以一个灰度图像的直方图有256有条目,0条目的给出的值为0的像素个数,1条目给出的值为1的像素个数,以此类推。也可以对直方图所有条目进行求和,则得到像素的总和,也可以归一化,归一化以后所有项之和为1。在Opencv中计算直方图可以使用cv::calcHist函数来统计,为了使用简单,一下我们进行封装一下,先封装一个类来处理单通道的灰度图像:
#ifndef HISTOGRAM_H
#define HISTOGRAM_H
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv.hpp>
using namespace cv;
class HistogramGray {
private:
int histSize[1];//项的数量
float hranges[2];//像素的最小及最大值
const float *ranges[1];
int channels[1];//一个通道
public:
HistogramGray(){
//准备单通道的直方图参数
histSize[0]=256;
hranges[0]=0.0;
hranges[1]=255.0;
ranges[0]=hranges;
channels[0]=0;
}
//计算灰度图单通道的直方图
cv::MatND getHistogram(const cv::Mat &image){
cv::MatND hist;
//计算直方图
cv::calcHist(&image,//输入图像
1,//计算单张图像的直方图
channels,//通道数量
cv::Mat(),//不使用图像作为掩码。如果要统计整幅图像的直方图就把它设为None。但是如果你想统计图像某一部分的直方图的话,你就需要制作一个掩模图像,并使用它。
hist,//返回的直方图
1,//灰度图的直方图
histSize,//项的数量
ranges);//像素值范围
return hist;
}
//计算单通道直方图,并返回直方图统计图像
cv::Mat getHistogramGrayImage(const cv::Mat &image){
//首先计算直方图
cv::MatND hist = getHistogram(image);
//获取最大值和最小值
double maxVal=0;
double minVal=0;
cv::minMaxLoc(hist,&minVal,&maxVal,0,0);
//显示直方图图像
cv::Mat histImg(histSize[0],histSize[0],CV_8U,cv::Scalar(255));//显示为黑色
//设置最高点nbins的90%
int hpt = static_cast<int>(0.9*histSize[0]);
//每条条目都绘制一条垂直的线
for(int h = 0 ; h<histSize[0];h++){
float binVal = hist.at<float>(h);
int intensity = static_cast<int>(binVal*hpt/maxVal);
//两点之间绘制一条线
cv::line(histImg,cv::Point(h,histSize[0]),cv::Point(h,histSize[0]-intensity),cv::Scalar::all(0));
}
return histImg;
}
};
#endif // HISTOGRAM_H
dims:需要统计的特征数目。(如果仅统计了灰度值,则dims=1,如果统计了BGR三色,则dims=3)
bins:每个特征空间子区段的数目。(直方图中,每一竖条表示某一值或一区间,则该直方图的bins=16)
range:要统计特征的取值范围。(我们将[0,255]区间分成了16个子区间,其中range=[0,255])
接下来我们测试一下,测试代码如下:
Mat image = imread("gray.jpg");
if(image.empty())
{
namedWindow("can not find image ");
waitKey(5000);
return -1;
}
HistogramGray h;
namedWindow("Histogram");
imshow("MyImage",h.getHistogramGrayImage(image));
while(1)
waitKey(5000);
计算并以图像形式输出。