Распознавание лиц на Android

Я пытаюсь разработать приложение для распознавания лиц на Android, и, поскольку я не хочу использовать NDK в проекте (просто нет времени на переключение), я придерживаюсь разработки всего приложения с помощью Java и поэтому У меня есть некоторые проблемы:

  1. Кажется, модуль Contrib не включен в OpenCV 2.4.2. можно ли его как-то использовать в проекте?

  2. Я попытался использовать JavaCV для использования класса «FaceRecognizer» модуля Contrib. доступны два класса: «FaceRecognizer» и «FaceRecognizerPtr». кто-нибудь знает, в чем разница между этими двумя?

  3. Упомянутые выше классы имеют метод под названием «Train», который (в C++) получает два вектора типов «Mat & Integer» ( model->train(images,labels) & train(Vector<mat> theImages, Vector<int> theLabels) . Я попытался передать им ArrayList<mat> & ArrayList<integer> и векторы в Java, но кажется, что метод явно принимает тип данных "CvArr", который я не уверен, как получить... Вот ошибка:

Метод train(opencv_core.CvArr, opencv_core.CvArr) в типе opencv_contrib.FaceRecognizer неприменим для аргументов (ArrayList, ArrayList)

Кто-нибудь знает, как изменить мой ArrayList на CvArr?!

Это мой первый пост, и я не был уверен, задавать ли все три вопроса в одном посте или в трех, так что извините за неудобства... Если вам нужна какая-либо другая информация о проекте, не стесняйтесь спрашивать.


person Cypher    schedule 28.07.2012    source источник
comment
Любая причина, по которой вам нужно использовать OpenCV? Android SDK имеет встроенный класс android.media.FaceDetector. Проверьте этот пример проекта: developer.com/ws /андроид/программирование/   -  person yurilo    schedule 28.07.2012
comment
На самом деле я ищу библиотеку распознавания лиц, а не только простую библиотеку распознавания лиц, и из-за этого я не могу использовать только Android SDK Face Detection...   -  person Cypher    schedule 28.07.2012
comment
@Cypher, как вы интегрировали распознавание лиц в Android?   -  person Vikas Pandey    schedule 28.09.2017


Ответы (2)


Обновлять

Следующая статья была написана Петтером Кристианом Бьелландом, поэтому вся заслуга принадлежит ему. Я публикую его здесь, потому что его блог, кажется, в данный момент находится в режиме обслуживания, но я думаю, что им стоит поделиться.

Выполнение распознавания лиц с помощью 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 (одна строка или один столбец). 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);
}

Опять же, просто взглянув на код, я думаю, что его можно использовать так:

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(), так как 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*/);

Итак, получив 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. Вот и все!

person bytefish    schedule 28.07.2012
comment
Спасибо, Филипп... Но у меня возникли небольшие проблемы с загрузкой данных в CvArr... У меня уже есть метки в виде строк данных... но я не могу найти способ вставить эти метки в виде массив в CvArr... назовем это недостатком знаний OpenCV... кто-нибудь может мне помочь с этой проблемой?! - person Cypher; 07.08.2012
comment
Либо используйте одну страницу QA, либо другую, спрашивать меня на обеих - это немного излишне. Я могу написать только то, что сказал на странице OpenCV QA. Я могу точно сказать, как это сделать в OpenCV С++, но вы спрашиваете, это JavaCV. Поэтому такие вопросы лучше задавать в списке рассылки JavaCV, который доступен по адресу: https://groups.google.com/forum/?fromgroups#!forum/javacv - person bytefish; 07.08.2012
comment
Я хочу знать, работало ли это когда-нибудь? Я пытался реализовать это раньше, но из того, что я мог сказать, JavaCV не реализовал библиотеку facerec должным образом. - person Daniel Jonker; 20.12.2012
comment
См. обновленный ответ для полного примера исходного кода. - person bytefish; 21.12.2012
comment
отлично сделано .. отлично :-) .. нужна информация, для каждого запуска алгоритма он всегда возвращает некоторую предсказанную метку, даже если лицо совсем не совпадает, так как же мы справляемся с этой частью? - person Abhishek Choudhary; 06.10.2013
comment
@bytefish: можете ли вы предоставить учебник для полного понимания платформы Android? - person Pratik Vyas; 06.04.2018

Я сделал приложение для Android для распознавания лиц, используя opencv. Для хорошего распознавания вам нужно лучшее обнаружение, вы можете проверить его по адресу: https://github.com/yaylas/AndroidFaceRecognizer Надеюсь, это поможет.

person user1488918    schedule 17.06.2014