前言
我一直不知道编译的过程以及cmake, make 这些工具是干什么的,所有抽时间研究了一下。
简单来说就是 cmake 是根据 CMakeLists.txt 用来生成 makefile文件的。而make 命令是根据 makefile 来生成可供使用的 动态连接库 或静态连接库的,以便你项目调用,make 命令在windows上与g++ gcc等命令在一个目录,不需要单独去下载,安装好 mingw 就是在其 bin 目录下,叫 mingw32-make.exe。
环境准备
- mingw
- opencv源码
- cmake(最好下载最新版的,对新版的vc++识别更好)
opecv 项目如果你下载了vc++版本的也不需要重新下载源码了,因为解压后目录中包含源码,也是存放在 source目录下。
mingw安装好后将其中的 bin 目录设置到path环境变量中,以便后面cmake可以直接识别。
1.编译动态链接库
1.1 mingw 预配置
Where is the source code: 解压opencv项目后源码存放路径
Where to build the binaries: 存放构建的目录,可以是不存在的,因为点击configure会自动建立
设置好两个路径后可以直接点击 configure
选择MinGw makefiles,用默认本地编译器,然后点击下一步。
The CXX compiler identification is GNU 8.1.0
The C compiler identification is GNU 8.1.0
Detecting CXX compiler ABI info
Detecting CXX compiler ABI info - done
Check for working CXX compiler: C:/MinGW/bin/g++.exe - skipped
Detecting CXX compile features
Detecting CXX compile features - done
Detecting C compiler ABI info
Detecting C compiler ABI info - done
Check for working C compiler: C:/MinGW/bin/gcc.exe - skipped
Detecting C compile features
Detecting C compile features - done
开始检测环境,可以看到识别到了 g++ gcc等命令
1.2配置参数
参数很多,说几个可能用到的。
名字 | 作用 |
CMAKE_INSTALL_PREFIX | 最终编译后存放的路径,此时这个目录可能是不存在的,只有最好执行make install命令才会建立 |
BUILD_opencv_python3 | 建立opencv python的第三方库,由于我这里不需要跟python相关的所以跟python相关我都取消勾选了 |
BUILD_opencv_world | 将多个连接库合并成一个world链接库方便连接,建议勾选上 |
BUILD_SHARED_LIBS | 是否建立dll, 如果不勾选就是建立静态库,默认勾选就是建立动态连接库 |
这里寻找选项有一个技巧,可以将
Grouped勾选上,方便寻找。
这里一步配置参数可以把 BUILD_opencv_world
选上,其余的几乎都不用动。
1.3生成makefile文件
点击 Generate 按钮, 等待生成。
这里可能会有一个问题Unable to open cache file for save. ....CMakeCache.txt
就是无法打开CMakeCache.txt 文件,我尝试过删除这个文件或者清除缓存,删不掉。
解决办法:1. 就是更换build目录,需要然后需要从新 configure, 修改配置和 generate,不推荐
2. 重启电脑,推荐
1.4 make编译项目
在 建立的目录 执行命令:
mingw32-make -j8
这里 -j8 是使用多线程来进行编译,也可以 -j16, 速度会比单独执行 mingw32-make 命令快很多。
1.5 安装
继续在编译后的目录执行命令:
mingw32-make install
这一步最终生成的文件就会在之前配置CMAKE_INSTALL_PREFIX
目录中。
这里注意 install 是命令,而不是参数,之前我目录名字就是 install,导致我以为这一步是指定一个目录来存放最终文件
在 \x64\mingw\bin
存放动态链接库 libopencv_world3414.dll (运行的时候使用), 在\x64\mingw\lib
存放静态链接库 libopencv_world3414.dll.a(编译链接时使用)。
1.6 使用opencv库
编写一个简单的调用摄像头的 opencv 程序:
using namespace cv;
int main()
{
namedWindow("test");
VideoCapture capture(0, CAP_DSHOW);
while (true)
{
Mat src;
capture >> src;
int width = src.cols;
int heigh = src.rows;
imshow("test", src);
if (waitKey(10) == 27) break;
}
destroyAllWindows();
return 0;
}
然后使用命令行进行编译:
g++ 文件名 -I 头文件目录 -L 连接库目录 -lopencv_world3414.dll
这里简单介绍一下参数作用:
-I :(注意这里是i的大写,不是L的小写)指定头文件目录 ,也就是 include 目录。
-L :指定链接库目录,与后面 -lopencv_world配合,意思是去-L 目录去查找 链接库。
-lopencv_world3414.dll: (这里是-L的小写,不是i的大写)使用这个链接库,文件名字其实是libopencv_world3414.dll.a
而使用的时候就需要去掉后面 .a 和前面的 lib 然后与 -l连接。所有链接库的使用方法都遵循这个模式。其实这个模式是去掉后缀名的内容,如果前面有 lib 那么去掉 lib 然后与-l链接,如果没有就直接与连接。
命令执行后会生成 a.exe 文件,而这个文件执行就会提示没有
libopencv_world3414.dll , 所以可以把 dll 文件放入与 a.exe 同级目录就可以了。或者一劳永逸的方法,将 dll 文件目录添加到 path 环境变量中,推荐这种方法。
2.编译静态链接库
编译静态链接库的方法和编译动态链接库步骤基本相同,只是在使用编译好的opencv静态链接库的时候有一些麻烦。后面也会详细说明如果使用 opencv静态链接库。
2.1 配置参数
1.2 配置参数
这步中将 BUILD_SHARED_LIBS 选项取消勾选,然后其余步骤与 编译动态连接库的步骤相同,当然我还是建立了world。
2.2 编译后内容介绍
\x64\mingw\staticlib
存放所有静态链接库,此时bin目录中就没有 dll 文件了,因为我们没有生成动态链接库,所有函数实现都在静态链接库中。细心你会发现 libopencv_world3414.a 会比 libopencv_world3414.dll.a 大很多,因为里面 多了函数实现,而 libopencv_world3414.dll.a作用是告诉程序去调用 libopencv_world3414.dll,其中没有真正的函数实现,函数真正实现全在dll文件中。
除了 libopencv_world3414.a 静态链接库之外还有:
很多其它这些链接库,而这些库在项目编译链接的时候也是需要使用的(如果不建立world会产生更多)。
2.3 使用opencv
程序还是上面那个程序,命令有一些不同(需要链接很多东西):
g++ test3.cpp -I 头文件目录 -L 链接库目录 -lopencv_world3414 -lzlib -lIlmImf -llibjasper -llibjpeg-turbo -llibpng -llibprotobuf -llibtiff -llibwebp -lquirc -lzlib
参数作用已经链接库调用写法前面已经介绍了。
这条命令下去应该会有几千个错误。因为静态链接还要使用很多windows内置的静态链接库才能编译成功。所有这里比较麻烦,需要安装vc++才会有这些链接库。我安装的是 visual studio 2019, 所有链接库在目录D:\Windows Kits\10\Lib\10.0.19041.0\um\x86
,最终命令:
g++ test3.cpp -I D:\mingw_opencv_static\include -L D:\mingw_opencv_static\x64\mingw\staticlib -lopencv_world3414 -lzlib -L "D:\Windows Kits\10\Lib\10.0.19041.0\um\x86" -lgdi32 -lComDlg32 -lOleAut32 -lOle32 -luuid -lIlmImf -llibjasper -llibjpeg-turbo -llibpng -llibprotobuf -llibtiff -llibwebp -lquirc -lzlib
也就是添加了-lgdi32 -lComDlg32 -lOleAut32 -lOle32 -luuid
这些连接库,报出了一些警告但是还是可以编译成功的。
这里我最初找这些链接库是很麻烦的过程,看报错,列如:
undefined reference to __imp_CoCreateInstance
这个报错说明找不到 __imp_CoCreateInstance 这个函数,那么 Search all across Microsoft Docs 搜索这个函数,可以看到:
其存在于 Ole32.lib 这个静态连接库中,那么就在编译参数中加入这个静态链接库,所以我加入了 -lOle32
。
编译成功这个 a.exe 就可以脱离 dll 运行了。但是会比需要 动态链接库那个a.exe 的大很多,其实可以进一步精简:在源程序中引入的时候只引入你需要的头文件,然后不编译 opencv 库的时候不建立 world ,然后链接的时候就只引入你需要的链接库。
其它
如果你真是重头看到尾,我相信能理解很多东西。也能解决一些问题。
作者:Hello_wshuo