直方图均衡化是一种用于图像增强的方法,通过调整图像的像素值分布,使得图像的对比度更好,细节更加显著。直方图均衡化的过程如下:
-
计算原始图像的灰度直方图,即统计图像中每个灰度级别的像素数量。
-
将灰度直方图归一化,将每个灰度级别出现的像素数量除以总像素数量,得到归一化后的直方图。
-
计算累积直方图,将归一化后的直方图进行累加操作,得到每个灰度级别对应的累积频率。
-
根据累积直方图调整原始图像的像素值。对于每个像素值v,将其映射到新的像素值v',计算公式为v' = (L - 1) * cumulative_hist(v),其中L为灰度级别的数量。
-
将调整后的像素值重新赋给原始图像的对应位置,完成图像均衡化。
直方图均衡化能够扩展图像的动态范围,使得图像中的像素值更均匀分布,从而提高图像的视觉效果和质量。
首先,我们选择一副较暗或者较量的图,对其进行直方图均衡化处理,首先我们先画一下下图的灰度直方图,可以看到图像整体亮度偏低,因此灰度级应该分布在较低的地方,没有充分利用到亮区的信息。
原图:直方图:
均衡化后的图像和直方图:
可以看到均衡化后的灰度分布就是在[0,255]上更加接近均匀分布,图像整体的对比度也增加。
#include<opencv2/core.hpp>
#include<opencv2/imgcodecs.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
//@para gray:需要统计的图 Hist:用于存放统计数据
void GetHist(Mat gray,Mat &Hist) //统计8Bit量化图像的灰度直方图
{
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);
}
void ShowHist(Mat &Hist)
{
//准备绘制直方图
int hist_w = 512;
int hist_h = 400;
int width = 2;
Mat histImage = Mat::zeros(hist_h,hist_w,CV_8UC3); //准备histImage为全黑背景色
for (int i = 0; i < Hist.rows; i++)
{
rectangle(histImage,Point(width*(i),hist_h-1),Point(width*(i+1),hist_h-cvRound(Hist.at<float>(i)/100)),
Scalar(255,255,255),-1);
//cout << "像素值为" << i << "的个数:" << Hist.at<float>(i) << endl;
}
namedWindow("histImage", WINDOW_AUTOSIZE);
imshow("histImage", histImage);
//waitKey(0);
}
//获得
void GetHistList(const Mat src,float *bin,int num)
{
//遍历所有的像素值
for (int r=0;r<src.rows;r++)
{
for (int c=0;c<src.cols;c++)
{
int index = src.at<uchar>(r,c);
bin[index]++; //bin为灰度级像素数的统计数组
}
}
//归一化操作
for (int i=0;i<num;i++)
{
bin[i]=bin[i]/src.rows/src.cols;
}
//累计操作
for (int i=0;i<num;i++)
{
if (i == 0)
{
bin[i] = bin[i];
continue;
}
bin[i] = bin[i] + bin[i - 1];
}
for (int i = 0; i < num; i++)
{
cout << bin[i] << endl;
}
}
//
/*直方图均衡化操作
参数: Mat src:原图
float* bin:src灰度概率统计的累计和数组
int num:bin数组的大小
Mat &des:直方图均衡化后的图像
*/
void MyHisteq(const Mat src,float *bin,int num, Mat& des)
{
//首先,先为des开辟src相同大小的空间
des.create(src.rows,src.cols,CV_8UC1);
//针对每个像素值计算直方图均衡化的结果
for (int r=0;r<src.rows;r++)
{
for (int c=0;c<src.cols;c++)
{
int index=src.at<uchar>(r,c);
des.at<uchar>(r, c) = int(255 * bin[index]);
}
}
}
int main(int argc,char *argv)
{
Mat src,gray,hist1; //hist用于统计gray的直方图
src=imread("dark.jpg");
cvtColor(src,gray,CV_BGR2GRAY);
GetHist(gray,hist1);//获得直方图
ShowHist(hist1);
namedWindow("gray");
imshow("gray",gray);
//获得灰度级统计矩阵
float bin[256] = {0};
int num = sizeof(bin) / sizeof(bin[0]);
GetHistList(gray,bin,num); //bin存放的是灰度值概率的累计和
Mat my_hist,hist2; //my_hist用于存放直方图均衡化的图像
MyHisteq(gray, bin, num, my_hist);
namedWindow("my_hist");
imshow("my_hist",my_hist);
GetHist(my_hist, hist2);
ShowHist(hist2);
waitKey(0);
return 0;
}