Android 上的人脸识别 [英] Face Recognition on Android

查看:78
本文介绍了Android 上的人脸识别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  1. 似乎 OpenCV 2.4.2 中不包含 Contrib 模块.有没有办法在项目中使用它?

  2. 我尝试使用 JavaCV 来使用 Contrib 模块的FaceRecognizer"类.有两个可用的类,称为FaceRecognizer"&人脸识别器Ptr".有人知道这两者有什么区别吗?

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

<块引用>

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

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

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

解决方案

更新

以下文章由 Petter Christian Bjelland 撰写,因此所有功劳均归他所有.我把它贴在这里,因为他的博客目前似乎处于维护模式,但我认为值得分享.

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

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

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

为了这篇文章的简单性,该类还要求训练图像具有文件名格式:.例如:

<前>1-jon_doe_1.png1-jon_doe_2.png2-jane_doe_1.png2-jane_doe_2.png

...等等.

代码:

import com.googlecode.javacv.cpp.opencv_core;导入静态 com.googlecode.javacv.cpp.opencv_highgui.*;导入静态 com.googlecode.javacv.cpp.opencv_core.*;导入静态 com.googlecode.javacv.cpp.opencv_imgproc.*;导入静态 com.googlecode.javacv.cpp.opencv_contrib.*;导入 java.io.File;导入 java.io.FilenameFilter;公共类 OpenCVFaceRecognizer {公共静态无效主(字符串 [] args){字符串 trainingDir = args[0];IplImage testImage = cvLoadImage(args[1]);文件根=新文件(trainingDir);文件名过滤器 pngFilter = 新文件名过滤器(){公共布尔接受(文件目录,字符串名称){返回 name.toLowerCase().endsWith(".png");}};File[] imageFiles = root.listFiles(pngFilter);MatVector 图像 = new MatVector(imageFiles.length);int[] 标签 = new int[imageFiles.length];整数计数器 = 0;整数标签;IplImage img;IplImage grayImg;对于(文件图像:imageFiles){//获取图片和标签:img = cvLoadImage(image.getAbsolutePath());label = Integer.parseInt(image.getName().split("\-")[0]);//将图像转换为灰度:grayImg = IplImage.create(img.width(), img.height(), IPL_DEPTH_8U, 1);cvCvtColor(img, grayImg, CV_BGR2GRAY);//将其添加到图像列表中:图像.放置(计数器,grayImg);//在标签列表中:标签[计数器] = 标签;//增加下一个图像的计数器:计数器++;}FaceRecognizer faceRecognizer = createFisherFaceRecognizer();//FaceRecognizer faceRecognizer = createEigenFaceRecognizer();//FaceRecognizer faceRecognizer = createLBHFaceRecognizer()faceRecognizer.train(图像,标签);//加载测试图像:IplImage greyTestImage = IplImage.create(testImage.width(), testImage.height(), IPL_DEPTH_8U, 1);cvCvtColor(testImage, greyTestImage, CV_BGR2GRAY);//并得到一个预测:int PredictedLabel = faceRecognizer.predict(greyTestImage);System.out.println("预测标签:" + 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><包装>罐</包装><name>opencvfacerecognizer</name><url>http://pcbje.com</url><依赖项><依赖><groupId>com.googlecode.javacv</groupId><artifactId>javacv</artifactId><version>0.3</version></依赖><!-- 对于 Linux x64 环境--><依赖><groupId>com.googlecode.javacv</groupId><artifactId>javacv</artifactId><classifier>linux-x86_64</classifier><version>0.3</version></依赖><!-- 对于 OSX 环境--><依赖><groupId>com.googlecode.javacv</groupId><artifactId>javacv</artifactId><classifier>macosx-x86_64</classifier><version>0.3</version></依赖></依赖项><存储库><存储库><id>javacv</id><name>JavaCV</name><url>http://maven2.javacv.googlecode.com/git/</url></repository></repositories></项目>

原帖

引用我对 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 {静态{ Loader.load();}公共人脸识别器(){}公共人脸识别器(指针 p){ 超级(p);}public/*abstract*/native void train(@ByRef MatVector src, @Adapter("ArrayAdapter") CvArr 标签);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(@Adapter("FileStorageAdapter") CvFileStorage fs);public native void load(@Adapter("FileStorageAdapter") CvFileStorage fs);}

啊哈,所以你需要为图像传递一个MatVector!您可以在 CvArr(一行或一列)中传递标签.MatVector 定义在 opencv_core,第 4629 行(在撰写本文时),它看起来像这样:

public static class MatVector extends Pointer {静态{加载();}公共 MatVector() {分配();}公共 MatVector(长 n){ 分配(n);}公共 MatVector(Pointer p) { super(p);}私有原生无效分配();私有原生无效分配(@Cast(size_t")long n);公共本机长尺寸();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;//分配一些内存:MatVector 图像 = new MatVector(numberOfImages);//然后填充 MatVector,你可能想做一些有用的事情:for(int idx = 0; idx 

您可能希望自己编写一个方法来将 Java ArrayList 转换为 MatVector(如果这样的函数在 javacv 中尚不存在).

现在回答你的第二个问题.FaceRecognizer 等价于 cv::FaceRecognizer.原生 OpenCV C++ 类返回一个 cv::Ptr,它是一个指向 cv::FaceRecognizer 的(智能)指针.这也必须包装.看到这里的模式了吗?

FaceRecognizerPtr 的界面现在是这样的:

@Name("cv::Ptr")公共静态类 FaceRecognizerPtr 扩展指针 {静态{加载();}公共 FaceRecognizerPtr() {分配();}公共 FaceRecognizerPtr(Pointer p) { super(p);}私有原生无效分配();公共本机 FaceRecognizer get();公共本机 FaceRecognizerPtr put(FaceRecognizer value);}

所以你可以从这个类中获取一个 FaceRecognizer 或者放入一个 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 createLBHFaceRecognizer(int radius/*=1*/,int 邻居/*=8*/, int grid_x/*=8*/, int grid_y/*=8*/, 双阈值/*=DBL_MAX*/);

因此,一旦获得 FaceRecognizerPtr,您就可以执行以下操作:

//保存你的训练数据和标签:MatVector 图像;CvArr 标签;//对图像和标签做些事情...可能会填充它们?//...//然后获取一个指向 FaceRecognizer (FaceRecognizerPtr) 的指针.//Java没有默认参数,需要自己添加一些,//如果将 0 作为 num_components 传递给 EigenFaceRecognizer,则//组件由数据决定,阈值使用尽可能大//如果你不想要一个值.我不知道 Java 中的常量:FaceRecognizerPtr 模型 = createEigenFaceRecognizer(0, 10000);//然后训练它.看看我如何调用 get() 来获取 FaceRecognizerPtr 中的 FaceRecognizer:模型.get().train(图像,标签);

这会为您学习一个特征脸模型.就是这样!

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 :

  1. It seems the Contrib Module isn't included in OpenCV 2.4.2 . is there anyway to use it in the project ?

  2. 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 ?

  3. 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 them ArrayList<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 :

The method train(opencv_core.CvArr, opencv_core.CvArr) in the type opencv_contrib.FaceRecognizer is not applicable for the arguments (ArrayList, ArrayList)

Does anyone know how to change my ArrayList to 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.

解决方案

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.

Doing face recognition with JavaCV (from 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.

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:

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:

<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.

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.

First have a look at how cv::FaceRecognizer has been wrapped (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);
}

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:

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).

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?

The interface of FaceRecognizerPtr 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 into. You should only be concerned about the get(), as the Pointer is filled by the method creating the concrete FaceRecognizer algorithm:

@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:

// 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!

这篇关于Android 上的人脸识别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆