第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集成CMake项目核心流程 获取Dlib源码 git submodule / 下载 配置CMake选项 关闭不需要的模块 add_subdirectory 集成到构建树 target_link_libraries 链接dlib库到你的目标 编译 & 运行 cmake .. && make && ./your_app ⚠️ 注意 版本一致性

避坑指南

集成Dlib的过程中,我遇到过几个比较头疼的问题,这里列出来,你遇到了可以直接对照解决。

问题 现象 解决方案
编译报错:undefined reference to dlib::... 链接时找不到Dlib的函数 检查target_link_libraries是否写对了,注意Dlib的target名称是dlib不是dlib::dlib(除非你用find_package)
编译极慢(超过10分钟) Dlib默认编译所有模块 设置DLIB_NO_GUI_SUPPORTDLIB_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