アップデート
次の記事はPetter Christian Bjellandによって書かれたので、すべてのクレジットは彼のものです。彼のブログは現在メンテナンス モードになっているようですので、ここに投稿しますが、共有する価値があると思います。
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);
}
あはは、画像には a を渡す必要がありMatVector
ます! ラベルをCvArr
(1 行または 1 列) で渡すことができます。はopencv_core の 4629 行目 (これを書いている時点) でMatVector
定義されており、次のようになります。
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);
}
おそらく、JavaArrayList
から への変換を行うメソッドを自分で作成する必要があります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
。get()
Pointer は具体的なFaceRecognizer
アルゴリズムを作成するメソッドによって満たされるため、についてのみ考慮する必要があります。
@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 モデルが学習されます。以上です!