目录
第1章 OpenCV简介
1.1 简介
1.1.1 OpencV 库简介
1.1.2 命名空间
1.2 OpenCV模块
1.3 装载、显示和存储图像
1.3.1 创建图像
1.3.2 读取图像
1.3.3 定义窗口与显示图像
1.3.4 图像翻转
1.3.5 保存图像
1.3.6 图像的复制
1.3.7 创建数组和向量
1.3.8 完整代码
1.4 深入了解cv::Mat
1.4.1 cv::Mat的用途
1.4.2 cv::Mat的结构
1.4.3 cv::Mat常见的属性
1.4.4 构造函数
Github代码地址:https://github.com/Qinong/OpenCV.git
第1章 OpenCV简介
1.1 简介
1.1.1 OpencV 库简介
OpencV 是一个开源的计算机视觉程序库,可在 Windows、 Linux、Mac、 Android、iOS等多种平台下运行。在 BSD 许可协议下,它可以用于学术应用和商业应用的开发,可随意使用、发布和修改。
从第3版开始,OpenCV 已经分成了两个主要部分。第一部分是包含了成熟算法的 OpenCV 主源码库。此外还有一个独立的代码库,它包含了最近加人 OpenCV 的计算机视觉算法。如果只想使用 OpenCV 的核心两数,就不需要这个 contrib包;但如果要使用最先进的算法,可能需要这个额外的模块。实际上,本专栏介绍了其中的几种高级算法,因此需要准备好 contrib 模块一一到(https://github.com/opencv/opencv_contrib)即可下载额外的 OpenCV 模块。模块解压后,可以放在任何目录下,但需要能够在openev_contrib-master/modules 中找到。
1.1.2 命名空间
在OpenCV 的C++ APT中,所有类和的数都在命名空间 cv 内定义。访问它们的方法共有两种,
(1)第一种是在定义 main 两数前使用如下声明:using namespace cv;
(2)第二种方法是根据命名空间规范给所有 OpenCV的类和两数加上前级cv::,本案例采用的就是这种方法,添加前缀后,代码中 OpenCV 的类和函数将更容易识别。
1.2 OpenCV模块
OpenCV 库有众多的功能模块:opencv_core 模块包含库的核心功能,openev_imgproc 模块包含主要的图像处理函数,openev_ highgui模块提供读写图像和视频的函数以及一些用户交互函数等等,OpenCV 库主要功能模块如下表格。
在使用某个模块之前,需要包含该模块对应的头文件。很多使用 OpenCV 的应用程序会在文件的开头处声明:
#include <opencv2 /core.hpp>
#include <opencv2 / imgoroc.hpp>
#include <opencv2/highgui .hpp>
在学习 OpenCV 的过程中,你会逐生发现它的众多模块中包含的大量功能。
OpenCV主要模块 | ||
模块 | 功能 | 说明 |
core | 核心模块 | 定义了多种数据结构(比如矩阵等)包括了重要的Mat模块。 |
imgproc | 图像处理模块 | 包含了图像相关的基础功能(图像过滤、几何图像变换、其他图像转换等),以及一些衍生的高级功能(结构分析和形状描述符、运动分析与目标跟踪、特征检测、目标检测、图像分割等。 |
highgui | 高级图形用户界面模块 | 比如图形交互界面,图像/视频文件的IO等。 |
calib3d | 摄像机标定与三维重建模块。 | 包含了基本的多视角几何算法,单个立体摄像头标定,物体姿态估计,立体相似性算法,3D信息的重建等。 |
features2d | 特征提取和特征匹配模块 | 用于特征检测和描述,关键点绘制函数和匹配功能绘制函数。 |
objdetect | 目标检测模块 | 包含Cascade Classification(级联分类)和Latent SVM这两个部分。 |
gpu | gpu加速计算机视觉模块 | OpenCV的GPU模块利用GPU计算功能的类和函数, 使用NVIDIA公司的CUDA API实现,仅支持NVIDIA GPU。 OpenCV GPU模块包括工具函数,和高级算法,为开发利用GPU的快速视觉算法提供了强大的基础。 |
ml | 机器学习模块 | 基本上是统计模型和分类算法,包含如下内容:统计模型、一般贝叶斯分类器、K-近邻 、支持向量机 、决策树 、提升、梯度提高树、随机树、超随机树、期望最大化、神经网络 。 |
stitching | 图像拼接模块 | 包含如下部分:拼接流水线、特点寻找和匹配图像、估计旋转、自动校准、图片歪斜、接缝估测、曝光补偿、图片混合。 |
1.3 装载、显示和存储图像
1.3.1 创建图像
(1)创建一个空图像,大小为0
cv::Mat image;
(2)指定矩阵大小,指定数据类型:
cv::Mat image(100,100,CV_8U); // 三个参数为:矩阵行数,矩阵列数,数据类型;
(3)指定矩阵大小,指定数据类型,设置初始值:
cv::Mat image1(300,300,CV_8U, 100); // 四个参数为:矩阵行数,矩阵列数,数据类型,初始值
cv::Mat image2(300,300,CV_8U, Scalar(0)); // 对于灰度图像:可以直接给出初始值,也可以使用Scalar()
cv::Mat image3(300,300,CV_8UC3, Scalar(0,0,255)); //对于三通道图像:使用Scalar()
1.3.2 读取图像
cv::Mat cv::imread(const String & filename,
int flags=IMREAD_COLOR)
- filename:需要打开图片的路径,可以是绝对路径或者相对路径,路径中不能出现中文。
- flag:图像的通道和色彩信息(默认值为1)。
cv::IMREAD_GRAYSCALE:读入灰度图片,可以直接写0
cv::IMREAD_UNCHANGED:读入完整图片,包括alpha通道,可以直接写-1
flag = -1, 8位深度,原通道
flag = 0, 8位深度,1通道
flag = 1, 8位深度,3通道
flag = 2, 原深度, 1通道
flag = 3, 原深度, 3通道
flag = 4, 8位深度,3通道
(1)读取并转换为灰度图像
在使用 imread装载图像时,可以通过设置选项把它转换为灰度图像。这个选项非常实用,因为有些计算机视觉算法是必须使用灰度图像的。在读人图像的同时进行色彩转换,可以提高运行速度并減少内存使用,做法如下所示:
image4 = cv::imread("Audi_S7.jpg");
image5 = cv::imread("Audi_S7.jpg", cv::IMREAD_GRAYSCALE);
注意:当用 imread 打开路径指定不完整的图像时,imread 会自动采用默认目录。如果从控制台运行程序,默认目录显然就是当前控制台的目录;但是如果直按在 DDE中运行程序,默认目录通常就是项目文件所在的目录。因此,要确保图像文件在正确的目录下。
1.3.3 定义窗口与显示图像
(1)定义窗口
cv::namedWindow(const String & winname,int flags = WINDOW_AUTOSIZE )
- winname:新建的窗口的名称。自己随便取。
- flags:窗口的标识,一般默认为WINDOW_AUTOSIZE 。
WINDOW_AUTOSIZE :窗口大小自动适应图片大小,并且不可手动更改。
WINDOW_NORMAL: 用户可以改变这个窗口大小。
WINDOW_OPENGL :窗口创建的时候会支持OpenGL。
(2)显示图像
cv::imshow(const String & winname,InputArray mat)
- winname:窗口的名称
- mat:待显示的图像
1.3.4 图像翻转
void cv::flip(cv::InputArray src, cv::OutputArray dst, int flipCode = 0);
- src:输入图像
- dst:输出图像
- flipCode:沿y-轴翻转, 0: 沿x-轴翻转, <0: x、y轴同时翻转
cv::flip(image4,image6,1);
1.3.5 保存图像
cv::imwrite(const String& filename, InputArray img,
const std::vector<int>& params = std::vector<int>());
- filename:图片名称
- img:Mat类型的图像数据,
- params:特定格式保存的参数编码,默认值std::vector<int>() 所以一般可以不写
1.3.6 图像的复制
// 所有图像指向同一个数据块,会影响原数据块
cv::Mat image7(image4);
image7= image4;
// 复制图像的副本,不会影响原数据
cv::Mat image8= image4.clone();
image8.copyTo(image4);
// 复制图像,两者的数据类型不一定相同,转换为浮点型图像[0,1]
image4.convertTo(image9,CV_32F,1/255.0,0.0);
1.3.7 创建数组和向量
cv::Matx33d matrix(3.0, 2.0, 1.0,
2.0, 1.0, 3.0,
1.0, 2.0, 3.0); // 创建3x3双精度型矩阵
cv::Matx31d vector(5.0, 1.0, 3.0); // 创建3x1双精度型矩阵(向量)
cv::Matx31d result = matrix*vector; // 相乘
1.3.8 完整代码
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
int main() {
// code1
// 创建图像
cv::Mat image1(100,100,CV_8U, 100); // 四个参数为:矩阵行数,矩阵列数,数据类型,初始值
cv::Mat image2(100,100,CV_8U, cv::Scalar(0)); // 对于灰度图像:可以直接给出初始值,也可以使用Scalar()
cv::Mat image3(240,320,CV_8UC3,cv::Scalar(0,0,255)); //对于三通道图像,创建一个红色的图像,通道顺序为BGR
// or:
// cv::Mat image3(cv::Size(320,240),CV_8UC3);
// image3= cv::Scalar(0,0,255);
cv::imshow("Image1", image1); // 显示图像
cv::imshow("Image2", image2); // 显示图像
cv::imshow("Image3", image3); // 显示图像
cv::waitKey(0); // 等待按键
// code2
// 读取图像
cv::namedWindow(image4,WINDOW_AUTOSIZE )
cv::namedWindow(image5,WINDOW_AUTOSIZE )
image4 = cv::imread("Audi_S7.jpg", cv::IMREAD_GRAYSCALE);
image5 = cv::imread("Audi_S7.jpg", cv::IMREAD_GRAYSCALE);
cv::imshow("Image4", image4);
cv::imshow("Image5", image5);
cv::waitKey(0);
// code3
// 转换图像
cv::flip(image4,image6,1);
cv::imshow("Image6", image6);
cv::waitKey(0);
// code4
// 图像的复制
// 所有图像指向同一个数据块,会影响原数据块
cv::Mat image7(image4);
image7= image4;
// 复制图像的副本,不会影响原数据
cv::Mat image8= image4.clone();
image8.copyTo(image4);
// 复制图像,两者的数据类型不一定相同,转换为浮点型图像[0,1]
image4.convertTo(image9,CV_32F,1/255.0,0.0);
// code5
// 数组和向量
// 创建3x3双精度型矩阵
cv::Matx33d matrix(3.0, 2.0, 1.0,
2.0, 1.0, 3.0,
1.0, 2.0, 3.0);
// 创建3x1双精度型矩阵(向量)
cv::Matx31d vector(5.0, 1.0, 3.0);
// 相乘
cv::Matx31d result = matrix*vector;
std::cout << result;
cv::waitKey(0);
return 0;
}
1.4 深入了解cv::Mat
1.4.1 cv::Mat的用途
(1)存储图像(图像可以看成一个矩阵)
(2)存储矩阵
1.4.2 cv::Mat的结构
cv::Mat有两个必不可少的组成部分,Mat头部和Mat数据块两部分。Mat头部分大小是固定的,包含矩阵的大小,存储的方式,矩阵存储的地址等,数据部分是一个指向矩阵包含像素值的指针(data)。
1.4.3 cv::Mat常见的属性
要熟练使用OpenCV,最重要的就是学会Mat数据结构,在OpenCV中Mat被定义为一个类,把它看作一个数据结构,以矩阵的形式来存储数据的,Mat常见的属性如下:
属性 | 功能 |
---|---|
flags | Mat中的一些标记,一共32位,从低位到高位,包含了数据类型(0-2位)、通道数(3-11)等 |
data | 指向数据的指针 |
dims | 矩阵的维度 |
rows | 矩阵行数,维度大于2 则rows = -1 |
cols | 矩阵列数,维度大于2 则cols = -1 |
channels | 通道数量,若图像为RGB、HSV等三通道图像,则channels = 3;若图像为灰度图,则为单通道,则channels = 1 |
type | type() 表示了矩阵中元素的类型以及矩阵的通道个数 |
depth | 矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值,将type的预定义值去掉通道信息就是depth值。 |
size | 数据内是一维数组,有个两元素,size[0]矩阵的行数,size[1]矩阵的列数 |
elemSize | 矩阵中每一个元素的数据大小,elemSize = channels * depth / 8 。例如:type是CV_8UC3,elemSize = 3 * 8 / 8 = 3bytes |
elemSize1 | 单通道的矩阵元素占用的数据大小,elemSize1 = depth / 8。例如:type是CV_8UC3,elemSize1 = 8 / 8 = 1bytes |
step | 数据内是一维数组,有个两元素,step[0]矩阵一行共多少字节,step[1]表示矩阵一个元素多少字节 |
refcount | 矩阵数据的引用次数,浅拷贝得到的矩阵同步这个参数,同时增加或减少,可以用来判断有多少矩阵使用该矩阵的数据内容 |
type()数据类型:type() 表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量。其命名规则为CV_(位数)+(数据类型)+(通道数)。
U(unsigned integer):无符号整数
S(signed integer):有符号整数
F(float): 浮点数
例如:CV_8UC3,可拆分为:CV_:type的前缀,8U:8位无符号整数(depth),C3:3通道(channels)
type | 数据类型 |
---|---|
CV_8UC1、CV_8UC2、CV_8UC3 | uchar |
CV_8SC1、CV_8SC2、CV_8SC3 | char |
CV_16UC1、CV_16UC2、CV_16UC3 | ushort |
CV_16SC1、CV_16SC2、CV_16SC3 | short |
CV_32SC1、CV_32SC2、CV_32SC3 | int |
CV_32FC1、CV_32FC2、CV_32FC3 | float |
CV_64FC1、CV_64FC2、CV_64FC3 | double |
1.4.4 构造函数
构造函数类型 | 构造函数说明 |
Mat() | 无参构造方法 |
Mat(int rows, int cols, int type) | 创建行数为 rows,列数为 col,类型为 type 的图像 |
Mat(Size size, int type) | 创建大小为 size,类型为 type 的图像 |
Mat(int rows, int cols, int type, const Scalar& s) | 创建行数为 rows,列数为 col,类型为 type 的图像,并将所有元素初始化为值 s |
Mat(Size size, int type, const Scalar& s) | 创建大小为 size,类型为 type 的图像,并将所有元素初始化为值 s |
Mat(const Mat& m) | 将m赋值给新创建的对象,此处不会对图像数据进行复制,m和新对象共用图像数据,属于浅拷贝 |
Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP) | 创建行数为rows,列数为col,类型为type的图像,此构造函数不创建图像数据所需内存,而是直接使用data所指内存,图像的行步长由 step指定 |
Mat(Size size, int type, void* data, size_t step=AUTO_STEP) | 创建大小为size,类型为type的图像,此构造函数不创建图像数据所需内存,而是直接使用data所指内存,图像的行步长由step指定 |
Mat(const Mat& m, const Range& rowRange, const Range& colRange) | 创建的新图像为m的一部分,具体的范围由rowRange和colRange指定,此构造函数也不进行图像数据的复制操作,新图像与m共用图像数据 |
Mat::Mat(const Mat& m, const Rect& roi) | 创建的新图像为m的一部分,具体的范围roi指定,此构造函数也不进行图像数据的复制操作,新图像与m共用图像数据 |