I am trying to write a simple software that, given a set of images, recognise if the person I am looking is in that set.
By looking at the openCV doc I came across the class FaceRecognizer
. I have tried to look for examples and this one was the only one I was able to find:
https://github.com/bytedeco/javacv/blob/master/samples/OpenCVFaceRecognizer.java
I have made some replacements because some classes were not available in Java since I am not using org.bytedeco
. This is the updated code:
/**
* I couldn't find any tutorial on how to perform face recognition using OpenCV and Java,
* so I decided to share a viable solution here. The solution is very inefficient in its
* current form as the training model is built at each run, however it shows what's needed
* to make it work.
*
* The class below takes two arguments: The path to the directory containing the training
* faces and the path to the image you want to classify. Not that all images has to be of
* the same size and that the faces already has to be cropped out of their original images
* (Take a look here http://fivedots.coe.psu.ac.th/~ad/jg/nui07/index.html if you haven't
* done the face detection yet).
*
* For the simplicity of this post, the class also requires that the training images have
* filename 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
* ...and so on.
*
* Source: http://pcbje.com/2012/12/doing-face-recognition-with-javacv/
*
* @author Petter Christian Bjelland
*/
public class OpenCVFaceRecognizer {
public static void main(String[] args) {
String trainingDir = args[0];
Mat testImage = Imgcodecs.imread(args[1], Imgcodecs.IMREAD_GRAYSCALE);
File root = new File(trainingDir);
FilenameFilter imgFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
name = name.toLowerCase();
return name.endsWith(".jpg") || name.endsWith(".pgm") || name.endsWith(".png");
}
};
File[] imageFiles = root.listFiles(imgFilter);
List<Mat> images = new ArrayList<>(imageFiles.length);
Mat labels = new Mat(imageFiles.length, 1, CvType.CV_32SC1);
IntBuffer labelsBuf = labels.createBuffer();
int counter = 0;
for (File image : imageFiles) {
Mat img = Imgcodecs.imread(image.getAbsolutePath(), Imgcodecs.IMREAD_GRAYSCALE);
int label = Integer.parseInt(image.getName().split("\\-")[0]);
images.add(counter, img);
labelsBuf.put(counter, label);
counter++;
}
FaceRecognizer faceRecognizer = FisherFaceRecognizer.create();
// FaceRecognizer faceRecognizer = EigenFaceRecognizer.create();
// FaceRecognizer faceRecognizer = LBPHFaceRecognizer.create();
faceRecognizer.train(images, labels);
//IntPointer label = new IntPointer(1);
int[] label = new int [1];
//DoublePointer confidence = new DoublePointer(1);
double[] confidence = new double [1];
faceRecognizer.predict(testImage, label, confidence);
int predictedLabel = label[0];
System.out.println("Predicted label: " + predictedLabel);
}
}
My questions are:
.createBuffer()
does not exist for theMat
class in the opencv dependency. So, how can I replace it?- Why do all images have to be of the same size? Because of course the training directory has images of different sizes.
- Why the faces have to be already cropped out? I am able to successfully detect a face by drawing a rectangle (if you want I can share the code here as well). So, I should save the detected faces to file or I can simply create a new instance of BufferedImages with the area of the detected face?
- What are labels and what's their use? Simply to determine that those photos represent the same person? For instance: 1-jon_at_the_sea.png 1-jon_at_the_beach.png
Thank you.