Android上的人脸识别

Posted

技术标签:

【中文标题】Android上的人脸识别【英文标题】:Face Recognition on Android 【发布时间】:2012-07-26 20:05:02 【问题描述】:

我正在尝试在 android 上开发人脸识别应用,由于我不想在项目中使用 NDK(只是没有时间切换),我坚持使用Java,因此我遇到了一些问题:

    似乎 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)的向量。我尝试在 Java 中传递ArrayList<mat> & ArrayList<integer> 和 Vectors,但似乎该方法明确接受了我不确定如何获取的“CvArr”数据类型......这是错误:

类型中的方法train(opencv_core.CvArr, opencv_core.CvArr) opencv_contrib.FaceRecognizer 不适用于参数 (数组列表,数组列表)

有人知道如何将我的 ArrayList 更改为 CvArr 吗?!

这是我的第一篇文章,我不确定是在一个帖子中还是在三个帖子中提出所有三个问题,非常抱歉给您带来不便......如果您需要有关该项目的任何其他信息,请随时提问。

【问题讨论】:

有什么理由需要使用 OpenCV? Android SDK 内置了 android.media.FaceDetector 类。检查此示例项目:developer.com/ws/android/programming/… 实际上我正在寻找一个人脸识别库,而不仅仅是一个人脸检测库,因此我不能只使用 Android SDK 人脸检测... @Cypher,你是如何在安卓中集成人脸识别的? 【参考方案1】:

更新

下面的文章是Petter Christian Bjelland写的,所以所有的功劳都是他的。我发在这里,因为他的博客目前似乎处于维护模式,但我认为值得分享。

用JavaCV做人脸识别(来自http://pcbje.com)

我找不到任何关于如何使用 OpenCV 和 Java 执行人脸识别的教程,所以我决定在这里分享一个可行的解决方案。该解决方案的当前形式效率非常低,因为每次运行都会构建训练模型,但它显示了使其工作所需的条件。

下面的类有两个参数:包含训练人脸的目录的路径和要分类的图像的路径。并不是所有图像都必须具有相同的大小,并且必须从原始图像中裁剪出人脸(如果您还没有进行人脸检测,请看这里)。

为简单起见,该课程还要求训练图像的文件名格式为:<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, line 845 at time of writing this):

@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(一行或一列)中传递标签。 MatVector 定义在 opencv_core, line 4629 (at time of writing 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);

再看代码,我猜可以这样使用:

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 中尚不存在这样的函数)。

现在回答你的第二个问题。 FaceRecognizer 等同于 cv::FaceRecognizer。本机 OpenCV C++ 类返回一个cv::Ptr&lt;cv::FaceRecognizer&gt;,它是一个指向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(),因为指针由创建具体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);

这会学习一个特征脸模型。就是这样!

【讨论】:

谢谢 Philipp...但是我在尝试将数据加载到 CvArr 时遇到了一些麻烦...我已经将标签作为数据字符串...但我似乎找不到一种将这些标签作为数组插入到 CvArr 的方法......让我们称之为缺乏 OpenCV 知识......有人可以帮我解决这个问题吗?! 要么使用一个 QA 页面,要么使用另一个,在这两个页面上都问我有点矫枉过正。我只能写我在 OpenCV QA 页面上说的话。我可以确定如何在 OpenCV C++ 中执行此操作,但您要问的是 JavaCV。因此,最好在 JavaCV 邮件列表中询问此类问题,该邮件列表位于:https://groups.google.com/forum/?fromgroups#!forum/javacv 我想知道这是否有效?我之前尝试过实现这一点,但据我所知,JavaCV 并没有很好地实现 facerec 库 请查看完整源代码示例的更新答案。 做得很好.. 太棒了 :-) .. 需要一个信息,对于每个算法运行,它总是发回一些 predictLabel ,即使面部根本不匹配,所以我们如何管理这个部分?【参考方案2】:

我使用 opencv 制作了一个用于人脸识别的安卓应用。为了获得良好的识别,您需要更好的检测,您可以从以下位置查看:https://github.com/yaylas/AndroidFaceRecognizer 希望对你有帮助。

【讨论】:

以上是关于Android上的人脸识别的主要内容,如果未能解决你的问题,请参考以下文章

想做人脸识别的开发,有没有android的功能全的人脸识别SDK?

刷脸背后:人脸检测人脸识别人脸检索_张重生资料整理

人脸识别人脸识别损失函数学习笔记

Apple Vision Framework 识别人脸

人脸识别 介绍

OpenCV中LBPH人脸识别器识别人脸实战(附Python源码)