详解图像直方图均衡化的原理,附自己写的MATLAB和OpenCV2.x下的直方图均衡化函数源码

图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601

提问1:图像直方图均衡化有啥效果?

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

详解图像直方图均衡化的原理,附自己写的MATLAB和OpenCV2.x下的直方图均衡化函数源码_第1张图片

详解图像直方图均衡化的原理,附自己写的MATLAB和OpenCV2.x下的直方图均衡化函数源码_第2张图片

从上面两幅的截图中我们发现,将直方图均衡化算法应用于左侧亮图、对比度不同的各个图像后,得到了右侧直方图大致相同的图像,这体现了直方图均衡化在图像增强方面的自适应性。

当原始图像的直方图不同而图像结构性内容相同时,直方图均衡化所得到的结果在视觉上几乎是完全于致的。这样的处理效果对于在进行图像分析和比较之前将图像转化为统一的形式是十分有益。

提问2:为啥要自己写图像直方图均衡化的MATLAB函数?MATLAB不是提供了histeq()专门做图像的直方图均衡化吗?

答案:因为MATLAB自带的直方图均衡函数histeq()与OpenCV的直方图均衡函数cvEqualizeHist()二者的结果不一致,不利于算法的彼此互验。故自己写图像直方图均衡化的函数,函数名取为“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()源码如下:

function dst = my_histeq(src)
%因为MATLAB自带的histeq()函数与OpenCV中的cvEqualizeHist()结果不一样,所以按OpenCV中的算法写了这个M函数
%作者微信/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;

end

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

其中pout.jpg的百度网盘下载链接如下:

链接:https://pan.baidu.com/s/11wgfEqJoIeP3BVyTyvGCOg 
提取码:bsqw

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

运行结果在下面一点哈!

利用OpenCV1.x自带的直方图均衡化函数cvEqualizeHist()进行直方图均衡化代码如下:

其中pout.jpg的百度网盘下载链接如下:

链接:https://pan.baidu.com/s/11wgfEqJoIeP3BVyTyvGCOg 
提取码:bsqw 

#include   
#include  
#include 
using namespace std;  

#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")  

int main()
{
	// 从文件中加载原图  
	IplImage *pSrcImage = cvLoadImage("pout.jpg", CV_LOAD_IMAGE_UNCHANGED);  

	//创建输出的图像
	IplImage *pOutImage_8U_1 = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U,1);
	IplImage *pOutImage_8U_2 = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U,1);

	// 转为灰度图  
	IplImage *g_pGrayImage =  cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);  
	cvCvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY); 

	cvConvertScale(g_pGrayImage,pOutImage_8U_1,2,-55);


	double watch_pOutImage_8U_1[100];
	int i;
	for(i=0;i< 100;i++)
	{
	 watch_pOutImage_8U_1[i]=cvGet2D(pOutImage_8U_1,0,i).val[0];  
	}

	cvEqualizeHist(pOutImage_8U_1,pOutImage_8U_2);

	double watch_I1_histe[100];
	for(i=0;i<100;i++)
	{
	 watch_I1_histe[i]=cvGet2D(pOutImage_8U_2,0,i).val[0];  
	}

	cvSaveImage("pOutImage_8U_1.jpg",pOutImage_8U_1);


return 0;
}

以下是OpenCV1.x自带的函数cvEqualizeHist()的源码,位于histogram.cpp中。

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是不一样的!

详解图像直方图均衡化的原理,附自己写的MATLAB和OpenCV2.x下的直方图均衡化函数源码_第3张图片

详解图像直方图均衡化的原理,附自己写的MATLAB和OpenCV2.x下的直方图均衡化函数源码_第4张图片

下面是利用OpnCV2.x的函数equalizeHist()进行灰度直方图均衡化的C++源码:

源码中用到的图片flower3.jpg的下载链接为:https://pan.baidu.com/s/1kUEDw5x

//OpenCV版本2.4.9  
//交流QQ2487872782 

#include 
int main()
{
   cv::Mat srcImage = cv::imread("flower3.jpg");
    if( !srcImage.data ) 
      return 1;
   cv::Mat srcGray;
   cv::cvtColor(srcImage, srcGray, CV_BGR2GRAY);
   cv::imshow("srcGray", srcGray);
   // 直方图均衡化
   cv::Mat heqResult;
   cv::equalizeHist(srcGray, heqResult);
   cv::imshow("heqResult", heqResult);
     cv::waitKey(0);
   return 0;
}

运行结果如下图所示:

详解图像直方图均衡化的原理,附自己写的MATLAB和OpenCV2.x下的直方图均衡化函数源码_第5张图片

下面是自己写的不利用函数equalizeHist()进行灰度直方图均衡化的C++源码:

源码中用到的图片flower3.jpg的下载链接为:https://pan.baidu.com/s/1kUEDw5x

//OpenCV版本2.4.9  
//交流QQ2487872782 

#include 
#include 
#include 
int main()
{
   	// 图像获取及验证
  	cv::Mat srcImage = cv::imread("flower3.jpg");
  	if( !srcImage.data ) 
    return 1;
    // 转化为灰度图像
    cv::Mat srcGray;
    cvtColor(srcImage, srcGray, CV_BGR2GRAY);
    // 计算图像的直方图
    const int channels[1]={0};
    const int histSize[1]={256};
    float hranges[2]={0,255};
    const float* ranges[1]={hranges};
    cv::MatND hist;
    calcHist(&srcGray, 1, channels, cv::Mat(),
    	      hist, 1, histSize, ranges);
	float table[256];
	int nPix = srcGray.cols * srcGray.rows;
	// 建立映射表
	for (int i = 0; i < 256; i++) {	
		float temp[256];
		// 像素变换
		temp[i] = hist.at(i) / nPix * 255;
		if (i != 0) {
			// 像素累计
			table[i] = table[i -1] + temp[i];
		} else {
			table[i] = temp[i];
		}
	}
	// 通过映射进行表查找
	cv::Mat lookUpTable(cv::Size(1, 256), CV_8U);
	for (int i =0; i <256; i++) {
		lookUpTable.at(i) = 
		    static_cast(table[i]);
	}
	cv::Mat histTransResult;
	cv::LUT(srcGray, lookUpTable, histTransResult);
       // 显示图像
	cv::imshow("srcGray",srcGray);
	cv::imshow("histTransResult",histTransResult);
	cv::waitKey(0);
	return 0;
}

最后再附一个彩色直方图均衡的c++源码如下:

源码中用到的图片flower3.jpg的下载链接: https://pan.baidu.com/s/1kUEDw5x

//OpenCV版本2.4.9  
//交流QQ2487872782 

#include 
int main()
{
  // 图像获取及验证
  cv::Mat srcImage = cv::imread("flower3.jpg");
  if( !srcImage.data ) 
    return 1;
  // 存储彩色直方图及图像通道向量
  cv::Mat colorHeqImage;   
  std::vector BGR_plane;  
  // 对BGR通道进行分离
  cv::split(srcImage,BGR_plane);
  // 分别对BGR进行直方图均衡化
  for(int i=0; i < BGR_plane.size(); i++)
      cv::equalizeHist(BGR_plane[i], BGR_plane[i]);
  // 合并对应各个通道
  cv::merge(BGR_plane,colorHeqImage);
     cv::imshow("srcImage", srcImage);
  cv::imshow("colorHeqImage", colorHeqImage);
  cv::waitKey(0);
  return 0;
}

源码中对彩色直方图均衡的步骤是将RGB颜色空间分类成单个通道,然后在每个通道上进行相应直方图均衡,最后再将各个通道下的直方图均衡结果进行合并。

运行结果如下图所示:

详解图像直方图均衡化的原理,附自己写的MATLAB和OpenCV2.x下的直方图均衡化函数源码_第6张图片

图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601

你可能感兴趣的:(图像处理原理,工具,代码,图像直方图均衡化)