是否可以在编译时加载/读取shape_predictor_68_face_landmarks.dat?

时间:2016-06-09 11:16:59

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

我正在尝试使用DLIBface_landmark_detection_ex.cpp Visual Studio 中构建 C ++ 应用程序。构建应用程序从命令promt运行,经过训练的模型和映像文件作为参数传递。

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都可以从此缓冲区中读取。

2 个答案:

答案 0 :(得分:5)

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

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

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

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

    1. 使用dlib :: deserialize
    2. 从此流反序列化
  4. 这是最小的例子,但没有资源阅读:

    #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,并将其全局用于所有线程

答案 1 :(得分:1)

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

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

以下命令将为deserialize生成头文件:

shape_predictor_68_face_landmarks.dat

如果您查看xxd -i shape_predictor_68_face_landmarks.dat > shape_predictor_68_face_landmarks.hpp 内部,将有2个变量:类型为shape_predictor_68_face_landmarks.hpp的{​​{1}},其中包含模型文件的内容,以及类型为{{1}的shape_predictor_68_face_landmarks_dat }。

unsigned char []驱动程序代码中,您将执行以下操作

shape_predictor_68_face_landmarks_dat_len

警告:请小心打开unsigned int生成的文件,因为它们可能很大,并且会导致文本编辑器崩溃。

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