web-dev-qa-db-ja.com

Androidでの顔認識

Android=で顔認識アプリを開発しようとしています。プロジェクトでNDKを使用したくないので(単に切り替える時間がないので)、こだわっていますJava=でアプリ全体を開発するため、いくつかの問題があります:

  1. ContribモジュールはOpenCV 2.4.2に含まれていないようです。とにかくプロジェクトで使用することはありますか?

  2. JavaCVを使用してContribモジュールの「FaceRecognizer」クラスを使用してみました。 「FaceRecognizer」と「FaceRecognizerPtr」という2つのクラスが利用可能です。これらの2つの違いは何ですか?

  3. 上記のクラスには、「Train」と呼ばれるメソッドがあり、(C++では)「Mat&Integer」型の2つのVector(model->train(images,labels) & train(Vector<mat> theImages, Vector<int> theLabels)を受け取ります。それらを渡してみましたArrayList<mat> & ArrayList<integer>およびJavaのベクターですが、メソッドは「CvArr」データ型を明示的に受け入れるようですが、取得方法がわかりません...ここにエラーがあります:

タイプopencv_contrib.FaceRecognizerのメソッドtrain(opencv_core.CvArr、opencv_core.CvArr)は、引数(ArrayList、ArrayList)には適用できません

ArrayListをCvArrに変更する方法を知っている人はいますか?

これは私の最初の投稿であり、3つの質問すべてを1つの投稿で行うか、それとも3つの投稿で行うかがわからなかったため、ご不便をおかけして申し訳ありません。プロジェクトに関するその他の情報が必要な場合は、お気軽にお問い合わせください。

14
Cypher

更新

次の記事は Petter Christian Bjelland によって書かれたため、すべてのクレジットは彼のものです。彼のブログは現在メンテナンスモードになっているようですので、ここに投稿しますが、共有する価値があると思います。

JavaCVで顔認識を行う(from http://pcbje.com

OpenCVとJavaを使用して顔認識を実行する方法に関するチュートリアルが見つからなかったので、ここで実行可能なソリューションを共有することにしました。このソリューションは、トレーニングモデルが実行ごとに作成されるため、現在の形式では非常に非効率的ですが、機能させるために必要なものを示しています。

以下のクラスは2つの引数を取ります:トレーニング顔を含むディレクトリへのパスと分類したい画像へのパス。すべての画像が同じサイズである必要があり、顔が元の画像から既に切り取られている必要があるわけではありません(顔の検出をまだ行っていない場合は、こちらをご覧ください)。

この投稿を簡単にするために、クラスではトレーニング画像のファイル名の形式も_<label>-rest_of_filename.png_にする必要があります。例えば:

 1-jon_doe_1.png 
 1-jon_doe_2.png 
 2-jane_doe_1.png 
 2-jane_doe_2.png 

... 等々。

コード:

_import com.googlecode.javacv.cpp.opencv_core;
import static com.googlecode.javacv.cpp.opencv_highgui.*;
import static com.googlecode.javacv.cpp.opencv_core.*;
import static com.googlecode.javacv.cpp.opencv_imgproc.*;
import static com.googlecode.javacv.cpp.opencv_contrib.*;
import Java.io.File;
import Java.io.FilenameFilter;

public class OpenCVFaceRecognizer {
  public static void main(String[] args) {
    String trainingDir = args[0];
    IplImage testImage = cvLoadImage(args[1]);

    File root = new File(trainingDir);

    FilenameFilter pngFilter = new FilenameFilter() {
      public boolean accept(File dir, String name) {
        return name.toLowerCase().endsWith(".png");
      }
    };

    File[] imageFiles = root.listFiles(pngFilter);

    MatVector images = new MatVector(imageFiles.length);

    int[] labels = new int[imageFiles.length];

    int counter = 0;
    int label;

    IplImage img;
    IplImage grayImg;

    for (File image : imageFiles) {
      // Get image and label:
      img = cvLoadImage(image.getAbsolutePath());
      label = Integer.parseInt(image.getName().split("\\-")[0]);
      // Convert image to grayscale:
      grayImg = IplImage.create(img.width(), img.height(), IPL_DEPTH_8U, 1);
      cvCvtColor(img, grayImg, CV_BGR2GRAY);
      // Append it in the image list:
      images.put(counter, grayImg);
      // And in the labels list:
      labels[counter] = label;
      // Increase counter for next image:
      counter++;
    }

    FaceRecognizer faceRecognizer = createFisherFaceRecognizer();
    // FaceRecognizer faceRecognizer = createEigenFaceRecognizer();
    // FaceRecognizer faceRecognizer = createLBPHFaceRecognizer()

    faceRecognizer.train(images, labels);

    // Load the test image:
    IplImage greyTestImage = IplImage.create(testImage.width(), testImage.height(), IPL_DEPTH_8U, 1);
    cvCvtColor(testImage, greyTestImage, CV_BGR2GRAY);

    // And get a prediction:
    int predictedLabel = faceRecognizer.predict(greyTestImage);
    System.out.println("Predicted label: " + predictedLabel);
  }
}
_

クラスにはOpenCV Javaインターフェースが必要です。Mavenを使用している場合は、次のpom.xmlを使用して必要なライブラリを取得できます。

_<project xmlns="http://maven.Apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.Apache.org/POM/4.0.0 http://maven.Apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.pcbje</groupId>
  <artifactId>opencvfacerecognizer</artifactId>
  <version>0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>opencvfacerecognizer</name>
  <url>http://pcbje.com</url>

  <dependencies>
    <dependency>
      <groupId>com.googlecode.javacv</groupId>
      <artifactId>javacv</artifactId>
      <version>0.3</version>
    </dependency>

    <!-- For Linux x64 environments -->
    <dependency>
      <groupId>com.googlecode.javacv</groupId>
      <artifactId>javacv</artifactId>
      <classifier>linux-x86_64</classifier>
      <version>0.3</version>
    </dependency>    

    <!-- For OSX environments -->
    <dependency>
      <groupId>com.googlecode.javacv</groupId>
      <artifactId>javacv</artifactId>
      <classifier>macosx-x86_64</classifier>
      <version>0.3</version>
    </dependency>
  </dependencies>

  <repositories>
    <repository>
      <id>javacv</id>
      <name>JavaCV</name>
      <url>http://maven2.javacv.googlecode.com/git/</url>
    </repository>
  </repositories>
</project>
_

元の投稿

http://answers.opencv.org/question/865/the-contrib-module-problem に対する私の返信からの引用.

Javacvを使用したことがない場合は、インターフェースを確認するだけでどの程度到達できるかを見てみましょう。プロジェクトはgooglecodeにあるため、コードを簡単に参照できます: http://code.google.com/p/javacv

まず、_cv::FaceRecognizer_がどのようにラップされているかを確認します( opencv_contrib.Java、これを書いている時点での行845 ):

_@Namespace("cv") public static class FaceRecognizer extends Algorithm {
    static { Loader.load(); }
    public FaceRecognizer() { }
    public FaceRecognizer(Pointer p) { super(p); }

    public /*abstract*/ native void train(@ByRef MatVector src, @Adapter("ArrayAdapter") CvArr labels);
    public /*abstract*/ native int predict(@Adapter("ArrayAdapter") CvArr src);
    public /*abstract*/ native void predict(@Adapter("ArrayAdapter") CvArr src, @ByRef int[] label, @ByRef double[] dist);
    public native void save(String filename);
    public native void load(String filename);
    public native void save(@Adapter("FileStorageAdapter") CvFileStorage fs);
    public native void load(@Adapter("FileStorageAdapter") CvFileStorage fs);
}
_

ああ、それであなたは画像のためにMatVectorを渡す必要があります!ラベルはCvArr(1行または1列)で渡すことができます。 MatVectoropencv_core、4629行目(これを書いている時点) で定義されており、次のようになります。

_public static class MatVector extends Pointer {
    static { load(); }
    public MatVector()       { allocate();  }
    public MatVector(long n) { allocate(n); }
    public MatVector(Pointer p) { super(p); }
    private native void allocate();
    private native void allocate(@Cast("size_t") long n);

    public native long size();
    public native void resize(@Cast("size_t") long n);

    @Index @ValueGetter public native @Adapter("MatAdapter") CvMat getCvMat(@Cast("size_t") long i);
    @Index @ValueGetter public native @Adapter("MatAdapter") CvMatND getCvMatND(@Cast("size_t") long i);
    @Index @ValueGetter public native @Adapter("MatAdapter") IplImage getIplImage(@Cast("size_t") long i);
    @Index @ValueSetter public native MatVector put(@Cast("size_t") long i, @Adapter("MatAdapter") CvArr value);
}
_

もう一度コードを見るだけで、次のように使用できると思います。

_int numberOfImages = 10;
// Allocate some memory:
MatVector images = new MatVector(numberOfImages);
// Then fill the MatVector, you probably want to do something useful instead:
for(int idx = 0; idx < numberOfImages; idx++){
   // Load an image:
   CvArr image = cvLoadImage("/path/to/your/image");
   // And put it into the MatVector:
   images.put(idx, image);
}
_

Java ArrayListからMatVectorへの変換を行うメソッドを自分で作成する必要がある場合(そのような関数がjavacvにまだ存在しない場合) 。

次に、2番目の質問です。 FaceRecognizerは_cv::FaceRecognizer_と同等です。ネイティブのOpenCV C++クラスは、_cv::Ptr<cv::FaceRecognizer>_への(スマート)ポインターである_cv::FaceRecognizer_を返します。これもラップする必要があります。こちらのパターンをご覧ください。

FaceRecognizerPtrのインターフェースは次のようになります。

_@Name("cv::Ptr<cv::FaceRecognizer>")
public static class FaceRecognizerPtr extends Pointer {
    static { load(); }
    public FaceRecognizerPtr()       { allocate();  }
    public FaceRecognizerPtr(Pointer p) { super(p); }
    private native void allocate();

    public native FaceRecognizer get();
    public native FaceRecognizerPtr put(FaceRecognizer value);
}
_

したがって、このクラスからFaceRecognizerを取得するか、FaceRecognizerを入れることができます。ポインタは具体的なFaceRecognizerアルゴリズムを作成するメソッドによって入力されるため、get()のみを考慮する必要があります。

_@Namespace("cv") public static native @ByVal FaceRecognizerPtr createEigenFaceRecognizer(int num_components/*=0*/, double threshold/*=DBL_MAX*/);
@Namespace("cv") public static native @ByVal FaceRecognizerPtr createFisherFaceRecognizer(int num_components/*=0*/, double threshold/*=DBL_MAX*/);
@Namespace("cv") public static native @ByVal FaceRecognizerPtr createLBPHFaceRecognizer(int radius/*=1*/,
        int neighbors/*=8*/, int grid_x/*=8*/, int grid_y/*=8*/, double threshold/*=DBL_MAX*/);
_

したがって、FaceRecognizerPtrを取得したら、次のようなことができます。

_// Holds your training data and labels:
MatVector images;
CvArr labels;
// Do something with the images and labels... Probably fill them?
// ...
// Then get a Pointer to a FaceRecognizer (FaceRecognizerPtr).
// Java doesn't have default parameters, so you have to add some yourself,
// if you pass 0 as num_components to the EigenFaceRecognizer, the number of
// components is determined by the data, for the threshold use the maximum possible
// value if you don't want one. I don't know the constant in Java:
FaceRecognizerPtr model = createEigenFaceRecognizer(0, 10000);
// Then train it. See how I call get(), to get the FaceRecognizer inside the FaceRecognizerPtr:
model.get().train(images, labels);
_

これはEigenfacesモデルを学習します。以上です!

14
bytefish

私はAndroidアプリを作成しました。良好な認識のためには、より良い検出が必要です。以下からチェックできます https://github.com/yaylas/ AndroidFaceRecognizer お役に立てば幸いです。

4
user1488918