直方图均衡化

转自http://blog.csdn.net/jaych/article/details/49133493#comments

直方图均衡化原理

直方图均衡化的主要思想是直方图统计,在统计之后,根据具体的灰度值及其对应的出现概率,对图片的灰度进行拉伸,使得图像的对比度得到扩展,如下图这张经典的图像所示。


可以看到左上角的图像灰度比较集中,经过直方图均衡化之后,右下角的图像的灰度就被拉伸。

其原理是将图像中的灰度值根据其概率分布在0-255的区间。


转换后的灰度值为:


其中,sk是转变后的灰度值

rk是输入的灰度值

nj是灰度值为 j 的像素个数;

k是当前的灰度值,取值范围0到L-1(L通常为256)。

L表示灰度值可取的个数,如8bit的图像可以表示的灰度值有256,L=256.

举个例子

下图为具体图像的像素值,已知该图像的灰度值为0~7,现要求对其进行直方图均衡化。

这里我们可以得到下图所示的表,其中,L-1=7;Pr=像素个数/25;


根据转换后的灰度值,替换原先的像素值,图像变成:


PC端代码实现

根据以上原理,将其在PC端用程序实现如下:

  1. <span style="font-size:14px;">bool getHist(unsigned char *pImgData, unsigned char *pHistData,int height,int width)  
  2. {     // 此处需要对输入的指针进行非空判断,此处略。  
  3.     int grayLvl[256]={0};  
  4.     float fP[256];  
  5.     int i;  
  6.     int sum = height*width; //计算总像素个数  
  7.     for(i=0 ; i < sum ;i++)  //统计各个灰度值的像素点个数  
  8.     {  
  9.         grayLvl[pImgData[i]]++;  
  10.     }  
  11.     for(i=0; i < 256;i++) //计算每个灰度值的比重  
  12.     {  
  13.         fP[i]=(float)grayLvl[i]/(float)sum;  
  14.     }  
  15.     for(i=1; i < 256;i++)   
  16.     {  
  17.         fP[i]=fP[i-1]+fP[i];  
  18.     }  
  19.     for(i=0; i <m_ByteOfData;i++) //均衡化  
  20.     {  
  21. <span style="white-space: pre;">      </span>pHistData[i]=(unsigned char)(fP[pImgData[i]]*255.0+0.5);// 加上0.5是为了能够四舍五入取值。  
  22.     }  
  23.     return true;  
  24. }</span><span style="font-size: 18px;">  
  25. </span>  
<span style="font-size:14px;">bool getHist(unsigned char *pImgData, unsigned char *pHistData,int height,int width)
{     // 此处需要对输入的指针进行非空判断,此处略。
	int grayLvl[256]={0};
	float fP[256];
	int i;
	int sum = height*width; //计算总像素个数
	for(i=0 ; i < sum ;i++)  //统计各个灰度值的像素点个数
	{
		grayLvl[pImgData[i]]++;
	}
	for(i=0; i < 256;i++) //计算每个灰度值的比重
	{
		fP[i]=(float)grayLvl[i]/(float)sum;
	}
	for(i=1; i < 256;i++) 
	{
		fP[i]=fP[i-1]+fP[i];
	}
	for(i=0; i <m_ByteOfData;i++) //均衡化
	{
<span style="white-space: pre;">		</span>pHistData[i]=(unsigned char)(fP[pImgData[i]]*255.0+0.5);// 加上0.5是为了能够四舍五入取值。
	}
	return true;
}</span><span style="font-size: 18px;">
</span>

安卓平台移植

在安卓平台,图像处理的操作由JNI实现,基本与上面的程序一致。具体可参考《【安卓开发】JNI程序开发》

  1. <span style="font-size:14px;">void Java_com_example_imageprocess_MainActivity_getHist(JNIEnv* env,jobject thiz,jbyteArray imgData, jbyteArray histData, jlong size)  
  2. {  
  3.     jint* graylvl=(jint*)malloc(256*sizeof(jint));  
  4.     jfloat* pb=(jfloat*)malloc(256*sizeof(jfloat));  
  5.     unsigned char* grayHist=(unsigned char*)malloc(256*sizeof(unsigned char));  
  6.     unsigned char *imgData_buf=(*env)->GetByteArrayElements(env,imgData,0); //获取jni数组  
  7.     unsigned char *histData_buf = (*env)->GetByteArrayElements(env,histData, NULL);  
  8.     int i;  
  9.     unsigned char index=0;  
  10.     memset(graylvl,0,256*sizeof(jint));  
  11.     for(i=0;i<size;i++)  
  12.     {  
  13.         graylvl[imgData_buf[i]]++;  
  14.     }  
  15.   
  16.     for(i=0;i<256;i++)  
  17.     {  
  18.         pb[i]=(jfloat)graylvl[i]/(jfloat)size;  
  19.     }  
  20.     grayHist[0]=(unsigned char)(pb[0]*255.0+0.5);  
  21.     for(i=1;i<256;i++)  
  22.     {  
  23.         pb[i]=pb[i-1]+pb[i];  
  24.         grayHist[i]=(unsigned char)(pb[i]*255.0+0.5);  
  25.     }  
  26.     for(i=0;i<size;i++)  
  27.     {  
  28.         histData_buf[i]=grayHist[imgData_buf[i]];  
  29.     }  
  30.     //save the data from histData_buf to histData  
  31.     (*env)->SetByteArrayRegion(env,histData, 0, size,histData_buf);  
  32.     free(graylvl);  
  33.     free(pb);  
  34.     free(grayHist);  
  35. }</span>  
<span style="font-size:14px;">void Java_com_example_imageprocess_MainActivity_getHist(JNIEnv* env,jobject thiz,jbyteArray imgData, jbyteArray histData, jlong size)
{
	jint* graylvl=(jint*)malloc(256*sizeof(jint));
	jfloat* pb=(jfloat*)malloc(256*sizeof(jfloat));
	unsigned char* grayHist=(unsigned char*)malloc(256*sizeof(unsigned char));
	unsigned char *imgData_buf=(*env)->GetByteArrayElements(env,imgData,0); //获取jni数组
	unsigned char *histData_buf = (*env)->GetByteArrayElements(env,histData, NULL);
	int i;
	unsigned char index=0;
	memset(graylvl,0,256*sizeof(jint));
	for(i=0;i<size;i++)
	{
		graylvl[imgData_buf[i]]++;
	}

	for(i=0;i<256;i++)
	{
		pb[i]=(jfloat)graylvl[i]/(jfloat)size;
	}
	grayHist[0]=(unsigned char)(pb[0]*255.0+0.5);
	for(i=1;i<256;i++)
	{
		pb[i]=pb[i-1]+pb[i];
		grayHist[i]=(unsigned char)(pb[i]*255.0+0.5);
	}
	for(i=0;i<size;i++)
	{
		histData_buf[i]=grayHist[imgData_buf[i]];
	}
	//save the data from histData_buf to histData
	(*env)->SetByteArrayRegion(env,histData, 0, size,histData_buf);
	free(graylvl);
	free(pb);
	free(grayHist);
}</span>

安卓代码优化

这里的优化是将浮点数优化成定点数,有一定的效果。
  1. <span style="font-size:14px;"><span style="font-size:18px;">void Java_com_example_imageprocess_MainActivity_getHist(JNIEnv* env,jobject thiz,jbyteArray imgData, jbyteArray histData, jlong size)  
  2. {  
  3.     jint* graylvl=(jint*)malloc(256*sizeof(jint));  
  4.     jint* graylvlPtr=graylvl;  
  5.     unsigned int* pb=(unsigned int*)malloc(256*sizeof(unsigned int));  
  6.     unsigned int *pbPtr=pb;  
  7.     unsigned char* grayHist=(unsigned char*)malloc(256*sizeof(unsigned char));  
  8.     unsigned char *grayHistPtr=grayHist+1;  
  9.     unsigned char *imgData_buf=(*env)->GetByteArrayElements(env,imgData,0);  
  10.     unsigned char *imgPtr=imgData_buf;  
  11.     unsigned char *histData_buf = (*env)->GetByteArrayElements(env,histData, NULL);  
  12.     unsigned char *histPtr=histData_buf;  
  13.     int i;  
  14.     unsigned char index=0;  
  15.     unsigned int temp = (255<<20)/size;  
  16.     memset(graylvl,0,256*sizeof(jint));  
  17.     for(i=size;i!=0;i--)  
  18.     {  
  19.         (*(graylvl+*imgPtr))++;  
  20.         imgPtr++;  
  21.     }  
  22.     for(i=256;i!=0;i--)  
  23.     {  
  24.         *pbPtr=*graylvlPtr*temp;  
  25.         pbPtr++;  
  26.         graylvlPtr++;  
  27.     }  
  28.     grayHist[0]=(pb[0]>>20);  
  29.     pbPtr=pb+1;  
  30.     for(i=255;i!=0;i--)  
  31.     {  
  32.         *pbPtr=*pbPtr+*(pbPtr-1);  
  33.         *grayHistPtr=(*pbPtr)>>20;  
  34.         pbPtr++;  
  35.         grayHistPtr++;  
  36.     }  
  37.     imgPtr=imgData_buf;  
  38.     for(i=size;i!=0;i--)  
  39.     {  
  40.         *histPtr=*(grayHist+*imgPtr);  
  41.         imgPtr++;  
  42.         histPtr++;  
  43.     }  
  44.     //save the data from histData_buf to histData  
  45.     (*env)->SetByteArrayRegion(env,histData, 0, size,histData_buf);  
  46.     free(graylvl);  
  47.     free(pb);  
  48.     free(grayHist);  
  49. }  
  50. </span></span>  
<span style="font-size:14px;"><span style="font-size:18px;">void Java_com_example_imageprocess_MainActivity_getHist(JNIEnv* env,jobject thiz,jbyteArray imgData, jbyteArray histData, jlong size)
{
	jint* graylvl=(jint*)malloc(256*sizeof(jint));
	jint* graylvlPtr=graylvl;
	unsigned int* pb=(unsigned int*)malloc(256*sizeof(unsigned int));
	unsigned int *pbPtr=pb;
	unsigned char* grayHist=(unsigned char*)malloc(256*sizeof(unsigned char));
	unsigned char *grayHistPtr=grayHist+1;
	unsigned char *imgData_buf=(*env)->GetByteArrayElements(env,imgData,0);
	unsigned char *imgPtr=imgData_buf;
	unsigned char *histData_buf = (*env)->GetByteArrayElements(env,histData, NULL);
	unsigned char *histPtr=histData_buf;
	int i;
	unsigned char index=0;
	unsigned int temp = (255<<20)/size;
	memset(graylvl,0,256*sizeof(jint));
	for(i=size;i!=0;i--)
	{
		(*(graylvl+*imgPtr))++;
		imgPtr++;
	}
	for(i=256;i!=0;i--)
	{
		*pbPtr=*graylvlPtr*temp;
		pbPtr++;
		graylvlPtr++;
	}
	grayHist[0]=(pb[0]>>20);
	pbPtr=pb+1;
	for(i=255;i!=0;i--)
	{
		*pbPtr=*pbPtr+*(pbPtr-1);
		*grayHistPtr=(*pbPtr)>>20;
		pbPtr++;
		grayHistPtr++;
	}
	imgPtr=imgData_buf;
	for(i=size;i!=0;i--)
	{
		*histPtr=*(grayHist+*imgPtr);
		imgPtr++;
		histPtr++;
	}
	//save the data from histData_buf to histData
	(*env)->SetByteArrayRegion(env,histData, 0, size,histData_buf);
	free(graylvl);
	free(pb);
	free(grayHist);
}
</span></span>


未优化的代码运行的时间为11ms,优化后代码运行时间为9ms。

处理的效果如下图所示,C1为未优化代码,C2为优化代码,发现C2运行后的结果存在一定误差,虽然误差较小(误差为1)。


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值