Android上的人脸识别

16

我正在尝试在Android上开发人脸识别应用程序,由于我不想在项目中使用NDK(简单来说没有时间切换),因此我决定使用Java开发整个应用程序,但是我遇到了一些问题:

  1. 似乎OpenCV 2.4.2中未包含贡献模块。有没有办法在项目中使用它?

  2. 我尝试使用JavaCV来使用Contrib Module的“FaceRecognizer”类。有两个可用的类分别称为“FaceRecognizer”和“FaceRecognizerPtr”。有人知道这两者之间的区别吗?

  3. 上述提到的类具有一个名为“Train”的方法,它(在C ++中)接收两个类型为“Mat&Integer”的向量(model->train(images,labels)和train(Vector<mat> theImages, Vector<int> theLabels)。我尝试将它们传递给Java中的ArrayList<mat>和ArrayList<integer>以及向量,但似乎该方法明确地接受“CvArr”数据类型,我不确定如何获得它...以下是错误:

opencv_contrib.FaceRecognizer中的train(opencv_core.CvArr, opencv_core.CvArr)方法不适用于参数(ArrayList,ArrayList)

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

这是我第一篇文章,我不确定是否要在一篇文章中询问所有三个问题还是分三篇文章问,所以如果您需要关于项目的任何其他信息,请随时提出。


1
你需要使用OpenCV的原因是什么?Android SDK内置了android.media.FaceDetector类。请查看此示例项目:http://www.developer.com/ws/android/programming/face-detection-with-android-apis.html - yurilo
4
实际上,我正在寻找一个人脸识别库,而不仅仅是一个简单的人脸检测库,因此我不能仅使用Android SDK的人脸检测功能... - Cypher
1
@Cypher,你是如何在安卓中集成人脸识别的? - Vikas Pandey
2个回答

16

更新

以下文章由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,在撰写本文时为第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);
}

啊哈,所以您需要传递一个 MatVector 来处理图像!您可以使用一个 CvArr (一行或一列) 传递标签。在opencv_core, line 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);
}

您可能希望自己编写一个方法,将Java ArrayList 转换为MatVector(如果 javacv 中没有这样的函数)。
现在来回答您的第二个问题。 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(),因为指针是由创建具体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模型。就是这样!

谢谢Philipp...但是我在尝试将数据加载到CvArr时遇到了一些问题...我已经有标签作为数据字符串...但是我似乎找不到一种将这些标签插入到CvArr作为数组的方法...让我们称之为缺乏OpenCV知识... 有人能帮我解决这个问题吗?! - Cypher
请使用一个QA页面,询问我两次有点过头了。我只能在OpenCV QA页面上写我说的话。我可以确定如何在OpenCV C++中执行此操作,但您所问的是JavaCV。因此,最好在JavaCV邮件列表上提出这些问题,该列表位于以下位置:https://groups.google.com/forum/?fromgroups#!forum/javacv。 - bytefish
我想知道这个是否曾经起作用?我之前尝试过实现它,但据我所知,JavaCV没有正确实现facerec库。 - Daniel Jonker
请查看更新后的答案,其中包含完整的源代码示例。 - bytefish
干得好..太棒了 :-) ..需要一个信息,对于每个算法运行,它总是发送一些预测标签,即使脸根本不匹配,那么我们如何管理这部分呢? - Abhishek Choudhary
@bytefish:你能提供一些完全理解安卓平台的教程吗? - Pratik Vyas

5

我使用OpenCV制作了一款面部识别的安卓应用。为了获得更好的识别效果,您需要更好的检测功能,您可以从以下链接查看:https://github.com/yaylas/AndroidFaceRecognizer

希望这对您有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接