前言
不知不觉,正儿八经地写代码已经有三年多了。OpenCV是我这三年来用的最多的库,在Visual Studio中配置它的开发环境,算是家常便饭,已经相当熟稔。回想自己初学OpenCV时的经历,在网上看教程找资料,走了不少弯路。所以我写下这篇博文,把配置过程中的东西都讲清楚,让读到的人少走些弯路。在HOW部分,我将叙述如何配置,并尽量使配置过程简单明了;在WHY部分,我会尽量讲清楚为什么这样配置。
教程开始前,我希望你能清楚集成开发环境(Integrated Development Environment, IDE)和编译器是两码事儿,Visual Studio是IDE,而VC是编译器。VC是VS的子集。在不严谨的情况下,可以把它们当作一回事儿,但一定要清楚你的VS/VC的版本。各版本的VS和VC对应关系如下表(太古老的版本就不列出来了):
IDE | 编译器 |
---|---|
VS2017 | VC15 |
VS2015 | VC14 |
VS2013 | VC12 |
VS2012 | VC11 |
VS2010 | VC10 |
VS2008 | VC9 |
VS2005 | VC8 |
另外,本文将使用VS2013和OpenCV-3.0为例。其他版本的配置差别不大,可以依葫芦画瓢。
HOW
(0) 准备工作
首先在VS中新建一个空的c++工程:
在项目中新增一个.cpp文件,然后写上一段简单的测试代码(读取一副图片然后将其显示出来):
// 一段简单的测试代码
#include <opencv2opencv.hpp>
int main() {
cv::Mat img = cv::imread("E:\cap.png"); //改成你自己的图片路径
cv::imshow("test", img);
cv::waitKey(0);
}
(1) 配置附加包含目录
对项目【右键】-【属性】
弹出属性页,依次点击【配置属性】-【C/C++】-【常规】-【附加包含目录】,在弹出的窗口中把OpenCV的include路径填进去。本文填写的是【E:ENVopencv-3.0include】。
注意上方【配置】和【平台】两个复选框里面的内容。后文将详细说明。
(2) 配置附加库目录
同样是在项目的属性页,依次点击【配置属性】-【链接器】-【常规】-【附加库目录】,在弹出的窗口中把OpenCV的lib路径填进去。
本文填写的路径是【E:ENVopencv-3.0uildx86vc12lib】,其中【x86vc12】代表此库由VC12编译生成,32位。如果你需要其他版本的库,也可以改成对应的路径。后文将详细说明。
(3) 配置附加依赖项
依然是在项目的属性页,依次点击【配置属性】-【链接器】-【输入】-【附加依赖项】,在弹出的窗口中填入【opencv_ts300.lib】和【opencv_world300.lib】。其中300代表OpenCV-3.0,是版本号。后文将详细说明。
(4) 配置dll
这一部有两种做法,第一种方法(推荐)是把【E:ENVopencv-3.0uildx86vc12in】目录下的dll文件【opencv_world300.dll】拷贝一份,放到本项目生成的exe文件所在目录中,见下图。如果你没有Debug这个目录,那么先把项目生成/运行一遍就有了。
第二种方法(不推荐)是把【E:ENVopencv-3.0uildx86vc12in】加到系统的环境变量,如下图,在右侧两个【Path】中选择一个编辑加入路径就行了。
(5) 测试
至此已完成全部配置工作!按下F5运行一开始准备好的测试代码,看看效果吧:
成功运行!
(*) 注意
某些教程会教你在【配置属性】-【VC++目录】中修改【附加包含目录】和【附加库目录】,如下图。在这里修改虽然方便,但我不推荐。
WHY
为了弄清楚为什么要这样配置,我们来看看OpenCV文件夹里都有什么:
- build
- sources
- LICENSE.txt
- README.md.txt
两个txt文件,分别是许可证和说明,不重要;两个文件夹,【sources】文件夹中是OpenCV的源代码,如果仅仅是调用OpenCV库,那么我们多半用不上这个文件夹里面的东西。【build】文件夹中包括头文件,和一些现成的库文件。这些库文件是由OpenCV官方生成的。(当然你也可以利用CMake自己生成这些库文件,CMake是一款跨平台的make软件,【sources】文件夹里面提供了CMake配置文件。我劝大家不要自己用去生成库,因为实在是太折腾了。)
再来仔细看看【build】文件夹里面的东西:
- etc
- include
- java
- python
- x64
- x86
- LICENSE
- OpenCVConfig.cmake
- OpenCVConfig-version.cmake
【etc】文件夹中是OpenCV某些算法所依赖的数据集;
【include】里面是一堆头文件,在 步骤(1) 中,我们将此目录加入了【附加包含目录】,就是为了使我们测试代码中的第一行#include <opencv2opencv.hpp>
生效。#include
是在【包含目录】中搜索头文件的,【附加包含目录】顾名思义,也属于【包含目录】;
【java】和【python】文件夹与本文讨论的内容无关;
【x86】和【x64】对应32位库和64位库。OpenCV-3.1官方没有提供32位的库,自然也就没有【x86】这个文件夹了。如果你需要32位的OpenCV-3.1库,可以自己去生成,但建议你先去网上找一找,看看第三方组织或个人生成的库文件中有没有你想要的。
最后是许可证以及两个CMake相关的文件,不重要。
在 步骤(2) 中,本文将【E:ENVopencv-3.0uildx86vc12lib】加入了【附加库目录】,这样做是在是调用“32位的、由vc12编译器生成的、动态链接库形式的、OpenCV库”,注意到引号里用了三个形容词,它们依次与路径中的【x86】、【vc12】、【lib】相对应。①【x86】:如果你想调用64位的库,那么在路径中写【x64】就行了(当然啦前提是这个库存在,前面也说过了,这里不赘述)。如果你不知道该调用32位还是64位的库,那么请和你自己的程序保持一致,这是最简单稳妥的做法。②【vc12】:与上一点一样,如果不知道该调用由哪个编译器生成的库,那么就与自己的程序保持一致。这是因为vcxx编译出来的库,依赖于一个叫做microsoft visual c++ 20xx redistributable package的东西(简称vcxx运行库),在安装VS20xx的时候会自动安装。本文调用vc12版的OpenCV库,VS版本为2013;如果使用VS2015编写程序,调用vc12版的OpenCV库,需安装vc12运行库。③【lib】:与该文件夹同级的,还有【bin】和【staticlib】文件夹。【lib】和【bin】提供动态链接库,【staticlib】提供静态库(关于静态库和动态库,可以看看这篇文章:C++静态库与动态库 吴秦)。如果你不知道该调用静态还是动态库,建议调用动态库。原因不多说,我自己也没有彻底弄清楚,就不误导别人了。
在 步骤(3) 中,本文在【附加依赖项】中填入了【opencv_ts300.lib】和【opencv_world300.lib】。很多教程在这里,针对程序的debug版本和release版本,填入了不同的内容:debug版本会在".lib"前,加上一个字母"d"。这是为了让程序在debug模式下,调用debug版本的OpenCV库。我认为,只要你不需要在调试代码的时候,进入OpenCV库函数里面一探究竟,就没必要做这一步,毕竟debug库速度不及release库。所以按照本文的配置,无论VS用debug还是release模式,调用的都是release库。
很多人不清楚debug模式和releas模式本的区别,简单按字面意思解释,debug版本用于调试,release版本用于发布。说一些它们的区别:前者会在编译阶段输出符号文件(.pdb文件),符号文件是调试的必需品;后者则会开启编译器的优化项,把一些中间变量优化掉——这就是为什么我们以release模式去调试的时候,很多变量的值都无法查看。
步骤(3) 也能以显式的方法,写在代码里。将下面两行代码加在测试代码开头,就能代替 步骤(3) 了。
#pragma comment(lib,"E:\ENV\opencv-3.0\build\x86\vc12\lib\opencv_ts300.lib")
#pragma comment(lib,"E:\ENV\opencv-3.0\build\x86\vc12\lib\opencv_world300.lib")
在 步骤(4) 中,本文这样配置,是为了让项目生成的exe文件在运行时,能够找到dll。exe在运行时,会在特定的目录下寻找自己依赖的库,它本身所在的目录肯定包含其中,故在第一种方法中,本文将OpenCV的dll拷贝到了exe的同级目录下;系统高级设置里面,环境变量【path】中的一连串目录也是exe搜索的范围,故在第二种方法中,本文将OpenCV的dll文件所在的目录,加入了环境变量【path】中。某些教程将OpenCV的dll拷贝到了system32这个系统目录下……虽然这方法行得通,但我觉得毫无优雅可言。
最后,如果你有任何疑问与建议,或是我的文章存在纰漏,欢迎留言交流!
本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。