暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

OpenCV配置攻略(二): 安装包里都有什么(下)

麟十一 2021-09-08
698


 第 25 篇  |  LINSHIYI 

build文件夹(续上篇)

3.3 include/, x64/,bin/


include/, x64/, bin/


(1)include


include下存放的是C++的头文件(Header Files),头文件的后缀一般为.h或.hpp,文件中通常包含函数和变量的声明,我们在编写源代码时可以通过#include命令导入相应的头文件。


Header files allow us to put declarations in one location and then import them wherever we need them. This can save a lot of typing in multi-file programs.


有关头文件的更多内容推荐看这一篇教程https://www.learncpp.com/cpp-tutorial/header-files/


(2)bin、lib 和 static 的位置


bin和lib文件夹都存放在x64(或x86)中的vc文件夹下,x64和vc文件夹的具体内容见 OpenCV配置攻略(一):OpenCV, VC++和VS,这里不做过多解释了。


直接拿OpenCV3.4.10->x64->vc15举例,该文件夹下有bin和lib两个文件夹,在2.x版本中,还会存在名叫staticlib的文件夹。


OpenCV3.4.10->bin和lib

OpenCV2.4.10->bin, lib和staticlib


另外,在build文件夹下,还有一个名为bin的文件夹,这里面存放的是ffmpeg算法的64位动态库文件,而且这个文件在x64->vc15->bin文件夹下也存在,不知道为什么会被单分出来。

两个opencv_ffmpeg3410_64.dll


(3)C++编译的4个步骤


为了更好地理解bin、lib和staticlib文件夹的内容和作用,先来说说C++程序的执行过程。写好C++源代码之后,需要进行预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)、链接(Linking)4步,执行“链接”步骤的链接器会生成一个可执行文件,运行该文件就会输出C++的结果。

在类似Visual Studio这样的IDE中,当我们点击这个带小三角的按钮,Visual Studio就可以帮我们自动完成所有的步骤:

调试器按钮

下面简单介绍一下C++编译的四个步骤每一步都在做什么,内容会比较概括,想看详细的代码和解释见这篇博客https://www.calleluks.com/the-four-stages-of-compiling-a-c-program/

第一阶段叫预处理(Preprocessing),由预处理器(Preprocessor)执行。主要处理以#字符开头的预处理指令,完成文件包含、条件编译、展开宏定义、删除注释等任务,生成预处理文件(.i, .ii)。

文件包含举例,预处理器会将#include指令后的头文件(.h/.hpp)中的内容复制在该指令所在的位置,再详细一点,假如预处理器遇到#include <opencv2\opencv.hpp>语句,就会将其转换为include/opencv2文件夹下opencv.hpp文件的内容。

第二阶段叫做编译(Compilation),由编译器(Compiler)执行。主要任务是分析代码的语法,并将预处理后的代码翻译成汇编指令,输出汇编文件(.s)。

第三阶段是汇编(Assembly),汇编器(Assembler)会将汇编指令翻译成目标机器可以识别的机器指令,并生成二进制格式的目标文件(.o或.obj)。

这里小小延展一下,编程语言一般分为3类:机器语言、汇编语言和高级语言。C++、Python、Java这些语言都属于高级语言(High Level Language),简单来说就是对于编程者友好、可读性更强的语言。但计算机是无法识别高级语言的,计算机可以直接识别的语言叫做机器语言(Machine Language),也就是二进制代码。而汇编语言(Assembly Language)就是一种简化了的机器语言,它使用数字、标识符和英文缩写代替了0、1指令,但它本质上还是对计算机硬件的直接操作。有关这部分的更多内容可以看我写的第一篇文  为什么说Python是一门动态语言?

所以说,预处理->编译->汇编三步就是将C++源代码(高级语言)翻译成目标机器可直接识别的代码(机器语言)的过程

第四阶段叫做链接(Linking),由链接器(Linker)执行。链接器会获取汇编器生成的所有目标文件链接代码中所需要的库文件,并且确保所有跨文件的依赖关系都是正确的,最终链接器会输出一个可执行文件(.exe或.out)

下面的示例图来自https://www.learncpp.com/cpp-tutorial/introduction-to-the-compiler-linker-and-libraries/,可以看到,链接器会链接目标文件(.o)C++标准库(比如iostream)和第三方库。OpenCV就属于第三方库,build文件夹下lib、bin和staticlib中存放的就是第三方库文件。

“链接”过程示例


最后说一说跨文件的依赖关系,在一些复杂的项目中,源文件往往不是孤立存在的,比如A.cpp调用了B.cpp中定义的函数、B.cpp调用了其他的库文件等等。链接器在这里的作用就是保证这些函数、变量、文件等都可以被正确地调用,即拥有跨文件依赖关系的文件都被正确地“链接”在一起。

(4)库文件


A library is a collection of pre-compiled object files that can be linked into your programs via the linker.


这里提到的(Library)可以被看作一些目标文件(Object File)的集合,还记得吗?目标文件是“汇编”这一步输出的二进制文件,后缀是.o或.obj(定义来自 https://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html)


库的存在可以让我们在使用一些算法模块时省去重新编译的麻烦,而且库是二进制文件,我们打开它只能看到一堆0和1,所以库也可以起到保护程序源代码的作用。


OpenCV2.4.10安装包中,我们可以看到staticlib文件夹下保存了很多后缀为lib的静态库,例如opencv_calib3d2410d.lib、opencv_core2410.lib等。这里的calib3d、contrib、core都是OpenCV的算法模块,例如calib3d可以实现相机校准,contrib可以实现人脸识别等等。


OpenCV2.4.10->staticlib文件夹(部分)


2410代表OpenCV版本是2.4.10,带d的库是在Debug(调试)模式下调用的,没有d的库是在Release(发布)模式下调用的。一般来说Debug模式速度会比Release模式慢,因为它会输出很多调试信息,上图中的.pdb(Program Data Base)文件就是其中的一种调试信息。


在OpenCV2.x中各个模块的库文件还是分开的,在3.x和4.x中,所有模块都被打包成了一个库文件,并统一命名为“opencv_worldxxxx.dll”。下图是OpenCV3.4.10的动态库:opencv_world3410.dll和opencv_world3410d.dll。


OpenCV3.4.10->bin文件夹


刚刚说到了静态库和动态库,在OpenCV2.x安装包中的staticlib文件夹中存储的是静态库(Static Library),这个文件夹在OpenCV3.x和4.x中都已经没有了,如果需要的话只能自己通过源代码编译。


OpenCV2.x,3.x和4.x中都存在bin和lib文件夹,其中bin文件夹下存放的是动态库(Dynamic Library),lib文件夹下存放的是导入库(Import Library)。


前文讲述C++执行过程的时候提到第四步是“链接”,即将所有的目标文件合并生成一个可执行程序的过程,链接有两种方式:静态链接和动态链接。静态链接需要用到静态库,动态链接需要用到动态库和导入库


(5)静态链接和静态库


(5)和(6)中引用的3段定义都来自https://www.learncpp.com/cpp-tutorial/a1-static-and-dynamic-libraries/ 这篇教程中详细介绍了静态库、动态库和导入库的概念,有兴趣的同学可以看全文。


A static library (also known as an archive) consists of routines that are compiled and linked directly into your program. When you compile a program that uses a static library, all the functionality of the static library that your program uses becomes part of your executable.


静态库可以被看作许多目标文件(汇编器生成的.o或.obj文件)的集合,它包含了所有函数的声明和具体实现。在Windows下静态链接库的扩展名是.lib,在Linux下一般是.a。


静态链接就是在“链接”这一步将“程序调用的”、“静态库中的”机器代码复制进可执行程序中。这样做的好处在于链接器生成的可执行程序已经具备了执行该程序所需要的所有内容,该程序可以独立运行,不再依赖库文件,而且由于少了查找、调用库文件和函数等步骤,程序的运行速度会比较快。


当然,静态链接也存在缺点,首先由于可执行程序中包含了库文件中的代码,所以文件体积会很大;其次,假如我们有多个项目,生成了多个可执行文件,每一个可执行文件都调用了库文件example.lib中的内容,那么库文件的代码就会被复制进每一个可执行文件中,造成无意义的空间浪费;还有一点就是扩展性问题,当某个库文件发生了变化,必须对所有文件进行重新编译,生成新的可执行文件,如果我们开发的是一款产品的话,在每一次更新之后,开发者都需要重新编译整个项目,再进行部署和发布,而用户都需要重新下载整个应用程序,费时费力。


(6)动态链接、动态库和导入库


为了解决静态链接的问题,动态链接出现了。动态链接在“链接”这一步不需要将函数代码复制到可执行文件中,它仅会从导入库中复制一些函数重定位、符号表等信息。在执行“可执行程序”的时候,程序会根据这些信息查找到动态库的位置并调用相应函数

An import library is a library that automates the process of loading and using a dynamic library.


导入库都存放在名为lib的文件夹下,由于动态链接在“运行”这一步才会调用动态库,所以在“链接”时就需要导入库来告诉可执行文件有关动态库的地址、索引等信息。换句话说,动态链接生成的可执行文件与动态库的交互,就是依靠导入库完成的。


OpenCV3.4.10->导入库(后缀为lib)


A dynamic library (also called a shared library) consists of routines that are loaded into your application at run time. When you compile a program that uses a dynamic library, the library does not become part of your executable -- it remains as a separate unit.


动态库存放在名为bin的文件夹下,在可执行程序“运行”时被调用。与静态链接生成的可执行程序相比,由于不需要复制大量的函数代码,动态链接生成的可执行程序会小很多;而且每一个库文件都是相对独立的存在,当多个应用程序使用同一个库时,内存中只需要一份库文件供所有程序调用就可以了,不必像静态链接那样给每一个应用程序复制一次代码,这大大节省了磁盘的空间;此外,当库文件有更新时,开发者也无需编译整个项目,只需要替换掉库文件,用户也只需更新库即可,这样对于程序的升级和部署都更加友好。


OpenCV3.4.10->动态库(后缀为dll)


动态链接的缺点在于运行可执行程序时,必须依赖相应的动态库,而且查找、定位、调用文件和函数的过程会减慢程序的执行速度。虽然速度会慢一点,但是整体来说,动态链接还是更受欢迎的链接方式。


在Windows下,导入库的后缀是.lib,动态库的后缀是.dll,在Linux下,后缀为.so的文件既是导入库也是动态库。


C++编译过程示意图


我把上两节的内容汇总了一下,画了一张示意图,希望能够帮助读者理解,被水印挡住的文字是动态库”。了解了不同库文件的位置和作用,我们在配置OpenCV环境的时候思路就会很清晰了。


opencv_contrib文件夹

4.1 .github/


.github/


.github文件夹下有ISSUE_TEMPLATE.md和PULL_REQUEST_TEMPLATE.md两个文件,这是两个模版文件。先来说第一个issue_template.md,从文件注释中我们就能理解这个文件主要是用来报告bug的


This is a template helping you to create an issue which can be processed as quickly as possible. This is the bug reporting section for the OpenCV library.


当我们发现了源代码中的bug,就可以按照这个模版给出的格式来描述具体的问题,并反馈给项目团队。


ISSUE_TEMPLATE.md


pull_request_template.md就是当我们对于源代码有了新改动的时候需要提交的内容模板。先来看看什么是pull request,官方文档中这样解释(https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests):


Pull requests let you tell others about changes you've pushed to a branch in a repository on GitHub. Once a pull request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits before your changes are merged into the base branch.


一句话总结:pull request就是发起一个请求,请求将自己的代码加入其他人的项目中去


PULL_REQUEST_TEMPLATE.md


拿OpenCV举例,如果我们对于源代码有了改进(比如修正了bug,写出了新算法),就可以将自己改动后的代码连带改动说明(pull_request_template.md)一同pull request给开发团队,开发人员在看到你的改动之后会进行代码检查和讨论。如果你的代码可行,就会被合并入OpenCV的源代码中,你也会被当作此项目的贡献者(Contributor)之一。


4.2 .gitattributes, .gitignore

.gitattributes, .gitignore

.gitignore是一个文本文件,Git官网中给了(https://git-scm.com/docs/gitignore)这样的定义:


A gitignore file specifies intentionally untracked files that Git should ignore. Files already tracked by Git are not affected.


当我们使用Git将本地仓库中的项目上传至GitHub远程仓库时,如果不想上传某个目录或者文件,就可以创建.gitignore并写入不想上传的目录或文件名称,这样Git在上传时就会忽略这些特定的文件。


上述定义中的untracked files专业翻译是“未被纳入版本管理中的文件”,简单理解就是“还没有上传至远程仓库的文件”。换句话说,.gitignore中的内容对还没上传的文件有用,对于已经上传并存在于远程仓库中的文件没有用。简单来看一下.gitignore中的内容:


.gitignore的内容


这里的.gitignore文件放在了根目录下,代表着符合上述条件的所有文件都不会被上传。举个例子,*.pyc代表Git会忽略所有后缀为pyc的文件,tegra/代表忽略所有名为tegra的文件夹和该文件夹下的所有内容。如果创建了多个.gitignore文件,还要考虑文件作用范围的问题。一般来说.gitignore对同目录及其子目录下的文件生效。更多介绍可以查看刚刚贴出的官网链接。


.gitattributes也是一个文本文件,具体介绍在这里(https://git-scm.com/docs/gitattributes),官网定义如下:


A gitattributes file is a simple text file that gives attributes to pathnames.


.gitattributes文件中每一行的格式都是pattern attr1 attr2 ...,pattern指的是文件模式,attr指的是给文件模式赋予的属性。直接来看OpenCV3.4.10中的文件:


.gitattributes的内容


上图中的*.c,*.cpp,*.h等都是文件模式,*.c指代所有后缀为c的文件,.cpp代表所有后缀为cpp的文件(也就是C++的源文件)等。text是给这些文件赋予的属性:


Setting the text attribute on a path enables end-of-line normalization and marks the path as a text file. End-of-line conversion takes place without guessing the content type.

text只是其中一种属性,我们还可以设置-text,eol等属性。拿上图中的*.c text举例,text属性将所有后缀为.c的文件标记为文本文件,并在用户使用Git命令进行提交、合并等操作时将行尾标志符统一为LF


在2.2节中我们就提到过行尾标识符,这里再复习一下。不同系统的行尾标志符是不同的,Windows中约定'\r\n'作为一行的结束标志,类Unix系统中是'\n',macOS中是'\r'。也就是说当我们在敲下“Enter”键的时候,系统给我们的代码或文本中添加的符号是不同的,只不过由于这些符号不是可见符号,所以在文本中没有显示出来而已。'\r'代表回车(Carriage Return, CR),'\n'代表换行(Line Feed, LF),'\r\n'就是回车换行,即CRLF。


总结来说,.gitattributes可以将不同格式的文件和代码在使用Git提交、合并至GitHub仓库的时候,调整为统一的文件格式


4.3 .travis.yml

.travis.yml

介绍这个文件之前,需要先来介绍一下持续集成的定义(https://docs.travis-ci.com/user/for-beginners)


Continuous Integration is the practice of merging in small code changes frequently - rather than merging in a large change at the end of a development cycle. The goal is to build healthier software by developing and testing in smaller increments. This is where Travis CI comes in.


持续集成简单理解就是代码每一次的小改动都会及时地与主干代码进行集成和测试的过程。持续集成有利于开发者及时发现新代码中的错误减少将新代码合并至主干代码过程中出现的问题,如果开发人员的新代码没有及时与主干代码进行集成或测试,那么在项目后期合并代码时很容易出现大量代码冲突或系统集成失败等问题,这种问题也被称为“集成地狱”


持续集成的过程其实很简单:配置环境、编译、链接、运行、测试等,我们可以手动完成这些步骤,也可以依靠工具自动完成,Travis CI就是一个可以自动进行持续集成的工具。



Travis CI LOGO


Travis CI(Continuous Integration)是一个托管的持续集成和部署系统,它能够自动构建和测试我们的代码,并及时提供给我们测试反馈(https://docs.travis-ci.com/user/for-beginners):

As a continuous integration platform, Travis CI supports your development process by automatically building and testing code changes, providing immediate feedback on the success of the change.


Travis CI目前已支持GitHub,不想使用GitHub的用户也可以将代码上传至官网进行测试。那么Travis CI是如何在GitHub上实现持续集成的?这里简单举一个例子,当开发者将新代码上传至GitHub时,Travis CI会将GitHub的存储库克隆到一个虚拟环境中,执行配置环境、编译、链接、运行、测试等一系列流程,并返回项目运行的结果。作为开发者,只需要上传代码并且根据结果检查代码即可,非常方便。

Travis CI对代码执行一系列操作的依据,就是.travis.yml文件中所写的内容。.travis.yml是Travis CI的配置文件,当上传至GitHub的文件中存在.travis.yml时,Travis CI就会自动读取该文件的内容并执行具体的操作

.travis.yml使用
YAML语言编写,这里对YAML不做过多介绍了,想了解详情的读者见下文链接:


.travis.yml


上图是opencv_contrib3.4.10中的.travis.yml,可以看到该文件指定了项目类型(language: cpp表示这是C++项目)、编译器类型编译前的命令(包括从GitHub上下载安装包、创建文件夹、使用CMake编译等)、编译命令等内容。

有关Travis CI和.travis.yml的更多内容见官方文档(
https://docs.travis-ci.com/)



4.4 其他文件和文件夹

其他文件和文件夹

modules文件夹存放了额外算法的源代码,例如face(人脸识别)、bioinspired(生物视觉)、tracking(视觉对象追踪)等等。如果想要使用这些算法模块,需要我们自己对源码进行编译,modules文件夹所在的路径也是编译时需要的参数,有关编译的具体细节在接下来的配置文中会有具体讲解。


doc下存放了某些算法的样例图片和训练好的模型,samples下是一些样例程序和图片文件。


README.md中是项目简介和配置所需的cmake语句,LICENSE是OpenCV的开源许可证->BSD 3-Clause License,CONTRIBUTING.md中是贡献指南的链接。这些文件在前文都有涉及,这里不再解释了。




这篇文依然是改了又改才发出来,从最开始设计介绍顺序的时候我就在纠结,目录画了好几版也不满意;写完初稿又发现自己对于make和cmake的理解有些偏差,所以又重写了第二节中的内容;最后在库文件那里又补上了C++的编译过程,总之是修修补补,现在才差不多满意


这个系列还有2篇,一篇是OpenCV在Ubuntu16.04上的配置攻略,一篇是在Windows10上的配置攻略两篇文的内容都会包括:


 源代码编译(包括opencv_contrib的编译)

 OpenCV2.x和3.x的配置过程,没想好要不要写4.x,因为3.x和4.x步骤相同

 我曾遇到的bug及处理方式


截图已经做好,只剩下写中间步骤和图片排版,我尽量在9月份完结这个系列



~END~

文章转载自麟十一,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论