直⽅图均衡化的原理及其实现
直⽅图均衡化在图像处理领域中运⽤⾮常⼴泛,⽽且⾮常简单易实现。
⾸先我们了解⼀下什么是图像的直⽅图:
设图像的灰度范围为[a,b],r为此灰度范围内的任⼀灰度级,p(r)为这幅图像中灰度级为r的像素出现的频率,可以看出p(r)是r的函数。该函数的图形称为这幅图像的直⽅图。
p(r)=灰度为r的像素数/图像上的总像素数
图像灰度级统计信息
图像灰度直⽅图
原灰度图灰度直⽅图
可以很清楚地看出,灰度直⽅图抛弃了原灰度图像的空间位置信息,反映了某⼀像素值在灰度图中出现的频率或者概率信息。如上图所⽰,可以很清楚地看出0-100灰度值所出现的频率⾮常⼤,且越趋近于0概率越⾼;⽽灰度值⼤于200后,出现频率⼤⼤降低。由此可以判断出,该图像整体较暗,细节部分不够突出。
于是这⾥我们引⼊---直⽅图均衡化的⽅法,希望按照⼀定的变换公式,将原图映射到新图,使得新图在原图的基础上,直⽅图分布更加均匀,这样图像的明暗分布更加均匀,给⼈的视觉效果就是对⽐度好,细节清晰。
那么问题的关键是,如何确定这个变换公式呢?
均衡化⽅法中,使⽤直⽅图的累积分布函数作为变换公式:
其中,
实际上就是⽤某灰度级的累积概率来代替其原出现概率,得到映射后新的灰度值(累积概率乘以最⼤灰度值)。
举个例⼦来说:
rk代表原图的⼋个灰度级;nk代表每个灰度级出现的频数;Prk代表每个灰度级出现的概率;Sk代表累积概率;Ps代表新图中rk所对应的出现概率。
于是,很容易得到,例如:
原图rk=0,累积概率Sk=1/7,于是其对应的新图灰度值为1*1/7=1/7。
经过意义映射之后,可以发现,新图中灰度级为0的出现概率为0,于是在rk=0时Ps=0;新图中灰度级为1/7的出现概率为0.19,于是在rk=1/7时Ps=0.19···
以此类推,很容易换算出新图的灰度级。
了解了直⽅图均衡化的原理,那么编程实际上就是⼩菜⼀碟了。⽆论是使⽤Matlab还是OpenCV,直⽅图均衡化都很好实现。这⾥要提⼀点的就是Matlab相对来说更加地⽅便,对于直⽅图的获取与绘制,都有现成的函数可供调⽤,⽽OpenCV相对来说⿇烦⼀点,需要⾃⼰写绘制函数。
另外,对输⼊进来的图像,⾸先要进⾏通道数的转换,⽐如说传⼊进来的是.jpg格式的图像,那么⽆论从外表上看它是不是⼀个灰度图像,读进来的都是三通道图像,所以,往往第⼀步是需要将多通道图像转换成单通道图像。
效果:
原图均衡化后
核⼼函数(来源于⽹络,我是⼤⾃然的搬运⼯)
1.直⽅图均衡化
void MyEqualizeHist_color(IplImage* src, int color)
{
if (color == 1)
{//多通道
IplImage* imgChannel[4] = { 0, 0, 0, 0 };
IplImage* dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);
int i;
for (i = 0; i < src->nChannels; i++)
{
imgChannel[i] = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); //要求单通道图像才能直⽅图均衡化 }
//通道分离
cvSplit(src, imgChannel[0], imgChannel[1], imgChannel[2], imgChannel[3]);//BGRA
for (i = 0; i < dst->nChannels; i++)
{
//直⽅图均衡化,原始图像和⽬标图像必须是单通道
cvEqualizeHist(imgChannel[i], imgChannel[i]);
}
//通道组合
cvMerge(imgChannel[0], imgChannel[1], imgChannel[2], imgChannel[3], dst);
cvNamedWindow("src", 1);
cvShowImage("src", src);
cvNamedWindow("Equalize", 1);
cvShowImage("Equalize", dst);
}
else
{//单通道
IplImage* dst = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
cvEqualizeHist(src, dst);
cvNamedWindow("src", 1);
cvShowImage("src", src);
cvNamedWindow("Equalize", 1);
cvShowImage("Equalize", dst);
}
}
2.绘制灰度直⽅图(单通道灰度图像)
IplImage* src = cvLoadImage("orange.JPG");//读取图像
IplImage* gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
cvCvtColor(src, gray, CV_BGR2GRAY);//彩⾊图像灰度化
CvHistogram *pcvHistogram = CreateGrayImageHist(&gray);//创建⼀个灰度直⽅图
int nHistImageWidth = 255; //0到255个灰度级
int nHistImageHeight = 200; //直⽅图图像⾼度
int nScale = 2;
IplImage* pHistImage =
CreateHisogramImage(nHistImageWidth, nScale,nHistImageHeight, pcvHistogram);//得到灰度直⽅图cvNamedWindow("Hist", 1);
cvShowImage("Hist", pHistImage);
/****
FillWhite是⼀个填充函数,将pImage填充成⽩⾊
*****/
void FillWhite(IplImage *pImage)
{
cvRectangle(pImage, cvPoint(0, 0), cvPoint(pImage->width, pImage->height), CV_RGB(255, 255, 255), CV_FILLED); }
/****
rectangle函数opencv创建灰度图像的直⽅图
*****/
CvHistogram* CreateGrayImageHist(IplImage **ppImage)
{
int nHistSize = 256;//灰度数
float fRange[] = { 0, 255 }; //灰度级的范围
float *pfRanges[] = { fRange };
CvHistogram *pcvHistogram = cvCreateHist(1, &nHistSize, CV_HIST_ARRAY, pfRanges);//创建直⽅图
cvCalcHist(ppImage, pcvHistogram);//计算ppImage的直⽅图
return pcvHistogram;
}
/****
根据直⽅图创建直⽅图图像、
*****/
IplImage * CreateHisogramImage(int nImageWidth, int nScale, int nImageHeight, CvHistogram *pcvHistogram)
{
IplImage *pHistImage = cvCreateImage(cvSize(nImageWidth * nScale, nImageHeight), IPL_DEPTH_
8U, 1); FillWhite(pHistImage);
//统计直⽅图中的最⼤直⽅块
float fMaxHistValue = 0;
cvGetMinMaxHistValue(pcvHistogram, NULL, &fMaxHistValue, NULL, NULL);
//分别将每个直⽅块的值绘制到图中
int i;
for (i = 0; i < nImageWidth; i++)
{
float fHistValue = cvQueryHistValue_1D(pcvHistogram, i); //像素为i的直⽅块⼤⼩
int nRealHeight = cvRound((fHistValue / fMaxHistValue) * nImageHeight); //要绘制的⾼度
cvRectangle(pHistImage,
cvPoint(i * nScale, nImageHeight - 1),
cvPoint((i + 1) * nScale - 1, nImageHeight - nRealHeight),
cvScalar(i, 0, 0, 0),
CV_FILLED
);
}
return pHistImage;
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论