绪:在OpenCV中,widthStep是相对于IplImage*进行图像像素访问操作的;而step是相对于Mat进行图像像素访问操作的;widthStep:存储一行像素需要的字节数;step:每一行中所有元素的字节总量,单位字节;本文主要介绍:widthStep定义;widthStep在IplImage中的作用;widthStep在图像像素访问中的应用;widthStep总结;step在Mat类中的作用;step在图像像素访问中的应用;
工具/原料
OpenCV2410
方法/步骤
1、widthStep定义:OpenCV中,默认图闻赙酵枭像原点为图像左上角,img->origin=IPL_ORIGIN_TL;如果想更改图像原点坐标也可以,如img->origin=IP讣嘬铮篌L_ORIGIN_BL,将图像原点更改为左下角;一般采用默认的图像原点;OpenCV用imread或者cvLoadImage得到的图像数据都是unsignedchar类型的;IplImage结构体中的widthStep元素大小不一定等于width*nChannels,在cxcore/cxarray.cpp文件中,cvInitImageHeader对widthStep大小赋值:image->widthStep=(((image->width*image->nChannels*(image->depth&~IPL_DEPTH_SIGN)+7)/8)+align-1)&(~(align-1));其中,cxtypes.h定义IPL_DEPTH_SIGN为:#defineIPL_DEPTH_SIGN0x80000000;cxmisc.h中定义align为:#defineCV_DEFAULT_IMAGE_ROW_ALIGN4;depth取8位深度;则可计算图像的widthStep;一些图像的widthStep如下:IplImage*image_33=cvCreateImage(cvSize(3,3),8,3);IplImage*image_31=cvCreateImage(cvSize(3,3),8,1);IplImage*image_53=cvCreateImage(cvSize(5,3),8,3);IplImage*image_51=cvCreateImage(cvSize(5,3),8,1);IplImage*image_73=cvCreateImage(cvSize(7,3),8,3);IplImage*image_71=cvCreateImage(cvSize(7,3),8,1);printf("%d,%d,%d,%d,%d,%d",image_33->widthStep,image_31->widthStep,image_53->widthStep,image_51->widthStep,image_73->widthStep,image_71->widthStep);运行结果为:12,4,16,8,24,8。因此,OpenCV分配的内存按4字节对齐,与上述计算结果相符,如宽度为通道数为3的图像,每一行需要的实际内存长度为3*3,为了内存对齐,OpenCV会在每行末尾自动补上3个字节的内存,内存初始化都为0,所以widthStep变为了12。
2、widthStep在IplImage*中的作用:跗柿椁焚如下:typedefstruct_IplImage{intnSize;/*霜杼厮贿sizeof(IplImage)*/intID;/*version(=0)*/intnChannels;/*MostofOpenCVfunctionssupport1,2,3or4channels*/intalphaChannel;/*IgnoredbyOpenCV*/intdepth;/*Pixeldepthinbits:IPL_DEPTH_8U,IPL_DEPTH_8S,IPL_DEPTH_16S,IPL_DEPTH_32S,IPL_DEPTH_32FandIPL_DEPTH_64Faresupported.*/charcolorModel[4];/*IgnoredbyOpenCV*/charchannelSeq[4];/*ditto*/intdataOrder;/*0-interleavedcolorchannels,1-separatecolorchannels.cvCreateImagecanonlycreateinterleavedimages*/intorigin;/*0-top-leftorigin,1-bottom-leftorigin(Windowsbitmapsstyle).*/intalign;/*Alignmentofimagerows(4or8).OpenCVignoresitanduseswidthStepinstead.*/intwidth;/*Imagewidthinpixels.*/intheight;/*Imageheightinpixels.*/struct_IplROI*roi;/*ImageROI.IfNULL,thewholeimageisselected.*/struct_IplImage*maskROI;/*MustbeNULL.*/void*imageId;/*""*/struct_IplTileInfo*tileInfo;/*""*/intimageSize;/*Imagedatasizeinbytes(==image->height*image->widthStepincaseofinterleaveddata)*/char*imageData;/*Pointertoalignedimagedata.*/intwidthStep;/*Sizeofalignedimagerowinbytes.*/intBorderMode[4];/*IgnoredbyOpenCV.*/intBorderConst[4];/*Ditto.*/char*imageDataOrigin;/*Pointertoveryoriginofimagedata(notnecessarilyaligned)-neededforcorrectdeallocation*/}IplImage;
3、IplImage*访问图像像素:widthStep对呖分甾胗8bit,单通道,unsignedchar类型的图像I---朐袁噙岿IplImage*img:I(x,y)~((unsignedchar*)(img->imageData+img->widthStep*y))[x];对8bit,3通道,unsignedchar类型的图像I---IplImage*img:I(x,y)blue~((unsignedchar*)(img->imageData+img->widthStep*y))[x*3];I(x,y)green~((unsignedchar*)(img->imageData+img->widthStep*y))[x*3+1];I(x,y)red~((unsignedchar*)(img->imageData+img->widthStep*y))[x*3+2];或者unsignedchar*ptr=&((unsignedchar*)(img->imageData+img->widthStep*y))[x*3];I(x,y)blue~ptr[0];I(x,y)green~ptr[1];I(x,y)red~ptr[2];对32bit,1通道,float*类型的图像I---IplImage*img:I(x,y)~((float*)(img->imageData+img->widthStep*y))[x];对32bit,3通道,float*类型的图像I---IplImage*img;I(x,y)blue~((float*)(img->imageData+img->widthStep*y))[3*x];I(x,y)green~((float*)(img->imageData+img->widthStep*y))[x*3+1];I(x,y)red~((float*)(img->imageData+img->widthStep*y))[x*3+2];对64bit,3通道,double*类型的图像数据I---IplImage*img;image=cvCreateImage(cvSize(111,113),IPL_DEPTH_64F,3);这里widthstep=(111*3*sizeof(double)+3)/4*4=2664;因为111*3*sizeof(double)=2664已经正好是4的倍数了,因此无需补充字节。如果用指针访问第31行、51列的图像数据,则这个数据为double类型的,image->imageData为unsignedchar类型,因此可以转换成double,通过double指针来访问:double*data=(double*)image->imageData;doubleval=*(data+31*width+51);或者通过unsignedchar指针找到(31,51)处的地址,然后转换成double指针进行访问:unsignedchar*data=image->imageData;doubleval=*(double*)(data+31*image->widthStep+51*sizeof(double));对于IplImage,指针访问可以参考以上两种方式,其实这本质就是数据类型的转换而已。一般,访问图像像素方法,格式:对于N通道,T类型的图像,I(x,y)c~((T*)(img->imageData+img->widthStep*y))[x*N+c];
4、widthStep知识总结:width表示图像的每行像素数,widthStep表示存储一行像素需要的字节数,widthStep必须是4的倍数,从而实现字节对齐,有利于提高运算速度。如果8U单通道图像宽度为3,那么widthStep是4,加一个字节补齐。这个图像的一行需要4个字节,只使用前3个,最后一个空着。也就是一个宽3高3的图像的imageData数据大小为4*3=12字节。【注】:不同数据类型长度的图像,widthStep也不相同;widthStep的值的计算有两种情况:当(width*3)%4=0,这时width*3=widthStep;当(width*3)%4!=0,此时widthStep=(width/4+1)*3。Mat的数据并不是字节对齐的;直接将cv::Mat转换为IplImage类型,并不会将字节对齐,只是加了个文件头而已;因此需要如下操作:
5、BYTE*与IplImage*之间的转换:IplImage*iplImage:opencv中图像数据头;BYTE*data:内存中的图像数据,一般为工业相机采集的图像数据;由IplImage*转BYTE*图像数据:data=iplImage->imageDataOrigin;//未对齐的原始图像数据或者data=iplImage->imageData;//已对齐的图像数据BYTE*转IplImage*图像数据iplImage=cvCreateImageHeader(cvSize(width,height),depth,channels);cvSetData(iplImage,data,step);首先,由cvCreateImageHeader()创建IplImage图像头,设置图像尺寸、深度和通道数;然后,由cvSetData()根据BYTE*图像数据指针设置IplImage图像头的数据,其中,step指定该IplImage图像,每行占的字节数,对于1通道的IPL_DEPTH_8U图像,step可以等于width。
6、Mat访问图像像素---step:data:unsignedchar类型的指针,指向Mat数据矩阵的首地址;dims:Mat矩阵的维度;rows:Mat矩阵的行数;cols:Mat矩阵的列数;size():是一个结构体,有image.size().width==image.cols;image.size().height==image.rowschannels():Mat矩阵元素拥有的通道数;depth:度量每一个像素中每一个通道的精度,但它本身与图像的通道数无关!depth数值越大,精度越高。在Opencv中,Mat.depth()得到的是一个0~6的数字,分别代表不同的位数,如下:{CV_8U=0,CV_8S=1,CV_16U=2,CV_16S=3,CV_32S=4,CV_32F=5,CV_64F=6}elemSize:表示矩阵中每一个元素的数据大小,单位字节,如果Mat中的数据类型是CV_8UC1,那么elemSize==1;如果是CV_8UC3或CV_8SC3,那么elemSize==3;如果是CV_16UC3或者CV_16SC3,那么elemSize==6;即elemSize是以8位(一个字节)为一个单位,乘以通道数和8位的整数倍;elemSize1:表示Mat矩阵中每一个元素单个通道的数据大小,单位字节,elemSize1=elemSize/channels;step:为Mat矩阵中每一行的“步长”,以字节为基本单位,每一行中所有元素的字节总量;step1():以字节为基本单位,Mat矩阵中每一个像素的大小step1==step/elemSize1;type:Mat矩阵的类型,包含有矩阵中元素的类型、通道数信息,type的命名格式为CV_(位数)+(数据类型)+(通道数),如下:
7、Mat访问图像像素---stepstep:为Mat矩阵中每一行的“步长”,以字节为基本单位,每一行中所有元素的字节总量;经常应用在访问图像像素操作中;如下:对8bit,单通道,unsignedchar类型的图像I---Matimg:unsignedchar*pData=(unsignedchar*)img.data;I(x,y)~pData[img.step*y+x];//对8bit,3通道,unsignedchar类型的图像I---IplImage*img:I(x,y)blue~((unsignedchar*)(img.data+img.step*y))[x*3];I(x,y)green~((unsignedchar*)(img.data+img.step*y))[x*3+1];I(x,y)red~((unsignedchar*)(img.data+img.step*y))[x*3+2];对32bit,1通道,float*类型的图像I---Matimg:I(x,y)~((float*)(img.data+img.step*y)[x];对32bit,3通道,float*类型的图像I---Matimg;I(x,y)blue~((float*)(img.data+img.step*y))[3*x];I(x,y)green~((float*)(img.data+img.step*y))[x*3+1];I(x,y)red~((float*)(img.data+img.step*y))[x*3+2];对64bit,1通道,double*类型的图像I---Matimg:I(x,y)~((double*)(img.data+img.step*y)[x];对64bit,3通道,double*类型的图像数据I---Matimg;I(x,y)blue~((double*)(img.data+img.step*y))[3*x];I(x,y)green~((double*)(img.data+img.step*y))[x*3+1];I(x,y)red~((double*)(img.data+img.step*y))[x*3+2];一般,访问图像像素方法,格式:对于N通道,T类型的图像,I(x,y)c~((T*)(img.Data+img.step*y))[x*N+c];