zoukankan      html  css  js  c++  java
  • Opencv C++ 基本数据结构 Mat

    Mat

    Mat代表矩阵,该类声明在头文件opencv2/core/core.hpp中
    其构造函数为:

    Mat(int rows, int cols, int type)
    

    rows代表行数,cols代表列数
    type可以设置为 CV_8UC(n)CV_8SC(n)CV_16SC(n)、、CV_16UC(n)CV_32FC(n)、*、CV_32SC(n)CV_64FC(n)
    其中8U、8S、16S、16U、32S、32F、64F前的数字代表Mat中每一个数值的位数
    U代表uchar类型、S代表int类型、F代表浮点型(32F为float、64F为double)其他类似。


    构造单通道Mat对象

    如代码所示:

    # include <opencv2corecore.hpp>
    using namespace cv;
    int main() {
    	//构造两行三列的float型矩阵
    	Mat m = Mat(2, 3, CV_32FC(1));
    	//利用Size对象构造两行3列矩阵,Size(列,行)
    	Mat m1 = Mat(Size(3, 2), CV_32FC(1));
    	//使用Mat类中的成员函数create完成Mat对象构造
    	Mat m2;
    	m2.create(Size(2, 3), CV_32FC1);
    	//构造零矩阵和1矩阵
    	Mat o = Mat::ones(2, 3, CV_32FC1);
    	Mat z = Mat::zeros(Size(3,2), CV_32FC1);
    	//初始化小型矩阵
    	Mat m = (Mat_<int>(2, 3) << 1, 2, 3, 4, 5, 6;
    	return 0;
    }
    

    获取单通道Mat的基本信息

    注:单通道与ndarray中的二维对应,多通道与ndarray的三维对应

    以三行两列的矩阵为例

    m = [ 11 12 33 43 51 16 ] m=left[ egin{matrix} 11 & 12 \ 33 & 43 \ 51& 16 end{matrix} ight] m=113351124316

    1、获取行数和列数

    # include <opencv2corecore.hpp>
    #include <iostream>
    using namespace std;
    using namespace cv;
    int main() {
    	//快速构造矩阵
    	Mat m = (Mat_<int>(3, 2) << 11, 12, 33, 43, 51, 16);
    	//矩阵的行数
    	cout << "行数:" << m.rows << endl;
    	//矩阵的列数
    	cout << "行数:" << m.cols << endl;
    	return 0;
    }
    

    输出:
    这里写图片描述


    2、使用成员函数size()获取矩阵的尺寸

    成员函数**size()**返回的是Size对象

    //获取尺寸
    	Size size = m.size();
    	cout << "尺寸:" << size << endl;
    

    这里写图片描述


    3、使用成员函数channels()获取矩阵的通道数

    cout << "通道数:" << m.channels() << endl;
    

    输出:
    在这里插入图片描述


    4、使用成员函数total获得面积(行数乘列数)

    	cout << "面积:" << m.total() << endl;
    

    输出
    在这里插入图片描述


    5、成员变量dims(维数)

    cout << "维数:" << m.dims << endl;
    

    输出:
    在这里插入图片描述


    访问单通道对象中的值

    1、使用成员函数at

    例如访问单通道数据类型为CV_32F的对象m,访问其第r行c列的值:
    格式为:m.at<类型>(行,列)

    m.at<float>(r,c)
    

    遍历上文中矩阵m中的值并输出:

    # include <opencv2corecore.hpp>
    #include <iostream>
    using namespace std;
    using namespace cv;
    int main() {
    	//快速构造矩阵
    	Mat m = (Mat_<int>(3, 2) << 11, 12, 33, 43, 51, 16);
    	//for循环打印m中的每一个值
    	for (int r = 0; r < m.rows; r++)
    	{
    		for (int c = 0; c < m.cols; c++)
    		{
    			cout << m.at<int>(r, c) << ",";
    		}
    		cout << endl;
    	}
    	//return 0;
    }
    

    还可以结合opencv中的Point类和at成员函数来实现:
    m.at(r, c)可用m.at(Point(r,c))代替


    2、利用成员函数ptr

    矩阵每一行的值值是连续存储在内存中的,行与行在内存中也可能存在间隔
    通过ptr成员函数可以获得执行每一行地址的指针
    格式为:m.ptr<类型>(第几行)
    遍历上文中矩阵m中的值并输出:

    for (int r = 0; r < m.rows; r++)
    	{
    		const int* ptr = m.ptr<int>(r);
    		//打印第r行所有值
    		for (int c = 0; c < m.cols; c++)
    		{
    			cout << ptr[c] << ",";
    			//cout << *(ptr+c) << ",";
    		}
    		cout << endl;
    	}
    

    3、使用成员函数isContinuous 和 ptr

    矩阵在存储时如果每一行在内存中没有间隔则isContinuous返回true
    遍历上文中矩阵m中的值并输出:

    if (m.isContinuous())
    	{
    		//获取指向第一行的指针
    		int* ptr = m.ptr<int>(0);
    		for (int i = 0; i < m.total(); i++)
    		{
    			cout << ptr[i] << ",";
    			if (i % m.cols)
    			{
    				cout << endl;
    			}
    		}
    	}
    

    4、使用成员变量step和data

    对于单通道矩阵来说,step[0]代表每一行所占的字节数,如果有间隔则间隔也作为字节数被计算在内;step[1]代表每一个数值所占的字节数,data是一个指针类型为uchar,它指向第一个数值。
    因此无论矩阵行与行之间在内存中是否有间隔,都可以使用以下代码来访问第r行c列:

    *((int *)(m.data+m.step[0]*r+c*m.step[1]))
    

    向量类Vec(构建多通道Mat的基础)

    可以把这里的向量理解为列向量,构造一个 _cn行x1列的数据类型为_Tp的列向量格式:Vec<Typename _Tp, int _cn>
    例如构造一个长度为3类型为int,且初始化为11,87,37的列向量:

    Vec<int, 3> vi(11, 87, 37);
    

    这样构建的向量默认为列向量,可以通过输出行数和列数查看

    	cout << vi.rows << endl;
    	cout << vi.cols << endl;
    

    在这里插入图片描述
    可以使用 “[]” 或"()"操作符f访问向量中的值

    //访问向量中的值
    	cout << "访问第0个元素" << vi(0) << endl;
    	cout << "访问第1个元素" << vi[1] << endl;
    

    在这里插入图片描述
    opencv 为向量类取了别名:

    typedef Vec<uchar ,3> Vec3b
    typedef Vec<int ,2> Vec2i
    typedef Vec<float,4> Vec4f
    typedef Vec<double ,3> Vec3d
    

    单通道矩阵每个元素都是一个数值,多通道矩阵每个元素可以看作一个向量。


    构造多通道Mat对象

    构造一个由n个rows*cols二维浮点型矩阵组成的三维矩阵的格式如下:

    Mat(int rows,int cols,CV_32FC(n))
    

    例如构造一个2行2列的float类型的三通道矩阵

    Mat mm = (Mat_<Vec3f>(2, 2) << Vec3f(1, 11, 21), Vec3f(2, 12, 32),
    		Vec3f(3, 13, 23), Vec3f(4, 24, 34));
    

    访问多通道对象中的值

    1、使用成员函数at

    可以将多通道Mat看作一个特殊的二维数组,数组的每个元素不是数值而是一个向量
    以上文 创建的mm多通道对象为例遍历输出多通道矩阵mm的每个元素

    	for (int r = 0; r < mm.rows; r++)
    	{
    		for (int c = 0; c < mm.cols; c++)
    		{
    			cout << mm.at<Vec3f>(r, c);
    		}
    		cout << endl;
    	}
    

    输出:
    在这里插入图片描述


    2、利用成员函数ptr

    多通道Mat的元素在内存中也是按行存储,每一个行存储在连续内存区域中,如果行与行之间由间隔,间隔相等。成员函数ptr返回指向指定行的第一个元素的指针。
    使用ptr访问mm对象的每个元素。

    for (int r = 0; r < mm.rows; r++)
    	{
    		Vec3f* ptr = mm.ptr<Vec3f>(r);
    		for (int c = 0; c < mm.cols; c++)
    		{
    			cout << ptr[c] << ",";
    			if (c % mm.cols)
    			{
    				cout << endl;
    			}
    		}
    	}
    

    输出与使用at相同


    3、使用成员函数isContinuous 和 ptr

    与单通道Mat对象一样,可通过isContinuous判断整个Mat对象中的元素是否存储在连续内存中,返回true即表示连续存储;如果是连续存储,就可以少一个循环用ptr遍历

    	if (mm.isContinuous())
    	{
    		//获取指向第一行的指针
    		Vec3f* ptr = mm.ptr<Vec3f>(0);
    		for (int i = 0; i < mm.total(); i++)
    		{
    			cout << ptr[i] << ",";
    			if (i % mm.cols)
    			{
    				cout << endl;
    			}
    		}
    	}
    

    4、使用成员变量step和data

    与单通道Mat类似,step[0]代表每一行所占的字节数,如果有间隔则间隔计算在内,step[1]代表每一个个元素所占的字节数,step[1] = elemSize1()*channels()
    elemSize1()代表一个元素中一个数值所占的字节数;data代表首个数值的地址
    下面使用step和data遍历输出mm中的元素:

    	for (int r = 0; r < mm.rows; r++)
    	{
    		for (int c = 0; c < mm.cols; c++)
    		{
    			cout <<*(Vec3f*) (mm.data + r*mm.step[0]+c*mm.step[1]);
    		}
    		cout << endl;
    	}
    

    输出与1,2,3相同


    5、分离通道

    以上述讨论的mm多通道对象为例,将所有向量第一个数值组成的二维矩阵作为第一通道,将所有向量第二个数值组成的单通道矩阵作为第二通道以此类推;
    所以mm分离后的三个通道为
    第一通道:2x2的矩阵,元素为 1、2、3、4
    第二通道:2x2的矩阵,元素为 11、12、13、24
    第三通道:2x2的矩阵,元素为 21、32、23、34
    使用split可以将mm分离为多个单通道:

    vector<Mat>planes;
    split(mm, planes);
    

    5、合并通道

    使用merge函数可以将多个单通道矩阵合并为一个三维矩阵
    该函数声明为:

    void merge (const Mat *mv, size_t count, OutputArray dst)
    //其重载函数
    void merge (InputArrayofArrays mv, OutputArray dst)
    

    例如将三个2*2int型单通道合并:

    	Mat plane0 = (Mat_<int>(2, 2) << 1, 2, 3, 4);
    	Mat plane1 = (Mat_<int>(2, 2) << 5, 6, 7, 8);
    	Mat plane2 = (Mat_<int>(2, 2) << 9, 10, 11, 12);
    	//初始化Mat数组也
    	Mat plane[]{ plane0, plane1, plane2 };
    	//声明一个mat对象用来保存合并后的多通道对象
    	Mat mat;
    	merge(plane, 3, mat);
    	//将三个单通道矩阵放在vector容器中
    	vector<Mat>pl;
    	pl.push_back(plane0);
    	pl.push_back(plane1);
    	pl.push_back(plane2);
    	Mat mat1;
    	merge(pl, mat);
    

    获得Mat中某一个区域的值

    1、使用row(i)或col(j)得到矩阵的第i行j列

    Mat mr = m.row(1);
    Mat mc = m.col(1);
    

    2、使用rowRange或colRange得到矩阵的连续行或连续列

    首先知道Opencv中的Range类,该类用于构造连续整数序列,左闭右开。
    Range(2,5)产生2、3、4序列。以5*5矩阵为例
    m a t r i x = [ 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 ] matrix = left[ egin{matrix} 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 end{matrix} ight] matrix=16111621271217223813182349141924510152025

    //构造一个5*5int型矩阵
    	Mat matrix = (Mat_<int>(5, 5) << 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);
    	//访问第2、3两行
    	Mat r_range = matrix.rowRange(Range(2, 4));//可以写为matrix.rowRange(2,4)
    	for (int r = 0; r < r_range.rows; r++)
    	{
    		for (int c = 0; c < r_range.cols; c++)
    		{
    			cout << r_range.at<int>(r, c) << ",";
    		}
    		cout << endl;
    	}
    

    输出:
    在这里插入图片描述
    colRange与之类似

    Mat c_range = matrix.colRange(Range(2, 4));
    	for (int r = 0; r < c_range.rows; r++)
    	{
    		for (int c = 0; c < c_range.cols; c++)
    		{
    			cout << c_range.at<int>(r, c) << ",";
    		}
    		cout << endl;
    	}
    

    输出:
    在这里插入图片描述
    注意:成员函数row、 col 、 rowRange 、colRange 返回的矩阵是指向原矩阵的,例如改变c_range第1行1列的值,原矩阵也会改变:

    c_range.at<int>(1, 1) = 1000;//改为1000
    	cout <<  "输出c_range" <<endl;
    	for (int r = 0; r < c_range.rows; r++)
    	{
    		for (int c = 0; c < c_range.cols; c++)
    		{
    			cout << c_range.at<int>(r, c) << ",";
    		}
    		cout << endl;
    	}
    	cout << "输出原矩阵" << endl;
    	for (int r = 0; r < matrix.rows; r++)
    	{
    		for (int c = 0; c < matrix.cols; c++)
    		{
    			cout << matrix.at<int>(r, c) << ",";
    		}
    		cout << endl;
    	}
    

    输出:
    在这里插入图片描述
    使用成员函数clone和copyTo可以解决这个问题


    3、使用rowRange或colRange得到矩阵的连续行或连续列

    使用这两个成员函数会将矩阵克隆一份,以上述r_range为例

    	Mat r_range = matrix.rowRange(Range(2, 4)).clone();
    	r_range.at<int>(0, 0) = 1000;//修改值为1000
    	cout << "输出r_range" << endl;
    	for (int r = 0; r < r_range.rows; r++)
    	{
    		for (int c = 0; c < r_range.cols; c++)
    		{
    			cout << r_range.at<int>(r, c) << ",";
    		}
    		cout << endl;
    	}
    	cout << "输出原矩阵" << endl;
    	for (int r = 0; r < matrix.rows; r++)
    	{
    		for (int c = 0; c < matrix.cols; c++)
    		{
    			cout << matrix.at<int>(r, c) << ",";
    		}
    		cout << endl;
    	}
    

    输出:
    在这里插入图片描述
    copyTo的用法为:

    matrix.rowRange(2,4).copyTo(r_range);
    

    4、使用Rect类

    如果我们需要矩阵中一块矩形区域,我们可以使用rowRange和colRange来定位,但是Opencv提供了Rect类来简化操作。知道矩形的左上角坐标,和矩形的宽高就可以确定一个矩形,所以其构造函数为:

    Rect(int _x,int _y, int _width, int _height);
    

    也可以将 _width 和 _height保存在一个Size中

    Rect(int _x,int _y, Size size);
    

    如果知道左上角和右下角的坐标也可以确定一个矩形,所以构造函数为:

    Rect(Point2i &pt1,Point2i &pt2);
    

    以之前叙述的5*5矩阵matrix为例,要获得使矩形框中的值为8,9,13,14
    可以按如下方式构建矩形框

    	Mat roi1 = matrix(Rect(Point(2, 1), Point(4, 3)));//左上角和右下角坐标,"左开右闭"
    	Mat roi2 = matrix(Rect(2, 1, 2, 2)) ;//x,y,宽,高
    	Mat roi3 = matrix(Rect(Point(2, 1), Size(2,2)));//左上角坐标,尺寸
    

    使用for循环输出:
    在这里插入图片描述
    这样得到的矩形区域仍然是指向原矩阵的所以,仍然可以使用clone和copyTo

    Mat roi1 = matrix(Rect(Point(2, 1), Point(4, 3))).clone;
    
  • 相关阅读:
    一个简单的ASP.NET MVC异常处理模块
    通过源码了解ASP.NET MVC 几种Filter的执行过程
    跨站请求伪造
    显示实现接口
    数组为什么可以使用linq查询
    编写轻量ajax组件03-实现(附源码)
    事件
    编写轻量ajax组件02-AjaxPro浅析
    委托
    静态构造函数
  • 原文地址:https://www.cnblogs.com/PythonFCG/p/13860134.html
Copyright © 2011-2022 走看看