#include<iostream>
#include<opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/imgproc/imgproc.hpp"
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; //256个箱子
hranges[0]=0.0; //从0开始(含)
hranges[1]=256.0; //到256(不含)
ranges[0]=hranges;
channels[0]=0; //先关注通道 0
}
Mat getHistogram(const Mat &img)
{
Mat hist;
//计算直方图
calcHist(&img,
1, //仅为一个图像的直方图
channels, //使用的通道
Mat(), //不使用掩码
hist, // 作为结果的直方图
1, //这是一维的直方图
histSize, //箱子数量
ranges //像素值的范围
);
return hist;
} //注意这里得到的hist是256行一列的Mat
Mat getHistogramImage(const Mat &img,int zoom=1)
{
Mat hist=getHistogram(img);
return getImageofHistogram(hist,zoom);
}
static Mat getImageofHistogram(const Mat &hist,int zoom) //根据直方图数据hist画直方图
{
double maxVal=0;
double minVal=0;
minMaxLoc(hist,&minVal,&maxVal,0,0);
cout<<"minVal= "<<minVal<<", maxVal="<<maxVal<<endl; //这里最小值0 最大值2280 个数!!
int histSize=hist.rows;
Mat histImg(histSize*zoom,histSize*zoom,CV_8U,Scalar(255));
int hpt=static_cast<int>(0.9*histSize);
for(int h=0;h<histSize;h++)
{
float binVal=hist.at<float>(h);
if(binVal>0)
{
int intensity=static_cast<int>(binVal*hpt/maxVal); //注意这里的归一化,binVal/maxVal(2280)*hpt 保证在255范围内
line(histImg,Point(h*zoom,(histSize)*zoom),Point(h*zoom,(histSize-intensity)*zoom),Scalar(0),zoom); //注意这里画直线的方法,
} //前面的point是起始点,后面的终点 背景图是白的,直线用黑的,起始点在最下面,然后减去长度。总感觉这里的histSize(256)需要-1
}
return histImg;
}
static Mat applyLookUp(const Mat &img,Mat &lookup)
{
Mat result;
LUT(img,lookup,result);//LUT是查找表,高效!即用空间换时间 就是需要创建矩阵lookup的查找表规则,把一个像素值映射到另一个像素值
return result;
}
Mat stretch(const Mat &img,int minValue=0) //所谓伸展,就是伸展直方图,使各个像素值平铺均匀,即增加对比度
{
Mat hist=getHistogram(img); //得到直方图
int imin=0;
for (;imin<histSize[0];imin++) //找到最小的横坐标 imin 使得次数大于minValue
{
if (hist.at<float>(imin)>minValue)
{
break;
}
}
int imax=histSize[0]-1;
for (;imax>=0;imax--) //找到最大的横坐标 imax 使得次数大于minValue
{
if (hist.at<float>(imax)>minValue)
{
break;
}
} //minValue代表的是次数、个数!!!像素值最小(0左右的)以及像素值最大(255左右的)
//这些极端的值都比较少, 找到比较少的个数对应的像素值坐标(横坐标)
Mat lookup(1,256,CV_8U); //LUT查找表的像素重映射的规则
for (int i=0;i<256;i++) //根据像素值大小划分
{
if(i<imin) // 像素值(横坐标)imin左边的都置为0 //极小的置0
lookup.at<uchar>(i)=0;
else if (i>imax) //像素值(横坐标)右边的都置255 //极大的置255
lookup.at<uchar>(i)=255;
else
lookup.at<uchar>(i)=cvRound(255.0*(i-imin)/(imax-imin)); //[min,max]重新分配 cvRound为取整 //中间的重新映射
}
Mat result;
result=applyLookUp(img,lookup);
return result; //返回处理好的增强的对比度 图片
} //这里需要分析下形参传进来的minValue! 如果minValue过大,两边的0,255就会多
//如果minValue过小,两边的0,255就会少
};
void main()
{
Mat img1=imread("C:\\Users\\Administrator\\Desktop\\工作\\testp\\group.jpg",0);
Histogram1D h;
Mat streteched=h.stretch(img1,200);
Histogram1D h_stretech;
Histogram1D h_src;
imshow("h_stretech.",h_stretech.getHistogramImage(streteched));
imshow("h_src",h_src.getHistogramImage(img1));
imshow("src",img1);
imshow("stretn",streteched);
waitKey(0);
}
minValue值为200的时候
下面的是为5的情况:
minValue值为5的时候
图中可以看出,200的时候2端的比较高, 200的直方图看到好像白条比较多,应该是映射的时候某些像素值的缺失比较多,比如180,后面直接182,185了。而原图都是乌漆麻黑一片,看起来比较柔和
LUT查找表原理讲的比较透彻 看的是这位仁兄的博客http://blog.csdn.net/jameshater/article/details/50759650