opencv学习笔记详解基本图像容器Mat以及常用数据结构和函数

Posted 非晚非晚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv学习笔记详解基本图像容器Mat以及常用数据结构和函数相关的知识,希望对你有一定的参考价值。

1. Mat简介

在Opencv1代的时候,是使用lplImage 和 CvMat 数据结构来表示图像的,他们都是C语言的结构,申请的内存需要自己手动管理,特别是采用 lplImage 会直接暴露内存,如果忘记释放内存,就会造成内存泄漏。为此,OpenCV在2.0版本中引入了一个新的C++接口,利用自动内存管理给出了解决问题的新方法。使用这个方法,你不需要纠结在管理内存上,而且你的代码会变得简洁。Mat的优点如下:

  • 不需要手动申请一块内存;
  • 在不需要时不用再手动释放内存
  • 赋值运算符和拷贝构造函数只复制信息头;但可以通过函数clone()或者copyTo()来复制一幅图像的矩阵。
  • 可以通过类的封装,方便的获取到数据的相关信息。

Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。
在这里插入图片描述

class CV_EXPORTS Mat
{
public/*..很多方法..*/
/*............*/
 
int flags;/*flags指定图像的颜色空间  
            flags > 0 3通道的彩色图像
            flags = 0 灰度图像
            flags < 0 不作改变
          */
int dims;  /*矩阵的维数*/
int rows,cols; /*行和列的数量;矩阵的维数超过2维时为(-1,-1)*/
uchar *data;   /*指向数据*/
int * refcount;   /*指针的引用计数器; 阵列指向用户分配的数据时,指针为 NULL
/* 其他成员 */ 
...
};

2. Mat常用成员及含义

下面介绍一下Mat的常用成员函数。

  1. data

Mat对象中的一个指针,指向存放矩阵数据的内存(uchar* data)。

  1. dims

矩阵的维度。

  1. channels

矩阵通道,矩阵中的每一个矩阵元素拥有的值的个数,比如说 3 * 4 矩阵中一共 12 个元素,如果每个元素有三个值,那么就说这个矩阵是 3 通道的,即 channels = 3。常见的是一张彩色图片有红、绿、蓝三个通道。

  1. depth

深度信息,即每一个像素的位数,也就是每个通道的位数。在opencv的Mat.depth()中得到的是一个0 – 6的数字。

enum
{
	CV_8U=0, 
	CV_8S=1, 
	CV_16U=2, 
	CV_16S=3, 
	CV_32S=4, 
	CV_32F=5, 
	CV_64F=6
};
//举例:  CV_8U3; 8位的 unsigned char 型,每个像素由三个元素组成三通道

3. cv::Mat类对象的创建方法

3.1 构造函数

	cv::Mat M1(3, 3, CV_8UC4, cv::Scalar(0, 0, 0, 255));
	std::cout << "M1 = " << std::endl << M1 << std::endl;

这里使用了构造函数对Mat进行定义,分别指定行数、列数、4通道的矩阵,每个点的颜色为(0, 0, 0, 255)。该例的输出结果为:

M1 =
[ 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255;
0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255;
0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255]

3.2 通过数组初始化矩阵维数

	int sz[2] = { 3, 3 };//矩阵维数3*3
	cv::Mat M2(2, sz, CV_8UC1, cv::Scalar::all(0));
	std::cout << "M2 = " << std::endl << M2 << std::endl;

M2的分别指定了2维、sz指定了3行3列、单通道8位无符号和全部元素都为0,输出结果如下:

M2 =
[ 0, 0, 0;
0, 0, 0;
0, 0, 0]

3.3 通过create函数来初始化

 cv::Mat M3;
 M3.create(4, 4, CV_8UC(1));
 std::cout << "M3 = " << std::endl << M3 << std::endl;

create构造了4*4的单通道8位无符号,并且矩阵中的数据位随机值。如下:

[205, 205, 205, 205;
205, 205, 205, 205;
205, 205, 205, 205;
205, 205, 205, 205]

3.4 通过opencv提供的类似于matlab的函数创建

 cv::Mat mat_eye= cv::Mat::eye(4, 4, CV_64F);//对角
 std::cout << "mat_eye= " << std::endl << mat_eye<< std::endl;

 cv::Mat mat_ones= cv::Mat::ones(4, 4, CV_64F);//全1
 std::cout << "mat_ones= " << std::endl << mat_ones<< std::endl;

 cv::Mat mat_zeros= cv::Mat::zeros(4, 4, CV_64F);//全0
 std::cout << "mat_zeros= " << std::endl << mat_zeros<< std::endl;

eye函数表示的是单位矩阵,ones为全1的矩阵,zeros表示全是0的矩阵。输出如下:

mat_eye=
[1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 1, 0;
0, 0, 0, 1]
mat_ones=
[1, 1, 1, 1;
1, 1, 1, 1;
1, 1, 1, 1;
1, 1, 1, 1]
mat_zeros=
[0, 0, 0, 0;
0, 0, 0, 0;
0, 0, 0, 0;
0, 0, 0, 0]

3.5 数据自定义矩阵Mat创建

 cv::Mat M4 = (cv::Mat_<double>(3, 3) << 0, -1, 0, -1, 0, 0, 0, 0, 1);
 std::cout << "M4 = " << std::endl << M4 << std::endl;

创建M4的矩阵如下所示:

M4 =
[0, -1, 0;
-1, 0, 0;
0, 0, 1]

3.6 通过clone或者copyTo() 函数创建不同的Mat

 cv::Mat M5 = M4.row(1).clone();//拷贝第一行
 std::cout << "M5 = " << std::endl << M5 << std::endl;

Mat F = A.clone();//拷贝A矩阵

Mat G;
A.copyTo(G);//拷贝A矩阵

clone为深拷贝,输出结果如下:

M5 =
[-1, 0, 0]

除了clone和copyTo函数,类似于拷贝构造函数、赋值运算符的运算等都是通过采用引用的方式,即多个Mat对象共享同一个矩阵数据,这里使用的原理类似c++11中的共享指针,也就是说它们共享矩阵数据,而不共享矩阵头。

3.7 创建感兴趣区域(ROI)

创建感兴趣区域也是共享矩阵。

Mat D(A, Rect(10, 10, 100, 100));//使用矩阵界定
Mat E(Range:all(), Range(1, 3));//使用行和列来界定

R

3.8 为已存在的IplImage指针创建信息头

IplImage* iplImg = cvLoadImage("greatwave.jpg", 1);
Mat mtx(iplImg);

4. 像素值的存储方法

存储像素值需要指定颜色空间和数据类型,最简单的颜色空间为灰度级空间,只处理黑色和白色,对它们进行组合便可以产生不同的灰色。

RGB颜色空间是最常用的颜色空间,这归功于它也是人眼内部构成颜色的方式。有时为了表示透明颜色也会加入第4个元素alpha(A)。

颜色系统有很多,具体如下:

  • RGB是最常见的,这是因为人眼采用相似的工作机制,它也被显示设备所采用的。
  • HSV和HLS把颜色分解成色调、饱和度和亮度/明度。这是描述颜色更自然的方式,比如可以通过抛弃最后一个元素,使算法对输入图像的光照条件不敏感。
  • YCrCb在JPEG图像格式中被广泛使用。
  • CIE Lab*是一种在感知上均匀的颜色空间,它适合用来度量两个颜色之间的距离。

每个组成元素都有其定义域,定义域取决于其数据类型。如最小的数据类型是char,占一个字节,可以是有符号类型(0到255之间)或无符号类型(-127到+127之间)。尽管使用三个char可以表示1600万种颜色,但是使用float或者double则能给出更加精细的颜色分辨能力。

5. 点的表示:Point类

Point类数据结构表示了二维坐标系下的点。

Point point;
point.x = 10;
point.y = 8;

或者

Point point = Point(10, 8);

关于Point有如下定义:

typedef Point_<int> Point2i;
typedef Point2i Point;//Point类型
typedef Point_<float> Point2f;//float类型,同理还有Point3f

5. 颜色的表示:Scalar类

Scalar()表示具有4个元素的数组,在OpenCV中被大量用于传递像素值,如RGB颜色值。

Scalar(a, b, c);//红色:c 绿色:b 蓝色:a

Scalar类的源头为Scalar_类,而Scalar_类是Vec4x的一个变种,我们常用的Scalar其实就是Scalar_<double>。这就解释了为什么很多函数的参数输入可以是Mat,也可以是Scalar。

Vec是Matx的一个派生类,Matx是一个轻量级的Mat,Matx必须在使用前规定好大小。

6. 尺寸的表示:Size类

Size的定义如下:

typedef Size_<int> Size2i;
typedef Size2i Size;

Size_是一个模板类,使用频率最高的是下面的这个构造函数:

Size_ (_Tp _width, _Tp -height);

下面给出示例:

Size(5, 5);//宽度和高度都是5

7. 矩阵的表示:Rect类

Rect的成员变量有x,y,width,height,分别为左上角点的坐标和矩阵的宽和高。常用的成员函数有:

  • Size():返回值为Size;
  • area();返回矩阵的面积;
  • contains(Point):判断点是否在矩形内;
  • inside(Rect):判断矩形是否在该矩形内;
  • tl():返回左上角点坐标;
  • br():返回右下角坐标;

求矩阵的常用操作:

Rect rect = rect1 & rect2;//交集
Rect rect = rect1 | rect2;//并集
Rect rectShift = rect + point;//矩阵平移
Rect rectScale = rect + size;//矩阵缩放

9. 颜色空间转换:cvtColor函数

关于cvtColor可以参考这篇文章

以上是关于opencv学习笔记详解基本图像容器Mat以及常用数据结构和函数的主要内容,如果未能解决你的问题,请参考以下文章

opencv学习笔记opencv加载图像修改图像显示图像保存图像以及代码举例

详解OpenCV的Mat类(构造方法初始化方法常用属性常用成员函数常用操作)

OpenCV常用库函数

数字图像处理OpenCV3 学习笔记

OpenCV学习三:Mat类详解

opencv学习笔记基本图像的绘制——直线椭圆矩形圆和多边形