1.4 基本数据类型

1.4.1 数据类型概述

OpenCV设计了许多种数据类型,让计算机视觉和机器学习任务变得更加简单、直观。本节介绍OpenCV中的三类主要数据类型,以及操作这些数据的基本函数。

OpenCV中的数据类型可分为三大类:

(1)基本数据类型。该类型直接由C++的数据类型(int或float等)组装而来,包括简单的向量和矩阵,以及简单的几何表示,如点、矩形和尺寸等。

(2)助手对象。这些对象表示更抽象的概念,如垃圾收集指针类等,用于表示切片的范围对象,以及对某些算法终止条件的抽象等。

(3)大型数组类型。该类型包含数组和其他常见的基本数据类型。大型数组类型的典型代表是cv::Mat类,用于表示包括任意基本元素的任意维度的数组。cv::Mat类的一个专门用途就是表示图像。

1.4.2 cv::Vec类

1. 基本概念

cv::Vec类可用来表示固定长度的向量(又称为固定向量类),是一个模板类,常使用[]来访问Vec向量成员,主要用来存储数值向量。除cv::Vec类外,还有固定维度的向量类cv::Matx。cv::Matx类可用来处理固定大小的小维度矩阵运算。在计算机视觉中,有许多2×2、3×3和少量4×4的矩阵,以及用于各类变换的矩阵,都可以使用cv::Matx类来表示。值得注意的是,无论cv::Vec类,还是cv::Matx类,在编译时都需要知道变量的长度,这使得计算变得十分高效。

2. 用法

(1)cv::Vec类可以定义任意类型的向量:

(2)cv::Vec类也可以使用以下预定义的类型:

(3)支持的运算如下:

3. 示例代码

示例代码1-1 cv:Vec类的初始化与基本运算

1)向量定义与向量元素的访问

运行示例代码1-1的定义与初始化部分,运行后屏幕输出如下:

使用变量查看窗口查看myVec向量,可以看到它是一个3行1列的int型cv::Matx类。cv::Matx类是cv:Vec类的基类,如图1-1所示。

图1-1 cv::Matx类的基类

2)基本运算

在示例代码1-1的计算部分,进行了向量的点积、叉积、加、减、标量乘、逻辑判断等运算,运行结果输出如下:

1.4.3 cv::Point类

1. 基本概念

与固定向量类密切相关的是cv::Point类(点类),它是一个用来存放2个或3个int或float等基本类型值的容器。cv::Point类是从它们自己的模板中派生的,并非来自固定向量类。点类和固定向量类之间的主要区别在于点类的成员可以通过命名变量(myPoint.x、myPoint.y等)访问,而不是通过向量索引(myVec [0],myVec [1])访问。与cv::Vec类一样,cv::Point类通常通过别名实例化适当的模板。这些别名包括cv::Point2i、cv::Point2f和cv::Point2d,或cv::Point3i、cv::Point3f和cv ::Point3d。

2. 用法

cv::Point类是OpenCV中最简单的类之一,其支持的构造方式与示例如表1-1所示。

表1-1 cv::Point类支持的构造方式与示例

3. 示例代码

示例代码1-2演示了cv:Point类的基本操作。

示例代码1-2 cv:Point类的基本操作

示例代码1-2的部分屏幕输出如下:

1.4.4 cv::Scalar类

cv::Scalar类(标量类)实际上是一个四维双精度向量类。cv::Scalar类有一些特殊的成员函数与计算机视觉中使用的4分量向量相关。cv::Scalar类支持的构造方式与示例如表1-2所示。

表1-2 cv::Scalar类支持的构造方式与示例

cv::Scalar类直接继承自固定向量类模板的实例,因此,它继承了所有向量的代数运算、成员访问函数(例如operator[])和固定长度向量类的其他属性。

1.4.5 cv::Size类

cv::Size类(尺寸类)在实践中与cv::Point类一样,并且可以与cv::Point类相互转换。两者的主要区别是cv::Point类的成员变量名是x和y,而cv:Size类的成员变量名是width和height。cv:Size类的三个别名是cv::Size、cv::Size2i和cv::Size2f。上述别名的前两个是等效的,成员为整型变量,而cv::Size2f的成员是32位浮点型变量。表1-3为cv::Size类支持的构造方式与示例。

表1-3 cv::Size类支持的构造方式与示例

与cv::Point类不同,cv::Size类不能转换为固定向量类,而cv::Point类和固定向量类可以转换为cv::Size类。

1.4.6 cv::Rect类

cv::Rect类(矩形类)包括cv::Point类的成员x和y(表示矩形的左上角)以及cv::Size类的成员width和height(表示矩形的大小)。矩形类不从cv::Point和cv::Size类继承,因此也不会继承它们相应的运算。表1-4为cv::Rect类支持的构造方式与示例。

表1-4 cv::Rect类支持的构造方式与示例

cv::Rect类还支持各种重载运算符,可用于计算两个矩形或一个矩形和另一个对象的各种几何属性,表1-5为cv::Rect类支持的重载操作与示例。

表1-5 cv::Rect类支持的重载操作与示例

为了更好地理解表1-5中的重载操作,我们通过示例代码1-3来演示。

示例代码1-3 cv::Rect类的重载操作

示例代码1-3的运行结果如图1-2所示。

图1-2 示例代码1-3的运行结果

示例代码1-3的屏幕输出如下:

1.4.7 cv::RotatedRect类

cv::RotatedRect类(有向矩形类)包含一个名为center(中点)的cv::Point2f变量、一个名为size(矩形大小)的cv::Size2f变量,以及一个名为angle的浮点数变量,表示矩形围绕中心的旋转。cv::RotatedRect类和cv::Rect类之间存在一个非常重要的区别——cv::RotatedRect类的位置相对于其center,而cv::Rect类的位置相对于其左上角。cv::RotatedRect类支持的构造方式与示例如表1-6所示。

表1-6 cv::RotatedRect类支持的构造方式与示例

示例代码1-4演示了如何旋转矩形。

示例代码1-4 旋转矩形

示例代码1-4的运行结果如图1-3(a)所示,图1-3(b)为cv::RotatedRect类成员变量的图示。

图1-3 示例代码1-4的运行结果与cv::RotatedRect类成员变量

示例代码1-4的屏幕输出如下:

1.4.8 cv::Mat类

cv::Mat类(数组类)用于表示任意维度的稠密数组(N-Dimensional Dense Arrays),其元素可以是任意类型的变量。在OpenCV中,我们熟悉的图像就是使用cv::Mat类来表示的。稠密数组指的是数组中每个元素都需要存储特定的值,例如图像。与稠密数组相对应的是稀疏数组cv::SparseMat类,在稀疏数组中,大多数元素都为0,因此只有非零元素才需要被存储,这样可以节约大量的存储空间,例如常见的直方图。感兴趣的读者可以查看帮助文档的描述,本书不作展开。

下面介绍如何创建一个数组。我们可以通过实例化一个cv::Mat类的变量来创建数组。数组的类型type决定了数组元素的类型。一个有效的类型包含类型和通道数,例如,对于CV_{8U, 16S, 16U, 32S, 32F, 64F}C{1, 2, 3}的组合,CV_8UC3表示一个三通道的8位无符号整型数据,CV_32FC1表示一个单通道的328位浮点数。在cv::Mat类中,有非常多的构造函数,包括未复制数据的构造函数与复制了数据的构造函数两种,它们的构造方式与示例分别如表1-7和表1-8所示。

表1-7 cv::Mat类未复制数据的构造方式与示例

表1-8 cv::Mat类复制了数据的构造方式与示例构造方式

表1-7中的基本构造函数主要采用以下四种方式来构造数组:

◎ 通过指定行和列构造一个二维数组。

◎ 使用cv::Size对象构造一个二维数组。

◎ 在构造n维数组时,使用一个整型的序列确定每一维数据的尺寸。

◎ 调用预置方法构造特殊类型的矩阵,例如全0、全1或者单位矩阵。

如果多维数组使用cv::Mat::ones或cv::Mat::eye构造函数,则只有第一通道会被设置为1,其他通道均为0。在构造数组时,还可以通过cv::Scalar变量给数组赋初值,如果使用这种方式赋初值,则数组中的每一个元素都会被初始化为该值。

cv::Mat类的构造如示例代码1-5所示。

示例代码1-5 cv::Mat类的构造

示例代码1-5的运行结果如图1-4所示。安装Image Watch插件后,可以看到内存中的图,便于调试程序,用Image Watch插件查看图像如图1-5所示。

图1-4 示例代码1-5的运行结果

图1-5 用Image Watch插件查看图像

单通道灰度图像数据存储示意图如图1-6所示。在多通道图像中,每列并列存放通道数量的子列,如RGB三通道彩色图像数据存储示意图如图1-7所示(按蓝、绿、红排列)。

图1-6 单通道灰度图像数据存储示意图

图1-7 RGB三通道彩色图数据存储示意图

示例代码1-5的屏幕输出如下:

1.4.9 基本矩阵运算

cv::Mat类的基本运算如表1-9所示。

表1-9 cv::Mat类的基本运算续表

cv::Mat类的构造如示例代码1-6所示。

示例代码1-6 cv::Mat类的构造

示例代码1-6的屏幕输出如下: