OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,它提供了很多函数,这些函数非常高效地实现了计算机视觉算法(最基本的滤波到高级的物体检测皆有涵盖)。 OpenCV 使用 C/C++ 开发,同时也提供了 Python、Java、MATLAB 等其他语言的接口。 OpenCV 是跨平台的,可以在  Windows、Linux、Mac OS、Android、iOS 等操作系统上运行。 OpenCV 的应用领域非常广泛,包括图像拼接、图像降噪、产品质检、人机交互、人脸识别、动作识别、动作跟踪、无人驾驶等。 OpenCV 还提供了机器学习模块,你可以使用正态贝叶斯、K最近邻、支持向量机、决策树、随机森林、人工神经网络等机器学习算法。

OpenCV 是一个开源的计算机视觉库,可以从 http://opencv.org 获取。 1999 年,Gary Bradski(加里·布拉德斯基)当时在英特尔任职,怀着通过为计算机视觉和人工智能的从业者提供稳定的基础架构并以此来推动产业发展的美好愿景,他启动了 OpenCV 项目。 OpenCV 库用C语言和 C++ 语言编写,可以在 Windows、Linux、Mac OS X 等系统运行。同时也在积极开发 Python、Java、Matlab 以及其他一些语言的接口,将库导入安卓和 iOS 中为移动设备开发应用。 OpenCV 自项目成立以来获得了来自英特尔和谷歌的大力支持,尤其需要感谢 Itseez,该公司完成了早期开发的大部分工作。此后,Arraiy 团队加入该项目并负责维护始终开源和免费的 OpenCV.org。

Itseez 是俄罗斯的一家视觉公司,专门从事计算机视觉算法。2016 年 5 月,英特尔收购该公司,以“帮助英特尔的用户打造创新型深度学习的 CV 应用,如果自动驾驶、数字安全监控和工业检测”(英特尔物联网总经理 Doug Dacies 如此说)。

OpenCV 设计用于进行高效的计算,十分强调实时应用的开发。它由 C++ 语言编写并进行了深度优化,从而可以享受多线程处理的优势。 OpenCV 的一个目标是提供易于使用的计算机视觉接口,从而帮助人们快速建立精巧的视觉应用。 OpenCV 库包含从计算机视觉各个领域衍生出来的 500 多个函数,包括工业产品质量检验、医学图像处理、安保领域、交互操作、相机校正、双目视觉以及机器人学。 因为计算机视觉和机器学习经常在一起使用,所以 OpenCV 也包含一个完备的、具有通用性的机器学习库(ML模块)。这个子库聚焦于统计模式识别以及聚类。ML 模块对 OpenCV 的核心任务(计算机视觉)相当有用,但是这个库也足够通用,可以用于任意机器学习问题。

IPPICV 加速

如果希望得到更多在英特尔架构上的自动优化,可以购买英特尔的集成性能基元(IPP)库,该库包含了许多算法领域的底层优化程序。在库安装完毕的情况下 OpenCV 在运行的时候会自动调用合适的 IPP 库。 从 OpenCV 3.0 开始,英特尔许可 OpenCV 研发团队和 OpenCV 社区拥有一个免费的 IPP 库的子库(称 IPPICV),该子库默认集成在 OpenCV 中并在运算时发挥效用。 如果你使用的是英特尔的处理器,那么 OpenCV 会自动调用 IPPICV。 IPPICV 可以在编译阶段链接到 OpenCV,这样一来,会替代相应的低级优化的C语言代码(在 cmake 中设置WITH_IPP=ON/OFF来开启或者关闭这一功能,默认情况为开启)。使用 IPP 获得的速度提升非常可观。  

opencv2计算机视觉编程手册 opencv视觉识别算法_计算机视觉

图:当 OpenCV 在 Intel Haswell 处理器上使用 IPPICV 时的加速效果

 给大家推荐一个国内OpenCV讲得最好的教程。


许多计算机科学家和经验丰富的程序员多多少少都了解计算机视觉的某些方面,但是很少有人熟谙计算机视觉的每一个应用。比如:

  • 很多人了解计算机视觉在安保行业的应用;
  • 一些人也知道它在网页端的图像和视频处理中的应用在逐渐增加。

但很少有人知道计算机视觉在游戏交互中的应用。同时,也很少有人认识到大部分航空图像和街景图像(比如说谷歌街景)已经大量应用相机校正和图像拼接技术。 有一些人略微知道一点视觉在自动监控、无人机或者生物制药分析上的应用,但很少有人知道计算机视觉早已经在制造业普遍使用。事实上,批量制造的所有东西都已经利用计算机视觉在进行某些方面的质检工作了。 自从测试版本在 1999 年 1 月发布以来,OpenCV 已经广泛用于许多应用、产品以及科研工作中。这些应用包括在卫星和网络地图上拼接图像,图像扫描校准,医学图像的降噪,目标分析,安保以及工业检测系统,自动驾驶和安全系统,制造感知系统,相机校正,军事应用,无人空中、地面、水下航行器。 它也被运用于声音和音乐的识别,在这些场景中,视觉识别方法被运用于声音的频谱图像。 OpenCV 亦是斯坦福大学的机器人斯坦利至关重要的一部分,这个机器人赢得了美国国防部高级研究计划署主持的 DARPA 机器人挑战赛野外机器人竞速的 200 万美元大奖。

DARPPA 机器人挑战赛(DRC)是机器人领域的一项重大赛事,堪称“机器人的奥林匹克”。

OpenCV 使用开源许可证

OpenCV 的开源许可允许任何人利用 OpenCV 包含的任何组件构建商业产品。你也没有义务开源自己的产品或者对该产品所涉及领域进行反馈和改进,虽然我们希望你这样做。 在这种自由许可的影响下,项目有着极其庞大的用户社区,社区用户包括一些来自大公司的员工(IBM、微软、英特尔、索尼、西门子和谷歌等)以及一些研究机构(例如斯坦福大学、麻省理工学院、卡内基梅隆大学、剑桥大学以及法国国家信息与自动化研究所)。 此外,OpenCV 项目还有一个雅虎论坛组为用户提供提问和讨论的地方,该论坛组有超过 50 000 名成员。 OpenCV 在世界范围内都非常流行,尤其是在中国、日本、俄罗斯、欧洲和以色列有着庞大的用户社区。


计算机视觉这种技术可以将静止图像或视频数据转换为一种决策或新的表示。所有这样的转换都是为了完成某种特定的目的而进行的。 输入数据可能包含一些场景信息,例如“相机是搭载在一辆车上的”或者“雷达发现了一米之外有一个目标”。一个新的表示,意思是将彩色图像转换为黑白图像,或者从一个图像序列中消除相机运动所产生的影响。

人类的视觉

因为我们是被赋予了视觉的生物,所以很容易误认为“计算机视觉也是一种很简单的任务”。计算机视觉究竟有多困难呢? 请说说你是如何从一张图像中观察到一辆车的。你最开始的直觉可能具有很强的误导性。人类的大脑将视觉信号划分为许多通道,好让不同的信息流输入大脑。大脑已经被证明有一套注意力系统,在基于任务的方式上,通过图像的重要部分检验其他区域的估计。在视觉信息流中存在巨量的信息反馈,并且到现在我们对此过程也知之甚少。 肌肉控制的感知器和其他所有感官都存在着广泛的相互联系,这让大脑能够利用人在世界上多年生活经验所产生的交叉联想,大脑中的反馈循环将反馈传递到每一个处理过程,包括人体的感知器官(眼睛),通过虹膜从物理上控制光线的量来调节视网膜对物体表面的感知。

计算机的视觉

然而在机器视觉系统中,计算机会从相机或者硬盘接收栅格状排列的数字,也就是说,最关键的是,机器视觉系统不存在一个预先建立的模式识别机制。没有自动控制焦距和光圈,也不能将多年的经验联系在一起。大部分的视觉系统都还处于一个非常朴素原始的阶段。 图 1 展示了一辆汽车。在这张图片中,我们看到后视镜位于驾驶室旁边。但是对于计算机而言,看到的只是按照栅格状排列的数字。所有在栅格中给出的数字还有大量的噪声,所以每个数字只能给我们提供少量的信息,但是这个数字栅格就是计算机所能够“看见”的全部了。我们的任务变成将这个带有噪声的数字栅格转换为感知结果“后视镜”。 图 2 给出了为什么计算机视觉如此困难的另一些解释。  

opencv2计算机视觉编程手册 opencv视觉识别算法_opencv2计算机视觉编程手册_02

图1:对于计算机来说,汽车的后视镜就是一组栅格状排列的数字 图2:视觉的不适定问题,物体的二维表示可能随着视点的不同完全改变

一个数学物理定解问题的解如果存在,唯一并且稳定的,则说明该问题是适定的(well-posed);如果不满足,则说明该问题是不适定的(ill-posed)。

实际上,这一问题,正如我们之前所提出的,用“困难”已经不足以形容它了,它在很多情况下根本不可能解决。 给定一个对于 3D 世界的二维(2D)观测,就不存在一个唯一的方式来重建三维信号。即使数据是完美的,相同的二维图像也可能表示一个无限的 3D 场景组合中的任一种情况。 而且,前面也提到过,数据会被噪声和畸变所污染。这样的污染源于现实生活中的很多方面(天气、光线、折射率和运动),还有传感器中的电路噪声以及其他的一些电路系统影响,还有在采集之后对于图像压缩产生的影响。 在这一系列的影响之下,我们又该如何推动事情的进展呢? 在经典的系统设计中,额外场景信息可以帮助我们从传感器的层面改善获取信息的质量。

场景信息可以辅助计算机视觉

考虑这样一个例子,一个移动机器人需要在一栋建筑中找到并且拿起一个订书机。机器人就可能用到这样的事实:桌子通常放在办公室里,而订书机通常收纳在桌子里。这也同样给出了一个关于尺寸的推断:订书机的大小一定可以被桌子所收纳。 更进一步,这还可以帮助减少在订书机不可能出现的地方错误识别订书机的概率(比如天花板或者窗口)。机器人可以安全忽略掉 200 英尺高的订书机形状的飞艇,因为飞艇没有满足被放置在木制桌面上的先验信息。 相对的,在诸如图像检索等任务中,数据集中所有的订书机图像都是来自真实的订书机,这样不合常理的尺寸以及一些奇形怪状的造型都会在我们进行图片采集的时候隐式消除——因为摄影师只会去拍摄普通的正常尺寸的订书机。人们同样倾向于在拍摄的时候将拍摄目标放在图片的中间,并且倾向于在最能够展现目标特征的角度拍摄。因此,通常也有很多无意的附加信息在人们拍摄照片的时候无意加进去。 场景信息同样可以(尤其是通过机器学习技术)进行建模。隐式的变量(比如尺寸、重力的方向等不容易被直接观测到的)可以从带标记的数据集中发现关系并推测出来。或者,可以尝试使用附加的传感器测量隐式变量的值,比如利用激光雷达来测量深度,从而准确得到目标的尺寸。

使用统计的方法来对抗噪声

计算机视觉所面临的下一个问题是噪声,我们一般使用统计的方法来对抗噪声。 比如,我们很难通过单独的像素点和它的相邻像素点判断其是否是一个边缘点,但如果观察它在一个区域的统计规律,边缘检测就会变得更加简单了。 一个真正的边缘应该表现为一个区域内一连串独立的点,所有点的朝向都与其最接近的点保持一致。我们也可以通过时间上的累计统计对噪声进行抑制,当然也有通过现有数据建立噪声模型来消除噪声的方法。例如,因为透镜畸变很容易建模,我们只需要学习一个简单的多项式模型来描述畸变就可以几乎完美矫正失真图像。 基于摄像机的数据,计算机视觉准备做出的动作或决定是在特定的目的或者任务的场景环境中执行的。我们也许想要移除噪声或者修复被损坏的照片,这样安全系统就可以对试图爬上栏杆等危险行为发出警报,或者对于穿过某个游乐场区域的人数进行统计。 而在大楼中漫游的机器人的视觉软件将会采取和安全系统完全不同的策略,因为两种策略处于不同的语境中。一般来说,视觉系统所处的环境约束越严格,我们就越能够依赖这些约束来简化问题,我们最终的解决方案也越可靠。OpenCV 的目标是为计算机视觉需要解决的问题提供工具。在某些情况下,函数库中的高级功能可以有效解决计算机视觉中的问题。即使遇到不能够一次性解决的问题,函数库中的基础组件也具有足够的完备性来增强解决方案的性能,以应对任意的计算机视觉难题。 在后一种情况下,也存在一些使用库的可靠方法,所有的这些方法都是从尽量多使用不同的组件库开始。通常,在开发了第一个粗糙的解决方案之后,就可以发现解决方案存在哪些缺陷并且使用自己的代码与聪明才智修复那些缺陷(更为熟知的说法是“解决真正存在的问题,而不是你想象中的那些问题”)。在此之后可以使用粗糙的解决方案作为一个评判标准,评价改善水平。从这一点出发,你可以解决任意问题。

 


OpenCV 缘起于英特尔想要增强 CPU 集群性能的研究。该项目的结果是英特尔启动了许多项目,包括实时光线追踪算法以及三维墙体的显示。 其中一位研究员 Gary Bradski(加里·布拉德斯基),当时正在为英特尔工作,他在访问大学的时候注意到很多顶尖大学研究机构,比如 MIT 的媒体实验室,拥有非常完备的内部公开的计算机视觉开发接口——代码从一个学生传到另一个学生手中,并且会给每个新来的学生一个有价值的由他们自己开发的视觉应用方案。相较于从头开始设计并完成基本功能,新来的学生可以在之前的基础上进行很多新的工作。 所以,OpenCV 怀着为计算机视觉提供通用性接口这一思想开始了策划。在英特尔性能实验室(Performance Library)团队的帮助下,OpenCV 最初的核心代码和算法规范是英特尔俄罗斯实验室团队完成的,这就是 OpenCV 的缘起,从英特尔软件性能组的实验研究开始,俄罗斯的专家负责实现和优化。

Shinn Horng Lee (李信弘)是主要推动者,时任 IPP(集成性能基元)首席架构师。

俄罗斯专家团队的负责人是 Vadim Pisarevsky(瓦迪姆·彼萨里夫斯基),他负责规划、编程以及大部分 OpenCV 的优化工作,并且到现在他仍是很多 OpenCV 项目的核心人物。 与他一同工作的 Victor Eruhimov(维克托·伊拉西莫夫)帮助构建了早期框架,Valery Kuriakin(瓦勒利·库里阿基恩)负责管理俄罗斯实验室并且为项目提供了非常大的助力。 以下是 OpenCV 想要完成的一些目标。

  • 为高级的视觉研究提供开源并且优化过的基础代码,不再需要重复造轮子。
  • 以提供开发者可以在此基础上进行开发的通用接口为手段传播视觉相关知识,这样代码有更强的可读性和移植性。
  • 以创造可移植的、优化过的免费开源代码来推动基于高级视觉的商业应用,这些代码可以自由使用,不要求商业应用程序开放或免费。

这些目标阐述了 Opencv 创建的目的。启用计算机视觉程序,将会增加对高性能处理器的需求,从而使得用户购买更快的处理器,相较于售卖额外的软件,能够更快增加英特尔的收入。 也许这就是这样一个开源并且免费的代码库是由一个硬件厂商而非软件厂商开发的原因。有时,硬件厂商内部拥有更多针对软件的创新空间。

OpenCV 目前的情况

对于开源项目来说,项目需要超过一个临界质量使其能够自我维持,这是开源项目非常重要的一点。 目前,OpenCV 已经拥有接近 1100 万次的下载量(2009年3月,200万次),并且这个数字还在以平均每个月 160 000 次下载的速度增长。OpenCV 得到了来自很多用户的贡献,研发主力也很大部分转移到英特尔之外。 Opencv 发展的时间线如图 1 所示。在发展过程中,OpenCV 受到互联网泡沫的影响,也受到管理层和方向变更等诸多变化的影响,在这些波动的过程中,有时候根本就没有英特尔公司任何人员参与。然而,随着多核处理器的出现以及计算机视觉的许多新应用的问世,OpenCV 的价值开始上升。 类似地,机器人领域的快速增长也推动了许多用户开始使用和开发这个库。在成为一个开源的函数库之后,OpenCV 经过柳树车库(Willow Garage,一个机器人研究机构和孵化器)几年的开发,现在已经得到了 OpenCV 基金会的支持。在今天,OpenCV 由基金会以及一些上市公司和私人机构开发。  

opencv2计算机视觉编程手册 opencv视觉识别算法_OpenCV_03

图1:OpenCV 发展历程

对 OpenCV 3.x 的补充说明:

  • 2016 年 12 月发布 OpenCV 3.2;
  • 2017 年 8 月发布 OpenCV 3.3;
  • 2017 年 12 月发布 OpenCV 3.4。

OpenCV 更详细的发展历程可以访问:Releases - OpenCV

谁拥有OpenCV

尽管 Gary Bradski 在英特尔发起了 OpenCV 项目,但这个函数库的宗旨是促进商业和研究,这正是它的使命。 因此,它是开源并且免费的,并且无论是研究还是商业目的代码都可以(全部或者部分的)使用或者被嵌入到其他程序中。它并不强制你声明基于此开发的代码必须是免费或者开源的,也不要求你将自己的改进反馈回 OpenCV 中,虽然我们希望可以。

 


OpenCV 是由很多模块组成的,这些模块可以分成很多层:

  • 最底层是基于硬件加速层(HAL)的各种硬件优化。
  • 再上一层是 opencv_contrib 模块所包含的 OpenCV 由其他开发人员所贡献的代码,其包含大多数高层级的函数功能。这就是OpenCV的核心。
  • 接下来是语言绑定和示例应用程序。
  • 处于最上层的是 OpenCV 和操作系统的交互。

下图显示了 OpenCV 的这种组织关系。  

opencv2计算机视觉编程手册 opencv视觉识别算法_人工智能_04

图1:OpenCV 层级结构及其所支持的操作系统

下表给出了 OpenCV 包含的具体模块,虽然这些模块会随着时间推移而不断的发展,但模块始终是组成这个库的基本单位,每个函数都是一个模块的一部分。  

表1:当前 OpenCV 所拥有的模块

模块

说明

Core

该模块包含 OpenCV 库的基础结构以及基本操作。

Improc

图像处理模块包含基本的图像转换,包括滤波以及类似的卷积操作。

Highgui

在 OpenCV 3.0中,分割为 imcodecs、videoio 以及 highgui 三部分。


这个模块包含可以用来显示图像或者简单的输入的用户交互函数。这可以看作是一个非常轻量级的 Windows UI 工具包。

Video

该模块包含读取和写视频流的函数。

Calib3d

这个模块包括校准单个、双目以及多个相机的算法实现。

Feature2d

这个模块包含用于检测、描述以及匹配特征点的算法。

Objdectect

这个模块包含检测特定目标,比如人脸或者行人的算法。也可以训练检测器并用来检测其他物体。

Ml

机器学习模块本身是一个非常完备的模块,包含大量的机器学习算法实现并且这些算法都能和 OpenCV 的数据类型自然交互。

Flann

Flann 的意思是“快速最邻近库”。这个库包含一些你也许不会直接使用的方法,但是其他模块中的函数会调用它在数据集中进行最邻近搜索。

GPU

在 OpenCV 中被分割为多个 cuda* 模块。


GPU 模块主要是函数在 CUDA GPU 上的优化实现,此外,还有一些仅用于 GPU 的功 能。其中一些函数能够返回很好的结果,但是需要足够好的计算资源,如果硬件没有GPU,则不会有什么提升。

Photo

这是一个相当新的模块,包含计算摄影学的一些函数工具。

Stitching

本模块是一个精巧的图像拼接流程实现。这是库中的新功能,但是,就像 Photo 模块一样,这个领域未来预计有很大的增长。

Nonfree

在 OpenCV 3.0 中,被移到 opencv_contrib/xfeatures2d。


OpenCV 包含一些受到专利保护的或者受到使用限制的(比如 SIFT 算法)算法。这些算法被隔离到它们自己的模块中,以表明你需要做一些特殊的工作,才可以在商业产品中使用它们。

Contrib

在 OpenCV 3.0 中,融合进了 opencv_contrib。


这个模块包含一些新的、还没有被集成进 OpenCV 库的东西。

Legacy

在 OpenCV 3.0 中,被取消。


这个模块包含一些老的尚未被完全取消的东西。

ocl

在OpenCV 3.0 中,被取消,取而代之的是 T-API。


这是一个较新的模块,可以认为它和 GPU 模块相似,它实现了开放并行编程的 Khronos OpenCL 标准。虽然现在模块的特性比 GPU 模块少很多,但 ocl 模块的目标是提供可以运行在任何 GPU 或者是其他可以搭载 Khronos 的并行设备。这与 GPU 模 块形成了鲜明的对比,后者使用 Nividia CUDA 工具包进行开发,因此只能在 Nividia GPU 设备上工作。

 


在 OpenCV 官方网站(Home - OpenCV)可以下载最新的且完整的源码以及大部分的 release 版本源码。具体的下载链接可以通过下载页面(Releases - OpenCV)访 问。 当然,最新的代码也会在 github(https://github.com/opencv/opencv)上进行即时更新。如果想要获取最新的高级函数功能,也可以下载和编译 open_contrib 模块(GitHub - opencv/opencv_contrib: Repository for OpenCV's extra modules)。

安装

现在,OpenCV 使用 Git 作为版本管理工具,使用 Cmake 来构建工程。 在早些时候,OpenCV 开发者使用 Subversion 作为版本管理工具以及 automake 来构建工程项。当然,这些日子都一去不复返了。 在许多情况下,你不必担心构建问题,因为许多环境中都有预编译好的库。但是,一旦成为更加专业的使用者,势必需要重新编译库,并且根据具体的应用程序定制特定的选项。

在 Windows 系统下安装 OpenCV

在 Releases - OpenCV 可以发现最新的为 Windows 准备的最新版本 OpenCV 下载链接。可以通过这个链接下载一个 EXE 文件,该文件会把预编译好的 OpenCV 解压到你的电脑上,预编译版本支持不同版本的 Visual Studio 环境。现在,你很快就可以开始使用 OpenCV了。

注意:尽管 Windows 环境拥有预编译的 release 版本的库,但是它并不包含 debug 版本的库。所以,在开发 OpenCV 之前,需要打开解决方案并且自行编译那些库。

另一个额外的细节是,需要再添加一个名为OPENCV_DIR的环境变量来告诉编译器在哪里找到 OpenCV 的二进制文件。可以通过命令行工具(cmd)对此进行设置。

setx -m OPENCV_DIR D:\OpenCV\Build\x64\vc10

如果希望静态链接 OpenCV,就只需要做到这一步。如果希望使用 OpenCV 的动态链接库(DLL),就需要告诉系统在哪里找到它的二进制库。为了完成这一目标,只需要在库路径中添加%OPENCV_DIR%\bin。 以 Windows 10 为例,具体操作步骤是:右击计算机图标,选择“属性”,然后单击“高级系统设置”,最后选择“环境变量”,把 OpenCV 二进制文件的路径%OPENCV_DIR%\bin添加到 path 变量中。 OpenCV3 集成了 IPP,所以如果使用最新的 x86 或者 x64 CPU,就可以获得或多或少的性能优势。

从源代码编译 OpenCV

也可以按照如下操作从源码编译 OpenCV,具体步骤是:

  1. 运行 CMake GUI。
  2. 指定 OpenCV 源码所在路径以及构建目标文件夹(必须和源码所在路径不同)。
  3. 按两次 Configure 键(选择可以使用的 Visual Studio 编译器或者 MinGW 构建文件,如果正在使用 MinGW 的话),直到所有条目没有红色警示。
  4. 使用 Visual Studio 打开生成的解决方案并构建。如果使用的是 MinGW,则按照 Linux 的安装指导进行。

Linux 系统下安装 OpenCV

由于 GCC 和 GLIBC 在不同 Linux 版本(SuSE、Debian、Ubuntu 等等)下拥有不同的版本,所以 OpenCV 的 Linux 的预编译版本不包含 Linux 的版本号。 然而,在很多情况下,你的 Linux 版本会提供 OpenCV。如果你的版本不提供 OpenCV,你将不得不从源代码中构建,与 Windows 安装一样,可以从 Releases - OpenCV 下载源代码,但是在这种情况下,该链接会把你转向 SourceForge,在这里可以为当前的 OpenCV 源代码选择压缩文件。 为了编译这个库和示例程序,你需要准备很多库或者工具,其中包括:

  • GTK+ 2.x 或者更高的版本;
  • GCC 编译器;
  • cmake 构建工具;
  • libtbb(英特尔线程构建模块);
  • 还有一些可选项目,例如 zlib、libpng、libjpeg、libtiff 和 libjasper 的开发者版本(例如模块名称后带有-dev的版本);
  • 你也需要用到 Python 2.6 或者更高的版本(开发者包)以及 NumPy 使 OpenCV 可以在 Python 环境下工作;
  • 此外,还需要来自 ffmpeg 的 libavcodec 以及其他的 libav* 库(包含头文件)。

对于后者,请安装 Linux 发行版本所提供的 libav/ffmpeg 包,或者前往 http://www.ffmpeg.org 下载。ffmpeg 库拥有较低的通用公开(LGPL)许可证,但是它的一些组件拥有更严格的通用公开许可证(GPL)。为了结合使用非 GPL 的软件,你需要构建并且使用一个共享的 ffmpeg 库:

$> ./configure --enable-shared $> make $> sudo make install

当动态链接一个 LGPL 的库时,不需要为自己的代码使用 GPL 许可证。最终得到:

  • /usr/local/lib/libavcodec.so.*
  • /usr/local/lib/libavformat.so.*
  • /usr/local/lib/libavutil.so.*
  • 以及在 /usr/local/include/libav* 路径下的头文件

为了编译这个库,需要解压 tar.gz 文件并且切换到解压过程创建的源码文件夹中,然后进行如下操作:

mkdir release cd release cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local .. make sudo make install # optional

对指令的说明: 1) 第一个命令和第二个命令将创建名为 release 的子目录并且切换到 release 中。 2) 第三个命令告诉 cmak 工具如何设置你的工程。我们提供的例子可能是让你入门的正确方法,但是其他选项允许你启用更多的设置,例如:

  • 确定哪些例子需要被构建;
  • 添加对 Python 的支持;
  • 添加 CUDA GPU 的支持。

默认情况下,OpenCV 的 cmak 配置脚本会尝试查找和使用尽可能多的第三方库,例如如果它探测到 CUDA SDK 的存在,就会自动支持 GPU 加速的 OpenCV 功能。 3) 最后两个指令将编译整个库并将其安装到正确的位置。注意,如果使用 CMake 来建工程,就没有必要安装 OpenCV,只需要指定生成的 OpenCVConfig.cmake 件的路径就可以了。在前面的例子中,文件将被安装在 /usr/local/share/OpenCV。 正如 Windows 的例子所示,Linux 建立的 OpenCV 将自动利用 IPP 进行加速(如果有安装的话)。从 OpenCV 3.0 版本开始,OpenCV 的 cmak 设置脚本将自动下载并且链接一个 IPP 的免费子库(IPPICV)。如果想要禁用 IPP 加速,请在执行 CMake 的时候加上-D WITH_IPP=OFF指令。

Mac系统下安装 OpenCV

在 Mac 上安装步骤和 Linux 上的安装步骤十分接近,不同的是,Mac 拥有自己的开发环境 Xcode,它包含大部分在 CMake 过程中需要的东西。 你不需要 GTK+、TBB、libjpeg,并且:

  • 在默认情况下,Cocoa 会取代 GTK+;
  • 在默认情况下,QTKit 会取代 ffmpeg;
  • GDC 会取代 TBB 以及 OpenMP。

安装步骤和 Linux 下安装一致。需要添加-G Xcode 指令到 CMake 中来生成一个 Xcode 工程,从而可构建和 debug 工程。

从 Git 获取最新的 OpenCV

OpenCV 现在也处于积极的开发状态中,当 bug 报告中包含有准确的描述以及代码复现 bug 的时候,该 bug 通常会被迅速修复。然而,官方的 OpenCV 通常每年只发布一次到两次,如果你正在开发一个项目或产品,可能想要 OpenCV 能够尽快修复 bug 并更新。为了完成这些目的,需要在 GitHub 网站上访问 OpenCV 的 Git 仓库。 本节并不打算引导你如何使用 Git,如果你还在使用另外的一些开源项目,也许对这套操作已经很熟悉了。 Git 的命令行工具有 Linux、Mac OS X 以及大部分的类 UNIX 系统所支持。针对 Windows,我们推荐 TortoiseGit(https://tortoisegit.org/);针对 Mac OS X,SourceTree 也许适合。 在 Windows 上,如果想要从 Git 获得 OpenCV 最新的版本,你需要访问 https://github.com/opencv/opencv.git。 在 Linux 上,只需要输入如下指令:

git clone https://github.com/opencv/opencv.git


给大家推荐一个国内OpenCV讲得最好的教程。

本教程中,我们假定 C++ 是图像处理应用编程的主要语言,尽管实际上也提供了其他编程语言的接口和封装器(例如,Python、Java、MATLAB/Octave 等)。 本节说明如何用 OpenCV 的 C++API(一种易用的跨平台框架)开发应用。

库的一般使用方法

为了用 C++ 开发一个 OpenCV 应用,需要在代码中:

  1. 包含 OpenCV 的头文件定义;
  2. 链接 OpenCV 库(二进制文件),以获取最终的可执行文件;

OpenCV 头文件位于 OPENCV_BUILD\install\include\opencv2 目录中,此处对于每个模块都有一个文件(*.hpp)。用 #include 指令包含头文件,如下所示:

//在代码中包含所使用的每个模块的头文件 #include <opencv2/<module_name>/<module_name>.hpp>

用这个指令,可以包含用户程序所需的每个头文件。另一方面,如果包含 opencv.hpp 头文件,那么所有的头文件都将被自动包含,如下所示:

//在代码中包含所有的Opencv头文件 #include <opencv2/opencv.hpp>

注意,本地安装的所有模块都在 OPENCV_BUILD\install\include\opencv2\opencv_modules.hpp 头文件中定义,并在 OpenCV 的构建过程中自动生成。 #include 指令的使用并非总能保证对头文件的正确包含,因为告诉编译器在哪里能找到包含文件是有必要的。这一点可以通过传递具有文件位置的一个特殊参数来实现(例如,对于 GNU 编译器为 I\<location>)。 链接过程需要提供(动态或静态)链接库,在这里可以找到所需的 OpenCV 功能。这个过程通常用链接器的两种类型参数完成:库的位置(例如,GNU 编译器的 -L\<location>)和库的名字(例如,-l<module_name>)。

在 GCC online documentation- GNU Project 和 GNU Make Manual - GNU Project - Free Software Foundation 下,可以找到对于 GNU GCC 和 Make 可用的在线文档的一个完整列表。

开发新项目的工具

开发我们自己的 OpenCV C++ 应用的主要先决条件是:

  • OpenCV 头文件和库二进制文件:当然,我们需要编译 OpenCV,并且对这样一个编译,辅助库是先决条件。该代码包应该使用与生成用户应用程序相同的编译器来编译。
  • 一个 C++ 编译器:一些关联工具用作代码编辑器、调试器、项目管理器、构建过程管理器(例如,CMake)、修订控制系统(例如,Git、Mercurial、SVN 等)、类检测器等是方便的。通常,这些工具在一个集成开发环境(IDE)中一起进行配置。
  • 任何其他辅助库:有时编写最终应用程序所需的任何其他辅助库,例如绘图、统计等,可能是需要的。

对于编写 OpenCV C++ 应用,最流行的可用编译工具包是:

  • Microsoft Visual C(MSVC):只在 Windows 上支持,与 IDE Visual Studio 集成得很好,当然也可以与其他跨平台的 IDE 集成,例如,Qt 生成器或 Eclipse。与目前最新的 OpenCV 发布兼容的 MSVC 版本是 VC 10、VC 11 和 VC 12(Visual Studio 2010、2012 和 2013)。
  • GNU Compiler Collection GNU GCC:这是由 GNU 项目开发的一个跨平台的编译器系统。对于 Windows 来说,该工具包就是众所周知的 MinGW(最小的 GNU GCC)。与目前 OpenCV 发布兼容的版本是 GNU GCC 4.8。该工具包可以和若干 IDE 一起使用,例如,Qt 生成器、Code::Blocks、Eclipse 等。

对于本书提供的示例,我们为 Windows+Qt 5.2.1 库和 Qt 生成器 IDE(3.0.1)使用 MinGW 4.8 编译工具包。编译 OpenCV 需要跨平台 Qt 库所提供的新 UI 功能。

注意,对于 Windows,可以从 http://qt-project.org/ 下载一个 Qt 包(包括 Qt 库、Qt 生成器和 MinGW 工具包)。这个包大约 700MB。

Qt 生成器是一个针对 C++ 的跨平台 IDE,它集成了我们编写应用时所需的工具。在 Windows 中,可以使用 MinGW 或 MSVC。图 1 展示了对于一个 OpenCV C++ 项目具有不同的面板和视图的 Qt 生成器的主窗口。

opencv2计算机视觉编程手册 opencv视觉识别算法_opencv2计算机视觉编程手册_05

图 1 某 OpenCV C++ 项目中具有视图的 QT 生成器的主窗口

使用 Qt 生成器创建 OpenCV C++ 程序

接下来,我们说明如何用 Qt 生成器的 IDE 创建代码项目。特别地,我们将这个描述应用到一个 OpenCV 示例上。 使用 Qt 生成器,可以为任意 OpenCV 应用程序创建项目。要做到这一点,需要导航到 File|New File or File|Project,然后导航到 Non-Qt Project|Plain C++Project。之后,必须选择一个项目名字和将要存储的位置。 再下一步,为该项目(在该示例中,台式机为 Qt 5.2.1MinGW 32位)选择一个工具包(即编译器),并为生成的二进制文件定位。通常,使用两种可能的方法构建配置(配置文件):debug 和 release。这些配置文件设置合适的标志(flag)来构建和运行二进制文件。 使用 Qt 生成器创建一个项目时,生成两个特殊文件(具有 .pro 和 .pro.user 扩展名)来配置构建和运行过程。该构建过程是通过项目创建期间所选择的工具包确定的。对于 Qt 5.2.1MinGW 32 位包,这个过程则依赖于 qmake 和 mingw32-make 工具。使用 *.pro 文件作为输入,qmake 会为驱动每个配置文件(即 release 和 debug)的构建过程,产生该生成文件。在 Qt 生成器 IDE 使用 qmake 工具替代 CMake,可简化软件项目的构建过程。只需几行信息,即可自动产生生成文件。 以下代码行表示一个 *.pro 文件的示例(例如,showImage.pro):

TARGET: showImage
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
SOURCES += \
    showImage.cpp
INCLUDEPATH += C:/opencv300-buildQt/install/include
LIBS += -LC:/opencv300-buildQt/install/x64/mingw/lib \
    -lopencv_core300.dll \
    -lopencv_imgcodecs300.dll\
    -lopencv_highgui300.dll\
    -lopencv_imgproc300.dll

上面的文件说明 qmake 所需的选项,以便为项目构建二进制代码产生合适的生成文件。以一个标签开始的每一行,指定一个选项(TARGET、CONFIG、SOURCES、INCLUDEPATH 和 LIBS),后跟一个标志来添加(+=)或删除(-=)该选项的值。在这个示例项目中,使用非 Qt 控制台应用。 可执行文件是 showImage.exe(TARGET)和源文件 showImage.cpp(SOURCES)。因为这个项目是一个基于 OpenCV 的应用,所以后两个标签指定头文件的位置(INCLUDEPATH)以及由这个特定项目(core、imgcodecs、highgui和imgproc)所使用的 OpenCV 库(LIBS)。

注意,行尾的反斜线表示续行。

有关 Qt 项目开发中用到的工具(包括 Qt 生成器和 qmake)的详细描述,可访问 Qt Documentation | Home。


 图像处理依赖于得到一幅图像(例如,一张照片和一个视频帧)并通过应用信号处理技术的“播放”(playing)来得到预期的结果。 本节展示如何使用由 OpenCV 提供的函数从文件中读取图像。

OpenCV 基本 API 概念

Mat 类是存储和操作 OpenCV 中图像的主要数据结构。这个类是在 core 模块中定义的。OpenCV 已经实现了对于这些数据结构自动分配和释放内存的机制。但是,当数据结构共享相同的缓冲存储器时,程序员仍然应该特别注意。 例如,赋值运算符并没有从一个对象(Mat A)到另一个对象(Mat B)复制内存内容;而只是对其引用(相应内容的存储地址)的复制。之后,一个对象(A 或 B)的改变对两个对象都有影响。为了复制一个 Mat 对象的内存内容,应该使用成员函数 Mat::clone()。 注意,OpenCV 中的许多函数在处理密集的单通道或多通道数组时,常使用 Mat 类。但是在某些场合,使用一个不同的数据类型可能很方便。例如,std::vector<>、Matx<>、Vec<> 或 Scalar。为此,OpenCV 提供了代理类 InputArray 和 OutputArray,允许前面的任意类型作为函数的参数使用。 Mat 类用于密集的 n 维单通道或多通道数组。实际上它可以存储实数或复数值向量和矩阵、彩色图像或灰度图像、直方图、点云等。 有许多种不同的方式可用来创建一个 Mat 对象,最流行的方法是构造函数,其数组的大小和类型被指定为:

Mat(nrows,ncols,type,fillValue)

数组元素的初始值可以由 Scalar 类设置为一个典型的四元素向量(对于存储在数组中的图像的每个 RGB 和透明度分量)。下面展示 Mat 的一个使用示例:

Mat img_A(4,4,CV_8U,Scalar(255)); //白色图像; //具有 8 位无符号整数的 4x4 单个通道数组 //(最多 255 个值,对灰度图像有效,例如,255=白色)

DataType 类定义 OpenCV 的基本数据类型。基本数据类型可以是 bool、unsigned char、signed char、unsigned short、signed short、int、float、double 或者是以这些基本类型之一的值构成的一个元组(或称数组)。任何基本类型都可以用一个标识符以下面的形式定义:

CV_<bit depth>{U|S|F}C(<number of channels>)

在上面的代码中,U、S 和 F 分别代表 unsigned、signed 和 float 数据类型。 对于单通道数组,可应用下面的枚举类型,其数据类型的描述为:

enum{CV_8U=0,CV_8S=1,CV_16U=2,CV_16S=3,CV_32S=4,CV_32F=5,CV_64F=6};

需要注意的是,这三个声明是等价的:CV_8U、CV_8UC1 和 CV_8UC(1)。该单通道声明最适合于灰度图像的整型数组,然而一个数组的三通道声明更适合于具有三个分量(例如,RGB、BRG、HSV 等)的图像。对于线性代数运算,可以使用 float(F) 类型的数组。 我们可以为多通道数组(高达 512 个通道)定义上面所有的数据类型。图 1 说明一幅具有单个通道(CV_8U,灰度)图像的内部表示和具有三个通道(CV_8UC3,RGB)的同一幅图像。图 1 是通过在一个 OpenCV 可执行文件(showImage 示例)的窗口中所显示的一幅图像的放大来获得的:

opencv2计算机视觉编程手册 opencv视觉识别算法_opencv_06

图 1 一幅 RGB 彩色图像和灰度图像的一个 8 位表示

注意,使用 OpenCV 函数正确地保存一幅 RGB 图像是很重要的,该图像必须按照 BGR 通道顺序在内存中存储。按照同样的方式,当从一个文件中读取一幅 RGB 图像时,应按照 BGR 通道顺序将其存储在内存中。而且,需要补充第四个通道(alpha)来处理具有三个通道(RGB)的图像,加上了一个透明度。对于 RGB 图像,整数值越大,意味着像素更亮或 alpha 通道更透明。 所有的 OpenCV 类和函数都在 cv 命名空间(namespace)中,因此,在源代码中还有如下两个选项:

  1. 在包含头文件后还应添加使用命名空间 cv 的声明(using namespace cv)(这是本书所有代码示例所使用的选项)。
  2. 对用到的所有 OpenCV 类、函数和数据结构上附加 cv:: 前缀。如果由 OpenCV 提供的外部名字与常用的标准模块库(standard template library,STL)或其他库冲突,那么就推荐使用这个选项。

OpenCV支持图像文件的格式

OpenCV 支持最常见的图像格式。但是,某些图像格式需要(免费提供的)第三方类库。由 OpenCV 支持的主要格式有:

  • Windows bitmaps(*.bmp、*dib);
  • Portable image formats(*.pbm、*.pgm、*.ppm);
  • Sun rasters(*.sr、*.ras);

需要辅助库的格式有:

  • JPEG(*.jpeg、*.jpg、*.jpe);
  • JPEG 2000(*.jp2);
  • Portable Network Graphics(*.png);
  • TIFF(*.tiff、*.tif);
  • WebP(*.webp);

对于 OpenCV 3.0 版本,除了上面列出的格式外,它还包含一个由地理数据抽象库(Geographic Data Abstraction Library,GDAL)所支持格式(NITF、DTED、SRTM等)的驱动器,通过 CMake 的选项 WITH_GDAL 来设置。注意,在 Windows OS 上,对 GDAL 的支持还没有经过广泛测试。 在 Windows 和 OS X 中,默认对这些格式(libjpeg、libjasper、libpng和libtiff)使用 OpenCV 附带的编解码器。之后,在这些操作系统中,可以读取 JPEG、PNG 和 TIFF 格式。Linux(和其他类 UNIX 开源操作系统)会寻找安装在系统中的编解码器。在 OpenCV 之前,可以安装编解码器或从 OpenCV 包中通过在 CMake 中设置正确的选项(例如,BUILD_JASPER、BUILD_JPEG、BUILD_PNG 和 BUILD_TIFF)来构建其他库。

OpenCV示例代码

为了说明如何使用 OpenCV 读、写图像文件,现在,我们将描述 showImage 示例,如图 2 所示。

opencv2计算机视觉编程手册 opencv视觉识别算法_opencv2计算机视觉编程手册_07

图 2 showImage示例的输出窗口

从命令行执行该示例,对应的输出窗口如下:

<bin_dir>\showImage.exe fruits.jpg fruits_bw.jpg

在本示例中,两个文件名称作为参数给出。第一个是读取的输入图像文件。第二个是使用输入图像的一个灰度副本写入的图像文件,接下来,展示源代码及其说明:

1. #include <opencv2/opencv.hpp>
2. #include <iostream>
3. 4. using namespace std;
5. using namespace cv;
6. int main(int, char *argv[])
7. {

8. Mat in_image, out_image;
9. // Usage: <cmd> <file_in> <file_out>
10. //读取原始图像
11. in_image = imread(argv[1], IMREAD_UNCHANGED);
12. if (in_image.empty()) {

13. //检查是否读取图像
14. cout << "Error! Input image cannot be read...\n";
15. return -1;
16. }
17. //创建两个具有图像名称的窗口
18. namedWindow(argv[1], WINDOW_AUTOSIZE);
19. namedWindow(argv[2], WINDOW_AUTOSIZE>;
20. //在之前创建的窗口中显示图片
21. imshow(argv[1], in_image);
22. cvtColor(in_image, out_image, COLOR BGR2GRAY);
23. imshow(argv[2], in_image);
24. cout << "Press any key to exit...\n";
25. waitKey(); // Wait for key press
26. //写入图像
27. imwrite(argv[2], in_image);
28. return 0;
29. }

此处,使用 #include 指令包含 opencv.hpp 头文件,实际上,它包含所有的 OpenCV 头文件。通过包含该单个文件,不再需要包含其他文件。 声明所使用的 cv 命名空间之后,在这个命名空间内的所有变量和函数都不再需要 cv:: 前缀。在主函数中要做的第一件事情就是检查命令行中传递的参数个数。之后,如果出现错误,则显示一个帮助消息。

OpenCV读取图像文件

如果参数个数正确,那么使用函数 imread(argv[1],IMREAD_UNCHANGED)将图像文件读入到 Mat 对象 in_image 中。这里,第一个参数是在命令行中传递的第一个实参(argv[1]),第二个参数是一个标志(IMREAD_UNCHANGED),这就意味着存储到内存图像中的图像不会被改变。函数 imread 决定图像类型(编解码器)来自文件内容而不是来自文件扩展名。 函数 imread 的原型如下:

Mat imread(const String& filename,int flags = IMREAD_COLOR)

flag 指定读取图像的颜色,并在 imgcodecs.hpp 头文件中由如下枚举类型定义和解释:

1. enum { IMREAD_UNCHANGED = -1, // 8 位,彩色或非彩色
2. IMREAD_GRAYSCALE = 0, // 8 位,灰度
3. IMREAD_COLOR = 1, //未改变深度、颜色
4. IMREAD_ANYDEPTH = 2, //任意深度,未改变颜色
5. IMREAD_ANYCOLOR = 4, //未改变深度,任意颜色
6. IMREAD_LOAD_GDAL = 8 // 使用 gdaL 驱动程序
7. };

注意,在 OpenCV 3.0 版本中,函数 imread 是在 imgcodecs 模块中,而不是像 OpenCV 2.x 在 highgui 模块中。另外,因为一些函数和声明被移入到 OpenCV 3.0 中,所以连接器可能会由于找不到一个或多个声明(符号和/或函数)而得到一些编译错误。 为了弄清楚在什么地方(*.hpp)定义一个符号并且链接到哪个库,推荐使用 Qt 生成器 IDE 的如下技巧:向代码添加声明 #include<opencv2/opencv.hpp>。将鼠标光标放在该符号或函数上并按 F2 功能键;这样就会打开声明了该符号或函数的 *.hpp 文件。 读取输入的图像文件之后,应检查操作是否成功。可使用成员函数 in_image.empty() 来实现这个检查。如果读取图像文件时没有发生错误,会创建两个窗口分别显示输入图像和输出图像。使用如下函数进行窗口的创建:

void namedWindow(const String& winname,int flags = WINDOW_AUTOSIZE)

OpenCV 窗口是通过程序中一个意义明确的名字来识别的。通过下面 highgui.hpp 头文件中的枚举给出该标志的定义及其说明:

1. enum { WINDOW_NORMAL = 0x00000000,
2. //用户可以调整窗口的大小(无约束)
3. //还可以将一个全屏窗口切换为常规窗口
4. WINDOW_AUTOSIZE = 0x00000001,
5. //用户不能调整窗口的大小,
6. //窗口的大小由显示的图像所限制
7. WINDOW_OPENGL = 0x00001000, // OpenGL 支持的窗口
8. WINDOW_FULLSCREEN = 1,
9. WINDOW_FREERATIO=0x00000100,
10. //图像可任意使用(调整图像时可没有比例限制)
11. WINDOW_KEE PRATIO = 0x00000000
12. //调整图像时保持图像的缩放比例
13. };

一个窗口的创建不会在屏幕上显示任何内容。在一个窗口中显示一幅图像的函数(属于 highgui 模块)是:

void imshow(const String& winname,InputArray mat)

如果使用 WINDOW_AUTOSIZE 标志创建该窗口(winname),那么所显示的是原始大小的图像(mat)。 在 showImage 示例中,第二个窗口显示输入图像的一个灰度副本。为了将一幅彩色图像转换为灰度图像,使用 imgproc 模块的函数 cvtColor。实际上使用这个函数来改变图像的颜色空间。 在一个程序中创建的任何窗口都可以从默认设置下调整大小和进行移动。当不再需要任何窗口时,应该销毁窗口,以便释放其资源。像示例中那样,在一个程序结束时,会隐式地完成资源的释放。

在内部循环中处理事件

如果在一个窗口上显示一幅图像之后不再做任何事情,出乎意料地,将不再显示图像。在一个窗口显示一幅图像之后,我们应该开始一个循环,以获取和处理与用户和窗口交互有关的事件。通过如下函数可执行这样一个任务(从 highgui 模块中):

int waitKey(int delay=0)

这个函数在数毫秒(delay>0)内等待一个按键操作,并返回键的编码,如果延迟结束时没有按键则返回 -1。如果 delay 是 0 或负数,那么函数一直等待直到一个键被按下。

记住,只有至少创建和激活一个窗口时,函数 waitKey 才会工作。

OpenCV写入图像文件

imgcodecs 模块中的另一个重要函数是:

bool imwrite(const String& filename,InputArray img,const vector<int>& params=vector<int>())

这个函数将一幅图像(img)保存到一个文件(filename),作为第三个可选参数,一个“属性-值”对的向量指定编解码器的参数(为使用默认值将其设置为空)。编解码器由文件的扩展名决定。


 视频处理的是运动图像,而不是静止图像。视频资源可以是一个专用摄像机、网络摄像头、视频文件或图像文件序列。 在 OpenCV 中,VideoCapture 类和 VideoWriter 类为视频处理中所涉及的捕获和记录任务提供了一个易用的 C++API。 下方的 recVideo 示例是一个简短的代码片段,可以让你了解如何使用一个默认摄像机作为一个捕捉设备来抓取帧,对它们进行边缘检测,并且将新的转换视频帧作为一个文件保存。而且,创建两个窗口同时显示原始帧和处理过的帧。 recVideo 示例的代码为:

1. #include <opencv2/opencv.hpp>
2. #include <iostream>
3. using namespace std;
4. using namespace cv;
5. 6. int main(int, char **)
7. {

8. Mat in_frame, out_frame;
9. const char win1[]="Grabbing...", win2[]="Recording...";
10. double fps=30;//每秒的帧数
11. char file_out[]="recorded.avi";
12. 13. VideoCapture inVid(O) ; //打开默认摄像机
14. if ( !inVid.isOpened () ) { //检查错误
15. cout << "Error! Camera not ready...\n";
16. return -1;
17. }
18. //获取输入视频的宽度和高度
19. int width = (int)inVid.get(CAP_PROP_FRAME_WIDTH);
20. int height = (int)inVid.get(CAP_PR〇P_FRAME_HEIGHT);
21. VideoWriter recVid(file out,VideoWriter::fourcc('M','S','V','C'), fps, Size(width, height));
22. if (!recVid.isOpened()) {

23. cout << "Error! Video file not opened...\n";
24. return -1;
25. }
26. //为原始视频和最终视频创建两个窗口
27. namedWindow(win1);
28. namedWindow(win2);
29. while (true) {

30. //从摄像机读取帧(抓取并解码)
31. inVid >> in frame;
32. //将帧转换为灰度
33. cvtColor(in_frame, out_frame, C0L0R_BGR2GRAY);
34. //将幀写入视频文件(编码并保存)
35. recVid << out_frame ?
36. imshow (win1, in_frame);// 在窗口中显示帧
37. imshow(win2, out_frame); // 在窗口中显示帧
38. if (waitKey(1000/fps) >= 0)
39. break;
40. }
41. inVid.release(); // 关闭摄像机
42. return 0;
43. }

在本示例中,应该快速浏览以下这些函数:

  • double VideoCapture::get(int propId):这个函数为一个 VideoCapture 对象返回指定的属性值。在 videoio.hpp 头文件中包含了基于 DC1394(IEEE 1394 数码相机规范)属性的一个完整列表。
  • static int VideoWriter::fourcc(char c1,char c2,char c3,char c4):这个函数把四个字符连接起来形成一个 fourcc 码。在示例中,MSVC 代表微软视频(仅在 Windows 上可用)。有效的 fourcc 码列表被发布在 http://www.fourcc.org/codecs.php 上。
  • bool VideoWriter::isOpened():如果写入视频的对象被成功初始化,这个函数返回 true。例如,使用一个不正确的编解码器会产生一个错误。 注意,在一个系统中有效的 fourcc 码依赖于本地安装的编解码器。为了了解在本地系统中安装的 fourcc 编解码器是否可用,推荐 MediaInfo 上对很多平台都可用的开源工具 MediaInfo。
  • VideoCapture&VideoCapture::operator>>(Mat&image):这个函数抓取、解码并返回下一帧。这个方法和布尔函数 VideoCapture::read(OutputArray image)等价。可以使用这个函数而不使用函数 VideoCapture::grab(),然后使用 VideoCapture::retrieve()。
  • VideoWriter&VideoWriter::operator<<(const Mat&image):这个函数写入下一帧。这个方法和布尔函数 VideoWriter::write(const Mat&image)等价。 在本示例中,有一个读取/写入循环,可同时地获取并处理窗口事件。waitKey(1000/fps)函数调用负责执行这个任务。在这个示例中,1000/fps 表示返回外部循环之前等待的毫秒数。尽管不精确,但对于录制的视频仍能获取每秒帧数的一个近似度量。
  • void VideoCapture::release():这个函数释放视频文件或采集设备。尽管在本示例中没有必要显式地包含,但为了说明它的使用,示例中仍包含了这个函数。