OpenCV  4.10.0
开源计算机视觉
正在加载...
正在搜索...
找不到匹配项
向人脸识别 API 添加新的算法

目标

在本教程中,您将学习如何

  • 将人脸标志检测器的新算法集成到人脸识别 API 中
  • 编译特定的供稿模块
  • 在函数中使用额外参数

说明

  • 添加类标题

    应该在新文件中将新算法的类标题添加到 include/opencv2/face 中。以下是可以用于集成新算法的模板,将 FacemarkNEW 更改为新算法的代表性名称,并使用相应的代表性文件名将其保存起来。

    @code{.cpp}
    class CV_EXPORTS_W FacemarkNEW : public Facemark {
    public:
        struct CV_EXPORTS Config {
            Config();
    
            /*read only parameters - just for example*/
            double detect_thresh;         //!<  detection confidence threshold
            double sigma;                 //!<  another parameter
    
            void read(const FileNode& /*fn*/);
            void write(FileStorage& /*fs*/) const;
        };
    
        /*Builder and destructor*/
        static Ptr<FacemarkNEW> create(const FacemarkNEW::Config &conf = FacemarkNEW::Config() );
        virtual ~FacemarkNEW(){};
    };
    @endcode
    
  • 添加实现代码

    在源文件夹中使用代表新算法的名称创建一个新文件。以下是可以使用的模板。

    @code{.cpp}
    #include "opencv2/face.hpp"
    #include "precomp.hpp"
    
    namespace cv
    {
        FacemarkNEW::Config::Config(){
            detect_thresh = 0.5;
            sigma=0.2;
        }
    
        void FacemarkNEW::Config::read( const cv::FileNode& fn ){
            *this = FacemarkNEW::Config();
    
            if (!fn["detect_thresh"].empty())
                fn["detect_thresh"] >> detect_thresh;
    
            if (!fn["sigma"].empty())
                fn["sigma"] >> sigma;
    
        }
    
        void FacemarkNEW::Config::write( cv::FileStorage& fs ) const{
            fs << "detect_thresh" << detect_thresh;
            fs << "sigma" << sigma;
        }
    
        /*implementation of the algorithm is in this class*/
        class FacemarkNEWImpl : public FacemarkNEW {
        public:
            FacemarkNEWImpl( const FacemarkNEW::Config &conf = FacemarkNEW::Config() );
    
            void read( const FileNode& /*fn*/ );
            void write( FileStorage& /*fs*/ ) const;
    
            void loadModel(String filename);
    
            bool setFaceDetector(bool(*f)(InputArray , OutputArray, void * extra_params));
            bool getFaces( InputArray image , OutputArray faces, void * extra_params);
    
            Config config;
    
        protected:
    
            bool addTrainingSample(InputArray image, InputArray landmarks);
            void training();
            bool fit(InputArray image, InputArray faces, InputOutputArray landmarks, void * runtime_params);
    
            Config config; // configurations
    
            /*proxy to the user defined face detector function*/
            bool(*faceDetector)(InputArray , OutputArray, void * );
        }; // class
    
        Ptr<FacemarkNEW> FacemarkNEW::create(const FacemarkNEW::Config &conf){
            return Ptr<FacemarkNEWImpl>(new FacemarkNEWImpl(conf));
        }
    
        FacemarkNEWImpl::FacemarkNEWImpl( const FacemarkNEW::Config &conf ) :
            config( conf )
        {
            // other initialization
        }
    
        bool FacemarkNEWImpl::addTrainingSample(InputArray image, InputArray landmarks){
            // pre-process and save the new training sample
            return true;
        }
    
        void FacemarkNEWImpl::training(){
            printf("training\n");
        }
    
        bool FacemarkNEWImpl::fit(
            InputArray image,
            InputArray faces,
            InputOutputArray landmarks,
            void * runtime_params)
        {
            if(runtime_params!=0){
                // do something based on the extra parameters
            }
    
            printf("fitting\n");
            return 0;
        }
    
        void FacemarkNEWImpl::read( const cv::FileNode& fn ){
            config.read( fn );
        }
    
        void FacemarkNEWImpl::write( cv::FileStorage& fs ) const {
            config.write( fs );
        }
    
        void FacemarkNEWImpl::loadModel(String filename){
            // load the model
        }
    
        bool FacemarkNEWImpl::setFaceDetector(bool(*f)(InputArray , OutputArray, void * extra_params )){
            faceDetector = f;
            isSetDetector = true;
            return true;
        }
    
        bool FacemarkNEWImpl::getFaces( InputArray image , OutputArray roi, void * extra_params){
            if(!isSetDetector){
                return false;
            }
    
            if(extra_params!=0){
                //extract the extra parameters
            }
    
            std::vector<Rect> & faces = *(std::vector<Rect>*)roi.getObj();
            faces.clear();
    
            faceDetector(image.getMat(), faces, extra_params);
    
            return true;
        }
    }
    
    @endcode
    
  • 编译代码

    清除构建文件夹,然后重建整个库。请注意,您可以通过向 cmake 添加 "-D BUILD_opencv_<MODULE_NAME>=OFF" 标志来禁用其他供稿模块的编译。在此之后,可以在 "<build_folder>/modules/face" 中执行 make 命令来加快编译过程。

最佳实践

  • 处理额外参数 要处理额外参数,应该创建一个新结构来容纳所有必需参数。以下是一个参数容器示例

    struct CV_EXPORTS Params
    {
    Params( Mat rot = Mat::eye(2,2,CV_32F),
    Point2f trans = Point2f(0.0,0.0),
    float scaling = 1.0
    );
    Mat R;
    Point2f t;
    float scale;
    };

    以下是一个提取额外参数的代码段

    if(runtime_params!=0){
    Telo* conf = (Telo*)params;
    Params* params
    std::vector<Params> params = *(std::vector<Params>*)runtime_params;
    for(size_t i=0; i<params.size();i++){
    fit(img, landmarks[i], params[i].R,params[i].t, params[i].scale);
    }
    }else{
    // 执行操作
    }

    以下是一个将额外参数传递到 fit 函数中的示例

    FacemarkAAM::Params * params = new FacemarkAAM::Params(R,T,scale);
    facemark->fit(image, faces, landmarks, params)

    为了理解这个原理,这里有一个简单的示例,您可以尝试编译它并查看其工作原理。

    struct Params{
    int x,y;
    Params(int _x, int _y);
    };
    Params::Params(int _x,int _y){
    x = _x;
    y = _y;
    }
    void test(int a, void * params=0){
    printf("a:%i\n", a);
    if(params!=0){
    Params* params = (Params*)params;
    printf("extra parameters:%i %i\n", params->x, params->y);
    }
    }
    int main(){
    Params* params = new Params(7,22);
    test(99, params);
    return 0;
    }
  • 最小化依赖关系 强烈建议在编译时尽可能使代码大小保持尽可能小。为此,鼓励开发人员避免使用 imgcodecs 和 highgui 等繁重的依赖关系。
  • 文档和示例 请依据需要随时更新文档,并为新算法添加示例代码。
  • 测试代码 算法应附有相应的测试代码,以确保算法与多种类型的环境(例如 Linux、Windows64、Windows32、Android 等)兼容。应执行一些基本测试,如 test/test_facemark_lbf.cpp 文件中所示,包括创建实例、添加训练数据、执行训练过程、加载训练好的模型以及执行拟合以获取面部特征点。
  • 数据组织 建议将新算法的数据分为 3 部分
    class CV_EXPORTS_W FacemarkNEW : public Facemark {
    public
    struct CV_EXPORTS Params
    {
    // 用作额外参数的变量
    }
    struct CV_EXPORTS Config
    {
    // 用于配置算法的变量
    }
    struct CV_EXPORTS Model
    {
    // 存储模型信息的变量
    }
    static Ptr<FacemarkNEW> create(const FacemarkNEW::Config &conf = FacemarkNEW::Config() );
    virtual ~FacemarkNEW(){};
    }