我为什么用Conan
前面的文章:深度学习之用CelebA_Spoof数据集搭建一个活体检测-训练好的模型用MNN来推理有提到怎么使用MNN对训练好的模型进行推理,里面并没有提到我是怎么编译和进行代码依赖包的管理的详细步骤,在这里我是用的是Conan:一个C/C++包管理器,可以管理项目依赖并自动下载编译所需的库文件。比较令人兴奋的是,Conan专为C/C++设计的去中心化包管理器,支持多平台(Windows/Linux/macOS),还能支持交叉编译,可管理二进制依赖和构建配置,还有是免费开源的。也就是用它来进行依赖包和软件包的管理,不需要你一个一个依赖库进行源码编译和因为环境不同导致别人提供给你的依赖库用不了。当然这只是简单的描述,有兴趣的同学可以参考这个大牛的博客:10km,里面有个conan的入门系列文章,有幸跟这位大牛共事过,因此吃了不少技术上的红利,非常感激!话不多说,我将会延续对前面文章的例子进行描述,毕竟还是属于怎么用模型的,所以这是深度学习之用CelebA_Spoof数据集搭建一个活体检测这个系列的最终完结篇。
前提
安装好conan并进行配置,可以参考Conan官网或者大牛博客conan的入门系列。由于我之前有在Windows下安装和配置(具体的安装和配置就不详细说明了),就暂且使用Windows下的环境对在用MNN来推理模型时候如何利用Conan对源码以及软件包进行管理。
项目结构配置
project/
├── conanfile.txt
├── CMakeLists.txt
├── include/
│ ├── InferenceInit.h
│ ├── LiveSpoofDetector.h
├── src/
│ ├── main.cpp
│ ├── InferenceInit.cpp
│ ├── LiveSpoofDetector.cpp
└── build/
关键配置文件
Conanfile.txt是Conan包管理器的核心配置文件,主要用于管理C/C++项目的依赖关系。以下是详细说明:
- conanfile.txt的作用:
- 声明项目依赖的第三方库及版本
- 配置构建选项和生成器
- 定义包的使用方式和参数
- 典型conanfile.txt结构:
text
[requires]
opencv/4.5.3
mnn/1.2.7
[generators]
CMakeToolchain
CMakeDeps
[options]
opencv:shared=True
- 替代方案:
- conanfile.py:更灵活的Python脚本方式
python
from conans import ConanFile
class MyProject(ConanFile):
settings = "os", "compiler", "build_type", "arch"
requires = "opencv/4.5.3", "mnn/1.2.7"
generators = "CMakeToolchain"
- 两种方式的对比:
- conanfile.txt:简单易用,适合基础需求
- conanfile.py:功能更强大,支持自定义逻辑
建议根据项目复杂度选择合适的配置方式。在这里因为依赖库就只有MNN和opencv,所以用conanfile.txt就足够了。这里提供工程使用的conanfile.txt文件。
bash
[requires]
opencv/4.5.3 # 指定OpenCV 4.5.3版本
mnn/1.2.7 # 指定MNN 1.2.7版本
[generators]
CMakeToolchain # 生成CMake工具链文件
CMakeDeps # 生成CMake依赖文件
[options]
*:fPIC=True # 强制所有库使用位置无关代码
*:shared=False # 默认静态链接所有库
openssl/*:no_dso=True # OpenSSL禁用动态加载
opencv/*:with_ffmpeg=False # 禁用FFmpeg支持
opencv/*:with_webp=False # 禁用WebP格式支持
opencv/*:with_quirc=False # 禁用QR码识别模块
#opencv/*:with_gtk=False
opencv/*:with_eigen=False
opencv/*:with_openexr=False
opencv/*:with_tesseract=False
opencv/*:with_tiff=False
opencv/*:gapi=False
opencv/*:ml=False
opencv/*:photo=False
opencv/*:stitching=False
#opencv/*:objdetect=False
#opencv/*:flann=False
opencv/*:videoio=False
mnn/*:build_tools=False # 不构建MNN工具
mnn/*:win_runtime_mt=True # Windows下使用MT运行时
使用conan进行管理的时候,conanfile.txt是非常重要的,这里面就是要告诉conan我要用哪些依赖包,什么版本,在编译的时候需要进行哪些选项的配置等。
进行conanfile.txt的配置后,我们的CMakeList.txt应该怎么写呢,会不会需要很复杂的配置?答案是否定的。以下是CMakeList.txt的内容:
bash
cmake_minimum_required(VERSION 3.12)
if(POLICY CMP0091)
cmake_policy(SET CMP0091 NEW)
endif()
project(LiveSpoofDetector)
# 查找依赖包
find_package(OpenCV REQUIRED COMPONENTS core highgui imgproc calib3d dnn)
find_package(mnn REQUIRED)
# 包含目录
include_directories(
include
${OpenCV_INCLUDE_DIRS}
${mnn_INCLUDE_DIRS}
)
message(STATUS "OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}")
message(STATUS "mnn_INCLUDE_DIRS: ${mnn_INCLUDE_DIRS}")
message(STATUS "mnn_INCLUDE_DIR: ${mnn_INCLUDE_DIR}")
message(STATUS "mnn_LIBRARIES: ${mnn_LIBRARIES}")
# 添加可执行文件
add_executable(live_spoof_detector
src/InferenceInit.cpp
src/LiveSpoofDetector.cpp
src/main.cpp
)
# 链接库
target_link_libraries(live_spoof_detector
PRIVATE
${OpenCV_LIBS}
${mnn_LIBRARIES}
)
可以看到,CMakeList.txt种,直接就用find_package查找包了,不需要指定依赖库的具体路径了,因为这个Conan已经处理好,默认会从Conan的安装指定位置进行查找,但是还需要一个很重要的步骤,后面会讲到。
构建工程
上面提到已经有了CMakeList.txt和conanfile.txt,我们需要使用conan的指令来构建了,这个就是很重要的步骤,为了方便,我们将这些指令和cmake的指令写在了一个脚本上。
首先是依赖包的配置文件,这个可以使用conan安装是默认的profiles路径下,也可以自己手动指定。
在这里,我是直接放到了工程目录下:
msvc便是conan在管理的时候需要对依赖库进行编译的一些选项:
bash
[settings]
os=Windows
compiler=msvc
#Visual Studio 2015 对应 compiler.version=190
#Visual Studio 2017 对应 compiler.version=191
#Visual Studio 2019 对应 compiler.version=192
#Visual Studio 2022 对应 compiler.version=193
compiler.version=192
#对于 Visual Studio 2015,您可以使用:v140或者v140_xp(如果您需要支持 Windows XP)
#compiler.toolset=v190_xp
# static :表示使用静态链接的运行时库。dynamic:表示使用动态链接的运行时库。
compiler.runtime=static
compiler.cppstd=14
build_type=Release
arch=x86_64
[options]
boost/*:without_stacktrace=False
#True
#[build_requires]
#[env]
当然,需要其他平台的,也能使用其他平台,比如上图中的对应的其他编译配置,这样很好的指出跨平台的编译。
conan的指令与cmake的指令集合在build.bat的脚本上:
bash
@echo off
setlocal
:: 检查必要工具
for %%t in (cmake conan python) do (
where %%t || (
echo %%t NOT FOUND.
exit /B -1
)
echo %%t found
)
:: 版本检查
conan --version | findstr /C:"Conan version 2" || (
echo Conan 2 required
exit /B -1
)
python --version | findstr /C:"Python 3" || (
echo Python 3 required
exit /B -1
)
:: 设置参数
set sh_folder=%~dp0
set build_type=RELEASE
if "%1" == "DEBUG" set build_type=%1
set OUTDIR=%sh_folder%release
:: 清理并创建构建目录
pushd %sh_folder%
if exist build rmdir /s/q build
mkdir build
pushd build
:: 安装依赖并构建
conan install .. --build missing -of . -pr:a %sh_folder%\conan2\profiles\msvc -r conan || exit /B
cmake .. -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=%build_type% ^
-DCMAKE_INSTALL_PREFIX=%OUTDIR% ^
-DCMAKE_TOOLCHAIN_FILE=%sh_folder%\build\conan_toolchain.cmake
cmake --build . --config %build_type% -- /m
cmake --install . --config %build_type%
popd
echo Build completed!
endlocal
可以看出来,在cmake构建工程的前面,conan对依赖库进行了编译和安装,使用的编译配置是%sh_folder%\conan2\profiles\msvc
,cmake构建工程使用的是-DCMAKE_TOOLCHAIN_FILE=%sh_folder%\build\conan_toolchain.cmake
的工具链,不需要其他的设置了。执行的过程种conan会执行编译,如果当前已经有编译过了,就直接从缓存种拿对应平台的依赖库,下图就是直接从缓存中拿。
在cmake的过程中,conan就自动寻找对应的依赖库所需要的依赖了。
编译的过程

编译成功。执行结果:

调用代码
提供上面执行结果的调用代码:main.cpp
cpp
#include "LiveSpoofDetector.h"
#include <opencv2/opencv.hpp>
#include <iostream>
int main(int argc, char** argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <model_path> <image_path>" << std::endl;
return -1;
}
// 1. 初始化活体检测器
mnncv::SqueezeNetLiveSpoof detector(argv[1]);
if (detector.Init(argv[1]) != 0) {
std::cerr << "Failed to initialize detector" << std::endl;
return -1;
}
// 2. 加载测试图像
cv::Mat image = cv::imread(argv[2]);
if (image.empty()) {
std::cerr << "Failed to load image: " << argv[2] << std::endl;
return -1;
}
// 3. 执行活体检测
float score = detector.detect_handler(
image.data,
image.cols,
image.rows,
image.channels(),
0 // 使用完整模型
);
// 4. 输出结果
std::cout << "Live Detection Score: " << score << std::endl;
std::cout << "Result: " << (score > 0.7 ? "LIVE" : "SPOOF") << std::endl;
return 0;
}
上面提到的CMakeList.txt和脚本都是能直接使用的,工程中的其他源码,可以从深度学习之用CelebA_Spoof数据集搭建一个活体检测-训练好的模型用MNN来推理拿过来按照上面的工程结构来进行安放,见下图:
结尾
Conan的使用可以很好的对需要用到的依赖库管理,不需要再一层一层的去寻找依赖库的依赖库,告别依赖库恐惧症。