OpenCV中ROI

OpenCV中ROI

ROI(region of interest),也就是感兴趣区域,如果你设置了图像了ROI,那么在使用OpenCV的函数的时候,会只对ROI区域操作,其他区域忽略。举个例子:

原图:

OpenCV中ROI

现在要将这幅图的蓝色通道加150

如果没有设置ROI,则函数作用在这个图像上,整个图像的所有像素的蓝色通道都会被加上150


OpenCV中ROI

但是现在我设置了ROI,

Rect  ROI(0,100,width/2,height/2);

则函数只会作用在我设置的ROI区域,其他区域保持不变。效果如下图

OpenCV中ROI


在OpenCV1.0中,我们看一下IplImage的数据结构

[cpp] view plain copy
  1. typedef struct _IplImage  
  2. {  
  3.     int  nSize;             /* sizeof(IplImage) */  
  4.     int  ID;                /* version (=0)*/  
  5.     int  nChannels;         /* Most of OpenCV functions support 1,2,3 or 4 channels */  
  6.     int  alphaChannel;      /* Ignored by OpenCV */  
  7.     int  depth;             /* Pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S, 
  8.                                IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported.  */  
  9.     char colorModel[4];     /* Ignored by OpenCV */  
  10.     char channelSeq[4];     /* ditto */  
  11.     int  dataOrder;         /* 0 - interleaved color channels, 1 - separate color channels. 
  12.                                cvCreateImage can only create interleaved images */  
  13.     int  origin;            /* 0 - top-left origin, 
  14.                                1 - bottom-left origin (Windows bitmaps style).  */  
  15.     int  align;             /* Alignment of image rows (4 or 8). 
  16.                                OpenCV ignores it and uses widthStep instead.    */  
  17.     int  width;             /* Image width in pixels.                           */  
  18.     int  height;            /* Image height in pixels.                          */  
  19.     struct _IplROI *roi;    /* Image ROI. If NULL, the whole image is selected. */  
  20.     struct _IplImage *maskROI;      /* Must be NULL. */  
  21.     void  *imageId;                 /* "           " */  
  22.     struct _IplTileInfo *tileInfo;  /* "           " */  
  23.     int  imageSize;         /* Image data size in bytes 
  24.                                (==image->height*image->widthStep 
  25.                                in case of interleaved data)*/  
  26.     char *imageData;        /* Pointer to aligned image data.         */  
  27.     int  widthStep;         /* Size of aligned image row in bytes.    */  
  28.     int  BorderMode[4];     /* Ignored by OpenCV.                     */  
  29.     int  BorderConst[4];    /* Ditto.                                 */  
  30.     char *imageDataOrigin;  /* Pointer to very origin of image data 
  31.                                (not necessarily aligned) - 
  32.                                needed for correct deallocation */  
  33. }  
  34. IplImage;  


ROI的实现是通过结构体

[cpp] view plain copy
  1. struct _IplROI *roi;  
来实现的,其中定义如下
[cpp] view plain copy
  1. typedef struct _IplROI  
[cpp] view plain copy
  1. {  
  2.     int  coi; /* 0 - no COI (all channels are selected), 1 - 0th channel is selected ...*/  
  3.     int  xOffset;  
  4.     int  yOffset;  
  5.     int  width;  
  6.     int  height;  
  7. }  
  8. IplROI;  


示例代码:

[cpp] view plain copy
  1. <span style="white-space:pre">    </span>IplImage *image = cvLoadImage(LENA_COLOR, -1);  
  2.       
  3.     //输出  
  4.     cout<<"width:"<<image->width<<endl;  
  5.     cout<<"height:"<<image->height<<endl;  
  6.     cout<<"widthStep:"<<image->widthStep<<endl;  
  7.       
  8.     //设置ROI  
  9.     cvSetImageROI(image, cvRect(0, 100, image->width / 2, image->height / 2));//设置ROI  
  10.     cvAddS(image, cvScalar(150), image);//将蓝色通道增加150  
  11.       
  12.     //输出  
  13.     cout<<"width:"<<image->width<<endl;  
  14.     cout<<"height:"<<image->height<<endl;  
  15.     cout<<"widthStep:"<<image->widthStep<<endl;  
  16.   
  17.     cvResetImageROI(image);//★释放ROI,否则,只会显示ROI区域  
  18.     cvNamedWindow("ROI", 1);  
  19.     cvShowImage("ROI", image);  
  20.     cvWaitKey(0);  

图片采用了512*512的彩色Lena图,实验结果:

OpenCV中ROI


OpenCV中ROI



在OpenCV2.0中,ROI的实现方式就不同了

大致有两种方式

1.通过重载运算符(其实也是通过构造函数实现的)

Mat  B= A(Range::all(), Range(1, 3));

Mat  B=A(Rect(0,0,100,100));

2.通过构造函数

Mat(const Mat& m, const Range&rowRange, const Range& colRange=Range::all());

Mat(const Mat& m, const Rect& roi);

大家可以参考1.0的程序写出2.0的版本,实现也非常简单。

下面我想说的是,通过下面的方式创建ROI

Mat  B= A(Range::all(), Range(1, 3));

Mat  B=A(Rect(0,0,100,100));

则B和A是共享数据内存的,B使用的还是A的内存,只是B的Mat结构体中一些参数被修改了。

有的时候我们想拷贝数据,创建一个独立的ROI子图像,可以通过下面程序中的方式。

下面直接给出一个简单的示例程序:

[cpp] view plain copy
  1. void Learn_ROI_Function()  
  2. {  
  3.     //创建一个简单的图像  
  4.     Mat simpleImage=(Mat_<int>(3,3)<<1,2,1,  
  5.                     1,2,1,  
  6.                     1,2,1  
  7.                     ;  
  8.   
  9.   
  10.     //////////////////////////////不拷贝数据////////////////////////////////////////////  
  11.     //设置ROI,!注意是共享内存的方式  
  12.     //方式1:Mat  B = A(Range::all(), Range(1, 3));  
  13.     //方式2: Mat  B= A(Rect(0,0,100,100));  
  14.     Mat ROI=simpleImage(Rect(1,1,2,2));  
  15.   
  16.     //行,列和每行的通道数(步长)  
  17.     cout<<"/////////////Not Copy Data///////////////////"<<endl;  
  18.     cout<<"rows:"<<ROI.rows<<endl;  
  19.     cout<<"cols:"<<ROI.cols<<endl;  
  20.     int widthStep=ROI.step1(0);  
  21.     cout<<"widthStep:"<<widthStep<<endl;//3:每行通道数没变  
  22.   
  23.     //遍历ROI,注意内存模型  
  24.     //输出2,1,1,2  
  25.     cout<<"data:"<<" ";  
  26.     int *data=(int *)ROI.data;  
  27.     for (int i=0;i<=3;++i)  
  28.     {  
  29.         cout<<data[i]<<",";  
  30.     }  
  31.   
  32.     //////////////////////////////////拷贝数据,创建独立ROI子图像///////////////////////////  
  33.     //采用数据拷贝方式创建独立的ROI图片  
  34.     IplImage iplImage=ROI;  
  35.     Mat copyedROI(&iplImage,true);  
  36.   
  37.     //行,列和每行的通道数(步长)  
  38.     cout<<"\n\n/////////////Copy Data///////////////////"<<endl;  
  39.     cout<<"rows:"<<copyedROI.rows<<endl;  
  40.     cout<<"cols:"<<copyedROI.cols<<endl;  
  41.     widthStep=copyedROI.step1(0);  
  42.     cout<<"widthStep:"<<widthStep<<endl;//3:每行通道数没变  
  43.       
  44.     //遍历ROI,注意内存模型,输出2,1,2,1  
[cpp] view plain copy
  1. <span style="white-space:pre">    </span>cout<<"data:"<<" ";  
[cpp] view plain copy
  1.     int *data2=(int *)copyedROI.data;  
  2.     for (int i=0;i<=3;++i)  
  3.     {  
  4.         cout<<data2[i]<<",";  
  5.     }  
  6.     cout<<endl;  
  7.   
  8. }  

结果:

OpenCV中ROI


内存示意图

没有拷贝数据


OpenCV中ROI

拷贝数据之后

OpenCV中ROI

没有拷贝之前,ROI实际上共享simpleImage的内存,只是改变了一下ROI的Mat中的一些成员变量:

data,rows,cols,从内存示意图中可以看出。

这里要注意,ROI的步长step1(0)并没有改变,还是与原图像一样,这样支持ROI的OpenCV函数遍历图像的时候才不致出错.因为遍历图像的时候,都是使用步长控制的。


ROI中有效数据在内存中是不连续的,如果你采用上面连续访问形式,会出错的,得到的数据位2,1,1,2,而我们需要的实际数据应该是2,1,2,1。如果需要访问,可以采用OpenCV中at()方法,或者使用步长控制指针。读者可以自己实现一下,也比较简单。


拷贝数据后,copyedROI实际上是一副独立的图片.有效数据在内存中是连续的,所以输出的数据是对的。


ROI有个非常实用的功能,就是实现滑动窗口,下篇博客就来说说滑动窗口问题。