gamma校正

首先解读下IplImage 的结构:

typedef struct _IplImage
{
int nSize; /* IplImage大小,等于width*height */
int ID; /* 版本 (=0)*/
int nChannels; /* 大多数OpenCV函数支持1,2,3 或 4 个通道 */
int alphaChannel; /* 被OpenCV忽略 */
int depth; /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U, IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支持 */
char colorModel[4]; /* 被OpenCV忽略 */
char channelSeq[4]; /* 同上 (ditto)*/
int dataOrder; /* 0 - 交叉存取颜色通道, 1 - 分开的颜色通道. cvCreateImage只能创建交叉存取图像 */
int origin; /* 0 - 顶—左结构,1 - 底—左结构 (Windows bitmaps 风格) */
int align; /* 图像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */
int width; /* 图像宽像素数 */
int height; /* 图像高像素数*/
struct _IplROI *roi; /* 图像感兴趣区域. 当该值非空只对该区域进行处理 */
struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */
void *imageId; /* 同上*/
struct _IplTileInfo *tileInfo; /*同上*/
int imageSize; /* 图像数据大小(在交叉存取格式下imageSize=image->height*image->widthStep),单位字节*/
char *imageData; /*指向排列的图像数据 */
int widthStep; /* 排列的图像行大小,以字节为单位 */
int BorderMode[4]; /* 边际结束模式, 被OpenCV忽略 */
int BorderConst[4]; /* 同上 */
char *imageDataOrigin; /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的 */
}

depth代表颜色深度,使用的是以下定义的宏,nChannels是通道数,为1,2,3或4。 
depth的宏定义: 
IPL_DEPTH_8U,无符号8bit整数(8u) 
IPL_DEPTH_8S,有符号8bit整数(8s) 
IPL_DEPTH_16S,有符号16bit整数(16s) 
IPL_DEPTH_32S,有符号32bit整数(32s) 
IPL_DEPTH_32F,32bit浮点数,单精度(32f) 
IPL_DEPTH_64F,64bit浮点数,双精度(64f)


注意,因为imageData的类型是char *,所以在利用指针读数据时,前面要进行对应的类型转换,如单通道字节型图像对应:

((uchar*)(img->imageData+i*img->widthStep))[j];

单通道浮点型图像对应:

((float*)(img->imageData+i*img->widthStep))[j];

多通道字节型图像对应:

((uchar *) (img->imageData + i*img->widthStep))[j*img->nChannels + 0] = 111;// B
((uchar *) (img->imageData + i*img->widthStep))[j*img->nChannels + 1] = 112;// G
((uchar *) (img->imageData + i*img->widthStep))[j*img->nChannels + 2] = 113;// R

然后直接上公式f(I)=I^gamma,I为原图像素值,f(I)为gamma校正后的像素值。

gamma<1在高灰度值区域内,动态范围变小,图像对比度降低,图像整体灰度值变大,显得亮一些;gamma>1在低灰度值区域内,动态范围变小,图像对比度降低,图像整体灰度值变小,变得暗淡。


gamma校正的实现代码如下:

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <time.h>

#define Direct

IplImage *srcImg = NULL, *dstImg = NULL;
float gGamma = 2.2;

void GammaCorrect(IplImage* src,IplImage* dst,float gamma);

int main(int argc, char* argv[])
{
	clock_t start,end;
	// 以灰度图为例
	srcImg = cvLoadImage("D:\\Documents\\Visual Studio 2012\\Projects\\opencvtest\\opencvtest\\lena.jpg",0);
	dstImg = cvCreateImage(cvGetSize(srcImg), 8, 1);

	cvNamedWindow("src");
	cvShowImage("src", srcImg);

	start = clock();
	GammaCorrect(srcImg,dstImg,gGamma);
	end = clock();
	printf("time = %2.4fs\n",(double)(end-start)/CLK_TCK);

	cvNamedWindow("dst");
	cvShowImage("dst", dstImg);
	cvWaitKey(0);

	cvReleaseImage(&srcImg);
	cvReleaseImage(&dstImg);
	cvDestroyWindow("src");
	cvDestroyWindow("dst");

	return 0;
}

void GammaCorrect(IplImage* src,IplImage* dst,float gamma)
{
	int height=src->height;  
	int width=src->width;  

#ifdef Get2D
	CvScalar s;
#endif
	float f;
	for(int i=0; i < height; i++)
	{  
		//p为当前行首指针
		unsigned char* p1=(unsigned char*)src->imageData + src->widthStep * i;
		unsigned char* p2=(unsigned char*)dst->imageData + dst->widthStep * i;

		for(int j = 0; j < width; j++) 
		{  
#ifdef Get2D
			f = (cvGet2D(src,i,j).val[0]+0.5)/256;
			f = powf(f,gamma);
			s.val[0] = (int)(f*256-0.5);
			cvSet2D(dst,i,j,s);
#endif

#ifdef Direct
			f = ((*p1++)+0.5)/256;
			f = powf(f,gamma);
			*p2++ = (int)(f*256-0.5);
#endif

		}  
	}  
}

采用#define Direct 的像素值获取方式,耗时0.022ms,采用#define Get2D的像素值获取方式,耗时0.1ms。使用cvGet2D的方式会较为耗时。

gamma效果图如下:



若采用查表法的方式,不用对每个像素点都做gamma校正的计算,实际只计算了256次gamma校正,用查表的方式完成映射节省了计算资源,可以减少gamma校正的计算复杂度。

查表法代码如下:

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <time.h>

IplImage *srcImg = NULL, *dstImg = NULL;
float gGamma = 2.2;
int gGammaLUT[256];

// 建立gamma校正查找表
void BuildTable(float gamma);
// 用查表法做gamma变换
void GammaCorrect(IplImage* src,IplImage* dst);

int main(int argc, char* argv[])
{
	clock_t start,end;
	// 以灰度图为例
	srcImg = cvLoadImage("D:\\Documents\\Visual Studio 2012\\Projects\\opencvtest\\opencvtest\\lena.jpg",0);
	dstImg = cvCreateImage(cvGetSize(srcImg), 8, 1);

	cvNamedWindow("src");
	cvShowImage("src", srcImg);

	start = clock();
	BuildTable(gGamma);
	GammaCorrect(srcImg,dstImg);
	end = clock();
	printf("time = %2.4fs\n",(double)(end-start)/CLK_TCK);

	cvNamedWindow("dst");
	cvShowImage("dst", dstImg);
	cvWaitKey(0);

	cvReleaseImage(&srcImg);
	cvReleaseImage(&dstImg);
	cvDestroyWindow("src");
	cvDestroyWindow("dst");

	return 0;
}

//根据gamma的值建立查找表
void BuildTable(float gamma)
{
	int i;
	float f;
	for(i=0;i<256;i++)
	{
		//预补偿,归一化,还原
		f = (i+0.5)/256;
		f = powf(f,gamma);
		gGammaLUT[i] = (int)(f*256-0.5);
	}
}

void GammaCorrect(IplImage* src,IplImage* dst)
{
	int height=src->height;  
	int width=src->width;  

	for(int i=0; i < height; i++)
	{  
		//p为当前行首指针
		unsigned char* p1=(unsigned char*)src->imageData + src->widthStep * i;
		unsigned char* p2=(unsigned char*)dst->imageData + dst->widthStep * i;
		for(int j = 0; j < width; j++) 
		{  
			*p2++ = gGammaLUT[(int)(*p1++)];

		}  
	}  
}


 在采用#define Direct的像素值获取方式,耗时0.001ms


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值