ROI是region of interest首字母的简写,翻译为感性趣的区域,其对象时图像。
对于图像,其实就是一个二维数组,只不过这个二维数组有点特殊,它有头信息,在头信息里会有描述这个二维数组的大小、图片类型和数组元素的数据类型等。下面是一张从官方教程里获取的一张辅助理解的图片。
上面的图片只是一张灰度图,而我们常见的基本都是彩色图,在灰度图中一个像素我们用一个值就可以表示了,但在彩色的图片中一个像素要用3个值或4个值(有的图片有alpha通道)来表示。下面就是一个由三个值表示一个像素的辅助理解图(BGR格式的图片)。
容易发现,其颜色排列时BGR顺序,其实在OpenCV中很多图片都是BGR格式存储,和我们常见的到RGB格式的存储方式是相反的。其他图片的格式有很多种,如HVS、CyCbCr、HSI等格式。
图片是一个二维数组,而一般情况下,我们处理图像时只对其中的部分区域进行处理,例如我们想在图片某个区域打马赛克,为了性能考虑我们,可以只让程序对这部分信息进行处理而将其他部分忽略,这时我们就要设置图片感性趣的区域。设置完感性趣的区域后,其实是指针指到了ROI区域的左上角,好像我们截取了一张小图片一样,我们只对这张小图片进行处理就可以了,因其ROI指向的还是原图只在告诉它图片的起始位置和大小变了,所以在对ROI区操作会影响原图。
在OpenCV中有C和C++的代码,最早OpenCV是用C写的,在开发中C的代码写起来不太方便在版本进入2.0之后后续加入的代码改用C++,所以设置ROI的方法有两种即C和C++的,C的已不常用不过这里还会列出已方便了解。
C++
(void)setImageROI:(cv::Mat)image{
// 设置ROI
// 方法一
cv::Mat roiImage = image(cv::Rect(100, 100, 200, 100));
// 方法二,第一个range表示起始行和终止行,第二个range是起始列和终止列
//cv::Mat roiImage = testImage(cv::Range(100, 100 + 100), cv::Range(100, 200 + 100));
// 画一个矩形
cv::rectangle(roiImage, cv::Rect(0, 0, 200, 100), cv::Scalar(255, 0, 0), 10);
}
设置ROI其实就是在原来图片上指定一个区域,而这个区域只是新创建了一个图片文件的头信息而已并没有产生新的图片,文件头里的图片区域的起始位置指向了ROI区域的左上角位置,所以在ROI上做的任何操作都会影响原图片。
C
(void)setImageROI:(IplImage *)image{
// 记录图片的大小和区域
CvRect currentRect = cvGetImageROI(image);
// 设置ROI区域
cvSetImageROI(image, cvRect(100, 100, 200, 100));
// 画一个矩形
cvRectangleR(image, cvRect(0, 0, 200, 100), CvScalar(255, 0, 0), 10);
// 还原ROI区域
cvSetImageROI(image, currentRect);
// 上面的还原ROI区域要一个临时变量,也可通过下面的方法,还原ROI区域而不用创建临时变量
//cvResetImageROI(image);
}
C没有生成一个文件头信息而是修改原来的文件头信息,所以要把文件头信息改回去。相比而言C++比C更加方便。
一、设置ROI并画矩形
头文件:
#import
#import
#import
下面是核心ROI代码
(UIImage *)getOpenCVImage{
// 获取测试用的图片路径
NSString * path = [[NSBundle mainBundle] pathForResource:@"test" ofType:nil];
// 读取图片
cv::Mat testImage = cv::imread([path cStringUsingEncoding:NSUTF8StringEncoding]);
// 设置ROI
cv::Mat roiImage = testImage(cv::Rect(100, 100, 100, 100));
// 在ROI区域做操作,画一个矩形
cv::rectangle(roiImage, cv::Rect(5, 5, 50, 50), cv::Scalar(255, 255, 255), 10);
// 将图片的格式从BGR转换成RGB,如果不转会造成显示的图片颜色出错
cv::cvtColor(testImage, testImage, cv::COLOR_BGR2RGB);
// 返回UIImage类型的图片
return MatToUIImage(testImage);
}
运行结果如下第一张是原图,第二张是处理后的图片,可以看到我们在ROI的(0,0)位置开始画矩形,但在大图中实际效果却不在左上角,这就是设置ROI的效果。
原始图
运行结果
超出ROI的效果
我们将上面代码改成正以下代码,让画矩形区域的高度大于ROI的高度。
// 放大矩形的高度,让其超出ROI的区域
cv::rectangle(roiImage, cv::Rect(0, 0, 200, 150), cv::Scalar(255, 0, 0), 10);
超出ROI操作的效果
可以看出,超出ROI的操作是被丢弃的。
二、设置ROI实现图片移位
上面说过设置ROI后我们就可以只对该区域进行操作。我们练习一下将一ROI区域的数据放到另一个ROI区域。
- (UIImage *)getOpenCVImage{
// 获取测试用的图片路径
NSString * path = [[NSBundle mainBundle] pathForResource:@"test" ofType:nil];
// 读取图片
cv::Mat testImage = cv::imread([path cStringUsingEncoding:NSUTF8StringEncoding]);
// 设置ROI区域A
cv::Mat roiImageA = testImage(cv::Rect(100, 100, 200, 100));
// 设置ROI区域B
cv::Mat roiImageB = testImage(cv::Rect(300, 30, 200, 100));
// 将roiImageB数据放到roiImageA的区域以实现图片区域移动效果
roiImageB.copyTo(roiImageA);
// 将图片的格式从BGR转换成RGB,如果不转会造成显示的图片颜色出错
cv::cvtColor(testImage, testImage, cv::COLOR_BGR2RGB);
// 将图片转成UIImage并返回
return MatToUIImage(testImage);
}
代码运行后结果:
实现图片区域移动