您的位置:首页 > 编程语言 > MATLAB

详解图像直方图均衡化原理,附自己写的在MATLAB下写的直方图均衡化源码,目的与OpenCV算法统一!

2016-04-25 11:53 585 查看
提问:图像直方图均衡化有啥效果?

答:看了下面的两幅截图你就知道了:





从上面两幅的截图中我们发现,将直方图均衡化算法应用于左侧的亮图、对比度不同的各个图像后,得到了右侧直方图大致相同的图像,这体现了直方图均衡化作为强大自适应性的增强工具的作用。当原始图像的直方图不同而图像结构性内容相同时,直方图均衡化所得到的结果在视觉上几乎是完全于致的,其原理我在下面对算法的描述中有详细介绍。这种结果对于在进行图像分析和比较之前将图像转化为统一的形式是十分有益

提问:为啥要在MATLAB下写的图像直方图均衡化源码?MATLAB不是提供了histeq()专门做图像的直方图均衡化吗?

答案:因为二者的结果不一致,不利于算法验证的统一,故在在MATLAB下写图像直方图均衡化源码,函数名取为“my_histeq()"

算法原理及实现步骤如下:

⑴统计图像中各灰度级的出现次数,存储在数组hist_sz中,比如hist_sz(1,200)=2代表第199灰度级的出现次数为2,注意灰度级是199而不是200,因为灰度级的范围是0-255,而MATLAB矩阵是从1开始编号;

⑵建立图像直方图均衡化映射表,并存储在数组lut中,比如lut(1,200)=204代表我们将把原图像中像素灰度值为200的点重新映射为204,建立映射表的方法如下:

①统计小于等于某一级的像素的点有多少个,并存储在变量sum中,比如语句sum=sum+hist_sz(1,3)→sum=4+2→6的意思是:灰度级小于等于1的点数有4个,灰度级为2的点数有2个,所以灰度级小于等于2的点数有6个;

②用对应灰度等级的sum值乘以scale=255/像素总点数,即“scale=255/(height*width); val=sum*scale;”就得到对应灰度级的映射值,并存储在lut数组中!

其实均衡化的核心就在第⑵部,均衡化的原理在于统计小于等于某一级的像素的点有多少个,这实际上是一种新的权重,我们容易发现,在这种权重的支配下,若第i灰度级的像素个数越多,则第i灰度级新的映射值与第i-1灰度级新的映射值之间的差距就越大,这样,就让整幅图像去突出显示占整幅图中个数较多的像素所代表的信息。实际上就是让整幅图按一种规则去显示图像,这种规则就是突出显示点数较多像素所代表的信息,而弱化点数较少像素所代表的信息。所以不管你对原图像怎样调整对比度,均衡化后的图像在对比度上看起来会感觉很相似。

⑶利用存储在lut数组中的映射表去把源图像的像素值作一个新的映射!语句dst(y,x)=lut(1,src_y_x);就实现了这个功能!

my_histeq() 对应的M文件下载链接:http://pan.baidu.com/s/1pLxiUUZ

my_histeq() 源码如下:

function dst = my_histeq(src)
%因为MATLAB自带的histeq()函数与OpenCV中的cvEqualizeHist()结果不一样,所以按OpenCV中的算法写了这个M函数
%作者:wenhao_ir 交流QQ 2487872782
%输入参数src要求是灰度图像,输出dst和src的大小相同,类型也为uint8

[height width]=size(src);
dst=zeros(height,width);
hist_sz=zeros(1,256);
src=src+1; %注意,因为MATLAB的数组索引是1到256所以需要加1,主要是因为后面要用像素值作为索引值,
%这里加了1,由于整个过程都是线性变换,所以最后的dst输出减1就可以了

for y=1:height
    for x=1:width
        src_y_x=src(y,x);
        hist_sz(1,src_y_x)=hist_sz(1,src_y_x)+1;
    end

end

scale=256/(height*width);%由于对src加了1,所以像素的最大值为256,而不是OpenCV源程序中的255
sum=0;
lut=zeros(1,257); %不知道为啥OpenCV为啥要把这个大小设定为257,个人感觉256就够了

for i=1:256
    sum=sum+hist_sz(1,i);
    val=sum*scale;
    lut(1,i)=uint8(val);
end

lut(1,1)=0;

for y=1:height
    for x=1:width
        src_y_x=src(y,x);
        dst(y,x)=lut(1,src_y_x);
    end
end

dst=uint8(dst);
dst=dst-1;

--------------------------------------------------------------

以下是调用自己写的my_histeq的MATLAB实例:

clear all;
close all;
clc;
I=imread('pout.jpg');
I=rgb2gray(I);
histeq_out=histeq(I); %这是系统自带的图像均衡化函数
my_histeq_out=my_histeq(I); %这是自己写的图像均衡化函数


以下是OpenCV的cvEqualizeHist函数源码,位于histogram.cpp中,我写的调用cvEqualizeHist的cpp源码下载链接如下:
http://pan.baidu.com/s/1cK9PBg
CV_IMPL void cvEqualizeHist( const CvArr* srcarr, CvArr* dstarr )
{
CvMat sstub, *src = cvGetMat(srcarr, &sstub);
CvMat dstub, *dst = cvGetMat(dstarr, &dstub);
CV_Assert( CV_ARE_SIZES_EQ(src, dst) && CV_ARE_TYPES_EQ(src, dst) &&
CV_MAT_TYPE(src->type) == CV_8UC1 );
CvSize size = cvGetMatSize(src);
if( CV_IS_MAT_CONT(src->type & dst->type) )
{
size.width *= size.height;
size.height = 1;
}
int x, y;
const int hist_sz = 256;//0到255,一共256个灰度值
int hist[hist_sz];
memset(hist, 0, sizeof(hist));
for( y = 0; y < size.height; y++ )
{
const uchar* sptr = src->data.ptr + src->step*y;
for( x = 0; x < size.width; x++ )
hist[sptr[x]]++; //这里实现了hist中存储各灰度值出现的次数
}
float scale = 255.f/(size.width*size.height);
int sum = 0;
uchar lut[hist_sz+1];
for( int i = 0; i < hist_sz; i++ )
{
sum += hist[i]; //逐级累计
int val = cvRound(sum*scale);
lut[i] = CV_CAST_8U(val);
}
lut[0] = 0;//这是通过程序计算出的灰度值映射关系,不管你怎么映射,0肯定是0
for( y = 0; y < size.height; y++ )
{
const uchar* sptr = src->data.ptr + src->step*y;
uchar* dptr = dst->data.ptr + dst->step*y;
for( x = 0; x < size.width; x++ )
dptr[x] = lut[sptr[x]];
}
}


-------------------------------------------------------------

以下是运行结果对比,大家可以看出自己写的函数my_histeq和OpenCV的cvEqualizeHist运算结果是一致的,但是MATLAB自带的和cvEqualizeHist是不一样的!







---------------------------------------------------------------------------

欢迎大家加入图像识别技术交流群:271891601,另外,特别欢迎成都从事图像识别工作的朋友交流,我的QQ号2487872782
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: