一、直方图均衡化
通过累计函数对图像灰度进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像元个数大致相等。
就是把原始图像的灰度直方图从比较集中在一定范围变换到全灰度范围的均匀分布,
直方图均衡化常用来增强图像全局对比度。但变化后图像灰度级减少,某些细节消失。
步骤:
(1)统计每个灰度级的像元个数
(2)统计每个像素级的累计像元个数
(3)重新计算每个像元灰度值
代码如下:
第一步:
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
{
n[s[i][j]]++;
}
}
for(i=0;i<L;i++)
p[i]=n[i]/(width*height);
这里,n[i]表示的是灰度级为i的像素的个数,L表示的是最大灰度级,width和height分别表示的是原始图像的宽度和高度,所以,p[i]表示的就是灰度级为i的像素在整幅图像中出现的概率(其实p[]这个数组存储的就是这幅图像的归一化之后的直方图)
第二步:
for(i=0;i<=L;i++)
{
for(j=0;j<=i;j++)
{
c[i]+=p[j];
}
}
c[]这个数组存储的就是累计的归一化直方图
第三步:找出像素的最大值和最小值
max=min=s[0][0];
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
{
if(max<s[i][j]) max=s[i][j];
else if(min>s[i][j]) min=s[i][j];
}
}
for(i=0;i<height;i++)
for(j=0;j<width;j++)
t[i][j]=c[s[i][j]]*(max-min)+min;
t[][]就是最终直方图均衡化之后的结果
处理前的图片:
处理后的图片:
对于彩色的图片来说,直方图均衡化一般不能直接对R、G、B三个分量分别进行上述的操作,而要将RGB转换成HSV来对V分量进行直方图均衡化的操作
颜色空间相关知识和代码,参考博客:
【OpenCV3图像处理】颜色空间转换(一)颜色空间分类总结
二、直方图匹配/规定化
直方图均衡化可以增强整个图像的对比度,但实际上有时需要变换直方图为特定形状,从而有选择的增强某个灰度范围内的对比度,就可以使用直方图匹配,一般来说,正确选择规定化的函数可以获得比直方图均衡化更好的效果。
直方图匹配是指把原图像直方图变化成某种特定形态的直方图或某种参考图像的直方图,按照直方图调整原图像的像元灰度,得到直方图匹配后的图像。
直方图匹配的原理是对两个直方图都做均衡化,变成相同的归一化的均匀直方图,以此均匀直方图为媒介,再对参考图像做均衡化的逆运算。
步骤:
1.分离图像通道
2.通道分别进行直方图匹配
1)计算原图像的第i通道累计直方图
2)计算待匹配图像的第i通道累计直方图
3)对第i通道直方图匹配,对原图像第i通道每个像元,重新赋予灰度值
3.通道合成,形成匹配后的新图像
代码如下:
#include<opencv.hpp>
#include<windows.h>
#include<vector>
using namespace cv;
using namespace std;
void statistictHistogram(Mat Img, double *Addup_hist) //统计图像的累计直方图分布
{
int hist[256] = { 0 };
for (int nrow = 0; nrow < Img.rows; nrow++)
for (int ncol = 0; ncol < Img.cols; ncol++)
{
int tag = Img.at<uchar>(nrow, ncol);
hist[tag]++;
}
double tmp = 0.0;
for (int i = 0; i<256; i++)
{
tmp += hist[i];
Addup_hist[i] = tmp / (Img.rows * Img.cols);
}
}
void HistogramMatching(Mat srcImg, Mat dstImg) //直方图匹配
{
Mat out(srcImg);
//srcImg.copyTo(out);
/**************原图像的累计直方图分布***********/
double srcAddup_hist[256] = { 0.0 };
statistictHistogram(srcImg, srcAddup_hist);
/***********待匹配图像的累计直方图分布**********/
double dstAddup_hist[256] = { 0.0 };
statistictHistogram(dstImg, dstAddup_hist);
/********************** 直方图匹配算法 ********************/
int histMap[256]; //存储原图像中像元经过直方图匹配后所对应的灰度值
int Tag;
for (int i = 0; i<256; i++)
{
double minMap = 1.0;
for (int j = 0; j<256; j++)
{
if (minMap > abs(srcAddup_hist[i] - dstAddup_hist[j]))
{
minMap = abs(srcAddup_hist[i] - dstAddup_hist[j]);
Tag = j;
}
}
histMap[i] = Tag;
}
for (int nrow = 0; nrow < out.rows; nrow++)
for (int ncol = 0; ncol < out.cols; ncol++)
{
int pixel = out.at<uchar>(nrow, ncol);
out.at<uchar>(nrow, ncol) = histMap[pixel];
}
}
int main()
{
Mat srcImg = imread("E:\\图像库\\彩色图像\\沙漠.jpg", 1);
imshow("原图", srcImg);
Mat dstImg = imread("E:\\图像库\\彩色图像\\海滩.jpg", 1);
imshow("标准图", dstImg);
vector<Mat>srcchannels;
split(srcImg, srcchannels); //分离原图srcImg的RGB三通道,存到容器srcchannels中,每个通道存为一个Mat
vector<Mat>dstchannels;
split(dstImg, dstchannels); //分离原图dstImg的RGB三通道,存到容器dstchannels中,每个通道存为一个Mat
//对每个通道,分别进行直方图匹配
HistogramMatching(srcchannels.at(0), dstchannels.at(0));
HistogramMatching(srcchannels.at(1), dstchannels.at(1));
HistogramMatching(srcchannels.at(2), dstchannels.at(2));
Mat outImg;
merge(srcchannels, outImg); //将容器srcchannels中存储的三个单通道Mat合并为一个图像
imshow("直方图匹配效果", outImg);
imwrite("E:\\图像库\\彩色图像\\直方图匹配后的沙漠.jpg", outImg);
waitKey();
return 0;
}
效果如下:
原图:
结果图: