OpenCV及基本用法

devtools/2024/12/21 21:26:08/

一.OpenCV介绍

   1.OpenCV 的全称是 Open Source Computer Vision Library,是一个开放源代码的

计算机视觉库。OpenCV 是最初由英特尔公司发起并开发,以 BSD 许可证授权发

行,可以在商业和研究领域中免费使用,现在美国 Willow Garage 为 OpenCV 提

供主要的支持。OpenCV 可用于开发实时的图像处理、计算机视觉以及模式识别

程序,目前在工业界以及科研领域广泛采用。

   2.OpenCV 的来源

    OpenCV 诞生于 Intel。Intel 最初希望提供一个计算机视觉库,使之能充分发

掘 CPU 的计算能力,当然更希望以此促进 Intel 的产品的销售。

    在 2008 年,一家美国公司,Willow Garage2,开始大力支持 OpenCV,Vadim

Pisarevsky 和 Gary Bradski 都加入了 Willow Garage。Gary Bradski 也是 OpenCV 开

发者中的元老级人物,他曾出版《Leaning OpenCV》一书,广受欢迎。

Willow Garage 是一家机器人公司,致力于为个人机器人开发开放的硬件平

台和软件。现在已经开发了 PR2 机器人,并支持 ROS、OpenCV、PCL 等软件。ROS

(Robot Operating System)是用于机器人的操作系统,是一个开放源代码的软件,

OpenCV 作为 ROS 的视觉模块嵌入。

自从获得 Willow Garage 支持后,OpenCV 的更新速度明显加快。大量的新特

性被被加入 OpenCV 中,很多算法都是最近一两年的新的科研成果。OpenCV 正

日益成为算法研究和产品开发不可缺少的工具。

3.OpenCV 的协议

OpenCV 采用 BSD 协议,这是一个非常宽松的协议。简而言之,用户可以修

改 OpenCV 的源代码,可以将 OpenCV 嵌入到自己的软件中,可以将包含 OpenCV

的软件销售,可以用于商业产品,也可以用于科研领域。BSD 协议并不具有“传

染性”,如果你的软件中使用了 OpenCV,你不需要公开代码。你可以对 OpenCV

做任何操作,协议对用户的唯一约束是要在软件的文档或者说明中注明使用了

OpenCV,并附上 OpenCV 的协议。

在这个宽松协议下,企业可以在 OpenCV 基础之上进行产品开发,而不需要

担心版权问题(当然你要注明使用了 OpenCV,并附上 OpenCV 的协议)。科研领

域的研究者,可以使用 OpenCV 快速地实现系统原型。因此可以这样说,OpenCV

的协议保证了计算机视觉技术快速的传播,让更多的人从 OpenCV 受益。

二.OpenCV的基本用法

1.Mat 类

早期的 OpenCV 中,使用 IplImage 和 CvMat 数据结构来表示图像。IplImage

和 CvMat 都是 C 语言的结构。使用这两个结构的问题是内存需要手动管理,开

发者必须清楚的知道何时需要申请内存,何时需要释放内存。这个开发者带来了

一定的负担,开发者应该将更多精力用于算法设计,因此在新版本的 OpenCV 中

引入了 Mat 类。

新加入的 Mat 类能够自动管理内存。使用 Mat 类,你不再需要花费大量精

力在内存管理上。而且你的代码会变得很简洁,代码行数会变少。但 C++接口唯

一的不足是当前一些嵌入式开发系统可能只支持 C 语言,如果你的开发平台支持

C++,完全没有必要再用 IplImage 和 CvMat。在新版本的 OpenCV 中,开发者依

然可以使用 IplImage 和 CvMat,但是一些新增加的函数只提供了 Mat 接口。本书

中的例程也都将采用新的 Mat 类,不再介绍 IplImage 和 CvMat。

Mat 类的定义如下所示,关键的属性如下方代码所示:

class CV_EXPORTS Mat

{

public:

 //一系列函数

 ...

 /* flag 参数中包含许多关于矩阵的信息,如:

 -Mat 的标识

 -数据是否连续

 -深度

 -通道数目

 */

 int flags;

 //矩阵的维数,取值应该大于或等于 2

 int dims;

 //矩阵的行数和列数,如果矩阵超过 2 维,这两个变量的值都为-1

 int rows, cols;

 //指向数据的指针

 uchar* data;

 //指向引用计数的指针

 //如果数据是由用户分配的,则为 NULL

 int* refcount;

24

 //其他成员变量和成员函数

 ...

};

2.创建 Mat 对象

Mat 是一个非常优秀的图像类,它同时也是一个通用的矩阵类,可以用来创

建和操作多维矩阵。有多种方法创建一个 Mat 对象。

2.1构造函数方法

Mat 类提供了一系列构造函数,可以方便的根据需要创建 Mat 对象。下面是

一个使用构造函数创建对象的例子。

Mat M(3,2, CV_8UC3, Scalar(0,0,255));

cout << "M = " << endl << " " << M << endl;

第一行代码创建一个行数(高度)为 3,列数(宽度)为 2 的图像,图像元

素是 8 位无符号整数类型,且有三个通道。图像的所有像素值被初始化为(0, 0,

255)。由于 OpenCV 中默认的颜色顺序为 BGR,因此这是一个全红色的图像。

第二行代码是输出 Mat 类的实例 M 的所有像素值。Mat 重定义了<<操作符,

使用这个操作符,可以方便地输出所有像素值,而不需要使用 for 循环逐个像素

输出。

2.2常用的构造函数有:

Mat::Mat()

无参数构造方法;

Mat::Mat(int rows, int cols, int type)

创建行数为 rows,列数为 col,类型为 type 的图像;

Mat::Mat(Size size, int type)

创建大小为 size,类型为 type 的图像;

Mat::Mat(int rows, int cols, int type, const Scalar& s)

创建行数为 rows,列数为 col,类型为 type 的图像,并将所有元素初始

化为值 s;

Mat::Mat(Size size, int type, const Scalar& s)

创建大小为 size,类型为 type 的图像,并将所有元素初始化为值 s;

Mat::Mat(const Mat& m)

将 m 赋值给新创建的对象,此处不会对图像数据进行复制,m 和新对象

共用图像数据;

Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)

创建行数为 rows,列数为 col,类型为 type 的图像,此构造函数不创建

图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step

指定。

Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)

创建大小为 size,类型为 type 的图像,此构造函数不创建图像数据所需

内存,而是直接使用 data 所指内存,图像的行步长由 step 指定。

Mat::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 共用图像数据。

这些构造函数中,很多都涉及到类型type。type可以是CV_8UC1,CV_16SC1,…,

CV_64FC4 等。里面的 8U 表示 8 位无符号整数,16S 表示 16 位有符号整数,64F

表示 64 位浮点数(即 double 类型);C 后面的数表示通道数,例如 C1 表示一个

通道的图像,C4 表示 4 个通道的图像,以此类推。

如果你需要更多的通道数,需要用宏 CV_8UC(n),例如:

Mat M(3,2, CV_8UC(5));//创建行数为 3,列数为 2,通道数为 5 的图像

2.3create()函数创建对象

除了在构造函数中可以创建图像,也可以使用 Mat 类的 create()函数创建图

像。如果 create()函数指定的参数与图像之前的参数相同,则不进行实质的内存

申请操作;如果参数不同,则减少原始数据内存的索引,并重新申请内存。使用

方法如下面例程所示:

Mat M(2,2, CV_8UC3);//构造函数创建图像

M.create(3,2, CV_8UC2);//释放内存重新创建图像

需要注意的时,使用 create()函数无法设置图像像素的初始值。

2.4Mat 表达式

利用 C++中的运算符重载,OpenCV 2 中引入了 Mat 运算表达式。这一新特

点使得使用 C++进行编程时,就如同写 Matlab 脚本,代码变得简洁易懂,也便于

维护。

如果矩阵 A 和 B 大小相同,则可以使用如下表达式:

C = A + B + 1;

其执行结果是 A 和 B 的对应元素相加,然后再加 1,并将生成的矩阵赋给 C

变量。

下面给出 Mat 表达式所支持的运算。下面的列表中使用 A 和 B 表示 Mat 类

型的对象,使用 s 表示 Scalar 对象,alpha 表示 double 值。

加法,减法,取负:A+B,A-B,A+s,A-s,s+A,s-A,-A

缩放取值范围:A*alpha

矩阵对应元素的乘法和除法: A.mul(B),A/B,alpha/A

矩阵乘法:A*B (注意此处是矩阵乘法,而不是矩阵对应元素相乘)

矩阵转置:A.t()

矩阵求逆和求伪逆:A.inv()

矩阵比较运算:A cmpop B,A cmpop alpha,alpha cmpop A。此处 cmpop

可以是>,>=,==,!=,<=,<。如果条件成立,则结果矩阵(8U 类型矩

阵)的对应元素被置为 255;否则置 0。

矩阵位逻辑运算:A logicop B,A logicop s,s logicop A,~A,此处 logicop

可以是&,|和^。

矩阵对应元素的最大值和最小值:min(A, B),min(A, alpha),max(A, B),max(A, alpha)。

矩阵中元素的绝对值:abs(A)

叉积和点积:A.cross(B),A.dot(B)

下面例程展示了 Mat 表达式的使用方法,例程的输出结果如图 3.8 所示。

#include <iostream>

#include "opencv2/opencv.hpp"

using namespace std;

using namespace cv;

int main(int argc, char* argv[])

{

 Mat A = Mat::eye(4,4,CV_32SC1);

 Mat B = A * 3 + 1;

 Mat C = B.diag(0) + B.col(1);

 cout << "A = " << A << endl << endl;

 cout << "B = " << B << endl << endl;

 cout << "C = " << C << endl << endl;

 cout << "C .* diag(B) = " << C.dot(B.diag(0)) << endl;

 return 0;

}

3读写图像文件

将图像文件读入内存,可以使用 imread()函数;将 Mat 对象以图像文件格式

写入内存,可以使用 imwrite()函数。

3.1 读图像文件

imread()函数返回的是 Mat 对象,如果读取文件失败,则会返回一个空矩阵,

即 Mat::data 的值是 NULL。执行 imread()之后,需要检查文件是否成功读入,你

可以使用 Mat::empty()函数进行检查。imread()函数的声明如下:

Mat imread(const string& filename, int flags=1 )

很明显参数 filename 是被读取或者保存的图像文件名;在 imread()函数中,

flag 参数值有三种情况:

flag>0,该函数返回 3 通道图像,如果磁盘上的图像文件是单通道的灰

度图像,则会被强制转为 3 通道;

flag=0,该函数返回单通道图像,如果磁盘的图像文件是多通道图像,则

会被强制转为单通道;

flag<0,则函数不对图像进行通道转换。

imread()函数支持多种文件格式,且该函数是根据图像文件的内容来确定文

件格式,而不是根据文件的扩展名来确定。所只是的文件格式如下:

Windows 位图文件 - BMP, DIB;

JPEG 文件 - JPEG, JPG, JPE;

便携式网络图片 - PNG;

便携式图像格式 - PBM,PGM,PPM;

Sun rasters - SR,RAS;

TIFF 文件 - TIFF,TIF;

OpenEXR HDR 图片 - EXR;

JPEG 2000 图片- jp2。

你所安装的 OpenCV 并不一定能支持上述所有格式,文件格式的支持需要特

定的库,只有在编译 OpenCV 添加了相应的文件格式库,才可支持其格式。

3.2写图像文件

将图像写入文件,可使用 imwrite()函数,该函数的声明如下:

bool imwrite(const string& filename, InputArray image,

const vector<int>& params=vector<int>())

文件的格式由 filename 参数指定的文件扩展名确定。推荐使用 PNG 文件格

式。BMP 格式是无损格式,但是一般不进行压缩,文件尺寸非常大;JPEG 格式

的文件娇小,但是 JPEG 是有损压缩,会丢失一些信息。PNG 是无损压缩格式,

推荐使用。

imwrite()函数的第三个参数 params 可以指定文件格式的一些细节信息。这

个参数里面的数值是跟文件格式相关的:

JPEG:表示图像的质量,取值范围从 0 到 100。数值越大表示图像质量

越高,当然文件也越大。默认值是 95。

PNG:表示压缩级别,取值范围是从 0 到 9。数值越大表示文件越小,

但是压缩花费的时间也越长。默认值是 3。

PPM,PGM 或 PBM:表示文件是以二进制还是纯文本方式存储,取值为

0 或 1。如果取值为 1,则表示以二进制方式存储。默认值是 1。

并不是所有的 Mat 对象都可以存为图像文件,目前支持的格式只有 8U 类型

的单通道和 3 通道(颜色顺序为 BGR)矩阵;如果需要要保存 16U 格式图像,只

能使用 PNG、JPEG 2000 和 TIFF 格式。如果希望将其他格式的矩阵保存为图像文

件,可以先用 Mat::convertTo()函数或者 cvtColor()函数将矩阵转为可以保存的格

式。

另外需要注意的是,在保存文件时,如果文件已经存在,imwrite()函数不会

进行提醒,将直接覆盖掉以前的文件。

下面例程展示了如何读入一副图像,然后对图像进行 Canny 边缘操作,最后

将结果保存到图像文件中。

#include <iostream>

#include "opencv2/opencv.hpp"

using namespace std;

using namespace cv;

int main(int argc, char* argv[])

{

 //读入图像,并将之转为单通道图像

 Mat im = imread("lena.jpg", 0);

 //请一定检查是否成功读图

 if( im.empty() )

 {

 cout << "Can not load image." << endl;

 return -1;

 }

 //进行 Canny 操作,并将结果存于 result

 Mat result;

 Canny(im, result, 50, 150);

 //保存结果

 imwrite("lena-canny.png", result);

 return 0;

}

4.OpenCV读写视频

OpenCV 2 中提供了两个类来实现视频的读写。读视频的类是 VideoCapture,

写视频的类是 VideoWriter。

4.1读视频

VideoCapture 既可以从视频文件读取图像,也可以从摄像头读取图像。可以

使用该类的构造函数打开视频文件或者摄像头。如果 VideoCapture 对象已经创

建,也可以使用 VideoCapture::open()打开,VideoCapture::open()函数会自动调用

VideoCapture::release()函数,先释放已经打开的视频,然后再打开新视频。

如果要读一帧,可以使用 VideoCapture::read()函数。VideoCapture 类重载了>>

操作符,实现了读视频帧的功能。下面的例程演示了使用 VideoCapture 类读视

频。

#include <iostream>

#include "opencv2/opencv.hpp"

using namespace std;

using namespace cv;

int main(int argc, char** argv)

{

 //打开第一个摄像头

 //VideoCapture cap(0);

 //打开视频文件

 VideoCapture cap("video.short.raw.avi");

 //检查是否成功打开

 if(!cap.isOpened())

 {

 cerr << "Can not open a camera or file." << endl;

 return -1;

 }

 Mat edges;

 //创建窗口

 namedWindow("edges",1);

 for(;;)

 {

 Mat frame;

 //从 cap 中读一帧,存到 frame

 cap >> frame;

 //如果未读到图像

 if(frame.empty())

 break;

 //将读到的图像转为灰度图

 cvtColor(frame, edges, CV_BGR2GRAY);

 //进行边缘提取操作

 Canny(edges, edges, 0, 30, 3);

 //显示结果

 imshow("edges", edges);

 //等待 30 秒,如果按键则推出循环

 if(waitKey(30) >= 0)

 break;

 }

 //退出时会自动释放 cap 中占用资源

 return 0;

}

4.2写视频

使用 OpenCV 创建视频也非常简单,与读视频不同的是,你需要在创建视频

时设置一系列参数,包括:文件名,编解码器,帧率,宽度和高度等。编解码器

使用四个字符表示,可以是 CV_FOURCC('M','J','P','G')、CV_FOURCC('X','V','I','D')及

CV_FOURCC('D','I','V','X')等。如果使用某种编解码器无法创建视频文件,请尝试其

他的编解码器。

将图像写入视频可以使用 VideoWriter::write()函数,VideoWriter 类中也重载

了<<操作符,使用起来非常方便。另外需要注意:待写入的图像尺寸必须与创建

视频时指定的尺寸一致。

下面例程演示了如何写视频文件。本例程将生成一个视频文件,视频的第 0

帧上是一个红色的“0”,第 1 帧上是个红色的“1”,以此类推,共 100 帧。

#include <stdio.h>

#include <iostream>

#include "opencv2/opencv.hpp"

using namespace std;

using namespace cv;

int main(int argc, char** argv)

{

 //定义视频的宽度和高度

 Size s(320, 240);

 //创建 writer,并指定 FOURCC 及 FPS 等参数

 VideoWriter writer = VideoWriter("myvideo.avi",

 CV_FOURCC('M','J','P','G'), 25, s);

 //检查是否成功创建

 if(!writer.isOpened())

 {

 cerr << "Can not create video file.\n" << endl;

 return -1;

 }

 //视频帧

 Mat frame(s, CV_8UC3);

 for(int i = 0; i < 100; i++)

 {

 //将图像置为黑色

 frame = Scalar::all(0);

 //将整数 i 转为 i 字符串类型

 char text[128];

 snprintf(text, sizeof(text), "%d", i);

 //将数字绘到画面上

 putText(frame, text, Point(s.width/3, s.height/3),

FONT_HERSHEY_SCRIPT_SIMPLEX, 3,

 Scalar(0,0,255), 3, 8);

 //将图像写入视频

 writer << frame;

 }

 //退出程序时会自动关闭视频文件

 return 0;

}


http://www.ppmy.cn/devtools/144182.html

相关文章

挑战一个月基本掌握C++(第七天)了解指针,引用,时间,输入输出,结构体,vector容器,数据结构 - 通用完结

一 指针 每一个变量都有一个内存位置&#xff0c;每一个内存位置都定义了可使用连字号&#xff08;&&#xff09;运算符访问的地址&#xff0c;它表示了在内存中的一个地址。 下面的实例&#xff0c;它将输出定义的变量地址&#xff1a; #include <iostream>using…

uniapp中的uni-file-picker组件上传多张图片到服务器

由于在uniapp官方文档中的uni-file-picker组件可实现图片上传功能&#xff0c;默认的是上传到自带的服务&#xff0c;所以我们要修改成自己的服务器 1. 添加 :auto-upload"false" 加上这个取消自动上传 <uni-file-picker v-model"jobAddUpdateForm.imag…

我应该如何安装Python3

安装Python3的步骤会因操作系统的不同而有所差异。以下是在不同操作系统上安装Python3的详细步骤&#xff1a; 一、在Windows系统上安装Python3 下载Python安装包&#xff1a; 打开任意浏览器&#xff0c;访问Python官方网站&#xff08;https://www.python.org&#xff09;。…

深度学习模型中增加随机性可以通过多种方式实现,以下是一些可以应用到你的 `TCNAttentionLSTM`

在深度学习模型中增加随机性可以通过多种方式实现&#xff0c;以下是一些可以应用到你的TCNAttentionLSTM模型中的方法&#xff1a; ### 1. Dropout 你已经在模型中使用了dropout&#xff0c;这是增加随机性的一种常见方法。你可以通过调整dropout率来控制随机性的程度。 ###…

Python球球大作战

系列文章 序号直达链接表白系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3Python无限弹窗满屏表白代码4Python李峋同款可写字版跳动的爱心5Python流星雨代码6Python漂浮爱心代码7Python爱心光波代码8Python普通的玫瑰花代码9Python炫酷的玫瑰花代码10Python多…

stm32进硬件错误怎么回事

STM32进入硬件错误状态&#xff0c;通常是由一些特定的编程或硬件问题引起的。以下是一些可能的原因及相应的解决方法&#xff1a; 可能的原因 数组越界操作&#xff1a;在编程过程中&#xff0c;如果数组访问超出了其定义的边界&#xff0c;可能会导致内存访问错误&#xff0…

使用 esrally race 测试 Elasticsearch 性能及 Kibana 可视化分析指南

前言&#xff1a; 在对 Elasticsearch 集群进行性能测试与调优的过程中&#xff0c;esrally 是官方推荐的测试工具。通过 esrally race 命令&#xff0c;我们可以模拟各种查询与索引负载&#xff0c;对集群进行基准测试。然而&#xff0c;仅看 esrally 的终端输出并不直观&…

android 登录界面编写

1、登录页面实现内容 1.实现使用两个EditText输入框输入用户名和密码。 2.使用CheckBox控件记住密码功能。 3.登录时候&#xff0c;验证用户名和密码是否为空。 4.当前CheckBox控件记住密码勾上时&#xff0c;使用SharedPreferences存储用户名和密码。 5.登录时候使用Prog…