Обновлять
Следующая статья была написана Петтером Кристианом Бьелландом, поэтому вся заслуга принадлежит ему. Я публикую его здесь, потому что его блог, кажется, в данный момент находится в режиме обслуживания, но я думаю, что им стоит поделиться.
Выполнение распознавания лиц с помощью 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