增强图像的对比度的方法,根据图像增强处理过程所在的空间不同,可分为基于空间域的增强方法和基于频域的增强方法两类。
基于空间域的增强方法按照所采用的技术不同可分为灰度变换和空域滤波两种方法。
(1) 灰度变换是基于像素点的增强方法,它将每一个像素的灰度值按照一定的数学变换公式转换为一个新的灰度值,如增强处理中常用的对比度增强:直方图均衡化等方法.
(2) 空域滤波是基于邻域处理的增强方法,它应用某一模板对每个像素及其周围邻域的所有像素进行某种数学运算,得到该像素的新的灰度值(即输出值),输出值的大小不仅与该像素的灰度值有关,而且还与其邻域内的像素的灰度值有关,常用的图像平滑与锐化技术就属于空域滤波的范畴。
直方图均衡化
灰度直方图均衡化的算法,简单地说,就是把直方图的每个灰度级进行归一化处理,求每种灰度的累积分布,得到一个映射的灰度映射表,然后根据相应的灰度值来修正原图中的每个像素。
如果原始图像对比度本来就很高,如果再均衡化则灰度调和,对比度降低。在泛白缓和的图像中,均衡化会合并一些象素灰度,从而增大对比度。均衡化后的图片如果再对其均衡化,则图像不会有任何变化。
概率密度函数(PDF)
为了计算方便,我们需要将直方图归一化,即把灰度范围由0~255变为0~1。归一化后的直方图其实就是一个概率密度函数(PDF,probability density function),均衡化就是令概率密度为1。
我们用Pr(r)来表示原图像的PDF,用Ps(s)表示均衡化之后的PDF,r、s分别代表均衡化前后的灰度值,r,s∈[0,1]。根据概率论的知识,可得出:
公式中,T(rk)来表示原图像的第k个灰度级的转换函数。∑表示总和。∑nj/N表示0~j个灰度级的像素数量总和与像素总数的比值,也就是前面讲过的百分位(当前色阶与前面色阶的所有像素数量÷总像素数量)。∑Pr(rk)表示第0~k的灰度级出现概率累积相加。因为s是归一化的数值(s∈[0,1]),要转换为0~255的颜色值,需要再乘上255,即S=∑Pr(rk)*255。
这个转换公式也被称为图像的累积分布函数
(CDF,cumulativedistribution function)。
彩色算法
彩色的直方图均衡化其实就是对图像某个或多个颜色通道进行灰度直方图均衡化运算,常见的有以下几种方法:
统计所有RGB颜色通道的直方图的数据并做均衡化运算,然后根据均衡化所得的映射表分别替换R、G、B通道颜色值。
分别统计R、G、B颜色通道的直方图的数据并做均衡化运算,然后根据R、G、B的映射表分别替换R、G、B通道颜色值。
用亮度公式或求RGB的平均值的方式计算亮度通道,然后统计亮度通道的直方图的数据并做均衡化运算,然后根据映射表分别替换R、G、B通道颜色值。
代码:
#include <opencv2/opencv.hpp>
#include <CvvImage.h>
int ImageStretchByHistogram(IplImage *src1,IplImage *dst1)
/*************************************************
Function: 通过直方图变换进行图像增强,将图像灰度的域值拉伸到0-255
src1: 单通道灰度图像
dst1: 同样大小的单通道灰度图像
*************************************************/
{
// assert(src1->width==dst1->width);
double p[256],p1[256],num[256];
memset(p,0,sizeof(p));
memset(p1,0,sizeof(p1));
memset(num,0,sizeof(num));
int height=src1->height;
int width=src1->width;
long wMulh = height * width;
//statistics
for(int x=0;x<src1->width;x++)
{
for(int y=0;y<src1-> height;y++)
{
uchar v=((uchar*)(src1->imageData + src1->widthStep*y))[x];
num[v]++;
}
}
//calculate probability
for(int i=0;i<256;i++)
{
p[i]=num[i]/wMulh;
}
//p1[i]=sum(p[j]); j<=i;
for(int i=0;i<256;i++)
{
for(int k=0;k<=i;k++)
p1[i]+=p[k];
}
// histogram transformation
for(int x=0;x<src1->width;x++)
{
for(int y=0;y<src1-> height;y++)
{
uchar v=((uchar*)(src1->imageData + src1->widthStep*y))[x];
((uchar*)(dst1->imageData + dst1->widthStep*y))[x]= p1[v]*255+0.5;
}
}
return 0;
}
void RGB2GRAY(IplImage*src)
{
cvNamedWindow("RGB");
cvNamedWindow("GRAY");
cvNamedWindow("Strong");
cvShowImage("RGB",src);
IplImage* dst = cvCreateImage(cvGetSize(src),src ->depth,1);
cvCvtColor(src,dst,CV_BGR2GRAY);
cvShowImage("GRAY",dst);
IplImage *dst2=cvCreateImage(cvGetSize(dst),dst->depth,dst->nChannels);
ImageStretchByHistogram(dst,dst2);
cvShowImage("Strong",dst2);
cvWaitKey(0);
cvReleaseImage(&dst);
cvReleaseImage(&dst2);
cvWaitKey(0);
cvDestroyWindow("RGB");
cvDestroyWindow("GRAY");
cvDestroyWindow("Strong");
}
int main()
{
IplImage* img = cvLoadImage("1.gif");
RGB2GRAY(img);
// while (1)
// {
// if (cvWaitKey(100)==27)
// {
// break;
// }
// }
cvReleaseImage(&img);
return 0;
}