【OpenCV】Learning OpenCV3 第四章笔记
第四章 动态和可变存储
接下来将介绍大规模数组类型,其中最重要的是cv::Mat
,这可被视作OpenCV c++实现的核心。cv::Mat
用来表示任意维的密集(dense)数组,这里的密集(dense)表示Mat中每个值都被存储在内存中(即便是0),其反义是稀疏(sparse)数组(仅存储非0变量),在OpenCV中是cv::SparseMat
cv::Mat:N维密集数组
cv::Mat
可存储任意维数组,且以行为主序存储,每个矩阵包含了
变量名 | flags | dims | rows | cols | data | refercount |
---|---|---|---|---|---|---|
含义 | 数组的一些量 | 维数 | 行数 | 列数(dims>2则invalid) | 指向数据存储内存的指针 | 记录被cv::Ptr<>引用的指针数 |
cv::Mat
中的每个元素不一定要是一个值,也可以是多个值
创建一个数组
cv::Mat m;
m.create(3,10,CV_32FC3); // 32-bit floating-point three-channel array
m.setTo(cv::Scalar(1.0f,0.0f,-1.0f));
cout << m << endl;
cv::Mat m2(3,10,CV_32FC3, cv::Scalar(1.0f,0.0f,-1.0f)); // equivalent
本书中最重要的一句话
需要认识到cv::Mat
和cv::Mat.data
的分离性,cv::Mat.data
才是对数据的指向而非cv::Mat
,当我们对cv::Mat
赋值时,如m=n
,此时m
中的data
指针就会指向n
的data
指向的区域,而原先data
指向的区域(如果是最后一个引用的话),则会自动销毁,同事m
中的dim
、col
等变量也会自动变得与n一致,注意到它们共享了data
所指向区域
cv::Mat
各种用法,见自74页起的表格
单独获取数组元素
两种方法,通过位置,或通过迭代
- 通过at<>()
在模板中指定类型为数组中的类型,并选择位置,注释中给出的是multichannel的数组,书p79表4-6列出了大部分accessor functioncv::Mat m = cv::Mat::eye(10, 10, 32FC1); // cv::Mat m = cv::Mat::eye(10, 10, 32FC2); print( "Element (3,3) is %f\n", m.at<float>(3,3) // m.at<cv::Vec2f>(3,3)[0] )
- 使用c风格的指针
数组迭代器:NAryMatIterator
通过内存连续的一个个plane来迭代N维数组,而非传统的逐个元素,详见书p82
通过块来获取元素
获取一个数组的子区域,比如cv::Mat.row(int i)
、cv::Mat.col(int j)
等方法,注意这些方法并没有复制数据,而仅仅给了一个指针,更复杂的方法包括cv::Mat.rowRange(cv::Range(i0,i1))
、cv::Mat.diag(int offset)
、cv::Mat(cv::Rect)
等等
矩阵表达式
一些关于cv::Mat
的运算符重载+-*/
以及elementwise的计算m1>m2
、~m0
等,详见书p87表格4-8
饱和投射
对于某个数据类型比如uchar,当出现数据溢出的时候,可以通过cv::saturate_cast
来以边缘值代替溢出值
unchar& Vxy = m0.at<uchar>(y,x);
Vxy = cv::saturate_cast<uchar>((Vxy-128)*2+128);
出现溢出时,输出为0
其他
m1=m0.clone()
等其他方法p88表格4-9
稀疏数组cv:SparseMat
cv::Mat
使用数组存储,而cv::SparseMat
使用哈希表存储,哈希表是自动维护的,详见书p90-93
使用模板
使用cv::Mat_<>
而非cv::Mat
创建数组可以简化代码,比如
cv::Mat m(10,10,CV_32FC2);
m.at<Vec2f>(i0,i1) = cv::Vec2f(x,y);
cv::Mat_<Vec2f> m(10,10);
m.at(i0,i1) = cv::Vec2f(x,y);
//or...
m(i0,i1)=cv::Vec2f(x,y)
在使用at
时,无须再指定数组中的元素类型了
建议使用这种模板的形式创建矩阵,这样可以使得一些问题在编译时被检测出来
总结
本章主要介绍了cv::Mat
的一些用法