Android 上的人脸识别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11699744/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Face Recognition on Android
提问by Cypher
I'm trying to develop a Face Recognition app on Android and since I don't want to use NDK on the project (simply don't have the time to switch), I'm sticking to develop the whole app with Java and therefor I'm having some problems :
我正在尝试在 Android 上开发人脸识别应用程序,因为我不想在项目中使用 NDK(只是没有时间切换),所以我坚持使用 Java 开发整个应用程序,因此我有一些问题:
It seems the Contrib Module isn't included in OpenCV 2.4.2 . is there anyway to use it in the project ?
I tried using JavaCV to use the Contrib Module's "FaceRecognizer" class. there are two classes available called "FaceRecognizer" & "FaceRecognizerPtr". does anybody know what the difference between these two is ?
The classes mentioned above have a method called "Train" which (In C++) receives two Vectors of types "Mat & Integer" (
model->train(images,labels) & train(Vector<mat> theImages, Vector<int> theLabels
) . I tried passing themArrayList<mat> & ArrayList<integer>
and Vectors in Java but it seems that the method explicitly accepts the "CvArr" Data type which I'm not sure how to acquire... Here is the error :
似乎 Contrib 模块不包含在 OpenCV 2.4.2 中。无论如何在项目中使用它?
我尝试使用 JavaCV 来使用 Contrib 模块的“FaceRecognizer”类。有两个可用的类,称为“FaceRecognizer”和“FaceRecognizerPtr”。有谁知道这两者之间的区别是什么?
上面提到的类有一个名为“Train”的方法,它(在 C++ 中)接收两个类型为“Mat & Integer” (
model->train(images,labels) & train(Vector<mat> theImages, Vector<int> theLabels
) 的向量。我尝试ArrayList<mat> & ArrayList<integer>
在 Java 中传递它们和向量,但似乎该方法明确接受了“CvArr”数据类型,我不确定如何获取...这是错误:
The method train(opencv_core.CvArr, opencv_core.CvArr) in the type opencv_contrib.FaceRecognizer is not applicable for the arguments (ArrayList, ArrayList)
opencv_contrib.FaceRecognizer 类型中的方法 train(opencv_core.CvArr, opencv_core.CvArr) 不适用于参数 (ArrayList, ArrayList)
Does anyone know how to change my ArrayList to CvArr ?!
有谁知道如何将我的 ArrayList 更改为 CvArr ?!
This is my first post and I wasn't sure whether to ask all three questions in one post or in three posts so sorry for any inconveniences... If you need any other Information about the project, feel free to ask.
这是我的第一篇文章,我不确定是在一个帖子中还是在三个帖子中提出所有三个问题,很抱歉给您带来任何不便......如果您需要有关该项目的任何其他信息,请随时提问。
回答by bytefish
Update
更新
The following article was written by Petter Christian Bjelland, so all credit is his. I am posting it here, because his blog seems to be in Maintenance mode at the moment, but I think it is worth sharing.
以下文章由Petter Christian Bjelland撰写,因此所有功劳都归于他。我把它贴在这里,因为他的博客目前似乎处于维护模式,但我认为值得分享。
Doing face recognition with JavaCV (from http://pcbje.com)
使用 JavaCV 进行人脸识别(来自http://pcbje.com)
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.
我找不到任何关于如何使用 OpenCV 和 Java 执行人脸识别的教程,所以我决定在这里分享一个可行的解决方案。该解决方案的当前形式非常低效,因为训练模型是在每次运行时构建的,但它显示了使其工作所需的条件。
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 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:
为简单起见,该类还要求训练图像的文件名格式为:<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
... and so on.
... 等等。
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) {
// 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);
}
}
The class requires the OpenCV Java interface. If you're using Maven, you can retrieve the required libraries with the following pom.xml:
该类需要 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>
Original Post
原帖
Quoting from my reply on http://answers.opencv.org/question/865/the-contrib-module-problem.
引用我对http://answers.opencv.org/question/865/the-contrib-module-problem 的回复。
Without ever having used javacv, let's see how far we can get by just looking at the interfaces! The project is on googlecode, which makes it easy to browse the code: http://code.google.com/p/javacv.
没用过javacv,看看接口能走多远!该项目位于 googlecode 上,便于浏览代码:http: //code.google.com/p/javacv。
First have a look at how cv::FaceRecognizer
has been wrapped (opencv_contrib.java, line 845 at time of writing this):
首先看看是如何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);
}
Aha, so you need to pass a MatVector
for the images! You can pass the labels in a CvArr
(one row or one column). The MatVector
is defined in opencv_core, line 4629 (at time of writing this)and it looks like this:
啊哈,所以你需要MatVector
为图像传递一个!您可以在CvArr
(一行或一列)中传递标签。该MatVector
定义在opencv_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);
}
Again just by looking at the code, I guess it can be used like this:
再次通过查看代码,我想它可以像这样使用:
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);
}
You probably want to write yourself a method that does the conversion from a Java ArrayList
to a MatVector
(if such a function does not exist in javacv yet).
您可能想自己编写一个方法来将 Java 转换ArrayList
为 a MatVector
(如果 javacv 中尚不存在这样的函数)。
Now to your second question. FaceRecognizer
is the equivalent to cv::FaceRecognizer
. The native OpenCV C++ classes return a cv::Ptr<cv::FaceRecognizer>
, which is a (Smart) Pointer to a cv::FaceRecognizer
. This has to be wrapped as well. See a pattern here?
现在回答你的第二个问题。FaceRecognizer
相当于cv::FaceRecognizer
。本机 OpenCV C++ 类返回 a cv::Ptr<cv::FaceRecognizer>
,它是一个指向 a 的(智能)指针cv::FaceRecognizer
。这也必须包装。看到这里的模式了吗?
The interface of FaceRecognizerPtr
now looks like this:
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);
}
So you can either get a FaceRecognizer
from this class or put a FaceRecognizer
into. You should only be concerned about the get()
, as the Pointer is filled by the method creating the concrete FaceRecognizer
algorithm:
所以你可以FaceRecognizer
从这个类中获取 a或放入 a 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*/);
So once you have got the FaceRecognizerPtr, you can do things like:
因此,一旦获得 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);
This learns you an Eigenfaces model. And that's it!
这将为您学习特征脸模型。就是这样!
回答by user1488918
I made an android app for face recognition using opencv. For good recognition you need a better detection, you can check it out from: https://github.com/yaylas/AndroidFaceRecognizerI hope it helps.
我使用opencv制作了一个用于人脸识别的Android应用程序。为了更好的识别,您需要更好的检测,您可以从以下位置查看:https: //github.com/yaylas/AndroidFaceRecognizer我希望它有所帮助。