ROI(region of interest),也就是感兴趣区域,如果你设置了图像了ROI,那么在使用OpenCV的函数的时候,会只对ROI区域操作,其他区域忽略。举个例子:
原图:
现在要将这幅图的蓝色通道加150
如果没有设置ROI,则函数作用在这个图像上,整个图像的所有像素的蓝色通道都会被加上150
但是现在我设置了ROI,
Rect ROI(0,100,width/2,height/2);
则函数只会作用在我设置的ROI区域,其他区域保持不变。效果如下图
在OpenCV1.0中,我们看一下IplImage的数据结构
- typedef struct _IplImage
- {
- int nSize;
- int ID;
- int nChannels;
- int alphaChannel;
- int depth;
-
- char colorModel[4];
- char channelSeq[4];
- int dataOrder;
-
- int origin;
-
- int align;
-
- int width;
- int height;
- struct _IplROI *roi;
- struct _IplImage *maskROI;
- void *imageId;
- struct _IplTileInfo *tileInfo;
- int imageSize;
-
-
- char *imageData;
- int widthStep;
- int BorderMode[4];
- int BorderConst[4];
- char *imageDataOrigin;
-
-
- }
- IplImage;
ROI的实现是通过结构体
来实现的,其中定义如下
- {
- int coi;
- int xOffset;
- int yOffset;
- int width;
- int height;
- }
- IplROI;
示例代码:
- <span style="white-space:pre"> </span>IplImage *image = cvLoadImage(LENA_COLOR, -1);
-
-
- cout<<"width:"<<image->width<<endl;
- cout<<"height:"<<image->height<<endl;
- cout<<"widthStep:"<<image->widthStep<<endl;
-
-
- cvSetImageROI(image, cvRect(0, 100, image->width / 2, image->height / 2));
- cvAddS(image, cvScalar(150), image);
-
-
- cout<<"width:"<<image->width<<endl;
- cout<<"height:"<<image->height<<endl;
- cout<<"widthStep:"<<image->widthStep<<endl;
-
- cvResetImageROI(image);
- cvNamedWindow("ROI", 1);
- cvShowImage("ROI", image);
- cvWaitKey(0);
图片采用了512*512的彩色Lena图,实验结果:
在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子图像,可以通过下面程序中的方式。
下面直接给出一个简单的示例程序:
- void Learn_ROI_Function()
- {
-
- Mat simpleImage=(Mat_<int>(3,3)<<1,2,1,
- 1,2,1,
- 1,2,1
- ;
-
-
-
-
-
-
- Mat ROI=simpleImage(Rect(1,1,2,2));
-
-
- cout<<"/Not Copy Data///"<<endl;
- cout<<"rows:"<<ROI.rows<<endl;
- cout<<"cols:"<<ROI.cols<<endl;
- int widthStep=ROI.step1(0);
- cout<<"widthStep:"<<widthStep<<endl;
-
-
-
- cout<<"data:"<<" ";
- int *data=(int *)ROI.data;
- for (int i=0;i<=3;++i)
- {
- cout<<data[i]<<",";
- }
-
-
-
- IplImage iplImage=ROI;
- Mat copyedROI(&iplImage,true);
-
-
- cout<<"\n\n/Copy Data///"<<endl;
- cout<<"rows:"<<copyedROI.rows<<endl;
- cout<<"cols:"<<copyedROI.cols<<endl;
- widthStep=copyedROI.step1(0);
- cout<<"widthStep:"<<widthStep<<endl;
-
-
- <span style="white-space:pre"> </span>cout<<"data:"<<" ";
- int *data2=(int *)copyedROI.data;
- for (int i=0;i<=3;++i)
- {
- cout<<data2[i]<<",";
- }
- cout<<endl;
-
- }
结果:
内存示意图
没有拷贝数据
拷贝数据之后
没有拷贝之前,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有个非常实用的功能,就是实现滑动窗口,下篇博客就来说说滑动窗口问题。