OPENCV 4学习笔记 ——01
1. 图像存储容器
不同于字符串、整数(分别以string、int类型保存),图像时以矩阵的形式存储的,OPENCV 4提供了 Mat 类来存储矩阵数据。
早期的OPENCV是采用 C 语言接口,以结构体IplImage来存储图像的矩阵数据,但是C语言中需要手动释放内存,这极容易导致因程序结束后没有释放内存而产生内存泄漏。OPENCV 4采用C++接口,提供的Mat类用于存储数据,利用自动内存管理技术很好的解决了内存自动释放问题,当变量不需要时内存自动释放。
创建Mat类示例:
1 2 3 | cv::Mat a; //创建一个名为a的矩阵头 a = cv::imread( "test.jpg" ); //向a中复制图像数据,矩阵指针指向像素数据。 cv::Mat b=a; //赋值矩阵头并命名为b |
声明一个指定类型的Mat类:
1 | cv::Mat A = Mat_< double >(3, 3); //创建一个3 * 3的矩阵用于存放double类型数据 |
通过opencv数据类型创建Mat类:
1 2 3 | cv::Mat a(640, 480, CV_8UC3); //创建640 * 480的三通道矩阵用于存放彩色图像。 cv::Mat a(4, 4, CV_8UC1); //创建4 * 4的八位无符号整型单通道矩阵 cv::Mat a(4, 4, CV_8U); //创建单通道矩阵,C1可以省略,是默认值 |
2. Mat类构造与赋值
——构造
根据OPENCV的源码定义,关于Mat类的构造方式共有20余种,然而,在平时一些简单的应用程序种,很多复杂的构造方法并没有太多的用武之地,此处介绍几种项目中常用的构造、赋值方式。
【1】利用默认构造函数
默认构造函数构造一个Mat类,不需要输入任何参数,后续赋值的时候会自动判断矩阵类型和大小,实现灵活存储。例:
1 | cv::Mat::Mat(); |
【2】根据输入矩阵的尺寸和类型构造
有以下几种方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //1. 用矩阵尺寸和参数类型构造Mat类 cv::Mat::Mat( int rows, int cols, int type ); //2. 用Size()结构构造Mat类(注意:size中的行列与第一种相反 cv::Mat::Mat( Size size(), int type ); //3. 利用已有矩阵构造Mat类(注:当希望复制两个一模一样的Mat类而彼此之间不受影响,那么可以使用m = a.clone()); cv::Mat::Mat( const Mat & m); //4. 构造Mat类的子类 cv::Mat::Mat( const Mat & m, const Range & rowRange, const Range & colRange = Range::all() ); |
示例:
1 2 3 4 5 6 7 | //示例2(此处为480行,640列,注意行列不可颠倒): cv::Mat::Mat(Size(480, 640), CV_8UC1); cv::Mat::Mat(Size(480, 640), CV_32FC3); //示例4: cv::Mat b(a, Range(2, 5), Range(2, 5)); //从a中截取部分数据构造b cv::Mat c(a, Range(2, 5)); //默认是最后一个参数构造c |
——赋值
【1】构造时赋值
利用该方法时,只能对一个通道中的数据赋相同的值,形如:
1 2 3 4 5 | cv::Mat::Mat( int rows, int cols, int type, const Scalar & s ); |
示例:
1 2 3 | cv::Mat a(2, 2, CV_8UC3, cv::Scalar(0, 0, 255)); cv::Mat b(2, 2, cv_8UC2, cv::Scalar(0, 255)); cv::Mat c(2, 2, CV_8UC1, cv::Scalar(255)); |
注意,scalar中的参数个数必须与前面的通道对应,如果超过通道数,则后面的参数将不会读取。而当少于通道数时后面的通道会全部赋值为0。可以在return之前设置一个断点,用image watch查看每一个Mat类变量里的数据。
【2】枚举法赋值
这种赋值方法是将矩阵中的元素一一列举,并用数据流的形式赋值给Mat类。具体的赋值形式如下:
1 2 | cv::Mat a = (cv::Mat_< int >(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9); cv::Mat b = (cv::Mat_< double >(2, 3) << 1.0, 3.2, 4.0, 5.1, 6.2); |
其中枚举的数据会按行填充,先填充第一行,再填充第二行....如果枚举的数据数量与矩阵的大小不符,则会报错。
【3】循环赋值法赋值(利用双for循环)
将矩阵看作一个二维数组,利用双循环按行以此填充数据,例如:
1 2 3 4 5 6 | cv::Mat c = cv::Mat_< int >(3, 3); for ( int i = 0; i < c.rows; i++) { for ( int j = 0; j < c.cols; j++) { c.at< int >(i, j) = i+j; } } |
需要注意,第一行定义的变量类型时int,则循环中声明的变量类型也应该是int,如果改为 c.at<double> 则会报错。
【4】利用数组进行赋值
该方法类似于枚举法,但是该方法可以根据需求改变Mat类矩阵的通道数,可以看作枚举法的拓展,如:
1 2 3 | float a[8] = {5, 6, 7, 8, 1, 2, 3, 4}; cv::Mat b = cv::Mat(2, 2, CV_32FC2, a); cv::Mat c = cv::Mat(2, 4, CV_32FC1, a); |
第一个赋值后得到的结果是一个2x2的双通道矩阵,分别存放1/2, 3/4, 5/6, 7/8。第二个赋值后得到的结果是一个2x4的单通道矩阵。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律