OpenCV修改ROI区域值时需注意数据类型

简介

本文介绍使用OpenCV在修改矩阵特定区域也就是ROI值的时候,需要注意的事项。如果类型错误,将得到错误的结果.

正式交代主题前,先看以下几个点.

imread函数)

我们主要是关注第二个参数,有以下四种:

1
2
3
4
CV_LOAD_IMAGE_ANYDEPTH - If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit.
CV_LOAD_IMAGE_COLOR - If set, always convert image to the color one
CV_LOAD_IMAGE_GRAYSCALE - If set, always convert image to the grayscale one
>0 Return a 3-channel color image.

默认下传的是CV_LOAD_IMAGE_COLOR,得到的Mat type为CV_8UC3,如果传的是CV_LOAD_IMAGE_GRAYSCALE则表示加载的是灰度图像,type为CV_8UC1

设置ROI

方式有大概有2种:

  1. 拷贝构造函数
1
Mat roi = Mat(gray, Rect(100, 0, 30, 30));

当然上面也完全等价于下面这句:

1
Mat roi(gray, Rect(100, 0, 30, 30));

可能是写Java多的原因,我更喜欢前面的那种写法.

  1. 使用操作符
1
Mat srcROI = src(Rect(0,0,src.cols/2,src.rows/2));

Rect的使用

最常见的一种构造方法是:

1
Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height);

其中x表示ROI区域的x坐标(横轴为X),也就是第几列的意思,y就不解释了。width表示ROI区域的宽度.

Mat元素访问

链接列举了13种访问方法,这里又精简到3种,如果对效率没什么太高要求那么还是第一种用起来比较顺手:

1
2
3
4
5
6
7
for (int i=0; i<ROWS ; i++)
{
for (int j=0; j<COLS ; j++)
{
img1.at<float>(i,j) = 3.2f;
}
}

这里的Mat.at(i, j),其中i表示第几行,j表示第几列.

修改特定区域的值

目的就是要将roi区域灰度值置为0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
using namespace std;
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
void clearMat(Mat a){
if(a.empty()){
return;
}
for(int i = 0; i<a.rows; i++)
for(int j = 0; j<a.cols; j++){
a.at<uchar>(i, j) = 0;
}
}
int main(int argc, const char * argv[]) {
// insert code here...
cout << "Hello, World!\n";
string path = "/Users/yanzi/Pictures/lena.jpg";
Mat img = imread(path);
//显示灰度图
Mat gray;
cvtColor(img, gray, COLOR_RGBA2GRAY);
Mat gray1 = imread(path, IMREAD_GRAYSCALE);
// Mat roi = Mat(gray, Rect(100, 0, 30, 30));
Mat roi(gray, Rect(100, 0, 30, 30));
clearMat(roi);
namedWindow("gray");
imshow("gray", gray);
waitKey(0);
return 0;
}

我们将gray矩阵的那个rect区域弄成黑色,效果图如下:
可以看到运行的丝毫不差。注意我们在访问矩阵元素是是将其设为uchar,如果将其改为int看一下效果:

起点是对的,但是宽度变成了需求的四倍。这是因为uchar只占一个字节,而int为4个字节.当错误的将其转为int后,就会发生实际修改的区域越位的情况。所以当修改Mat时一定格外注意.

下面是深度和取值范围的对应关系:

1
2
3
4
5
6
7
CV_8U - 8-bit unsigned integers ( 0..255 )
CV_8S - 8-bit signed integers ( -128..127 )
CV_16U - 16-bit unsigned integers ( 0..65535 )
CV_16S - 16-bit signed integers ( -32768..32767 )
CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

我们还要清楚这个depth和数据类型的对应关系:

1
2
3
4
5
6
7
Mat_<uchar>---------CV_8U
Mat<char>-----------CV_8S
Nat_<short>---------CV_16S
Mat_<ushort>--------CV_16U
Mat_<int>-----------CV_32S
Mat_<float>----------CV_32F
Mat_<double>--------CV_64F

这样就不会出错了。
再贴一段如果是三通道时的访问方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
int ROWS = 100; // height
2 int COLS = 200; // width
3 Mat img1(ROWS , COLS , CV_8UC3);
4
5 for (int i=0; i<ROWS ; i++)
6 {
7 for (int j=0; j<COLS ; j++)
8 {
9 img1.at<vec3b>(i,j)[0]= 3.2f; // B 通道
10 img1.at<vec3b>(i,j)[1]= 3.2f; // G 通道
11 img1.at<vec3b>(i,j)[2]= 3.2f; // R 通道
12 }
13 }

参考

  1. http://www.cnblogs.com/dupuleng/articles/4072736.html
  2. opencv2.4.13官方文档
显示 Gitment 评论