第78例:Dlib——集成Dlib机器学习库
Dlib这个库,说实话,我第一次接触它是在做一个人脸检测的项目。那时候OpenCV的人脸检测器在侧脸和遮挡情况下表现不太理想,团队里一位老哥推荐我试试Dlib。一试之下,嗯,确实有两把刷子。今天我们就来聊聊,怎么在CMake项目里把Dlib集成进来,以及我踩过的那些坑。
Dlib是什么?为什么选它?
Dlib是一个C++工具包,里面包含了机器学习算法、计算机视觉工具、数值计算等等。我个人最常用的是它的人脸检测和人脸关键点检测功能。相比OpenCV的Haar级联,Dlib的HOG+SVM检测器在精度上更胜一筹,尤其是对偏转角度较大的人脸。
它的核心优势其实就三点:
- 纯C++实现,没有奇奇怪怪的依赖,编译起来相对省心
- 文档和示例非常详细,每个API都有对应的demo
- 支持GPU加速(通过CUDA),训练深度学习模型时能快不少
重要提醒:Dlib的深度学习模块需要C++11以上标准,如果你的项目还在用C++98,建议先升级编译器。
CMake集成Dlib的两种方式
我在项目中试过两种集成方式,各有优劣。下面分别说说。
方式一:直接使用系统安装的Dlib
如果你用的是Ubuntu,可以直接apt安装:
sudo apt install libdlib-dev
然后在CMakeLists.txt里这样写:
find_package(dlib REQUIRED)
target_link_libraries(my_project PRIVATE dlib::dlib)
这种方式最省事,但有个问题——系统仓库里的版本通常比较旧。我记得有一次项目需要用到Dlib的深度学习模型序列化功能,系统自带的19.17版本不支持,最后只能自己编译。
方式二:将Dlib作为子模块集成(我推荐)
我个人更习惯把Dlib作为Git子模块或者直接拷贝到项目里。这样版本可控,而且跨平台编译时不会因为系统差异出问题。
具体做法是这样的:
# 在项目根目录下
git submodule add https://github.com/davisking/dlib.git third_party/dlib
然后在CMakeLists.txt里:
add_subdirectory(third_party/dlib)
target_link_libraries(my_project PRIVATE dlib)
这里有个小坑——Dlib默认会编译所有模块,包括一些你可能用不到的(比如GUI、SQLite等)。我建议在添加子目录之前,先设置一些选项来精简编译:
set(DLIB_NO_GUI_SUPPORT ON CACHE BOOL "" FORCE)
set(DLIB_ENABLE_ASSERTS OFF CACHE BOOL "" FORCE)
add_subdirectory(third_party/dlib)
小技巧:如果你只需要人脸检测功能,可以只编译核心模块,这样编译时间能从5分钟缩短到1分钟以内。具体做法是设置DLIB_USE_CUDA为OFF,除非你真的需要GPU加速。
一个完整的人脸检测示例
光说不练假把式。我们写一个完整的人脸检测程序,看看Dlib到底怎么用。
先看项目结构:
face_detector/
├── CMakeLists.txt
├── main.cpp
└── third_party/
└── dlib/
CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
project(face_detector)
set(CMAKE_CXX_STANDARD 11)
# 关闭不需要的模块
set(DLIB_NO_GUI_SUPPORT ON CACHE BOOL "" FORCE)
set(DLIB_USE_CUDA OFF CACHE BOOL "" FORCE)
add_subdirectory(third_party/dlib)
add_executable(face_detector main.cpp)
target_link_libraries(face_detector PRIVATE dlib)
main.cpp:
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_io.h>
#include <iostream>
int main(int argc, char** argv) {
if (argc != 2) {
std::cerr << "用法: face_detector <图片路径>" << std::endl;
return 1;
}
// 加载图片
dlib::array2d<unsigned char> img;
dlib::load_image(img, argv[1]);
// 创建人脸检测器
dlib::frontal_face_detector detector = dlib::get_frontal_face_detector();
// 检测人脸
std::vector<dlib::rectangle> faces = detector(img);
std::cout << "检测到 " << faces.size() << " 张人脸" << std::endl;
for (size_t i = 0; i < faces.size(); ++i) {
std::cout << "人脸 " << i+1 << ": "
<< "左上(" << faces[i].left() << ", " << faces[i].top() << ") "
<< "右下(" << faces[i].right() << ", " << faces[i].bottom() << ")"
<< std::endl;
}
return 0;
}
编译运行:
mkdir build && cd build
cmake ..
make
./face_detector /path/to/your/image.jpg
Dlib集成核心流程
下面这张图展示了Dlib集成到CMake项目中的完整流程,我画出来方便你理解:
避坑指南
集成Dlib的过程中,我遇到过几个比较头疼的问题,这里列出来,你遇到了可以直接对照解决。
| 问题 | 现象 | 解决方案 |
|---|---|---|
编译报错:undefined reference to dlib::... |
链接时找不到Dlib的函数 | 检查target_link_libraries是否写对了,注意Dlib的target名称是dlib不是dlib::dlib(除非你用find_package) |
| 编译极慢(超过10分钟) | Dlib默认编译所有模块 | 设置DLIB_NO_GUI_SUPPORT、DLIB_USE_CUDA等选项,只编译需要的部分 |
运行时崩溃:dlib::serialize异常 |
模型文件版本不匹配 | 确保训练模型时用的Dlib版本和运行时一致,我曾经因为这个排查了一整天 |
找不到dlib/config.h |
头文件路径问题 | 检查add_subdirectory的路径是否正确,或者手动设置include_directories |
曾经踩过的坑:有一次我把Dlib作为子模块集成,但忘记初始化子模块了。结果编译时各种报错,我还以为是版本兼容问题,折腾了半天才发现third_party/dlib目录是空的。所以记得:git submodule update --init --recursive
进阶:使用Dlib的深度学习模型
如果你需要用到Dlib的预训练模型(比如人脸关键点检测),需要额外下载模型文件。Dlib官方提供了几个常用的模型:
- shape_predictor_68_face_landmarks.dat:68个人脸关键点检测模型
- dlib_face_recognition_resnet_model_v1.dat:人脸识别模型
- mmod_human_face_detector.dat:基于深度学习的MMOD人脸检测器
这些模型文件比较大(几十MB到上百MB),我建议不要放到Git仓库里,而是用脚本自动下载。可以在CMake里加一个自定义命令:
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/models/shape_predictor_68_face_landmarks.dat
COMMAND wget -O ${CMAKE_BINARY_DIR}/models/shape_predictor_68_face_landmarks.dat
http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
COMMAND bzip2 -d ${CMAKE_BINARY_DIR}/models/shape_predictor_68_face_landmarks.dat.bz2
COMMENT "下载人脸关键点模型..."
)
然后在代码里加载模型:
dlib::shape_predictor sp;
dlib::deserialize("models/shape_predictor_68_face_landmarks.dat") >> sp;
总结
集成Dlib到CMake项目,说白了就三步:拿到源码、配置选项、链接库。我个人建议用子模块的方式,版本可控,跨平台也省心。如果你只是临时用一下,apt安装也行,但记得检查版本是否满足需求。
最后说一句,Dlib的文档真的写得不错,遇到问题先去翻翻examples目录,里面几乎每个API都有对应的示例代码。嗯,今天就聊到这里,希望对你有所帮助。
公众号:蓝海资料掘金营,微信deep3321