目录
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共用图像数据 |