【问题标题】:Is it possible to load/read shape_predictor_68_face_landmarks.dat at compile time?是否可以在编译时加载/读取 shape_predictor_68_face_landmarks.dat?
【发布时间】:2016-10-10 00:25:39
【问题描述】:

我正在尝试使用DLIBface_landmark_detection_ex.cppVisual Studio 中构建一个C++ 应用程序。从命令提示符运行的构建应用程序和经过训练的模型和图像文件作为参数传递。

face_landmark_detection_ex.exe shape_predictor_68_face_landmarks.dat image.jpg

这个shape_predictor_68_face_landmarks.dat 是针对 68 个地标对输入图像执行检测的训练模型,每次都需要在运行时加载以执行任何检测。我正在尝试做以下事情。

  • 在构建应用程序或编译时加载此 shape_predictor_68_face_landmarks.dat
  • 在代码中阅读此 shape_predictor_68_face_landmarks.dat,这样我的应用程序每次开始执行时都不会占用更多内存。

有什么方法可以将此文件打包到我的应用程序中,以减少运行所需的物理内存。

更新:

如何将这个shape_predictor_68_face_landmarks.dat 文件存储在静态缓冲区中,以便每次shape_predictor 都可以从这个缓冲区中读取。

【问题讨论】:

  • 文件内容是什么?它的结构是什么
  • @HumamHelfawi shape_predictor_68_face_landmarks.dat 是使用train_shape_predictor_ex.cpp 进行训练后的训练模型。它基本上是二进制文件。

标签: c++ file memory-management static-members dlib


【解决方案1】:

是的,有可能,但取决于 Visual Studio 而不是跨平台

  1. 您应该创建资源文件并将 hape_predictor_68_face_landmarks.dat 包含到您的项目中。有关详细信息,请参阅https://msdn.microsoft.com/ru-ru/library/7zxb70x7.aspx。这将使编译器将此文件放入您的 exe/dll

  2. 在运行时打开资源并获取内存指针https://msdn.microsoft.com/en-us/library/windows/desktop/ee719660(v=vs.85).aspx

  3. 从指针创建内存流(std::istream)。

    1. 使用 dlib::deserialize 从此流中反序列化

这是最小的例子,但没有资源阅读:

#include <string>
#include <iostream>
#include <dlib/image_processing/shape_predictor.h>


struct membuf : std::streambuf {
    membuf(char const* base, size_t size) {
        char* p(const_cast<char*>(base));
        this->setg(p, p, p + size);
    }
};
struct imemstream : virtual membuf, std::istream {
    imemstream(char const* base, size_t size)
            : membuf(base, size)
            , std::istream(static_cast<std::streambuf*>(this)) {
    }
};
using namespace dlib; //its important to use namespace dlib for deserialize work correctly
using namespace std;
int main(int argc, const char* argv[])
{
    const char* file_name = "shape_predictor_68_face_landmarks.dat";
    ifstream fs(file_name, ios::binary | ios::ate);
    streamsize size = fs.tellg();
    fs.seekg(0, ios::beg);
    std::vector<char> buffer(size);
    if (fs.read(buffer.data(), size))
    {
        cout << "Successfully read " << size << " bytes from " << file_name << " into buffer" << endl;
        imemstream stream(&buffer.front(), size); // here we are loading from memory buffer. you can change this line to use pointer from Resource
        shape_predictor sp;
        deserialize(sp, stream);
        cout << "Deserialized shape_predictor" << endl;
    }
    else cout << "Failed to read " << file_name << " into buffer" << endl;
    return 0;
}

关于内存使用情况。

首先你应该知道 shape_predictor::operator() 是 const,并且文档说对不同的线程使用一个 shape_predictor 是安全的。

因此,您可以在程序开始时创建一个 shape_predictor 并多次使用它,即使来自不同的线程

接下来,将形状预测器放入资源中会在程序启动时将其加载到 RAM 中,但是从资源中反序列化它会复制此内存,这将导致 RAM 使用开销。如果您需要尽可能少的 RAM 使用量 - 您应该从文件中加载它

最后一个问题 - 如何通过编译器初始化它。它没有现成的解决方案,但您可以使用 shape_predictor.h/deserialize 函数中的代码并手动加载它。我认为,这是一个糟糕的解决方案,因为与加载文件相比,您不会获得更少的 RAM 使用量

所以我的建议是从文件中加载一个 shape_predictor 并将其全局用于所有线程

【讨论】:

  • 是否可以声明这个内存 membuff static 然后每个新的 shape_predictor 对象读取/使用这个 membuff 作为可共享资源,并且在创建新对象时不会增加内存消耗.
  • 如何将内存流反序列化为 Shape_predictor 对象
  • @VIctor,我已经改进了我的答案。希望对你有帮助
  • 感谢您的帮助!我将在全球范围内使用一个 shape_predictor。
  • 这个VS只有哪些部分?
【解决方案2】:

我知道这是一个老问题,但由于我在 Linux/macOS 中使用 dlib,因此仅 Visual Studio 的解决方案在我的情况下不起作用。这是我想出的与 Unix 兼容的解决方案。

我所做的是使用xxd 工具将模型文件转换为文件内容的unsigned char [] 表示,将其写入自定义头文件,然后在deserialize 中使用它(而不是读入执行期间的文件)。

以下命令将为shape_predictor_68_face_landmarks.dat生成头文件:

xxd -i shape_predictor_68_face_landmarks.dat > shape_predictor_68_face_landmarks.hpp

如果您查看 shape_predictor_68_face_landmarks.hpp 内部,将会有 2 个变量:shape_predictor_68_face_landmarks_dat 类型的 unsigned char [] 包含模型文件的内容和 shape_predictor_68_face_landmarks_dat_len 类型 unsigned int

在您的 dlib 驱动程序代码中,您将执行以下操作

...
#include "shape_predictor_68_face_landmarks.hpp"
...

shape_predictor sp;
std::stringstream landmarksstream;
landmarksstream.write((const char*)shape_predictor_68_face_landmarks_dat, shape_predictor_68_face_landmarks_dat_len);
deserialize(sp, landmarksstream);

警告:打开由xxd 生成的文件时要小心,因为它们可能非常大并导致您的文本编辑器崩溃。

我无法回答这种方法的效率,但它确实允许在编译时而不是执行时“读入”模型文件。

【讨论】:

  • 您如何看待使用 mmap 加载模型是否有助于减少 RAM 使用并提高速度? mmap 中的字节数组可以毫无问题地馈送到std::istream 吗?
  • 我没有资格回答这个问题,但据我所知,使用mmap 可能会更有效。当我提出这个解决方案时,我主要关心的是开发速度,而不是最佳实现。
猜你喜欢
  • 2014-11-27
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-20
  • 1970-01-01
相关资源
最近更新 更多