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

目的

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

  • FacemarkAAM のインスタンスを生成する
  • AAM モデルを学習する
  • FacemarkAAM を使ったフィッティング

準備

このチュートリアルを続ける前に、顔ランドマーク検出のデータセットをダウンロードしておく必要がある。https://ibug.doc.ic.ac.uk/download/annotations/lfpw.zip から取得できる LFPW データセットのダウンロードを推奨する。

アノテーション形式が API でサポートされていることを確認すること。アノテーションファイルの内容は以下のスニペットのようになっているはずである:

version: 1
n_points: 68
{
212.716603 499.771793
230.232816 566.290071
...
}

次に行うことは、画像ファイルの一覧とアノテーションファイルの一覧をそれぞれ含む2つのテキストファイルを作成することである。両方のファイルで画像とアノテーションの順序が一致していることを確認すること。さらに、相対パスではなく絶対パスを使うことを推奨する。Linux マシンでファイル一覧を作成する例

ls $PWD/trainset/*.jpg > images_train.txt
ls $PWD/trainset/*.pts > annotation_train.txt

images_train.txt の内容例

/home/user/lfpw/trainset/100032540_1.jpg
/home/user/lfpw/trainset/100040721_1.jpg
/home/user/lfpw/trainset/100040721_2.jpg
/home/user/lfpw/trainset/1002681492_1.jpg

annotation_train.txt の内容例

/home/user/lfpw/trainset/100032540_1.pts
/home/user/lfpw/trainset/100040721_1.pts
/home/user/lfpw/trainset/100040721_2.pts
/home/user/lfpw/trainset/1002681492_1.pts

必要に応じて、テストセット用にも同様のファイルを作成できる。

このチュートリアルでは、ファイルサイズが大きい(約500MB)ため、学習済みモデルは提供されない。このチュートリアルに従えば、数分で独自の学習済みモデルを学習・取得できる。

AAMアルゴリズムの利用

完全な動作コードは face/samples/facemark_demo_aam.cpp ファイルにある。このチュートリアルでは、いくつかの重要な部分の説明を扱う。

  1. AAM アルゴリズムのインスタンスの生成

    /*create the facemark instance*/
    params.scales.push_back(2.0);
    params.scales.push_back(4.0);
    params.model_filename = "AAM.yaml";
    Ptr<FacemarkAAM> facemark = FacemarkAAM::create(params);

    まず、AAM アルゴリズム用のパラメータのインスタンスを生成する。この場合、スケーリング係数のデフォルト一覧を変更する。デフォルトでは、使用されるスケーリング係数は 1.0(スケーリングなし)である。ここでは2つのスケーリング係数を追加し、これによりインスタンスはスケール2と4で2つのモデルを追加で学習する(それぞれ2分の1、4分の1のサイズで、フィッティング時間がより高速になる)。ただし、このスケーリング係数を大きくしすぎると画像が非常に小さくスケーリングされてしまうため、注意が必要である。そうなると、ランドマーク検出に重要な情報をすべて失ってしまう。

    あるいは、この例と同様の方法でデフォルトのスケーリングを上書きすることもできる:

    std::vector<float>scales;
    scales.push_back(1.5);
    scales.push_back(2.4);
    FacemarkAAM::Params params;
    params.scales = scales;
  2. データセットの読み込み

    /*Loads the dataset*/
    std::vector<String> images_train;
    std::vector<String> landmarks_train;
    loadDatasetList(images_path,annotations_path,images_train,landmarks_train);

    データセットの一覧をプログラムに読み込む。次のステップでデータセットのサンプルを1つずつ追加していく。

  3. トレーナーへのサンプルの追加

    Mat image;
    std::vector<Point2f> facial_points;
    for(size_t i=0;i<images_train.size();i++){
    image = imread(images_train[i].c_str());
    loadFacePoints(landmarks_train[i],facial_points);
    facemark->addTrainingSample(image, facial_points);
    }

    データセット一覧の画像と、それに対応するアノテーションデータが1つずつ読み込まれる。そして、サンプルのペアがトレーナーに追加される。

  4. 学習処理

    /* trained model will be saved to AAM.yml */
    facemark->training();

    学習処理は1行のコードで呼び出される。必要な学習サンプルがすべてトレーナーに追加済みであることを確認すること。

  5. フィッティングの準備

    まず、テストファイルの一覧を読み込む必要がある。

    /*test using some images*/
    String testFiles(images_path), testPts(annotations_path);
    if(!test_images_path.empty()){
    testFiles = test_images_path;
    testPts = test_images_path; //unused
    }
    std::vector<String> images;
    std::vector<String> facePoints;
    loadDatasetList(testFiles, testPts, images, facePoints);

    AAM は初期化パラメータ(回転、平行移動、スケーリング)を必要とするため、これらの情報を格納する変数を宣言する必要があり、その情報はカスタム関数を使って取得される。この例における getInitialFitting() 関数の実装は最適ではないため、独自の関数を作成してもよい。

    初期化は、学習済みモデルのベース形状と現在の顔画像を比較することで得られる。この場合、回転は入力顔画像における両目で形成される線の角度と、ベース形状における同じ線とを比較することで得られる。一方、スケーリングは入力画像における両目間の線の長さをベース形状と比較することで得られる。

  6. フィッティング処理

    フィッティング処理は、与えられた画像から顔を検出することによって開始される。

    image = imread(images[i]);
    myDetector(image, faces, &face_cascade);

    少なくとも1つの顔が見つかった場合、次のステップは初期化パラメータの計算である。この場合、getInitialFitting() 関数は最適ではないため、与えられた顔から両目のペアを見つけられないことがある。そのため、初期化パラメータを持たない顔を除外する。この場合、conf ベクトルの各要素は、除外されずに残った各顔の初期化パラメータを表す。

    std::vector<FacemarkAAM::Config> conf;
    std::vector<Rect> faces_eyes;
    for(unsigned j=0;j<faces.size();j++){
    if(getInitialFitting(image,faces[j],s0,eyes_cascade, R,T,scale)){
    conf.push_back(FacemarkAAM::Config(R,T,scale,(int)params.scales.size()-1));
    faces_eyes.push_back(faces[j]);
    }
    }

    conf ベクトルに格納されたフィッティングパラメータについて、最後のパラメータはフィッティング処理で使用されるスケーリング係数のIDを表す。この例では、フィッティングは最も大きいスケーリング係数(4)を使用する。これは他のスケールと比べて最も計算時間が速いことが期待される。もしIDがモデル内で利用可能な学習済みスケールよりも大きい場合、最も大きいスケールIDを持つモデルが使用される。

    フィッティング処理は非常に単純で、対応する画像、与えられた画像内のすべての顔のROIを表す cv::Rect のベクトル、landmarks 変数で表されるランドマーク点のコンテナ、および設定変数を渡すだけでよい。

    if(conf.size()>0){
    printf(" - face with eyes found %i ", (int)conf.size());
    std::vector<std::vector<Point2f> > landmarks;
    double newtime = (double)getTickCount();
    facemark->fitConfig(image, faces_eyes, landmarks, conf);
    double fittime = ((getTickCount() - newtime)/getTickFrequency());
    for(unsigned j=0;j<landmarks.size();j++){
    drawFacemarks(image, landmarks[j],Scalar(0,255,0));
    }
    printf("%f ms\n",fittime*1000);
    imshow("fitting", image);
    waitKey(0);
    }else{
    printf("initialization cannot be computed - skipping\n");
    }

    フィッティング処理が完了したら、drawFacemarks 関数を使って結果を可視化できる。