Update
The next article was written by Petter Christian Bjelland , so all his merits. I post it here because his blog seems to be in maintenance mode at the moment, but I think it's worth sharing.
Performing face recognition using JavaCV (from http://pcbje.com )
I could not find a tutorial on how to perform face recognition using OpenCV and Java, so I decided to share a viable solution with them. The solution is very inefficient in its current form, since the training model is built on each run, however, it shows what is needed to make it work.
The following class takes two arguments: the path to the directory containing the learning faces, and the path to the image you want to classify. Not all images should be the same size and that faces should already be cropped from their original images (see here if you have not already done face detection).
For simplicity of this message, the class also requires that the training images have the file format: <label>-rest_of_filename.png . For example:
1-jon_doe_1.png
1-jon_doe_2.png
2-jane_doe_1.png
2-jane_doe_2.png
... etc.
The code:
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) {
The class requires the OpenCV Java interface. If you use Maven, you can get the necessary libraries with the following 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> <dependency> <groupId>com.googlecode.javacv</groupId> <artifactId>javacv</artifactId> <classifier>linux-x86_64</classifier> <version>0.3</version> </dependency> <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>
Original post
Quote from my answer at http://answers.opencv.org/question/865/the-contrib-module-problem .
Without using javacv, let's see how far we can get just by looking at the interfaces! The project is located on googlecode, which makes it easier to view the code: http://code.google.com/p/javacv .
First, consider how cv::FaceRecognizer was wrapped ( opencv_contrib.java, line 845 at the time of this writing ):
@Namespace("cv") public static class FaceRecognizer extends Algorithm { static { Loader.load(); } public FaceRecognizer() { } public FaceRecognizer(Pointer p) { super(p); } public native void train(@ByRef MatVector src, @Adapter("ArrayAdapter") CvArr labels); public native int predict(@Adapter("ArrayAdapter") CvArr src); public 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); }
Aha, so you need to pass MatVector for images! You can pass tags to CvArr (single row or single column). MatVector defined in opencv_core, line 4629 (as of this writing) and looks like this:
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); }
Just by looking at the code again, I think it can be used as follows:
int numberOfImages = 10;
You probably want to write yourself a method that performs a conversion from Java ArrayList to MatVector (if such a function does not already exist in javacv).
Now to your second question. FaceRecognizer is the equivalent of cv::FaceRecognizer . OpenCV C ++ native classes return cv::Ptr<cv::FaceRecognizer> , which is a (smart) pointer to cv::FaceRecognizer . It should also be wrapped. See the template here?
The FaceRecognizerPtr interface now looks like this:
@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); }
So, you can either get a FaceRecognizer from this class, or put a FaceRecognizer in. You only need to worry about get() , as the pointer is populated with a method that creates a specific FaceRecognizer algorithm:
@Namespace("cv") public static native @ByVal FaceRecognizerPtr createEigenFaceRecognizer(int num_components, double threshold); @Namespace("cv") public static native @ByVal FaceRecognizerPtr createFisherFaceRecognizer(int num_components, double threshold); @Namespace("cv") public static native @ByVal FaceRecognizerPtr createLBPHFaceRecognizer(int radius, int neighbors, int grid_x, int grid_y, double threshold);
So, once you get FaceRecognizerPtr, you can do things like:
Here you will find the Eigenfaces model. What is it!