[OpenCv]通过对IplImage 源码分析观察widthStep和 width之间的区别

一、疑问

    在这几天查看OpenCv帮助文档的时候,运行其中的一个C++例程,主要是对IplImage的操作,结果没有问题,但是对其中的过程中widthStepwidth之间的关系产生了兴趣,先看例程代码

////////////////////////////////////////////////////////////////////////
//
// hello-world.cpp
//
// 该程序从文件中读入一幅图像,将之反色,然后显示出来. 
//
////////////////////////////////////////////////////////////////////////
#include 
#include 
#include 
#include 
#include  

int main(int argc, char *argv[])
{
  IplImage* img = 0; 
  int height,width,step,channels,depth;
  uchar *data;
  int i,j,k; 

  if(argc<2){
    printf("Usage: main \n\7");
    exit(0);
  } 

  // load an image  
  img=cvLoadImage(argv[1]);
  if(!img){
    printf("Could not load image file: %s\n",argv[1]);
    exit(0);
  } 

  // get the image data
  height    = img->height;
  width     = img->width;
  step      = (img->widthStep);
  channels  = img->nChannels;
  depth     = img->depth;
  data      = (uchar*)img->imageData;
  printf("bef test\n");
  printf("Processing a %dx%d image with %d channels , step = %d depth=    
%d\n",height,width,channels,step,depth); 

  // create a window
  cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE); 
  cvMoveWindow("mainWin", 100, 100); 

  // invert the image
  // 相当于 cvNot(img);
  // IplImage *pDstImg = cvCreateImage(cvGetSize(img),img->depth,img->nChannels);
  // cvNot(img, pDstImg);
  for(i=0;ifor(j=0;jfor(k=0;k//printf("%d-%d-%d value= %d\t",i,j,k,data[i*step+j*channels+k]);
                data[i*step+j*channels+k]=255-data[i*step+j*channels+k]; 
                //printf("%d-%d-%d value= %d\n",i,j,k,data[i*step+j*channels+k]);
            }
        }
  }

  // show the image
  cvShowImage("mainWin", img ); 

  printf("aft test2\n");
  // wait for a key
  cvWaitKey(0); 

  // release the image
  cvReleaseImage(&img );
  printf("aft test3\n");
  return 0;

}



正常图片如图 1
[OpenCv]通过对IplImage 源码分析观察widthStep和 width之间的区别_第1张图片



编译运行,得到反色之后的cat如图 2
[OpenCv]通过对IplImage 源码分析观察widthStep和 width之间的区别_第2张图片

结果当然是正确的了,但是看图 2 中的打印出来的结果可以知道,图片的width =375,channels = 3,根据channel的定义(每个像素中的保存信息的字节的个数),这个Matrix的每一行的应该有 width*channels = 1125,但是运行的结果是widthStep = 1128,由此我们应该可以猜测出

      width*channels   !=   widthStep

那么widthStep 和 width 什么关系呢???

二、解决疑问

   最好的办法当然是查看IplImage实现的源代码了,参考博客 找到了在OpenCv-2.4.10版本中的位置:

C:\opencv2.4.10\opencv\sources\modules\core\src\array.cpp



代码中具体内容如图 3

[OpenCv]通过对IplImage 源码分析观察widthStep和 width之间的区别_第3张图片

分析此问题最重要代码如下:

image->widthStep = (((image->width * image->nChannels *(image->depth &
~IPL_DEPTH_SIGN) + 7)/8)+ align - 1) & (~(align - 1));

在该公式中,表明了widthStep、width、和nChannels之间的关系

为了方便查看:

A= (((image->width * image->nChannels *(image->depth &
~IPL_DEPTH_SIGN) + 7)/8);
B = align -1;

widthStep = (A+B)&(~B); …… (1)

(1)式主要的作用就是将 widthStep调整为align的倍数,而这里align代表的意思就是OpenCV分配的内存对齐的单位,在OpenCv中一般为4或者8;
所以当align = 4时:
eg1:
如果 A= 9 ,通过(1)式的计算可以得到
widthStep = 12; (调整为了4的倍数);

eg2:
对于我们上面的运行结果分析,
width*channels = 1125
经过(1)式的调整,就可以得到最后结果为 1128,也就是最后打印出来的结果。

到此,我们的疑惑也就解决了,有兴趣的同学也可以去看看源码!


三、总结

   其实,这里这么处理也不无道理,当每个Step被调整为align的倍数,比实际需求多出来的位置全部置0,这样就可以方便对其中数据的管理,比如这N*align中的数据保存的都是这个矩阵中某一行的数据,而不会出现某个align中的数据既有第i行的数据,又有第i+1行的数据。

你可能感兴趣的:(opencv)