OpenCV 4.13.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 4.13.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
Facemark APIへの新しいアルゴリズムの追加

目標

このチュートリアルでは、以下の方法を学ぶ:

  • 新しい顔ランドマーク検出アルゴリズムを Facemark API に統合する
  • 特定のcontribモジュールをコンパイルする
  • 関数で追加の引数を使用する

説明

  • クラスヘッダの追加

    新しいアルゴリズムのクラスヘッダは、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
    
  • 実装コードの追加

    新しいアルゴリズムを表す名前で source フォルダに新しいファイルを作成する。以下に使用できるテンプレートを示す。

    @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
    
  • コードのコンパイル

    build フォルダをクリアしてから、ライブラリ全体を再ビルドする。cmake に "-D BUILD_opencv_<MODULE_NAME>=OFF" フラグを追加することで、他のcontribモジュールのコンパイルを無効化できる。その後、"<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{
    // do something
    }

    そして以下は追加の引数を 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;
    }
  • 依存関係の最小化 コンパイル時にコードを可能な限り小さく保つことが強く推奨される。この目的のため、開発者は imgcodecshighgui のような重い依存関係を必要とすることを避けるよう推奨される。
  • ドキュメントと例 必要に応じてドキュメントを更新し、新しいアルゴリズムのサンプルコードを用意すること。
  • テストコード アルゴリズムには、それが様々な種類の環境(Linux、Windows64、Windows32、Android など)と互換性があることを保証するための対応するテストコードを付随させるべきである。test/test_facemark_lbf.cpp ファイルで示されているように、実行すべきいくつかの基本的なテストがある。これには、インスタンスの生成、訓練データの追加、訓練処理の実行、訓練済みモデルの読み込み、および顔ランドマークを取得するためのフィッティングの実行が含まれる。
  • データの構成 新しいアルゴリズムのデータは3つの部分に分割することが推奨される:
    class CV_EXPORTS_W FacemarkNEW : public Facemark {
    public:
    struct CV_EXPORTS Params
    {
    // variables utilized as extra parameters
    }
    struct CV_EXPORTS Config
    {
    // variables used to configure the algorithm
    }
    struct CV_EXPORTS Model
    {
    // variables to store the information of model
    }
    static Ptr<FacemarkNEW> create(const FacemarkNEW::Config &conf = FacemarkNEW::Config() );
    virtual ~FacemarkNEW(){};
    }