一些头文件:
#include
#include
#include
#include
using namespace std;
using namespace cv;
首先建立一个类:
class Histogram1D
{
private:
//直方图的点数
int histSize[1];
//直方图的范围
float hranges[2];
//指向该范围的指针
const float* ranges[1];
//通道
int channels[1];
public:
//构造函数
Histogram1D()
{
histSize[0] = 256;
hranges[0] = 0.0;
hranges[1] = 255.0;
ranges[0] = hranges;
channels[0] = 0;
}
在OpenCV中,使用calcHist计算直方图
Mat getHistogram(const Mat &image)
{
Mat hist;
//计算直方图函数
//参数为:源图像(序列)地址,输入图像的个数,通道数,掩码,输出结果,直方图维数,每一维的大小,每一维的取值范围
calcHist(&image,1,channels,Mat(),hist,1,histSize,ranges);
return hist;
}
但是,返回的结果并不是我们希望的“直方图”,hist只是记录了每个bin下的像素个数。我们需要自己利用line函数画图:
Mat getHistogramImage(const Mat &image)
{
//首先计算直方图
Mat hist = getHistogram(image);
//获取最大值和最小值
double maxVal = 0;
double minVal = 0;
//minMaxLoc用来获得最大值和最小值,后面两个参数为最小值和最大值的位置,0代表不需要获取
minMaxLoc(hist,&minVal,&maxVal,0,0);
//展示直方图的画板:底色为白色
Mat histImg(histSize[0],histSize[0],CV_8U,Scalar(255));
//将最高点设为bin总数的90%
int hpt = static_cast(0.9*histSize[0]);
//为每一个bin画一条线
for(int h = 0; h < histSize[0];h++)
{
float binVal = hist.at(h);
int intensity = static_cast(binVal*hpt/maxVal);
//int intensity = static_cast(binVal);
line(histImg,Point(h,histSize[0]),Point(h,histSize[0]-intensity),Scalar::all(0));
}
return histImg;
}
函数中对亮度进行了“压缩”,主要目的是让直方图能够更好的显示在画板上。
查找表,就是一个一对一或者多对一函数,指定了一个亮度经过查找表以后变成另外一个像素。在OpenCV中,使用LUT函数来实现
Mat applyLookUp(const Mat& image,const Mat& lookup)
{
Mat result;
LUT(image,lookup,result);
return result;
}
而变化的具体内容,依赖于你自己定义的查找表,比如,如果你想让查找表的结果为当前图像的翻转,可以在main函数中定义查找表为:
//用查找表翻转图像灰度
int dim(256);
Mat lut(1,&dim,CV_8U);
for(int i = 0; i < 256; i++)
{
lut.at(i) = 255-i;
}
imshow("灰度翻转后的图像",h.applyLookUp(image,lut));
当你想让图像经过查找表以后直方图拉伸,变得效果好一点,可以这样:
将个数低于指定数目(默认为0)的bin舍去,剩下的最小值变为0,最大值变为255,中间的部分线性拉伸:
Mat strech(const Mat &image,int minValue = 0)
{
//首先计算直方图
Mat hist = getHistogram(image);
//左边入口
int imin = 0;
for(;imin< histSize[0];imin++)
{
cout<(imin)<(imin) > minValue)
break;
}
//右边入口
int imax = histSize[0]-1;
for(;imax >= 0; imax--)
{
if(hist.at(imax) > minValue)
break;
}
//创建查找表
int dim(256);
Mat lookup(1,&dim,CV_8U);
for(int i = 0; i < 256; i++)
{
if(i < imin)
{
lookup.at(i) = 0;
}
else if(i > imax)
{
lookup.at(i) = 255;
}
else
{
lookup.at(i) = static_cast(255.0*(i-imin)/(imax-imin)+0.5);
}
}
Mat result;
result = applyLookUp(image,lookup);
return result;
}
当然,这样的效果还不是最好的,最好的均衡应该使得直方图非常平缓,每个bin中的数目差不多,在OpenCV中,使用equalizeHist函数可以实现直方图均衡功能:
Mat equalize(const Mat &image)
{
Mat result;
equalizeHist(image,result);
return result;
}
有了上面定义的类,我们main函数就变得简单多了:
#include "histogram.h"
int main()
{
//以灰度方式读取图像
Mat image = imread("D:/picture/images/group.jpg",0);
imshow("源灰度图像",image);
if (!image.data)
return -1;
Histogram1D h;
imshow("直方图",h.getHistogramImage(image));
//用查找表翻转图像灰度
int dim(256);
Mat lut(1,&dim,CV_8U);
for(int i = 0; i < 256; i++)
{
lut.at(i) = 255-i;
}
imshow("灰度翻转后的图像",h.applyLookUp(image,lut));
//用查找表拉伸图像灰度值
//忽略那些个数少于100个像素的bin
Mat strech = h.strech(image,100);
imshow("拉伸后的图像",strech);
imshow("拉伸后的直方图",h.getHistogramImage(strech));
//图像均衡
Mat afterEqualize = h.equalize(image);
imshow("均衡后的解果",afterEqualize);
imshow("均衡后的直方图",h.getHistogramImage(afterEqualize));
waitKey(0);
return 0;
}
由于画的图比较多,这里就不贴结果了。