OpenCV学习笔记(二)—IplImage数据结构

IplImage结构准确定义如下

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;

width和height这两个变量很重要,其次是depth和nchannals.depth变量的值与矩阵中看到的对应变量不同.因为在图像中,我们往往将深度和通道数分开处理,而在矩阵中,我们往往同时表示他们.

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;
		}
	}
}

在以上的程序中,我们用指针ptr指向第y行的起始位置.接着,我们从指针中析出饱和度和高度在x维的值.因为这是一个三通道图像,所以c通道在x行的位置是3*x+c.

与CvMat的成员data相比,IpImage 和 CvMat之间的一个重要区别在于imageData.CvMat的data是联合类型,所以你必须说明需要使用指针的类型.imageData指针是字节型指针(unchar*).我们已经知道这种类型的指针是指向unchar类型的,这意味着,在图像上进行指针运算时,你可以简单的增加widthstep(也字节为单位),而不用关心实际数据类型.

当要处理的是矩阵时,必须进行偏移并进行调整,因为数据指针可能是非字节类型;当要处理的是图像时,可以直接使用偏移,因为数据指针总指向字节类型.

对ROI和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;
}

通过巧妙地使用widthStep,我们可以打到同样的效果.要做到这一点,我们创建了另一个图像头,让他的width和height的值等于interest_rect的width和height的值.我们还需要按interest_rect 起点设置图像起点(左上角或者左下角),下一步,设置子图像的widthStep与较大的interest_img相同.这样,即可在子图像中逐步步进到大图像里子区域中下一行开始处的合适位置.最后设置子图像的imageData指针指向兴趣子区域的开始,如下例所示

	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和掩码的交集区域.掩码或者模版只能在指定了其图像的函数中使用.



你可能感兴趣的:(OpenCV学习笔记(二)—IplImage数据结构)