1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > OpenCV 4.x API 详解与C++实例-Mat数据类型详解

OpenCV 4.x API 详解与C++实例-Mat数据类型详解

时间:2021-03-04 19:41:00

相关推荐

OpenCV 4.x API 详解与C++实例-Mat数据类型详解

第二节 Mat数据类型详解

1、Mat数据类型描述

我们有多种从现实世界中获取数字图像的方法:数码相机,扫描仪,计算机断层扫描和磁共振成像等等。 在每种情况下,我们(人类)看到的都是图像。 但是,当将其转换为数字设备时,我们记录的是图像每个点的数值。如下图所示:

例如,在上图中,您可以看到汽车的镜子不过是一个包含所有像素点强度值的矩阵。OpenCV中的Mat是一个N维稠密数组,或多通道数组。它可用于存储实值或复值矢量和矩阵,灰度或彩色图像,体素体积,矢量场,点云,张量,直方图(尽管非常高维的直方图可能更好地存储在SparseMat中)。数组M的数据布局由数组M.step []定义,以便元素$(i_0,\cdots,i_{M.dims-1}) $的地址,其中 0 ≤ i k < M . s i z e [ k ] 0\leq i_k<M.size[k] 0≤ik​<M.size[k], 计算为:

a d d r ( M i 0 , ⋯ , i M . d i m s − 1 ) = M . d a t a + M . s t e p [ 0 ] ∗ i 0 + M . s t e p [ 1 ] ∗ i 1 + ⋯ + M . s t e p [ M . d i m s − 1 ] ∗ i M . d i m s − 1 addr(M_{i_0,\cdots,i_{M.dims-1}}) = M.data + M.step[0]*i_0 + M.step[1]*i_1 + \cdots + M.step[M.dims-1]*i_{M.dims-1} addr(Mi0​,⋯,iM.dims−1​​)=M.data+M.step[0]∗i0​+M.step[1]∗i1​+⋯+M.step[M.dims−1]∗iM.dims−1​

在二维数组的情况下,上述公式可简化为:

a d d r ( M i , j ) = M . d a t a + M . s t e p [ 0 ] ∗ i + M . s t e p [ 1 ] ∗ j addr(M_{i,j}) = M.data + M.step[0]*i + M.step[1]*j addr(Mi,j​)=M.data+M.step[0]∗i+M.step[1]∗j

注意, M . s t e p [ i ] ≥ M . s t e p [ i + 1 ] M.step [i]\ge M.step [i + 1] M.step[i]≥M.step[i+1](实际上, M . s t e p [ i ] ≥ M . s t e p [ i + 1 ] ∗ M . s i z e [ i + 1 ] M.step [i]\ge M.step [i + 1] * M.size [i + 1] M.step[i]≥M.step[i+1]∗M.size[i+1])。 这意味着二维矩阵逐行存储,三维矩阵逐平面存储,依此类推。 M.step [M.dims-1]是最小的,并且始终等于元素大小M.elemSize()。

因此,Mat中的数据布局与标准工具包和SDK中的大多数密集数组类型兼容,例如Numpy(ndarray),Win32(独立设备位图)以及其他,也就是说,与使用步骤( 或跨步)以计算像素的位置。 由于这种兼容性,可以为用户分配的数据制作Mat头,并使用OpenCV函数就地对其进行处理。

2、Mat的基本操作

2.1 、Mat创建

OpenCV提供很多创建Mat的方法,常用的几种方法如下:

// 1、创建一个3x3矩阵,float类型,单通道矩阵cv::Mat m1(3,3,CV_32FC1);for(int y = 0;y < 3;y++){for (int x = 0;x < 3;x++){m1.at<float>(y,x) = (y+1) * (x+1);}}std::cout <<"m1 = \n"<< m1 << std::endl;// 2、重新创建矩阵,将m1的旧值删除,如果新矩阵比旧矩阵空间大,则重新分配空间m1.create(5,5,CV_32FC1);std::cout << "new m1 = \n" << m1 << std::endl;// 3、重新赋值m1.setTo(cv::Scalar(255.0));std::cout << "new m1 value = " << m1 << std::endl;// 4、创建一个3x3矩阵,float类型,3通道cv::Mat m2(3,3,CV_32FC3,cv::Scalar(255));std::cout << "m2 = \n" << format(m2, cv::Formatter::FMT_PYTHON) << std::endl;// 5、创建一个3x3矩阵,float类型,1通道cv::Mat m3(cv::Size(3,3),CV_32FC1);m3.setTo(cv::Scalar(255.0));std::cout << "m3 = \n" << m3 << std::endl;// 6、创建一个3x3矩阵,float类型,1通道,数组赋值float data[] = {1,2,3,4,5,6,7,8,9};cv::Mat m4(cv::Size(3,3),CV_32FC1,data);std::cout << "m4 = \n" << m4 << std::endl;// 7、创建一个3x3全零矩阵,1通道cv::Mat m5 = cv::Mat::zeros(cv::Size(3,3),CV_32FC1);std::cout << "m5 = \n" << m5 << std::endl;// 8、创建一个3x3的全1矩阵,1通道cv::Mat m6 = cv::Mat::ones(3,3,CV_32FC1);std::cout << "m6 = \n" << m6 << std::endl;// 9、创建一个3x3的单位矩阵,1通道cv::Mat m7 = cv::Mat::eye(3,3,CV_32FC1);std::cout << "m7 = \n" << m7 << std::endl;// 10、使用逗号分隔的初始化程序创建矩阵cv::Mat m8 = (cv::Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);std::cout << "m8 = \n" << m8 << std::endl;

2.2、Mat元素访问

1)通过at<>访问,单通道矩阵

// 1、元素访问,单通道std::cout << "m4.at<float>(1,1) = " << m4.at<float>(1,1) << std::endl;

2)通过at<>访问,多通道矩阵

// 2、元素访问,多通道int counts = 1;cv::Mat m9(3,3,CV_32FC3);for(int i = 0;i < 3;i++){for(int j = 0;j < 3;j++){for(int k = 0;k < 3;k++){m9.at<cv::Vec3f>(i,j)[k] = counts;counts++;}}}std::cout << "m9 = \n" << format(m9, cv::Formatter::FMT_PYTHON) << std::endl;std::cout << "m9.at<cv::Vec3f>(1,1) = " << m9.at<cv::Vec3f>(1,1) << std::endl;

3)通过指针方式访问

// 3、通过指针方式访问for(int y = 0;y < 3;y++){float * dataptr = m9.ptr<float>(y);for(int x = 0;x < 3;x++){float * curdata = dataptr + x * 3;curdata[0] = curdata[0] * curdata[0];curdata[1] = curdata[1] * curdata[1];curdata[2] = curdata[2] * curdata[2];}}std::cout << "m9 = " << format(m9, cv::Formatter::FMT_PYTHON) << std::endl;

4)通过迭代器访问

// 4、通过迭代器访问// 常量迭代器cv::MatConstIterator_<float> it = m9.begin<float>();cv::MatConstIterator_<float> itend = m9.end<float>();float sum = 0.0;for(;it != itend;++it){sum += *it;}std::cout << "sum of m9 = " << sum << std::endl;// 改变矩阵元素的值cv::Mat_<cv::Vec3f>::iterator it1 = m9.begin<cv::Vec3f>();cv::Mat_<cv::Vec3f>::iterator it1end = m9.end<cv::Vec3f>();for(;it1 != it1end;++it1){(*it1)[0] = (float) sqrt((*it1)[0]);(*it1)[1] = (float) sqrt((*it1)[1]);(*it1)[2] = (float) sqrt((*it1)[2]);}std::cout << "\nm9 = " << format(m9, cv::Formatter::FMT_PYTHON) << std::endl;

5)块方式访问

// 5、通过块访问// 提取整行数据cv::Mat rowdata = m9.rowRange(0,1);std::cout << "row = \n" << format(rowdata, cv::Formatter::FMT_PYTHON) << std::endl;rowdata = m9.row(0);std::cout << "row = \n" << format(rowdata, cv::Formatter::FMT_PYTHON) << std::endl;//提取整列数据cv::Mat coldata = m9.colRange(0,1);std::cout << "col = \n" << format(coldata, cv::Formatter::FMT_PYTHON) << std::endl;coldata = m9.col(2);std::cout << "col = \n" << format(coldata, cv::Formatter::FMT_PYTHON) << std::endl;

2.3、Mat运算

1)加法运算

// 1、加法运算cv::Mat m10 = m3 + m4;std::cout << "m3 + m4 = \n" << m10 << std::endl;m10 = m3 + 10;std::cout << "m3 + 10 = \n" << m10 << std::endl;

2)减法运算

// 2、减法运算cv::Mat m11 = m3 - m4;std::cout << "m3 - m4 = \n" << m11 << std::endl;m11 = m3 - 100;std::cout << "m3 - 100 = \n" << m11 << std::endl;

3)乘法运算

// 3、乘法运算// 与常量相乘cv::Mat m12 = m3 * 0.5;std::cout << "m3 * 0.5 = " << m12 << std::endl;// 按矩阵乘法运算cv::Mat m13 = m3 * m4;std::cout << "m3 * m4 = " << m13 << std::endl;// 按逐个元素相乘cv::Mat m14 = m3.mul(m4);std::cout << "m3.mul(m4) = " << m14 << std::endl;

4)除法运算

// 4、除法运算// 与常量相除cv::Mat m15 = m3 / 0.5;std::cout << "m15 = " << m15 << std::endl;// 按元素相除cv::Mat m16 = m3 / m4;std::cout << "m16 = " << m16 << std::endl;

6)矩阵求逆

// 矩阵求逆cv::Mat m17 = m4.inv();std::cout << "m17 = " << m17 << std::endl;

7)矩阵转置

// 矩阵转置cv::Mat m22 = m3.t();std::cout << "m22 = " << m22 << std::endl;

8)矩阵逻辑运算

// 矩阵逻辑运算// 按元素与运算cv::Mat m18 = m3 & m4;std::cout << "m18 = " << m18 << std::endl;// 与常量进行与运算m18 = m3 & 0xF0;std::cout << "m18 = " << m18 << std::endl;// 按元素或运算cv::Mat m19 = m3 | m4;std::cout << "m19 = " << m19 << std::endl;// 与常量进行或运算m19 = m3 | 0xFA;std::cout << "m19 = " << m19 << std::endl;// 按元素异或运算cv::Mat m20 = m3 ^ m4;std::cout << "m20 = " << m20 << std::endl;// 与常量进行异或运算m20 = m3 ^ 0xFF;std::cout << "m20 = " << m20 << std::endl;// 非运算cv::Mat m21 = ~m3;std::cout << "m21 = " << m21 << std::endl;

9)矩阵比较

// 矩阵比较,按元素比较cv::Mat m23 = m3 >= m4;std::cout << "m3 >= m4?\n" << m23 << std::endl;

10)矩阵最大、最小值提取

// 矩阵最大值、最小值提取// 矩阵间最大、最小值提取cv::Mat m24 = cv::max(m3,m4);std::cout << "m24 = " << m24 << std::endl;cv::Mat m25 = cv::min(m3,m4);std::cout << "m25 = " << m25 << std::endl;// 矩阵与常量最大、最小值提取cv::Mat m26 = cv::min(m3,0.5);std::cout << "m26 = " << m26 << std::endl;cv::Mat m27 = cv::max(m3,1.2);std::cout << "m27 = " << m27 << std::endl;

2.4 其他函数

Opencv的Mat类型还提供了其他很多函数,方便使用。

m1 = m0.clone():完全复制所有数据元素m0.copyTo(m1):将m0复制给m1;如果需要给m1分配空间,则与clone方法效果一样m0.converTo(m1,type,scale,offset):转换矩阵数据类型并进行尺度变换和增加偏置之后赋值给m1m0.assignTo(m1,type):只在内部使用,被convertTo函数调用m0.setTo(s,mask):将矩阵所有元素设置为s,如果mask存在,则只对mask区域进行操作m0.reshape(chan,rows):改变矩阵形状m0.push_back(s):在末尾增加一个mx1大小数组m0.push_back(m1):向mxn大小矩阵增加k行并复制到m1中,m1大小必须为kxnm0.pop_back(n):向mxm大小矩阵移除n行(默认是1)m0.locateROI(size,offset):将m0矩阵全尺寸写入cv::Size变量size,如果m0矩阵只是一个大矩阵的一小块区域,还会写入一个Point类型的offsetm0.adjustROI(t,b,l,r),通过上、下、左、右调整ROI范围m0.total():计算数组序列的元素的数目(不包括通道)m0.isContinous():如果m0没有空隙,则返回truem0.elemSize():返回矩阵元素的位长度(比如三通道浮点矩阵将返回12)m0.elemSize1():返回矩阵基本元素的位长度(比如三通道浮点矩阵将返回4)m0.type():返回矩阵元素的类型(如CV_32FC3)m0.depth():返回矩阵中的元素类型(比如CV_32F)m0.size():返回矩阵m0的大小m0.empty():如果矩阵没有元素,则返回true,否则,返回false。

3、SparseMat数据类型描述及操作

在OpenCV中,SparseMat类表示多维稀疏数值数组。稀疏数组可以存储Mat可以存储的任何类型的元素。 稀疏意味着仅存储非零元素(尽管由于对稀疏矩阵进行操作,其存储的元素中的某些元素实际上可以变为0。您可以检测这些元素并使用SparseMat :: erase删除它们 )。 非零元素存储在哈希表中,该表在填充时会增长,因此搜索时间平均为O(1)(无论元素是否存在)。

1)创建SparseMat

// 创建SparseMatconst int dims = 5;int size[5] = {10, 10, 10, 10, 10};cv::SparseMat sparse_mat(dims, size, CV_32F);

2)SparseMat数据访问

SparseMat的数据访问有四种方式:cv::SparseMat::ptr()、cv::SparseMat::ref()、cv::SparseMat::value()、cv::SparseMat::find()

ptr方式访问示例如下:

// 创建SparseMatconst int dims = 5;int size[5] = {10, 10, 10, 10, 10};cv::SparseMat sparse_mat(dims, size, CV_32F);for(int i = 0; i < 1000; i++){int idx[dims];for(int k = 0; k < dims; k++)idx[k] = rand() % size[k];// 赋值给SparseMatsparse_mat.ref<float>(idx) += 1.f;}cout << "nnz = " << sparse_mat.nzcount() << endl;

SparseMat还有很多函数,在这里就不做一一说明,感兴趣的同学可以参考文档。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。