width是图像宽度,可为任意值;widthstep是行字节数,应该是4的倍数,不一定等于width,nchannels为图像通道数。

#define WIDTHBYTES(bits) (((bits)+31)/32*4) 看看这个公式你就明白了。

 

对ROI和widthStep的补充

ROI和widthStep在实际工作中有很重要的作用,在很多情况下,使用它们会提高计算机视觉代码的执行速度。这是因为它们允许对图像的某一小部分进行操作,而不是对整个图像进行运算。在OpenCV中 ,普遍支持ROI和widthStep,函数的操作被限于感兴趣区域。要设置或取消ROI,就要使用cvSetImageROI()和cvResetImageROI()函数。如果想设置ROI,可以使用函数cvSetImageROI(),并为其传递一个图像指针和矩形。而取消ROI,只需要为函数cvResetImageROI()传递一个图像指针。

  1. void cvSetImageROI( IplImage* p_w_picpath, CvRect rect );
  2. void cvResetImageROI( IplImage* p_w_picpath );

为了解释ROI的用法,我们假设要加载一幅图像并修改一些区域,如例3-12的代码,读取了一幅图像,并设置了想要的ROI的x,y,width和height的值,最后将ROI区域中像素都加上一个整数。本例程中通过内联的cvRect()构造函数设置ROI。通过cvResetImageROI()函数释放ROI是非常重要的,否则,将忠实地只显示ROI区域。

例3-12:用p_w_picpathROI来增加某范围的像素

 

  1. // roi_add <p_w_picpath> <x> <y> <width> <height> <add> 
  2. #include <cv.h> 
  3. #include <highgui.h> 
  4. int main(int argc, char** argv) 
  5.      IplImage* src; 
  6.      if( argc == 7 && ((cvLoadImage(argv[1],1)) != 0 )) < /span>
  7.      { 
  8.          int x = atoi(argv[2]); 
  9.          int y = atoi(argv[3]); 
  10.          int width = atoi(argv[4]); 
  11.          int height = atoi(argv[5]); 
  12.          int add = atoi(argv[6]); 
  13.          cvSetImage ROI(src, cvRect(x,y,width,height)); 
  14.          cvAddS(src, cvScalar(add),src); 
  15.          cvResetImageROI(src); 
  16.          cvNamedWindow( "Roi_Add", 1 ); 
  17.          cvShowImage( "Roi_Add", src ); 
  18.          cvWaitKey(); 
  19.      } 
  20.      return 0; 

使用例3-12中的代码把ROI集中于一张猫的脸部,并将其蓝色通道增加150后的效果.

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

例3-13:利用其他widthStep方法把interest_img的所有像素值增加1

  1. // Assuming IplImage *interest_img; and 
  2. //   CvRect interest_rect; 
  3. //   Use widthStep to get a region of interest 
  4. // 
  5. // (Alternate method) 
  6. // 
  7. IplImage *sub_img = cvCreateImageHeader( 
  8.     cvSize( 
  9.        interest_rect.width, 
  10.        interest_rect.height 
  11.     ), 
  12.     interest_img->depth, 
  13.     interest_img->nChannels 
  14. ); 
  15. sub_img->origin = interest_img->origin; 
  16. sub_img->widthStep = interest_img->widthStep; 
  17. sub_img->p_w_picpathData = interest_img->p_w_picpathData + 
  18.     interest_rect.y * interest_img->widthStep    + 
  19.     interest_rect.x * interest_img->nChannels; 
  20. cvAddS( sub_img, cvScalar(1), sub_img ); 
  21. cvReleaseImageHeader(&sub_img); 

看起来设置和重置ROI更方便一些,为什么还要使用widthStep?原因在于有些时候在处理的过程中,想在操作过程中设置和保持一幅图像的多个子区域处于活动状态,但是ROI只能串行处理并且必须不断地设置和重置。

最后,我们要在此提到一个词-- 掩码或模板,在代码示例中cvAddS()函数允许第四个参数默认值为空:const CvArr* mask=NULL。这是一个8位单通道数组,它允许把操作限制到任意形状的非0像素的掩码区,如果ROI随着掩码或模板变化,进程将会被限制在ROI和掩码的交集区域。掩码或模板只能在指定了其图像的函数中使用。