视觉图像:基于直方图均衡化的图像增强
1、直方图均衡化算法的步骤:
①统计原始图像各灰度级的像素数目nk,k=0,1,…,L-1;
②计算原始图像的直方图,即各灰度级的概率密度pr(rk)=nk/n;
③计算各灰度级的累积概率分布:
④计算最后的输出灰度级:
其中,Int[*]代表取整运算符;
⑤利用rk和sk的映射关系,修改原图的灰度级,获得增强图像,使得图像直方图为近似均匀分布。
故,实质是把src原图像中像素s经过T变换转化后到dst源图像中r;

2、OpenCV中直方图均衡化函数:
彩色图像的直方图均衡化实现:
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
int main(int argc, char *argv[])
{
Mat image = imread("Test.jpg", 1);
imshow("原图像", image);
Mat imageRGB[3];
split(image, imageRGB);
for (int i = 0; i < 3; i++)
{
equalizeHist(imageRGB[i], imageRGB[i]);
}
merge(imageRGB, 3, image);
imshow("直方图均衡化图像增强效果", image);
waitKey();
return 0;
}


3、opencv编写直方图均衡化程序:
Mat ImageHistogramEqualize(Mat img)
{
int wid = img.cols;
int heig = img.rows;
double hist[256] = {0};//灰度
double p_hist[256] = {0};//灰度比例
double sum_hist[256] = {0};//累积灰度比例
double pixel_total = img.rows*img.cols;//总像素数
for (int i = 0;i < heig;i++)//hang
{
unsigned char *u_data = (unsigned char *)img.data + i*img.cols;
for (int j=0;j<wid;j++)//lie
{
hist[ u_data[j] ]++;//1.统计各灰度等级的像素数量
}
}
//p(r) = n(k)/n; k = 0、1、2...;
//0<= r <= 1;n为总像素数,n(k)表示灰度k的像素数
for (int i=0;i<256;i++)
{
p_hist[i] = (double)(hist[i]/pixel_total);//2.各灰度级的概率密度
if (i==0)
sum_hist[i] = p_hist[i];
else
sum_hist[i] = sum_hist[i-1]+p_hist[i];//3.计算各灰度级的累积概率分布
}
Mat dst_img=Mat::zeros(img.rows,img.cols,CV_8UC1);
for (int i=0;i<heig;i++)//hang
{
unsigned char *dst_data = (unsigned char *)dst_img.data +i*dst_img.cols;
unsigned char *src_data = (unsigned char *)img.data +i*img.cols;
for (int j=0;j<wid;j++)//lie
{
dst_data[j] = (sum_hist[ src_data[j] ]*255+0.5);//4.增强图像
}
}
imshow("src_img",img);
imshow("dst_img",dst_img);
return dst_img;
}


4、直方图绘制:
步骤:
首先,统计各灰度等级数量;
其次,初始化绘制直方图大小,高400,宽256;
最后,将各灰度等级数量值缩放到图像内;
//
//绘制直方图
//
void DrawHistogram(Mat img)
{
int hist_size = 256;
int width = hist_size;//绘制直方图的宽度==hist_size
int height = 400;//定义绘制直方图的高度
int hist[256]={0};
for (int i = 0;i < img.rows;i++)//hang
{
unsigned char *u_data = (unsigned char *)img.data + i*img.cols;
for (int j=0;j<img.cols;j++)//lie
{
hist[ u_data[j] ]++;//1.统计各灰度等级的像素数量
}
}
int max_val =0;
for(int i=0;i<256;i++)
{
if (hist[i]>max_val)
{
max_val = hist[i];//最大灰度等级数量
}
}
Mat hist_img = Mat::zeros(height,width, CV_8UC3);
for (int i = 0; i < width; i++)//lie
{
float bin_value = (float)hist[i];
float real_value = (bin_value / max_val)*height;
//cout << "i: " << i << " ,binValue: " << bin_value << endl;
//cout << "i: " << i << " ,realValue: " << real_value << endl;
line(hist_img, Point(i, height - 1), Point(i, height - 1 - real_value), Scalar(255,0,0), 1);
}
imshow("dstHist", hist_img);
}


5、OpenCV中calcHist()函数:
功能:计算图像直方图;
格式:
void calcHist(const Mat* arrays,
int narrays,
const int* channels,
InputArray mask,
OutputArray hist,
int dims,
const int* histSize,
const float** ranges,
bool uniform=true,
bool accumulate=false );
参数:
arrays:输入的图像的指针,可以是多幅图像,所有的图像必须有同样的深度(CV_8U or CV_32F)。同时一副图像可以有多个channes。
narrays:输入的图像的个数。
channels:用来计算直方图的channes的数组,比如输入是2副图像,第一副图像有0,1,2共三个channel,第二幅图像只有0一个channel,那么输入就一共有4个channes,如果int channels[3] = {3, 2, 0},那么就表示是使用第二副图像的第一个通道和第一副图像的第2和第0个通道来计算直方图。
mask:掩码,如果mask不为空,那么它必须是一个8位(CV_8U)的数组,并且它的大小的和arrays[i]的大小相同,值为1的点将用来计算直方图。
hist:计算出来的直方图
dims:计算出来的直方图的维数。
histSize:在每一维上直方图的个数。简单把直方图看作一个一个的竖条的话,就是每一维上竖条的个数。
ranges:用来进行统计的范围。比如 float rang1[] = {0, 20}; float rang2[] = {30, 40}; const float *rangs[] = {rang1, rang2};那么就是对0,20和30,40范围的值进行统计。
uniform:每一个竖条的宽度是否相等。
accumulate: 是否累加。如果为true,在下次计算的时候不会首先清空hist。
6、opencv计算直方图程序:
int img_Hist()
{
//1.灰度直方图均衡化
Mat src_img = imread("raw.jpg",0);
Mat dst_img;
equalizeHist(src_img,dst_img);
imshow("src_img",src_img);
imshow("dst_img",dst_img);
//2.计算直方图
int nimages = 1;//图像的个数
int channels = 0;//需要统计通道的索引
Mat mask = Mat();
Mat hist_src;//存放srcImg输出的直方图
Mat hist_dst;//存放dstImg输出的直方图
int dims = 1;//计算的直方图的维度
int hist_size = 256;//计算的直方图的分组数
float range[] = { 0, 256 };//表示直方图每一维度的取值范围[0,256)
const float* ranges[] = { range };//参数形式需要,表示每一维度数值的取值范围
calcHist(&src_img, nimages, &channels, mask, hist_src, dims, &hist_size, ranges);//计算srcImg直方图
calcHist(&dst_img, nimages, &channels, mask, hist_dst, dims, &hist_size, ranges);//计算dstImg直方图
//3绘制srcImg的直方图
double min_value = 0;
double max_value = 0;
minMaxLoc(hist_src, &min_value, &max_value);//得到计算出的直方图中的最小值和最大值
int width = hist_size;//定义绘制直方图的宽度,令其等于histSize
int height = 400;//定义绘制直方图的高度
Mat show_src = Mat::zeros(Size(width, height), CV_8UC3);//宽为histSize,高为height
for (int i = 0; i < hist_size; i++)//遍历histImg
{
float bin_value = hist_src.at<float>(i);//得到histImg中每一分组的值
cout << "i: " << i << " ,binValue: " << bin_value << endl;
float real_value = (bin_value / max_value)*height;//归一化数据,缩放到图像的height之内
cout << "i: " << i << " ,realValue: " << real_value << endl;
//用直线方法绘制直方图,注意两端点坐标的计算
line(show_src, Point(i, height - 1), Point(i, height - 1 - real_value), Scalar(255, 0, 0), 1);
}
imshow("srcHist", show_src);
imwrite("srcHist.bmp",show_src);
//4绘制dstImg的直方图
double minValue_dst = 0;
double maxValue_dst = 0;
minMaxLoc(hist_dst, &minValue_dst, &maxValue_dst);//得到计算出的直方图中的最小值和最大值
Mat Show_dst = Mat::zeros(Size(width, height), CV_8UC3);//宽为histSize,高为height
for (int i = 0; i < hist_size; i++)//遍历histImg
{
float bin_value = hist_dst.at<float>(i);//得到histImg中每一分组的值
cout << "i: " << i << " ,binValue: " << bin_value << endl;
float real_value = (bin_value / maxValue_dst)*height;//归一化数据,缩放到图像的height之内
cout << "i: " << i << " ,realValue: " << real_value << endl;
//用直线方法绘制直方图,注意两端点坐标的计算
line(Show_dst, Point(i, height - 1), Point(i, height - 1 - real_value), Scalar(255, 0, 0), 1);
}
imshow("dstHist", Show_dst);
imwrite("dstHist.bmp",Show_dst);
return 0;
}

