参考学习书籍:《OpenCV3编程入门》
环境:OpenCV4.1.1 + VS2019
一.基础图像容器Mat
Mat是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等),一个指向存储所有像素值的矩阵(根据所选存储方式不同,矩阵可以是不同维数的)。矩阵头所占内存小,通常是一个常数,而矩阵本身的尺寸很大,∵它存储了所有的像素值。而且图像处理本身就是一个计算量很大的过程,除非万不得已,都不应该进行大图像的复制。
为了解决占用空间大这一问题,OpenCV引入了计数机制:让每个Mat对象都有自己的矩阵头,但可以共同指向一个矩阵(即让矩阵指针指向同一个地址)。
在OpenCV1.0中,还不存在Mat类,在那时,OpenCV是基于C接口实现的。而其中的图像存储格式IplImage*,在退出前没有relase的话,就会造成内存泄漏,使用非常不方便。在C++出现后,有了类的概念,就不用担心这一问题。如Mat类,是有自动的存储管理。在创建对象时,是不会分配内存空间的。
Mat src; //创建Mat类对象 不分配内存空间
src = imread("1.jpg"); //为src开辟内存空间
上面是一种创建Mat类对象以及赋值的方法,第一行仅是创建对象,即创建矩阵头信息;第二行才是为其分配内存。同样也可以使用下面这种方法创建一个对象并初始化。这就是C++类的好处,同样,跟C++一样的面向对象的语言Java也是这样的。
Mat src = imread("1.jpg");
下面是两种方法的结构都是一样的,都是让dst对象指向src对象的矩阵,只是矩阵头(对象名)不一样。
Mat dst(src); //使用拷贝构造函数
Mat dst;
dst = src; //赋值运算符
显示创建Mat对象的七种方法
对于显示创建Mat对象的七种方法,很多博客与书上都有讲解,这里就不再赘述。可以参考这位博主的博客:https://blog.csdn.net/qq_34106574/article/details/93471025
不过我个人认为最常用的方法还是用Mat()构造函数创建。
OpenCV的格式化输出
r为创建的对象,用randu函数来生成一个随机矩阵,并给定一个上限下限值。
#include <iostream>
#include <opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat r = Mat(10, 3, CV_8UC3);
randu(r, Scalar::all(0), Scalar::all(255));
cout << "r(opencv默认风格)=" << r << ";" << endl << endl;
cout << "r(python风格)=" <<format(r, Formatter::FMT_PYTHON)<< ";" << endl << endl;
cout << "r(逗号分割风格)=" << format(r, Formatter::FMT_CSV) << ";" << endl << endl;
cout << "r(numpy风格)=" << format(r, Formatter::FMT_NUMPY) << ":" << endl << endl;
cout << "r(c语言风格)=" << format(r, Formatter::FMT_C) << ":" << endl << endl;
}
OpenCV默认风格:
python风格:
逗号分隔风格:
numpy风格:
C语言风格:
以上,对于矩阵值的大小是由randu函数随机生成的,而矩阵的大小是由Mat r = Mat(10, 3, CV_8UC3);所决定的。
在VS中,右击Mat,选择速览定义,就会出现下面的代码。
456行的函数就是Mat的构造函数,这个函数定义在mat.inl.hpp这一头文件中,当然你还可以看到除了456行这一函数之外的其他构造函数,它们都有同样的函数名,但形参不同。在使用构造函数创建对象时,会根据填入的实参来自动选择所使用的构造函数。如这里就是使用的456行的函数,三个参数分别为矩阵的行数,列数,和存储的数据类型。
对于数据类型,有许多种,它定义了每个元素(图片中的像素)的通道数和每个通道上表示像素值的比特数(位数)。
如:8UC3 ——> 8 unsigned 3 channel 即 3通道 8位无符号整型
依次类推有:
8S (单通道8位有符号整数)
16U (单通道16位无符号整数)
16S (单通道16位有符号整数)
32S (单通道32位有符号整数)
32F (单通道32位浮点数)
64F (单通道64位浮点数)
8UC1 (单通道8位无符号整数)
8UC2 (2通道8位无符号整数)
8UC3 (3通道8位无符号整数)
8UC4 (4通道8位无符号整数)
8UC(n) (n通道8位无符号整数 (n 可以从 1 到 512) )
更详细的讲解可以参考这位博主的博客,可以帮助我们从视觉上理解:https://blog.csdn.net/CV_YOU/article/details/53177173
所以,上面的示例代码所生成的矩阵的含义是输出一个3*10的矩阵,每个元素有3个通道值。以python风格为例:
二.其他常用数据结构和函数
1.点 Point类
point类
Point point;
point.x = 1;
point.y = 2;
//或
Point point = Point(1,2);
定义输出二维点
Point2f p(6,2);
count<<"二维点 p="<<p<<";\n"<<endl;
定义输出三维点
Point3f p3f = (1,2,3);
count<<"三维点 p3f="<<p3f<<";\n"<<endl;
定义输出基于Mat的std::vector
vector<float> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
count<<"vector="<<Mat(v)<<";\n"<<endl;
定义输出std::vector点
vector<Point2f> points(20);
for(size_t i = 0; i<points.size(); ++i)
{
points[i] = Point2f((float)(i*5),(float)(i%7));
}
count<<"二维点向量 points="<<points<<";\n"<<endl;
2.颜色 Scalar类
Scalar(a,b,c);
Scalar()表示有4个元素的数组,即RGB颜色值和透明颜色alpha。若只填了3个参数,则默认我们想表示3个参数。值得注意的是:RGB的颜色定义顺序不再是R(红),G(绿),B(蓝),而是BGR。
3.尺寸 Size类
Size(5,5);
4.矩形 Rect类
Rect rec = Rect(1,2,3,4);
Rect()的成员变量有x,y,width,height。上面的例子表示一个左上角坐标为(1,2)长为3,高为4的矩形。
5.颜色空间转换 cvtColor()函数
#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
void main()
{
Mat src = imread("1.jpg");
Mat dst;
cvtColor(src,dst,COLOR_BGR2BGRA);
imshow("效果图",dst);
waitKey();
}
COLOR_BGR2BGRA为一个标识符,表示将RGB转化成BGR。除此之外还有其他许多标识符,可以在使用时在网上查询。