typedef struct _IplImage { int nSize; /* sizeof(IplImage) */ int ID; /* version (=0)*/ int nChannels; /* Most of OpenCV functions support 1,2,3 or 4 channels */ int alphaChannel; /* Ignored by OpenCV */ int depth; /* Pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported. */ char colorModel[4]; /* Ignored by OpenCV */ char channelSeq[4]; /* ditto */ int dataOrder; /* 0 - interleaved color channels, 1 - separate color channels. cvCreateImage can only create interleaved images */ int origin; /* 0 - top-left origin, 1 - bottom-left origin (Windows bitmaps style). */ int align; /* Alignment of image rows (4 or 8). OpenCV ignores it and uses widthStep instead. */ int width; /* Image width in pixels. */ int height; /* Image height in pixels. */ struct _IplROI *roi; /* Image ROI. If NULL, the whole image is selected. */ struct _IplImage *maskROI; /* Must be NULL. */ void *imageId; /* " " */ struct _IplTileInfo *tileInfo; /* " " */ int imageSize; /* Image data size in bytes (==image->height*image->widthStep in case of interleaved data)*/ char *imageData; /* Pointer to aligned image data. */ int widthStep; /* Size of aligned image row in bytes. */ int BorderMode[4]; /* Ignored by OpenCV. */ int BorderConst[4]; /* Ditto. */ char *imageDataOrigin; /* Pointer to very origin of image data (not necessarily aligned) - needed for correct deallocation */ } IplImage;
depth可用取值是:
IPL_DEPTH_8U,无符号8位整数(8u)
IPL_DEPTH_8S,有符号8位整数(8s)
IPL_DEPTH_16S,有符号16位整数(16s)
IPL_DEPTH_32S,有符号32位整数(32s)
IPL_DEPTH_32F ,32位浮点数单精度(32f)
IPL_DEPTH_64F .64为浮点数双精度(64f)
通道数nChannels可取的值是1,2,3或4
随后两个重要成员是Origin和dataOrder.
origin变量可以有两种取值:IPL_ORIGIN_TL和IPL_ORIGIN_BL,分别设置坐标原点的位置置于图像左上角或者左下角
在计算机视觉领域,一个重要的错误来源就是原点位置的定义不统一,具体而言,图像的来源操作系统,编码解码器和存储格式等因素都可以影响图像坐标原点的选取,.举例来说,你或许认为自己正在从图像上面的脸部部分取样,但实际上却是在图像下方的裙子处取样,避免此类现象反生的最好方法是在最开始的时候检测一下系统,所在操作图像块的地方画点东西试试.
dataOrder的取值可以是IPL_DATA_ORDER_PIXEL或IPL_DATA_ORDER_PLANE.前者指明数据是将像素点不同通道的值交错排列在一起(这是常用的交错排列方式),后者是把所有像素同通道值排在一起,形成通道平面,再把平面排列起来.
参数widthStep与CvMat中step参数类似,包括相邻的同列点之间的字节数.仅凭width是不能计算这个值的,因为为了处理过程更高效,每行都会用固定的字节数来对齐;因此在第i行末 和第i+1行开始处可能会有些冗余字节.
参数imageData包含一个指向第一行图像的指针.如果图像中有些独立的平面 那么把他们作为单独的图像连续摆放,总行数为height和nChannels的乘积.但通常情况下,他们是交错的,使得行数等于高度,而且每一行都有序地包含交错的通道.
最后还有一个重要的参数--感兴趣的区域(ROI),实际上他是另一个IPL/IPP结构IplRoI的实例.IPlROI包括xOffset,yOffet,height和width和coi成员变量其中COI代表Channel of inter(感兴趣的通道).ROI的思想是:一旦设定了ROI,通道作用于整幅图像的函数变只会对ROI锁表示的子图像进行操作.如果IPImage变量中设置了ROI,则所有的OpenCV函数就会使用该ROI变量.如果COI被设置成飞0值,则对该图像的操作就只能作用于被指定的通道上了.不幸的是,许多OpenCV函数都忽略参数COI.
虽然OpenCV中有很多优化函数帮助我们完成大多数的 图像处理任务,但是还有一些任务,库中没有预先包装好的函数可以帮助我们解决.例如,如果我们有一个三通道HSV图像,在色度保持不变的情况下,我们要设置每个点的饱和度和高度为255,我们可以使用指针遍历图像,
void statrate_sv( IplImage* img) { for(int y = 0 ; y < img->height; y++) { uchar* ptr = (uchar*)(img->imageData + y * img->widthStep); for (int x= 0;x < img ->width ;x++) { ptr[3*x + 1] = 255; ptr[3*x + 2] = 255; } } }
与CvMat的成员data相比,IpImage 和 CvMat之间的一个重要区别在于imageData.CvMat的data是联合类型,所以你必须说明需要使用指针的类型.imageData指针是字节型指针(unchar*).我们已经知道这种类型的指针是指向unchar类型的,这意味着,在图像上进行指针运算时,你可以简单的增加widthstep(也字节为单位),而不用关心实际数据类型.
当要处理的是矩阵时,必须进行偏移并进行调整,因为数据指针可能是非字节类型;当要处理的是图像时,可以直接使用偏移,因为数据指针总指向字节类型.
在OpenCV中,普遍支持ROI和widthStep,函数的操作被限制于感兴趣的区域,要设置或者取消ROI,就要使用cvSetImageROI()和cvResetImage()函数.如过想设置ROI,可以使用函数cvSetImageROI(),并为其传递一个图像指针和矩形.而取消ROI,只需要为函数cvResetImageROI()传递一个图像指针
void cvSetImageROI( IplImage* image, CvRect rect );
void cvResetImageROI( IplImage* image );
如例3.12,读取一幅图像,并设置了想要的ROI的x,y,width,和height的值,最后将ROI区域都加上了一个整数,本例程中,通过内联的cvRrect()构造函数设置ROI.通过cvResetIMageROI()函数释放ROI是非常重要的,否则将忠实的显示ROI区域
//例3.12 用imageROI来增加某范围的像素 #include "stdafx.h" #include <cv.h> #include <highgui.h> int _tmain(int argc, _TCHAR* argv[]) { IplImage* src; src = cvLoadImage("C:/Users/Administrator/Desktop/lena.bmp"); if (src != nullptr) { int x = 20; int y = 20; int width = 150; int height = 150; int add = 150; cvSetImageROI(src,cvRect(x,y,width,height)); cvAddS(src,cvScalar(add),src); cvResetImageROI(src); cvNamedWindow("ROI_add",1); cvShowImage("ROI_add",src); cvWaitKey(); } return 0; }
IplImage *interest_img; CvRect interest_rect; IplImage* sub_img = cvCreateImageHeader(cvSize(interest_rect.width,interest_rect.height), interest_img->depth,interest_img->nChannels); sub_img->origin = interest_img->origin; sub_img->widthStep = interest_img->widthStep; sub_img->imageData = interest_img->imageData + interest_rect.y * interest_img->widthStep + interest_rect.x * interest_img->nChannels; cvAddS(sub_img,cvScalar(1),sub_img); cvReleaseImageHeader(&sub_img);看起来设置和重置ROI更方便一些,为什么还要使用widthStep? 原因是有些时候在处理的过程中,想在操作过程中设置和保持一副图像的多个子区域处于活动状态,但是ROI只能串行处理并且必须不断的设置和重置.
掩码和模版, 在代码示例中cvAddS()函数允许第四个参数默认值为空:const CvArr* mask = NULL.这是一个8位单通道数组,它允许把操作限制到任意形状的非0像素和掩码区,如果ROI随着掩码或者模版变化,进程将会被限制在ROI和掩码的交集区域.掩码或者模版只能在指定了其图像的函数中使用.