首先解读下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